comparison Source/TypeBuilder/Builders/AbstractClassBuilder.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.Collections.Generic;
3 using System.Linq;
4 using System.Reflection;
5 using System.Diagnostics.CodeAnalysis;
6
7 namespace BLToolkit.TypeBuilder.Builders
8 {
9 using Properties;
10 using Reflection;
11 using Reflection.Emit;
12
13 internal class AbstractClassBuilder : ITypeBuilder
14 {
15 public AbstractClassBuilder(Type sourceType)
16 {
17 _sourceType = sourceType;
18 }
19
20 readonly Type _sourceType;
21
22 public string AssemblyNameSuffix
23 {
24 get { return TypeBuilderConsts.AssemblyNameSuffix; }
25 }
26
27 public Type Build(AssemblyBuilderHelper assemblyBuilder)
28 {
29 _context = new BuildContext(_sourceType);
30 _builders = new AbstractTypeBuilderList();
31
32 _context.TypeBuilders = GetBuilderList(_context.Type);
33 _context.AssemblyBuilder = assemblyBuilder;
34
35 _builders.AddRange(_context.TypeBuilders);
36 _builders.Add(_defaultTypeBuilder);
37
38 return Build();
39 }
40
41 internal static string GetTypeFullName(Type type)
42 {
43 var name = type.FullName;
44
45 if (type.IsGenericType)
46 {
47 name = name.Split('`')[0];
48
49 foreach (var t in type.GetGenericArguments())
50 name += "_" + GetTypeFullName(t).Replace('+', '_').Replace('.', '_');
51 }
52
53 return name;
54 }
55
56 internal static string GetTypeShortName(Type type)
57 {
58 var name = type.Name;
59
60 if (type.IsGenericType)
61 {
62 name = name.Split('`')[0];
63
64 foreach (var t in type.GetGenericArguments())
65 name += "_" + GetTypeFullName(t).Replace('+', '_').Replace('.', '_');
66 }
67
68 return name;
69 }
70
71 public string GetTypeName()
72 {
73 var typeFullName = _sourceType.FullName;
74 var typeShortName = _sourceType.Name;
75
76 if (_sourceType.IsGenericType)
77 {
78 typeFullName = GetTypeFullName (_sourceType);
79 typeShortName = GetTypeShortName(_sourceType);
80 }
81
82 typeFullName = typeFullName. Replace('+', '.');
83 typeShortName = typeShortName.Replace('+', '.');
84
85 typeFullName = typeFullName.Substring(0, typeFullName.Length - typeShortName.Length);
86 typeFullName = typeFullName + "BLToolkitExtension." + typeShortName;
87
88 return typeFullName;
89 }
90
91 public Type GetBuildingType()
92 {
93 return _sourceType;
94 }
95
96 private static AbstractTypeBuilderList GetBuilderList(TypeHelper type)
97 {
98 var attrs = type.GetAttributes(typeof(AbstractTypeBuilderAttribute));
99 var builders = new AbstractTypeBuilderList(attrs.Length);
100
101 foreach (AbstractTypeBuilderAttribute attr in attrs)
102 {
103 var builder = attr.TypeBuilder;
104
105 if (builder != null)
106 {
107 builder.TargetElement = type;
108 builders.Add(builder);
109 }
110 }
111
112 return builders;
113 }
114
115 private static readonly DefaultTypeBuilder _defaultTypeBuilder = new DefaultTypeBuilder();
116
117 private BuildContext _context;
118 private AbstractTypeBuilderList _builders;
119
120 private Type Build()
121 {
122 DefineNonAbstractType();
123
124 SetID(_builders);
125
126 _context.BuildElement = BuildElement.Type;
127
128 Build(BuildStep.Before, _builders);
129 Build(BuildStep.Build, _builders);
130
131 var ids = _builders.ToDictionary(builder => builder, builder => builder.ID);
132
133 DefineAbstractProperties();
134 DefineAbstractMethods();
135 OverrideVirtualProperties();
136 OverrideVirtualMethods();
137 DefineInterfaces();
138
139 foreach (var builder in ids.Keys)
140 builder.ID = ids[builder];
141
142 _context.BuildElement = BuildElement.Type;
143
144 Build(BuildStep.After, _builders);
145
146 var initMethod = _context.Type.GetMethod("InitInstance", typeof(InitContext));
147
148 // Finalize constructors.
149 //
150 if (_context.TypeBuilder.IsDefaultConstructorDefined)
151 {
152 if (initMethod != null)
153 _context.TypeBuilder.DefaultConstructor.Emitter
154 .ldarg_0
155 .ldnull
156 .callvirt (initMethod)
157 ;
158
159 _context.TypeBuilder.DefaultConstructor.Emitter.ret();
160 }
161
162 if (_context.TypeBuilder.IsInitConstructorDefined)
163 {
164 if (initMethod != null)
165 _context.TypeBuilder.InitConstructor.Emitter
166 .ldarg_0
167 .ldarg_1
168 .callvirt (initMethod)
169 ;
170
171 _context.TypeBuilder.InitConstructor.Emitter.ret();
172 }
173
174 if (_context.TypeBuilder.IsTypeInitializerDefined)
175 _context.TypeBuilder.TypeInitializer.Emitter.ret();
176
177 // Create the type.
178 //
179 return _context.TypeBuilder.Create();
180 }
181
182 private static int _idCounter;
183
184 private static void SetID(AbstractTypeBuilderList builders)
185 {
186 foreach (var builder in builders)
187 builder.ID = ++_idCounter;
188 }
189
190 private static void CheckCompatibility(BuildContext context, AbstractTypeBuilderList builders)
191 {
192 for (var i = 0; i < builders.Count; i++)
193 {
194 var cur = builders[i];
195
196 if (cur == null)
197 continue;
198
199 for (var j = 0; j < builders.Count; j++)
200 {
201 var test = builders[j];
202
203 if (i == j || test == null)
204 continue;
205
206 if (cur.IsCompatible(context, test) == false)
207 builders[j] = null;
208 }
209 }
210
211 for (var i = 0; i < builders.Count; i++)
212 if (builders[i] == null)
213 builders.RemoveAt(i--);
214 }
215
216 private void DefineNonAbstractType()
217 {
218 var interfaces = new List<Type>();
219
220 if (_context.Type.IsInterface)
221 {
222 interfaces.Add(_context.Type);
223 _context.InterfaceMap.Add(_context.Type, null);
224 }
225
226 foreach (var tb in _builders)
227 {
228 var types = tb.GetInterfaces();
229
230 if (types != null)
231 {
232 foreach (var t in types)
233 {
234 if (t == null)
235 continue;
236
237 if (!t.IsInterface)
238 {
239 throw new InvalidOperationException(
240 string.Format(Resources.AbstractClassBuilder_TypeIsNotAnInterface, t.FullName));
241 }
242
243 if (interfaces.Contains(t) == false)
244 {
245 interfaces.Add(t);
246 _context.InterfaceMap.Add(t, tb);
247 }
248 }
249 }
250 }
251
252 var typeName = GetTypeName();
253
254 _context.TypeBuilder = _context.AssemblyBuilder.DefineType(
255 typeName,
256 TypeAttributes.Public | TypeAttributes.BeforeFieldInit | (TypeFactory.SealTypes? TypeAttributes.Sealed: 0),
257 _context.Type.IsInterface? typeof(object): (Type)_context.Type,
258 interfaces.ToArray());
259
260 if (_context.Type.IsSerializable)
261 _context.TypeBuilder.SetCustomAttribute(typeof(SerializableAttribute));
262 }
263
264 class BuilderComparer : IComparer<IAbstractTypeBuilder>
265 {
266 public BuilderComparer(BuildContext context)
267 {
268 _context = context;
269 }
270
271 readonly BuildContext _context;
272
273 [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
274 public int Compare(IAbstractTypeBuilder x, IAbstractTypeBuilder y)
275 {
276 return y.GetPriority(_context) - x.GetPriority(_context);
277 }
278 }
279
280 private void Build(BuildStep step, AbstractTypeBuilderList builders)
281 {
282 _context.Step = step;
283 _context.TypeBuilders.Clear();
284
285 foreach (var builder in builders)
286 if (builder.IsApplied(_context, builders))
287 _context.TypeBuilders.Add(builder);
288
289 if (_context.IsVirtualMethod || _context.IsVirtualProperty)
290 _context.TypeBuilders.Add(_defaultTypeBuilder);
291
292 if (_context.TypeBuilders.Count == 0)
293 return;
294
295 CheckCompatibility(_context, _context.TypeBuilders);
296
297 _context.TypeBuilders.Sort(new BuilderComparer(_context));
298
299 for (var i = 0; i < _context.TypeBuilders.Count; i++)
300 {
301 var builder = _context.TypeBuilders[i];
302
303 builder.Build(_context);
304 }
305 }
306
307 private void BeginEmitMethod(MethodInfo method)
308 {
309 _context.CurrentMethod = method;
310 _context.MethodBuilder = _context.TypeBuilder.DefineMethod(method);
311
312 var emit = _context.MethodBuilder.Emitter;
313
314 // Label right before return and catch block.
315 //
316 _context.ReturnLabel = emit.DefineLabel();
317
318 // Create return value.
319 //
320 if (method.ReturnType != typeof(void))
321 {
322 _context.ReturnValue = _context.MethodBuilder.Emitter.DeclareLocal(method.ReturnType);
323 emit.Init(_context.ReturnValue);
324 }
325
326 // Initialize out parameters.
327 //
328 var parameters = method.GetParameters();
329
330 if (parameters != null)
331 emit.InitOutParameters(parameters);
332 }
333
334 private void EmitMethod(
335 AbstractTypeBuilderList builders, MethodInfo methdoInfo, BuildElement buildElement)
336 {
337 SetID(builders);
338
339 _context.BuildElement = buildElement;
340
341 var isCatchBlockRequired = false;
342 var isFinallyBlockRequired = false;
343
344 foreach (var builder in builders)
345 {
346 isCatchBlockRequired = isCatchBlockRequired || IsApplied(builder, builders, BuildStep.Catch);
347 isFinallyBlockRequired = isFinallyBlockRequired || IsApplied(builder, builders, BuildStep.Finally);
348 }
349
350 BeginEmitMethod(methdoInfo);
351
352 Build(BuildStep.Begin, builders);
353
354 var emit = _context.MethodBuilder.Emitter;
355 var returnLabel = _context.ReturnLabel;
356
357 // Begin catch block.
358 //
359
360 if (isCatchBlockRequired || isFinallyBlockRequired)
361 {
362 _context.ReturnLabel = emit.DefineLabel();
363 emit.BeginExceptionBlock();
364 }
365
366 Build(BuildStep.Before, builders);
367 Build(BuildStep.Build, builders);
368 Build(BuildStep.After, builders);
369
370 if (isCatchBlockRequired || isFinallyBlockRequired)
371 {
372 emit.MarkLabel(_context.ReturnLabel);
373 _context.ReturnLabel = returnLabel;
374 }
375
376 // End catch block.
377 //
378 if (isCatchBlockRequired)
379 {
380 emit
381 .BeginCatchBlock(typeof(Exception));
382
383 _context.ReturnLabel = emit.DefineLabel();
384 _context.Exception = emit.DeclareLocal(typeof(Exception));
385
386 emit
387 .stloc (_context.Exception);
388
389 Build(BuildStep.Catch, builders);
390
391 emit
392 .rethrow
393 .end();
394
395 emit.MarkLabel(_context.ReturnLabel);
396 _context.ReturnLabel = returnLabel;
397 _context.Exception = null;
398 }
399
400 if (isFinallyBlockRequired)
401 {
402 emit.BeginFinallyBlock();
403 _context.ReturnLabel = emit.DefineLabel();
404
405 Build(BuildStep.Finally, builders);
406
407 emit.MarkLabel(_context.ReturnLabel);
408 _context.ReturnLabel = returnLabel;
409 }
410
411 if (isCatchBlockRequired || isFinallyBlockRequired)
412 emit.EndExceptionBlock();
413
414 Build(BuildStep.End, builders);
415
416 EndEmitMethod();
417 }
418
419 private void EndEmitMethod()
420 {
421 var emit = _context.MethodBuilder.Emitter;
422
423 // Prepare return.
424 //
425 emit.MarkLabel(_context.ReturnLabel);
426
427 if (_context.ReturnValue != null)
428 emit.ldloc(_context.ReturnValue);
429
430 emit.ret();
431
432 // Cleanup the context.
433 //
434 _context.ReturnValue = null;
435 _context.CurrentMethod = null;
436 _context.MethodBuilder = null;
437 }
438
439 private static AbstractTypeBuilderList GetBuilders(object[] attributes, object target)
440 {
441 var builders = new AbstractTypeBuilderList(attributes.Length);
442
443 foreach (AbstractTypeBuilderAttribute attr in attributes)
444 {
445 var builder = attr.TypeBuilder;
446
447 builder.TargetElement = target;
448 builders.Add(builder);
449 }
450
451 return builders;
452 }
453
454 private static AbstractTypeBuilderList GetBuilders(MemberInfo memberInfo)
455 {
456 return GetBuilders(
457 memberInfo.GetCustomAttributes(typeof(AbstractTypeBuilderAttribute), true), memberInfo);
458 }
459
460 private static AbstractTypeBuilderList GetBuilders(ParameterInfo parameterInfo)
461 {
462 return GetBuilders(
463 parameterInfo.GetCustomAttributes(typeof(AbstractTypeBuilderAttribute), true), parameterInfo);
464 }
465
466 private static AbstractTypeBuilderList GetBuilders(ParameterInfo[] parameters)
467 {
468 var builders = new AbstractTypeBuilderList();
469
470 foreach (var pi in parameters)
471 {
472 var attributes = pi.GetCustomAttributes(typeof(AbstractTypeBuilderAttribute), true);
473
474 foreach (AbstractTypeBuilderAttribute attr in attributes)
475 {
476 var builder = attr.TypeBuilder;
477
478 builder.TargetElement = pi;
479 builders.Add(builder);
480 }
481 }
482
483 return builders;
484 }
485
486 private static AbstractTypeBuilderList Combine(params AbstractTypeBuilderList[] builders)
487 {
488 var list = new AbstractTypeBuilderList();
489
490 foreach (var l in builders)
491 list.AddRange(l);
492
493 return list;
494 }
495
496 private bool IsApplied(IAbstractTypeBuilder builder, AbstractTypeBuilderList builders, BuildStep buildStep)
497 {
498 _context.Step = buildStep;
499 return builder.IsApplied(_context, builders);
500 }
501
502 private bool IsApplied(BuildElement element, AbstractTypeBuilderList builders)
503 {
504 _context.BuildElement = element;
505
506 foreach (var builder in builders)
507 {
508 if (IsApplied(builder, builders, BuildStep.Before)) return true;
509 if (IsApplied(builder, builders, BuildStep.Build)) return true;
510 if (IsApplied(builder, builders, BuildStep.After)) return true;
511 if (IsApplied(builder, builders, BuildStep.Catch)) return true;
512 if (IsApplied(builder, builders, BuildStep.Finally)) return true;
513 }
514
515 return false;
516 }
517
518 private static void GetAbstractProperties(Type type, List<PropertyInfo> props)
519 {
520 if (props.FirstOrDefault(mi => mi.DeclaringType == type) == null)
521 {
522 props.AddRange(
523 type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
524
525 if (type.IsInterface)
526 foreach (var t in type.GetInterfaces())
527 GetAbstractProperties(t, props);
528 }
529 }
530
531 private void DefineAbstractProperties()
532 {
533 var props = new List<PropertyInfo>();
534
535 GetAbstractProperties(_context.Type, props);
536
537 foreach (var pi in props)
538 {
539 _context.CurrentProperty = pi;
540
541 var propertyBuilders = GetBuilders(pi);
542
543 var getter = pi.GetGetMethod(true);
544 var setter = pi.GetSetMethod(true);
545
546 if (getter != null && getter.IsAbstract ||
547 setter != null && setter.IsAbstract)
548 {
549 DefineAbstractGetter(pi, getter, propertyBuilders);
550 DefineAbstractSetter(pi, setter, propertyBuilders);
551 }
552 }
553
554 _context.CurrentProperty = null;
555 }
556
557 private void DefineAbstractGetter(
558 PropertyInfo propertyInfo, MethodInfo getter, AbstractTypeBuilderList propertyBuilders)
559 {
560 // Getter can be not defined. We will generate it anyway.
561 //
562 if (getter == null)
563 #if SILVERLIGHT
564 return;
565 #else
566 getter = new FakeGetter(propertyInfo);
567 #endif
568
569 var builders = Combine(
570 GetBuilders(getter.GetParameters()),
571 GetBuilders(getter.ReturnParameter),
572 GetBuilders(getter),
573 propertyBuilders,
574 _builders);
575
576 EmitMethod(builders, getter, BuildElement.AbstractGetter);
577 }
578
579 private void DefineAbstractSetter(
580 PropertyInfo propertyInfo,
581 MethodInfo setter,
582 AbstractTypeBuilderList propertyBuilders)
583 {
584 // Setter can be not defined. We will generate it anyway.
585 //
586 if (setter == null)
587 #if SILVERLIGHT
588 return;
589 #else
590 setter = new FakeSetter(propertyInfo);
591 #endif
592
593 var builders = Combine(
594 GetBuilders(setter.GetParameters()),
595 GetBuilders(setter.ReturnParameter),
596 GetBuilders(setter),
597 propertyBuilders,
598 _builders);
599
600 EmitMethod(builders, setter, BuildElement.AbstractSetter);
601 }
602
603 private static void GetAbstractMethods(Type type, List<MethodInfo> methods)
604 {
605 if (!methods.Exists(mi => mi.DeclaringType == type))
606 {
607 methods.AddRange(
608 type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
609
610 if (type.IsInterface)
611 foreach (var t in type.GetInterfaces())
612 GetAbstractMethods(t, methods);
613 }
614 }
615
616 private void DefineAbstractMethods()
617 {
618 var methods = new List<MethodInfo>();
619
620 GetAbstractMethods(_context.Type, methods);
621
622 foreach (var method in methods)
623 {
624 if (method.IsAbstract && (method.Attributes & MethodAttributes.SpecialName) == 0)
625 {
626 var builders = Combine(
627 GetBuilders(method.GetParameters()),
628 GetBuilders(method.ReturnParameter),
629 GetBuilders(method),
630 _builders);
631
632 EmitMethod(builders, method, BuildElement.AbstractMethod);
633 }
634 }
635 }
636
637 private void OverrideVirtualProperties()
638 {
639 var props = _context.Type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
640
641 foreach (var pi in props)
642 {
643 _context.CurrentProperty = pi;
644
645 var propertyBuilders = GetBuilders(pi);
646
647 var getter = pi.GetGetMethod(true);
648
649 if (getter != null && getter.IsVirtual && !getter.IsAbstract && !getter.IsFinal)
650 OverrideGetter(getter, propertyBuilders);
651
652 var setter = pi.GetSetMethod(true);
653
654 if (setter != null && setter.IsVirtual && !setter.IsAbstract && !setter.IsFinal)
655 OverrideSetter(setter, propertyBuilders);
656 }
657
658 _context.CurrentProperty = null;
659 }
660
661 private void OverrideGetter(MethodInfo getter, AbstractTypeBuilderList propertyBuilders)
662 {
663 var builders = Combine(
664 GetBuilders(getter.GetParameters()),
665 GetBuilders(getter.ReturnParameter),
666 GetBuilders(getter),
667 propertyBuilders,
668 _builders);
669
670 if (IsApplied(BuildElement.VirtualGetter, builders))
671 EmitMethod(builders, getter, BuildElement.VirtualGetter);
672 }
673
674 private void OverrideSetter(MethodInfo setter, AbstractTypeBuilderList propertyBuilders)
675 {
676 var builders = Combine(
677 GetBuilders(setter.GetParameters()),
678 GetBuilders(setter.ReturnParameter),
679 GetBuilders(setter),
680 propertyBuilders,
681 _builders);
682
683 if (IsApplied(BuildElement.VirtualSetter, builders))
684 EmitMethod(builders, setter, BuildElement.VirtualSetter);
685 }
686
687 private void OverrideVirtualMethods()
688 {
689 var methods = _context.Type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
690
691 foreach (var method in methods)
692 {
693 if (method.IsVirtual &&
694 method.IsAbstract == false &&
695 method.IsFinal == false &&
696 (method.Attributes & MethodAttributes.SpecialName) == 0 &&
697 method.DeclaringType != typeof(object))
698 {
699 var builders = Combine(
700 GetBuilders(method.GetParameters()),
701 GetBuilders(method.ReturnParameter),
702 GetBuilders(method),
703 _builders);
704
705 if (IsApplied(BuildElement.VirtualMethod, builders))
706 EmitMethod(builders, method, BuildElement.VirtualMethod);
707 }
708 }
709 }
710
711 private void DefineInterfaces()
712 {
713 foreach (var de in _context.InterfaceMap)
714 {
715 _context.CurrentInterface = de.Key;
716
717 var interfaceMethods = _context.CurrentInterface.GetMethods();
718
719 foreach (var m in interfaceMethods)
720 {
721 if (_context.TypeBuilder.OverriddenMethods.ContainsKey(m))
722 continue;
723
724 BeginEmitMethod(m);
725
726 // Call builder to build the method.
727 //
728 var builder = de.Value;
729
730 if (builder != null)
731 {
732 builder.ID = ++_idCounter;
733
734 _context.BuildElement = BuildElement.InterfaceMethod;
735 _context.Step = BuildStep.Build;
736 builder.Build(_context);
737 }
738
739 EndEmitMethod();
740 }
741
742 _context.CurrentInterface = null;
743 }
744 }
745 }
746 }