comparison Source/TypeBuilder/Builders/DuckTypeBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Reflection;
3 using System.Reflection.Emit;
4
5 using BLToolkit.Patterns;
6 using BLToolkit.Properties;
7 using BLToolkit.Reflection.Emit;
8
9 namespace BLToolkit.TypeBuilder.Builders
10 {
11 class DuckTypeBuilder : ITypeBuilder
12 {
13 public DuckTypeBuilder(MustImplementAttribute defaultAttribute, Type interfaceType, Type[] objectTypes)
14 {
15 _interfaceType = interfaceType;
16 _objectTypes = objectTypes;
17 _defaultAttribute = defaultAttribute;
18 }
19
20 private readonly Type _interfaceType;
21 private readonly Type[] _objectTypes;
22 private TypeBuilderHelper _typeBuilder;
23 private readonly MustImplementAttribute _defaultAttribute;
24
25 #region ITypeBuilder Members
26
27 public string AssemblyNameSuffix
28 {
29 get { return "DuckType." + AbstractClassBuilder.GetTypeFullName(_interfaceType).Replace('+', '.'); }
30 }
31
32 public Type Build(AssemblyBuilderHelper assemblyBuilder)
33 {
34 _typeBuilder = assemblyBuilder.DefineType(GetTypeName(), typeof(DuckType), _interfaceType);
35
36 if (!BuildMembers(_interfaceType))
37 return null;
38
39 foreach (Type t in _interfaceType.GetInterfaces())
40 if (!BuildMembers(t))
41 return null;
42
43 return _typeBuilder.Create();
44 }
45
46 public string GetTypeName()
47 {
48 string name = String.Empty;
49
50 foreach (Type t in _objectTypes)
51 {
52 if (t != null)
53 name += AbstractClassBuilder.GetTypeFullName(t).Replace('+', '.');
54 name += "$";
55 }
56
57 return name + AssemblyNameSuffix;
58 }
59
60 public Type GetBuildingType()
61 {
62 return _interfaceType;
63 }
64
65 #endregion
66
67 private static bool CompareMethodSignature(MethodInfo m1, MethodInfo m2)
68 {
69 if (m1 == m2)
70 return true;
71
72 if (m1.Name != m2.Name)
73 return false;
74
75 if (m1.ReturnType != m2.ReturnType)
76 return false;
77
78 ParameterInfo[] ps1 = m1.GetParameters();
79 ParameterInfo[] ps2 = m2.GetParameters();
80
81 if (ps1.Length != ps2.Length)
82 return false;
83
84 for (int i = 0; i < ps1.Length; i++)
85 {
86 ParameterInfo p1 = ps1[i];
87 ParameterInfo p2 = ps2[i];
88
89 if (p1.ParameterType != p2.ParameterType ||
90 #if !SILVERLIGHT
91 p1.IsIn != p2.IsIn ||
92 #endif
93 p1.IsOut != p2.IsOut)
94 return false;
95 }
96
97 return true;
98 }
99
100 private bool BuildMembers(Type interfaceType)
101 {
102 FieldInfo objectsField = typeof(DuckType).GetField("_objects", BindingFlags.NonPublic | BindingFlags.Instance);
103 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance
104 | (DuckTyping.AllowStaticMembers? BindingFlags.Static | BindingFlags.FlattenHierarchy: 0);
105
106 foreach (MethodInfo interfaceMethod in interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
107 {
108 MethodInfo targetMethod = null;
109 int typeIndex = 0;
110
111 for (; typeIndex < _objectTypes.Length; typeIndex++)
112 {
113 if (_objectTypes[typeIndex] == null)
114 continue;
115
116 foreach (MethodInfo mi in _objectTypes[typeIndex].GetMethods(flags))
117 {
118 if (CompareMethodSignature(interfaceMethod, mi))
119 {
120 targetMethod = mi;
121 break;
122 }
123 }
124
125 if (targetMethod == null)
126 {
127 foreach (Type intf in _objectTypes[typeIndex].GetInterfaces())
128 {
129 if (intf.IsPublic || intf.IsNestedPublic)
130 {
131 foreach (MethodInfo mi in intf.GetMethods(flags))
132 {
133 if (CompareMethodSignature(interfaceMethod, mi))
134 {
135 targetMethod = mi;
136 break;
137 }
138 }
139
140 if (targetMethod != null)
141 break;
142 }
143 }
144 }
145
146 if (targetMethod != null)
147 break;
148 }
149
150 ParameterInfo[] ips = interfaceMethod.GetParameters();
151 MethodBuilderHelper builder = _typeBuilder.DefineMethod(interfaceMethod);
152 EmitHelper emit = builder.Emitter;
153
154 if (targetMethod != null)
155 {
156 Type targetType = targetMethod.DeclaringType;
157
158 if (!targetMethod.IsStatic)
159 {
160 emit
161 .ldarg_0
162 .ldfld (objectsField)
163 .ldc_i4 (typeIndex)
164 .ldelem_ref
165 .end()
166 ;
167
168 if (targetType.IsValueType)
169 {
170 // For value types we have to use stack.
171 //
172 LocalBuilder obj = emit.DeclareLocal(targetType);
173
174 emit
175 .unbox_any (targetType)
176 .stloc (obj)
177 .ldloca (obj)
178 ;
179 }
180 else
181 emit
182 .castclass (targetType)
183 ;
184 }
185
186 foreach (ParameterInfo p in ips)
187 emit.ldarg(p);
188
189 if (targetMethod.IsStatic || targetMethod.IsFinal || targetMethod.DeclaringType.IsSealed)
190 emit
191 .call (targetMethod)
192 .ret();
193 else
194 emit
195 .callvirt (targetMethod)
196 .ret();
197 }
198 else
199 {
200 // Method or property was not found.
201 // Insert an empty stub or stub that throws the NotImplementedException.
202 //
203 MustImplementAttribute attr = (MustImplementAttribute)
204 Attribute.GetCustomAttribute(interfaceMethod, typeof (MustImplementAttribute));
205
206 if (attr == null)
207 {
208 attr = (MustImplementAttribute)Attribute.GetCustomAttribute(
209 interfaceMethod.DeclaringType, typeof (MustImplementAttribute));
210 if (attr == null)
211 attr = _defaultAttribute;
212 }
213
214 // When the member is marked as 'Required' throw a build-time exception.
215 //
216 if (attr.Implement)
217 {
218 if (attr.ThrowException)
219 {
220 throw new TypeBuilderException(string.Format(
221 Resources.TypeBuilder_PublicMethodMustBeImplemented,
222 _objectTypes.Length > 0 && _objectTypes[0] != null ? _objectTypes[0].FullName : "???",
223 interfaceMethod));
224 }
225 else
226 {
227 // Implement == true, but ThrowException == false.
228 // In this case the null pointer will be returned.
229 // This mimics the 'as' operator behaviour.
230 //
231 return false;
232 }
233 }
234
235 if (attr.ThrowException)
236 {
237 string message = attr.ExceptionMessage;
238
239 if (message == null)
240 {
241 message = string.Format(Resources.TypeBuilder_PublicMethodNotImplemented,
242 _objectTypes.Length > 0 && _objectTypes[0] != null ? _objectTypes[0].FullName : "???",
243 interfaceMethod);
244 }
245
246 emit
247 .ldstr (message)
248 .newobj (typeof(InvalidOperationException), typeof(string))
249 .@throw
250 .end();
251 }
252 else
253 {
254 // Emit a 'do nothing' stub.
255 //
256 LocalBuilder returnValue = null;
257
258 if (interfaceMethod.ReturnType != typeof(void))
259 {
260 returnValue = emit.DeclareLocal(interfaceMethod.ReturnType);
261 emit.Init(returnValue);
262 }
263
264 // Initialize out parameters.
265 //
266 ParameterInfo[] parameters = ips;
267
268 if (parameters != null)
269 emit.InitOutParameters(parameters);
270
271 if (returnValue != null)
272 emit.ldloc(returnValue);
273
274 emit.ret();
275 }
276 }
277 }
278
279 return true;
280 }
281 }
282 }