Mercurial > pub > bltoolkit
comparison Source/TypeBuilder/Builders/DefaultTypeBuilder.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; | |
3 using System.Reflection; | |
4 using System.Reflection.Emit; | |
5 using System.Collections.Generic; | |
6 | |
7 namespace BLToolkit.TypeBuilder.Builders | |
8 { | |
9 using Properties; | |
10 using Reflection; | |
11 using Reflection.Emit; | |
12 | |
13 public class DefaultTypeBuilder : AbstractTypeBuilderBase | |
14 { | |
15 #region Interface Overrides | |
16 | |
17 public override bool IsCompatible(BuildContext context, IAbstractTypeBuilder typeBuilder) | |
18 { | |
19 return IsRelative(typeBuilder) == false; | |
20 } | |
21 | |
22 public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders) | |
23 { | |
24 if (context == null) throw new ArgumentNullException("context"); | |
25 | |
26 if (context.IsAbstractProperty && context.IsBeforeOrBuildStep) | |
27 { | |
28 return context.CurrentProperty.GetIndexParameters().Length <= 1; | |
29 } | |
30 | |
31 return context.BuildElement == BuildElement.Type && context.IsAfterStep; | |
32 } | |
33 | |
34 #endregion | |
35 | |
36 #region Get/Set Implementation | |
37 | |
38 protected override void BuildAbstractGetter() | |
39 { | |
40 var field = GetField(); | |
41 var index = Context.CurrentProperty.GetIndexParameters(); | |
42 | |
43 switch (index.Length) | |
44 { | |
45 case 0: | |
46 Context.MethodBuilder.Emitter | |
47 .ldarg_0 | |
48 .ldfld (field) | |
49 .stloc (Context.ReturnValue) | |
50 ; | |
51 break; | |
52 | |
53 case 1: | |
54 Context.MethodBuilder.Emitter | |
55 .ldarg_0 | |
56 .ldfld (field) | |
57 .ldarg_1 | |
58 .boxIfValueType (index[0].ParameterType) | |
59 .callvirt (typeof(Dictionary<object,object>), "get_Item", typeof(object)) | |
60 .castType (Context.CurrentProperty.PropertyType) | |
61 .stloc (Context.ReturnValue) | |
62 ; | |
63 break; | |
64 } | |
65 } | |
66 | |
67 protected override void BuildAbstractSetter() | |
68 { | |
69 var field = GetField(); | |
70 var index = Context.CurrentProperty.GetIndexParameters(); | |
71 | |
72 switch (index.Length) | |
73 { | |
74 case 0: | |
75 Context.MethodBuilder.Emitter | |
76 .ldarg_0 | |
77 .ldarg_1 | |
78 .stfld (field) | |
79 ; | |
80 //Context.MethodBuilder.Emitter.AddMaxStackSize(6); | |
81 break; | |
82 | |
83 case 1: | |
84 Context.MethodBuilder.Emitter | |
85 .ldarg_0 | |
86 .ldfld (field) | |
87 .ldarg_1 | |
88 .boxIfValueType (index[0].ParameterType) | |
89 .ldarg_2 | |
90 .boxIfValueType (Context.CurrentProperty.PropertyType) | |
91 .callvirt (typeof(Dictionary<object,object>), "set_Item", typeof(object), typeof(object)) | |
92 ; | |
93 break; | |
94 } | |
95 } | |
96 | |
97 #endregion | |
98 | |
99 #region Call Base Method | |
100 | |
101 protected override void BuildVirtualGetter() | |
102 { | |
103 CallBaseMethod(); | |
104 } | |
105 | |
106 protected override void BuildVirtualSetter() | |
107 { | |
108 CallBaseMethod(); | |
109 } | |
110 | |
111 protected override void BuildVirtualMethod() | |
112 { | |
113 CallBaseMethod(); | |
114 } | |
115 | |
116 private void CallBaseMethod() | |
117 { | |
118 var emit = Context.MethodBuilder.Emitter; | |
119 var method = Context.MethodBuilder.OverriddenMethod; | |
120 var ps = method.GetParameters(); | |
121 | |
122 emit.ldarg_0.end(); | |
123 | |
124 for (var i = 0; i < ps.Length; i++) | |
125 emit.ldarg(i + 1); | |
126 | |
127 emit.call(method); | |
128 | |
129 if (Context.ReturnValue != null) | |
130 emit.stloc(Context.ReturnValue); | |
131 } | |
132 | |
133 #endregion | |
134 | |
135 #region Properties | |
136 | |
137 private static TypeHelper _initContextType; | |
138 protected static TypeHelper InitContextType | |
139 { | |
140 get { return _initContextType ?? (_initContextType = new TypeHelper(typeof(InitContext))); } | |
141 } | |
142 | |
143 #endregion | |
144 | |
145 #region Field Initialization | |
146 | |
147 #region Overrides | |
148 | |
149 protected override void BeforeBuildAbstractGetter() | |
150 { | |
151 CallLazyInstanceInsurer(GetField()); | |
152 } | |
153 | |
154 protected override void BeforeBuildAbstractSetter() | |
155 { | |
156 var field = GetField(); | |
157 | |
158 if (field.FieldType != Context.CurrentProperty.PropertyType) | |
159 CallLazyInstanceInsurer(field); | |
160 } | |
161 | |
162 #endregion | |
163 | |
164 #region Common | |
165 | |
166 protected FieldBuilder GetField() | |
167 { | |
168 var propertyInfo = Context.CurrentProperty; | |
169 var fieldName = GetFieldName(); | |
170 var fieldType = GetFieldType(); | |
171 var field = Context.GetField(fieldName); | |
172 | |
173 if (field == null) | |
174 { | |
175 field = Context.CreatePrivateField(propertyInfo, fieldName, fieldType); | |
176 | |
177 if (TypeAccessor.IsInstanceBuildable(fieldType)) | |
178 { | |
179 var noInstance = propertyInfo.GetCustomAttributes(typeof(NoInstanceAttribute), true).Length > 0; | |
180 | |
181 if (IsObjectHolder && noInstance) | |
182 { | |
183 BuildHolderInstance(Context.TypeBuilder.DefaultConstructor.Emitter); | |
184 BuildHolderInstance(Context.TypeBuilder.InitConstructor.Emitter); | |
185 } | |
186 else if (!noInstance) | |
187 { | |
188 if (fieldType.IsClass && IsLazyInstance(fieldType)) | |
189 { | |
190 BuildLazyInstanceEnsurer(); | |
191 } | |
192 else | |
193 { | |
194 BuildDefaultInstance(); | |
195 BuildInitContextInstance(); | |
196 } | |
197 } | |
198 } | |
199 } | |
200 | |
201 return field; | |
202 } | |
203 | |
204 #endregion | |
205 | |
206 #region Build | |
207 | |
208 private void BuildHolderInstance(EmitHelper emit) | |
209 { | |
210 var fieldName = GetFieldName(); | |
211 var field = Context.GetField(fieldName); | |
212 var fieldType = new TypeHelper(field.FieldType); | |
213 var objectType = new TypeHelper(GetObjectType()); | |
214 | |
215 var ci = fieldType.GetPublicDefaultConstructor(); | |
216 | |
217 if (ci != null) | |
218 { | |
219 emit | |
220 .ldarg_0 | |
221 .newobj (ci) | |
222 .stfld (field) | |
223 ; | |
224 } | |
225 else | |
226 { | |
227 if (!CheckObjectHolderCtor(fieldType, objectType)) | |
228 return; | |
229 | |
230 emit | |
231 .ldarg_0 | |
232 .ldnull | |
233 .newobj (fieldType, objectType) | |
234 .stfld (field) | |
235 ; | |
236 } | |
237 } | |
238 | |
239 private void CreateDefaultInstance( | |
240 FieldBuilder field, TypeHelper fieldType, TypeHelper objectType, EmitHelper emit) | |
241 { | |
242 if (!CheckObjectHolderCtor(fieldType, objectType)) | |
243 return; | |
244 | |
245 if (objectType.Type == typeof(string)) | |
246 { | |
247 emit | |
248 .ldarg_0 | |
249 .LoadInitValue (objectType) | |
250 ; | |
251 } | |
252 else if (objectType.IsArray) | |
253 { | |
254 var initializer = GetArrayInitializer(objectType); | |
255 | |
256 emit | |
257 .ldarg_0 | |
258 .ldsfld (initializer) | |
259 ; | |
260 } | |
261 else | |
262 { | |
263 var ci = objectType.GetPublicDefaultConstructor(); | |
264 | |
265 if (ci == null) | |
266 { | |
267 if (objectType.Type.IsValueType) | |
268 return; | |
269 | |
270 var message = string.Format( | |
271 Resources.TypeBuilder_PropertyTypeHasNoPublicDefaultCtor, | |
272 Context.CurrentProperty.Name, | |
273 Context.Type.FullName, | |
274 objectType.FullName); | |
275 | |
276 emit | |
277 .ldstr (message) | |
278 .newobj (typeof(TypeBuilderException), typeof(string)) | |
279 .@throw | |
280 .end() | |
281 ; | |
282 | |
283 return; | |
284 } | |
285 | |
286 emit | |
287 .ldarg_0 | |
288 .newobj (ci) | |
289 ; | |
290 } | |
291 | |
292 if (IsObjectHolder) | |
293 { | |
294 emit | |
295 .newobj (fieldType, objectType) | |
296 ; | |
297 } | |
298 | |
299 emit | |
300 .stfld (field) | |
301 ; | |
302 } | |
303 | |
304 private void CreateParametrizedInstance( | |
305 FieldBuilder field, TypeHelper fieldType, TypeHelper objectType, EmitHelper emit, object[] parameters) | |
306 { | |
307 if (!CheckObjectHolderCtor(fieldType, objectType)) | |
308 return; | |
309 | |
310 Stack<ConstructorInfo> genericNestedConstructors; | |
311 if (parameters.Length == 1) | |
312 { | |
313 var o = parameters[0]; | |
314 | |
315 if (o == null) | |
316 { | |
317 if (objectType.IsValueType == false) | |
318 emit | |
319 .ldarg_0 | |
320 .ldnull | |
321 .end() | |
322 ; | |
323 | |
324 if (IsObjectHolder) | |
325 { | |
326 emit | |
327 .newobj (fieldType, objectType) | |
328 ; | |
329 } | |
330 else | |
331 { | |
332 if (objectType.Type.IsGenericType) | |
333 { | |
334 Type nullableType = null; | |
335 genericNestedConstructors = GetGenericNestedConstructors( | |
336 objectType, | |
337 typeHelper => | |
338 typeHelper.IsValueType == false || | |
339 (typeHelper.Type.IsGenericType && typeHelper.Type.GetGenericTypeDefinition() == typeof (Nullable<>)), | |
340 typeHelper => { nullableType = typeHelper.Type; }, | |
341 () => nullableType != null); | |
342 | |
343 if (nullableType == null) | |
344 throw new Exception("Cannot find nullable type in generic types chain"); | |
345 | |
346 if (nullableType.IsValueType == false) | |
347 { | |
348 emit | |
349 .ldarg_0 | |
350 .ldnull | |
351 .end() | |
352 ; | |
353 } | |
354 else | |
355 { | |
356 var nullable = emit.DeclareLocal(nullableType); | |
357 | |
358 emit | |
359 .ldloca(nullable) | |
360 .initobj(nullableType) | |
361 .ldarg_0 | |
362 .ldloc(nullable) | |
363 ; | |
364 | |
365 if (genericNestedConstructors != null) | |
366 { | |
367 while (genericNestedConstructors.Count != 0) | |
368 { | |
369 emit | |
370 .newobj(genericNestedConstructors.Pop()) | |
371 ; | |
372 } | |
373 } | |
374 } | |
375 } | |
376 } | |
377 | |
378 emit | |
379 .stfld (field) | |
380 ; | |
381 | |
382 return; | |
383 } | |
384 | |
385 if (objectType.Type == o.GetType()) | |
386 { | |
387 if (objectType.Type == typeof(string)) | |
388 { | |
389 emit | |
390 .ldarg_0 | |
391 .ldstr ((string)o) | |
392 .stfld (field) | |
393 ; | |
394 | |
395 return; | |
396 } | |
397 | |
398 if (objectType.IsValueType) | |
399 { | |
400 emit.ldarg_0.end(); | |
401 | |
402 if (emit.LoadWellKnownValue(o) == false) | |
403 { | |
404 emit | |
405 .ldsfld (GetParameterField()) | |
406 .ldc_i4_0 | |
407 .ldelem_ref | |
408 .end() | |
409 ; | |
410 } | |
411 | |
412 emit.stfld(field); | |
413 | |
414 return; | |
415 } | |
416 } | |
417 } | |
418 | |
419 var types = new Type[parameters.Length]; | |
420 | |
421 for (var i = 0; i < parameters.Length; i++) | |
422 { | |
423 if (parameters[i] != null) | |
424 { | |
425 var t = parameters[i].GetType(); | |
426 | |
427 types[i] = (t.IsEnum) ? Enum.GetUnderlyingType(t) : t; | |
428 } | |
429 else | |
430 types[i] = typeof(object); | |
431 } | |
432 | |
433 // Do some heuristics for Nullable<DateTime> and EditableValue<Decimal> | |
434 // | |
435 ConstructorInfo objectCtor = null; | |
436 genericNestedConstructors = GetGenericNestedConstructors( | |
437 objectType, | |
438 typeHelper => true, | |
439 typeHelper => { objectCtor = typeHelper.GetPublicConstructor(types); }, | |
440 () => objectCtor != null); | |
441 | |
442 if (objectCtor == null) | |
443 { | |
444 if (objectType.IsValueType) | |
445 return; | |
446 | |
447 throw new TypeBuilderException( | |
448 string.Format(types.Length == 0? | |
449 Resources.TypeBuilder_PropertyTypeHasNoPublicDefaultCtor: | |
450 Resources.TypeBuilder_PropertyTypeHasNoPublicCtor, | |
451 Context.CurrentProperty.Name, | |
452 Context.Type.FullName, | |
453 objectType.FullName)); | |
454 } | |
455 | |
456 var pi = objectCtor.GetParameters(); | |
457 | |
458 emit.ldarg_0.end(); | |
459 | |
460 for (var i = 0; i < parameters.Length; i++) | |
461 { | |
462 var o = parameters[i]; | |
463 var oType = o.GetType(); | |
464 | |
465 if (emit.LoadWellKnownValue(o)) | |
466 { | |
467 if (oType.IsValueType) | |
468 { | |
469 if (!pi[i].ParameterType.IsValueType) | |
470 emit.box(oType); | |
471 else if (Type.GetTypeCode(oType) != Type.GetTypeCode(pi[i].ParameterType)) | |
472 emit.conv(pi[i].ParameterType); | |
473 } | |
474 } | |
475 else | |
476 { | |
477 emit | |
478 .ldsfld (GetParameterField()) | |
479 .ldc_i4 (i) | |
480 .ldelem_ref | |
481 .CastFromObject (types[i]) | |
482 ; | |
483 | |
484 if (oType.IsValueType && !pi[i].ParameterType.IsValueType) | |
485 emit.box(oType); | |
486 } | |
487 } | |
488 | |
489 emit | |
490 .newobj (objectCtor) | |
491 ; | |
492 | |
493 if (genericNestedConstructors != null) | |
494 { | |
495 while (genericNestedConstructors.Count != 0) | |
496 { | |
497 emit | |
498 .newobj(genericNestedConstructors.Pop()) | |
499 ; | |
500 } | |
501 } | |
502 | |
503 if (IsObjectHolder) | |
504 { | |
505 emit | |
506 .newobj (fieldType, objectType) | |
507 ; | |
508 } | |
509 | |
510 emit | |
511 .stfld (field) | |
512 ; | |
513 } | |
514 | |
515 private Stack<ConstructorInfo> GetGenericNestedConstructors(TypeHelper objectType, | |
516 Predicate<TypeHelper> isActionable, | |
517 Action<TypeHelper> action, | |
518 Func<bool> isBreakCondition) | |
519 { | |
520 Stack<ConstructorInfo> genericNestedConstructors = null; | |
521 | |
522 if (isActionable(objectType)) | |
523 action(objectType); | |
524 | |
525 while (objectType.Type.IsGenericType && !isBreakCondition()) | |
526 { | |
527 var typeArgs = objectType.Type.GetGenericArguments(); | |
528 | |
529 if (typeArgs.Length == 1) | |
530 { | |
531 var genericCtor = objectType.GetPublicConstructor(typeArgs[0]); | |
532 | |
533 if (genericCtor != null) | |
534 { | |
535 if (genericNestedConstructors == null) | |
536 genericNestedConstructors = new Stack<ConstructorInfo>(); | |
537 | |
538 genericNestedConstructors.Push(genericCtor); | |
539 objectType = typeArgs[0]; | |
540 | |
541 if (isActionable(objectType)) | |
542 action(objectType); | |
543 } | |
544 } | |
545 else | |
546 { | |
547 throw new TypeBuilderException( | |
548 string.Format(Resources.TypeBuilder_GenericShouldBeSingleTyped, | |
549 Context.CurrentProperty.Name, | |
550 Context.Type.FullName, | |
551 objectType.FullName)); | |
552 } | |
553 } | |
554 | |
555 return genericNestedConstructors; | |
556 } | |
557 | |
558 #endregion | |
559 | |
560 #region Build InitContext Instance | |
561 | |
562 private void BuildInitContextInstance() | |
563 { | |
564 var fieldName = GetFieldName(); | |
565 var field = Context.GetField(fieldName); | |
566 var fieldType = new TypeHelper(field.FieldType); | |
567 var objectType = new TypeHelper(GetObjectType()); | |
568 | |
569 var emit = Context.TypeBuilder.InitConstructor.Emitter; | |
570 | |
571 var parameters = TypeHelper.GetPropertyParameters(Context.CurrentProperty); | |
572 var ci = objectType.GetPublicConstructor(typeof(InitContext)); | |
573 | |
574 if (ci != null && ci.GetParameters()[0].ParameterType != typeof(InitContext)) | |
575 ci = null; | |
576 | |
577 if (ci != null || objectType.IsAbstract) | |
578 CreateAbstractInitContextInstance(field, fieldType, objectType, emit, parameters); | |
579 else if (parameters == null) | |
580 CreateDefaultInstance(field, fieldType, objectType, emit); | |
581 else | |
582 CreateParametrizedInstance(field, fieldType, objectType, emit, parameters); | |
583 } | |
584 | |
585 private void CreateAbstractInitContextInstance( | |
586 FieldBuilder field, TypeHelper fieldType, TypeHelper objectType, EmitHelper emit, object[] parameters) | |
587 { | |
588 if (!CheckObjectHolderCtor(fieldType, objectType)) | |
589 return; | |
590 | |
591 var memberParams = InitContextType.GetProperty("MemberParameters").GetSetMethod(); | |
592 var parentField = Context.GetItem<LocalBuilder>("$BLToolkit.InitContext.Parent"); | |
593 | |
594 if (parentField == null) | |
595 { | |
596 Context.Items.Add("$BLToolkit.InitContext.Parent", parentField = emit.DeclareLocal(typeof(object))); | |
597 | |
598 var label = emit.DefineLabel(); | |
599 | |
600 emit | |
601 .ldarg_1 | |
602 .brtrue_s (label) | |
603 | |
604 .newobj (InitContextType.GetPublicDefaultConstructor()) | |
605 .starg (1) | |
606 | |
607 .ldarg_1 | |
608 .ldc_i4_1 | |
609 .callvirt (InitContextType.GetProperty("IsInternal").GetSetMethod()) | |
610 | |
611 .MarkLabel (label) | |
612 | |
613 .ldarg_1 | |
614 .callvirt (InitContextType.GetProperty("Parent").GetGetMethod()) | |
615 .stloc (parentField) | |
616 ; | |
617 } | |
618 | |
619 emit | |
620 .ldarg_1 | |
621 .ldarg_0 | |
622 .callvirt (InitContextType.GetProperty("Parent").GetSetMethod()) | |
623 ; | |
624 | |
625 var isDirty = Context.GetItem<bool?>("$BLToolkit.InitContext.DirtyParameters"); | |
626 | |
627 if (parameters != null) | |
628 { | |
629 emit | |
630 .ldarg_1 | |
631 .ldsfld (GetParameterField()) | |
632 .callvirt (memberParams) | |
633 ; | |
634 } | |
635 else if (isDirty != null && (bool)isDirty) | |
636 { | |
637 emit | |
638 .ldarg_1 | |
639 .ldnull | |
640 .callvirt (memberParams) | |
641 ; | |
642 } | |
643 | |
644 if (Context.Items.ContainsKey("$BLToolkit.InitContext.DirtyParameters")) | |
645 Context.Items["$BLToolkit.InitContext.DirtyParameters"] = (bool?)(parameters != null); | |
646 else | |
647 Context.Items.Add("$BLToolkit.InitContext.DirtyParameters", (bool?)(parameters != null)); | |
648 | |
649 if (objectType.IsAbstract) | |
650 { | |
651 emit | |
652 .ldarg_0 | |
653 .ldsfld (GetTypeAccessorField()) | |
654 .ldarg_1 | |
655 .callvirtNoGenerics (typeof(TypeAccessor), "CreateInstanceEx", _initContextType) | |
656 .isinst (objectType) | |
657 ; | |
658 } | |
659 else | |
660 { | |
661 emit | |
662 .ldarg_0 | |
663 .ldarg_1 | |
664 .newobj (objectType.GetPublicConstructor(typeof(InitContext))) | |
665 ; | |
666 } | |
667 | |
668 if (IsObjectHolder) | |
669 { | |
670 emit | |
671 .newobj (fieldType, objectType) | |
672 ; | |
673 } | |
674 | |
675 emit | |
676 .stfld (field) | |
677 ; | |
678 } | |
679 | |
680 #endregion | |
681 | |
682 #region Build Default Instance | |
683 | |
684 private void BuildDefaultInstance() | |
685 { | |
686 var fieldName = GetFieldName(); | |
687 var field = Context.GetField(fieldName); | |
688 var fieldType = new TypeHelper(field.FieldType); | |
689 var objectType = new TypeHelper(GetObjectType()); | |
690 | |
691 var emit = Context.TypeBuilder.DefaultConstructor.Emitter; | |
692 var ps = TypeHelper.GetPropertyParameters(Context.CurrentProperty); | |
693 var ci = objectType.GetPublicConstructor(typeof(InitContext)); | |
694 | |
695 if (ci != null && ci.GetParameters()[0].ParameterType != typeof(InitContext)) | |
696 ci = null; | |
697 | |
698 if (ci != null || objectType.IsAbstract) | |
699 CreateInitContextDefaultInstance( | |
700 "$BLToolkit.DefaultInitContext.", field, fieldType, objectType, emit, ps); | |
701 else if (ps == null) | |
702 CreateDefaultInstance(field, fieldType, objectType, emit); | |
703 else | |
704 CreateParametrizedInstance(field, fieldType, objectType, emit, ps); | |
705 } | |
706 | |
707 private bool CheckObjectHolderCtor(TypeHelper fieldType, TypeHelper objectType) | |
708 { | |
709 if (IsObjectHolder) | |
710 { | |
711 var holderCi = fieldType.GetPublicConstructor(objectType); | |
712 | |
713 if (holderCi == null) | |
714 { | |
715 var message = string.Format( | |
716 Resources.TypeBuilder_PropertyTypeHasNoCtorWithParamType, | |
717 Context.CurrentProperty.Name, | |
718 Context.Type.FullName, | |
719 fieldType.FullName, | |
720 objectType.FullName); | |
721 | |
722 Context.TypeBuilder.DefaultConstructor.Emitter | |
723 .ldstr (message) | |
724 .newobj (typeof(TypeBuilderException), typeof(string)) | |
725 .@throw | |
726 .end() | |
727 ; | |
728 | |
729 return false; | |
730 } | |
731 } | |
732 | |
733 return true; | |
734 } | |
735 | |
736 private void CreateInitContextDefaultInstance( | |
737 string initContextName, | |
738 FieldBuilder field, | |
739 TypeHelper fieldType, | |
740 TypeHelper objectType, | |
741 EmitHelper emit, | |
742 object[] parameters) | |
743 { | |
744 if (!CheckObjectHolderCtor(fieldType, objectType)) | |
745 return; | |
746 | |
747 var initField = GetInitContextBuilder(initContextName, emit); | |
748 var memberParams = InitContextType.GetProperty("MemberParameters").GetSetMethod(); | |
749 | |
750 if (parameters != null) | |
751 { | |
752 emit | |
753 .ldloc (initField) | |
754 .ldsfld (GetParameterField()) | |
755 .callvirt (memberParams) | |
756 ; | |
757 } | |
758 else if ((bool)Context.Items["$BLToolkit.Default.DirtyParameters"]) | |
759 { | |
760 emit | |
761 .ldloc (initField) | |
762 .ldnull | |
763 .callvirt (memberParams) | |
764 ; | |
765 } | |
766 | |
767 Context.Items["$BLToolkit.Default.DirtyParameters"] = parameters != null; | |
768 | |
769 if (objectType.IsAbstract) | |
770 { | |
771 emit | |
772 .ldarg_0 | |
773 .ldsfld (GetTypeAccessorField()) | |
774 .ldloc (initField) | |
775 .callvirtNoGenerics (typeof(TypeAccessor), "CreateInstanceEx", _initContextType) | |
776 .isinst (objectType) | |
777 ; | |
778 } | |
779 else | |
780 { | |
781 emit | |
782 .ldarg_0 | |
783 .ldloc (initField) | |
784 .newobj (objectType.GetPublicConstructor(typeof(InitContext))) | |
785 ; | |
786 } | |
787 | |
788 if (IsObjectHolder) | |
789 { | |
790 emit | |
791 .newobj (fieldType, objectType) | |
792 ; | |
793 } | |
794 | |
795 emit | |
796 .stfld (field) | |
797 ; | |
798 } | |
799 | |
800 private LocalBuilder GetInitContextBuilder( | |
801 string initContextName, EmitHelper emit) | |
802 { | |
803 var initField = Context.GetItem<LocalBuilder>(initContextName); | |
804 | |
805 if (initField == null) | |
806 { | |
807 Context.Items.Add(initContextName, initField = emit.DeclareLocal(InitContextType)); | |
808 | |
809 emit | |
810 .newobj (InitContextType.GetPublicDefaultConstructor()) | |
811 | |
812 .dup | |
813 .ldarg_0 | |
814 .callvirt (InitContextType.GetProperty("Parent").GetSetMethod()) | |
815 | |
816 .dup | |
817 .ldc_i4_1 | |
818 .callvirt (InitContextType.GetProperty("IsInternal").GetSetMethod()) | |
819 | |
820 .stloc (initField) | |
821 ; | |
822 | |
823 Context.Items.Add("$BLToolkit.Default.DirtyParameters", false); | |
824 } | |
825 | |
826 return initField; | |
827 } | |
828 | |
829 #endregion | |
830 | |
831 #region Build Lazy Instance | |
832 | |
833 private bool IsLazyInstance(Type type) | |
834 { | |
835 var attrs = Context.CurrentProperty.GetCustomAttributes(typeof(LazyInstanceAttribute), true); | |
836 | |
837 if (attrs.Length > 0) | |
838 return ((LazyInstanceAttribute)attrs[0]).IsLazy; | |
839 | |
840 attrs = Context.Type.GetAttributes(typeof(LazyInstancesAttribute)); | |
841 | |
842 foreach (LazyInstancesAttribute a in attrs) | |
843 if (a.Type == typeof(object) || type == a.Type || type.IsSubclassOf(a.Type)) | |
844 return a.IsLazy; | |
845 | |
846 return false; | |
847 } | |
848 | |
849 private void BuildLazyInstanceEnsurer() | |
850 { | |
851 var fieldName = GetFieldName(); | |
852 var field = Context.GetField(fieldName); | |
853 var fieldType = new TypeHelper(field.FieldType); | |
854 var objectType = new TypeHelper(GetObjectType()); | |
855 var ensurer = Context.TypeBuilder.DefineMethod( | |
856 string.Format("$EnsureInstance{0}", fieldName), | |
857 MethodAttributes.Private | MethodAttributes.HideBySig); | |
858 | |
859 var emit = ensurer.Emitter; | |
860 var end = emit.DefineLabel(); | |
861 | |
862 emit | |
863 .ldarg_0 | |
864 .ldfld (field) | |
865 .brtrue_s (end) | |
866 ; | |
867 | |
868 var parameters = TypeHelper.GetPropertyParameters(Context.CurrentProperty); | |
869 var ci = objectType.GetPublicConstructor(typeof(InitContext)); | |
870 | |
871 if (ci != null || objectType.IsAbstract) | |
872 CreateInitContextLazyInstance(field, fieldType, objectType, emit, parameters); | |
873 else if (parameters == null) | |
874 CreateDefaultInstance(field, fieldType, objectType, emit); | |
875 else | |
876 CreateParametrizedInstance(field, fieldType, objectType, emit, parameters); | |
877 | |
878 emit | |
879 .MarkLabel(end) | |
880 .ret() | |
881 ; | |
882 | |
883 Context.Items.Add("$BLToolkit.FieldInstanceEnsurer." + fieldName, ensurer); | |
884 } | |
885 | |
886 private void CreateInitContextLazyInstance( | |
887 FieldBuilder field, | |
888 TypeHelper fieldType, | |
889 TypeHelper objectType, | |
890 EmitHelper emit, | |
891 object[] parameters) | |
892 { | |
893 if (!CheckObjectHolderCtor(fieldType, objectType)) | |
894 return; | |
895 | |
896 var initField = emit.DeclareLocal(InitContextType); | |
897 | |
898 emit | |
899 .newobj (InitContextType.GetPublicDefaultConstructor()) | |
900 | |
901 .dup | |
902 .ldarg_0 | |
903 .callvirt (InitContextType.GetProperty("Parent").GetSetMethod()) | |
904 | |
905 .dup | |
906 .ldc_i4_1 | |
907 .callvirt (InitContextType.GetProperty("IsInternal").GetSetMethod()) | |
908 | |
909 .dup | |
910 .ldc_i4_1 | |
911 .callvirt (InitContextType.GetProperty("IsLazyInstance").GetSetMethod()) | |
912 | |
913 ; | |
914 | |
915 if (parameters != null) | |
916 { | |
917 emit | |
918 .dup | |
919 .ldsfld (GetParameterField()) | |
920 .callvirt (InitContextType.GetProperty("MemberParameters").GetSetMethod()) | |
921 ; | |
922 } | |
923 | |
924 emit | |
925 .stloc (initField); | |
926 | |
927 if (objectType.IsAbstract) | |
928 { | |
929 emit | |
930 .ldarg_0 | |
931 .ldsfld (GetTypeAccessorField()) | |
932 .ldloc (initField) | |
933 .callvirtNoGenerics (typeof(TypeAccessor), "CreateInstanceEx", _initContextType) | |
934 .isinst (objectType) | |
935 ; | |
936 } | |
937 else | |
938 { | |
939 emit | |
940 .ldarg_0 | |
941 .ldloc (initField) | |
942 .newobj (objectType.GetPublicConstructor(typeof(InitContext))) | |
943 ; | |
944 } | |
945 | |
946 if (IsObjectHolder) | |
947 { | |
948 emit | |
949 .newobj (fieldType, objectType) | |
950 ; | |
951 } | |
952 | |
953 emit | |
954 .stfld (field) | |
955 ; | |
956 } | |
957 | |
958 #endregion | |
959 | |
960 #region Finalize Type | |
961 | |
962 protected override void AfterBuildType() | |
963 { | |
964 var isDirty = Context.GetItem<bool?>("$BLToolkit.InitContext.DirtyParameters"); | |
965 | |
966 if (isDirty != null && isDirty.Value) | |
967 { | |
968 Context.TypeBuilder.InitConstructor.Emitter | |
969 .ldarg_1 | |
970 .ldnull | |
971 .callvirt (InitContextType.GetProperty("MemberParameters").GetSetMethod()) | |
972 ; | |
973 } | |
974 | |
975 var localBuilder = Context.GetItem<LocalBuilder>("$BLToolkit.InitContext.Parent"); | |
976 | |
977 if (localBuilder != null) | |
978 { | |
979 Context.TypeBuilder.InitConstructor.Emitter | |
980 .ldarg_1 | |
981 .ldloc (localBuilder) | |
982 .callvirt (InitContextType.GetProperty("Parent").GetSetMethod()) | |
983 ; | |
984 } | |
985 | |
986 FinalizeDefaultConstructors(); | |
987 FinalizeInitContextConstructors(); | |
988 } | |
989 | |
990 private void FinalizeDefaultConstructors() | |
991 { | |
992 var ci = Context.Type.GetDefaultConstructor(); | |
993 | |
994 if (ci == null || Context.TypeBuilder.IsDefaultConstructorDefined) | |
995 { | |
996 var emit = Context.TypeBuilder.DefaultConstructor.Emitter; | |
997 | |
998 if (ci != null) | |
999 { | |
1000 emit.ldarg_0.call(ci); | |
1001 } | |
1002 else | |
1003 { | |
1004 ci = Context.Type.GetConstructor(typeof(InitContext)); | |
1005 | |
1006 if (ci != null) | |
1007 { | |
1008 var initField = GetInitContextBuilder("$BLToolkit.DefaultInitContext.", emit); | |
1009 | |
1010 emit | |
1011 .ldarg_0 | |
1012 .ldloc (initField) | |
1013 .call (ci); | |
1014 } | |
1015 else | |
1016 { | |
1017 if (Context.Type.GetConstructors().Length > 0) | |
1018 throw new TypeBuilderException(string.Format( | |
1019 Resources.TypeBuilder_NoDefaultCtor, | |
1020 Context.Type.FullName)); | |
1021 } | |
1022 } | |
1023 } | |
1024 } | |
1025 | |
1026 private void FinalizeInitContextConstructors() | |
1027 { | |
1028 var ci = Context.Type.GetConstructor(typeof(InitContext)); | |
1029 | |
1030 if (ci != null || Context.TypeBuilder.IsInitConstructorDefined) | |
1031 { | |
1032 var emit = Context.TypeBuilder.InitConstructor.Emitter; | |
1033 | |
1034 if (ci != null) | |
1035 { | |
1036 emit | |
1037 .ldarg_0 | |
1038 .ldarg_1 | |
1039 .call (ci); | |
1040 } | |
1041 else | |
1042 { | |
1043 ci = Context.Type.GetDefaultConstructor(); | |
1044 | |
1045 if (ci != null) | |
1046 { | |
1047 emit.ldarg_0.call(ci); | |
1048 } | |
1049 else | |
1050 { | |
1051 if (Context.Type.GetConstructors().Length > 0) | |
1052 throw new TypeBuilderException( | |
1053 string.Format(Resources.TypeBuilder_NoDefaultCtor, | |
1054 Context.Type.FullName)); | |
1055 } | |
1056 } | |
1057 } | |
1058 } | |
1059 | |
1060 #endregion | |
1061 | |
1062 #endregion | |
1063 } | |
1064 } |