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