Mercurial > pub > bltoolkit
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 } |