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