1 //---------------------------------------------------------------------
2 // <copyright file="LightweightCodeGenerator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Objects
13 using System.Data.Common.Utils;
14 using System.Data.Metadata.Edm;
15 using System.Data.Objects.DataClasses;
16 using System.Diagnostics;
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.Runtime.CompilerServices;
20 using System.Security;
21 using System.Security.Permissions;
24 /// CodeGenerator class: use lightweight code gen to dynamically generate code to get/set properties.
26 internal static class LightweightCodeGenerator
28 /// <summary>For an OSpace ComplexType returns the delegate to construct the clr instance.</summary>
29 internal static Delegate GetConstructorDelegateForType(ClrComplexType clrType)
31 return (clrType.Constructor ?? (clrType.Constructor = CreateConstructor(clrType.ClrType)));
34 /// <summary>For an OSpace EntityType returns the delegate to construct the clr instance.</summary>
35 internal static Delegate GetConstructorDelegateForType(ClrEntityType clrType)
37 return (clrType.Constructor ?? (clrType.Constructor = CreateConstructor(clrType.ClrType)));
40 /// <summary>for an OSpace property, get the property value from a clr instance</summary>
41 internal static object GetValue(EdmProperty property, object target)
43 Func<object, object> getter = GetGetterDelegateForProperty(property);
44 Debug.Assert(null != getter, "null getter");
46 return getter(target);
49 internal static Func<object,object> GetGetterDelegateForProperty(EdmProperty property)
51 return property.ValueGetter ?? (property.ValueGetter = CreatePropertyGetter(property.EntityDeclaringType, property.PropertyGetterHandle));
54 /// <summary>for an OSpace property, set the property value on a clr instance</summary>
55 /// <exception cref="System.Data.ConstraintException">
56 /// If <paramref name="value"/> is null for a non nullable property.
58 /// <exception cref="System.InvalidOperationException">
59 /// Invalid cast of <paramref name="value"/> to property type.
61 /// <exception cref="System.ArgumentOutOfRangeException">
62 /// From generated enties via StructuralObject.SetValidValue.
64 /// <permission cref="System.Security.Permissions.ReflectionPermission">
65 /// If the property setter is not public or declaring class is not public.
67 /// <permission cref="System.Security.NamedPermissionSet">
68 /// Demand for FullTrust if the property setter or declaring class has a <see cref="System.Security.Permissions.SecurityAction.LinkDemand"/>
70 internal static void SetValue(EdmProperty property, object target, object value)
72 Action<object, object> setter = GetSetterDelegateForProperty(property);
73 setter(target, value);
76 /// <summary>For an OSpace property, gets the delegate to set the property value on a clr instance.</summary>
77 internal static Action<object, object> GetSetterDelegateForProperty(EdmProperty property)
79 Action<object, object> setter = property.ValueSetter;
82 setter = CreatePropertySetter(property.EntityDeclaringType, property.PropertySetterHandle,
84 property.ValueSetter = setter;
86 Debug.Assert(null != setter, "null setter");
91 /// Gets the related end instance for the source AssociationEndMember by creating a DynamicMethod to
92 /// call GetRelatedCollection or GetRelatedReference
94 internal static RelatedEnd GetRelatedEnd(RelationshipManager sourceRelationshipManager, AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd)
96 Func<RelationshipManager, RelatedEnd, RelatedEnd> getRelatedEnd = sourceMember.GetRelatedEnd;
97 if (null == getRelatedEnd)
99 getRelatedEnd = CreateGetRelatedEndMethod(sourceMember, targetMember);
100 sourceMember.GetRelatedEnd = getRelatedEnd;
102 Debug.Assert(null != getRelatedEnd, "null getRelatedEnd");
104 return getRelatedEnd(sourceRelationshipManager, existingRelatedEnd);
107 #region Navigation Property
109 internal static Action<object, object> CreateNavigationPropertySetter(Type declaringType, PropertyInfo navigationProperty)
111 MethodInfo mi = navigationProperty.GetSetMethod(true);
112 Type realType = navigationProperty.PropertyType;
116 ThrowPropertyNoSetter();
120 ThrowPropertyIsStatic();
122 if (mi.DeclaringType.IsValueType)
124 ThrowPropertyDeclaringTypeIsValueType();
127 // the setter always skips visibility so that we can call our internal method to handle errors
128 // because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
129 DynamicMethod method = CreateDynamicMethod(mi.Name, typeof(void), new Type[] { typeof(object), typeof(object) });
130 ILGenerator gen = method.GetILGenerator();
131 GenerateNecessaryPermissionDemands(gen, mi);
133 gen.Emit(OpCodes.Ldarg_0);
134 gen.Emit(OpCodes.Castclass, declaringType);
135 gen.Emit(OpCodes.Ldarg_1);
136 gen.Emit(OpCodes.Castclass, navigationProperty.PropertyType);
137 gen.Emit(OpCodes.Callvirt, mi); // .Property =
138 gen.Emit(OpCodes.Ret);
140 return (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>));
145 #region get the delegate
147 /// <summary>Gets a parameterless constructor for the specified type.</summary>
148 /// <param name="type">Type to get constructor for.</param>
149 /// <returns>Parameterless constructor for the specified type.</returns>
150 internal static ConstructorInfo GetConstructorForType(Type type)
152 System.Diagnostics.Debug.Assert(type != null);
153 ConstructorInfo ci = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, System.Type.EmptyTypes, null);
156 ThrowConstructorNoParameterless(type);
163 /// generate a delegate equivalent to
164 /// private object Constructor() { return new XClass(); }
166 internal static Delegate CreateConstructor(Type type)
168 ConstructorInfo ci = GetConstructorForType(type);
170 // because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
171 DynamicMethod method = CreateDynamicMethod(ci.DeclaringType.Name, typeof(object), Type.EmptyTypes);
172 ILGenerator gen = method.GetILGenerator();
173 GenerateNecessaryPermissionDemands(gen, ci);
175 gen.Emit(OpCodes.Newobj, ci);
176 gen.Emit(OpCodes.Ret);
177 return method.CreateDelegate(typeof(Func<object>));
181 /// generate a delegate equivalent to
182 /// private object MemberGetter(object target) { return target.PropertyX; }
183 /// or if the property is Nullable<> generate a delegate equivalent to
184 /// private object MemberGetter(object target) { Nullable<X> y = target.PropertyX; return ((y.HasValue) ? y.Value : null); }
186 private static Func<object, object> CreatePropertyGetter(RuntimeTypeHandle entityDeclaringType, RuntimeMethodHandle rmh)
188 if (default(RuntimeMethodHandle).Equals(rmh))
190 ThrowPropertyNoGetter();
193 Debug.Assert(!default(RuntimeTypeHandle).Equals(entityDeclaringType), "Type handle of entity should always be known.");
194 var mi = (MethodInfo)MethodBase.GetMethodFromHandle(rmh, entityDeclaringType);
198 ThrowPropertyIsStatic();
200 if (mi.DeclaringType.IsValueType)
202 ThrowPropertyDeclaringTypeIsValueType();
205 if (0 != mi.GetParameters().Length)
207 ThrowPropertyIsIndexed();
210 Type realType = mi.ReturnType;
211 if ((null == realType) || (typeof(void) == realType))
213 ThrowPropertyUnsupportedForm();
215 if (realType.IsPointer)
217 ThrowPropertyUnsupportedType();
220 // because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
221 DynamicMethod method = CreateDynamicMethod(mi.Name, typeof(object), new Type[] { typeof(object) });
222 ILGenerator gen = method.GetILGenerator();
223 GenerateNecessaryPermissionDemands(gen, mi);
225 // the 'this' target pointer
226 gen.Emit(OpCodes.Ldarg_0);
227 gen.Emit(OpCodes.Castclass, mi.DeclaringType);
228 gen.Emit(mi.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi);
230 if (realType.IsValueType)
233 if (realType.IsGenericType && (typeof(Nullable<>) == realType.GetGenericTypeDefinition()))
235 elementType = realType.GetGenericArguments()[0];
237 Label lableFalse = gen.DefineLabel();
238 LocalBuilder local = gen.DeclareLocal(realType);
239 gen.Emit(OpCodes.Stloc_S, local);
241 gen.Emit(OpCodes.Ldloca_S, local);
242 gen.Emit(OpCodes.Call, realType.GetMethod("get_HasValue"));
243 gen.Emit(OpCodes.Brfalse_S, lableFalse);
245 gen.Emit(OpCodes.Ldloca_S, local);
246 gen.Emit(OpCodes.Call, realType.GetMethod("get_Value"));
247 gen.Emit(OpCodes.Box, elementType = realType.GetGenericArguments()[0]);
248 gen.Emit(OpCodes.Ret);
250 gen.MarkLabel(lableFalse);
251 gen.Emit(OpCodes.Ldnull);
255 // need to box to return value as object
256 elementType = realType;
257 gen.Emit(OpCodes.Box, elementType);
260 gen.Emit(OpCodes.Ret);
261 return (Func<object, object>)method.CreateDelegate(typeof(Func<object, object>));
265 /// generate a delegate equivalent to
267 /// // if Property is Nullable value type
268 /// private void MemberSetter(object target, object value) {
269 /// if (AllwNull && (null == value)) {
270 /// ((TargetType)target).PropertyName = default(PropertyType?);
273 /// if (value is PropertyType) {
274 /// ((TargetType)target).PropertyName = new (PropertyType?)((PropertyType)value);
277 /// ThrowInvalidValue(value, TargetType.Name, PropertyName);
281 /// // when PropertyType is a value type
282 /// private void MemberSetter(object target, object value) {
283 /// if (value is PropertyType) {
284 /// ((TargetType)target).PropertyName = (PropertyType)value;
287 /// ThrowInvalidValue(value, TargetType.Name, PropertyName);
291 /// // when PropertyType is a reference type
292 /// private void MemberSetter(object target, object value) {
293 /// if ((AllwNull && (null == value)) || (value is PropertyType)) {
294 /// ((TargetType)target).PropertyName = ((PropertyType)value);
297 /// ThrowInvalidValue(value, TargetType.Name, PropertyName);
301 /// <exception cref="System.InvalidOperationException">
302 /// If the method is missing or static or has indexed parameters.
303 /// Or if the delcaring type is a value type.
304 /// Or if the parameter type is a pointer.
305 /// Or if the method or declaring class has a <see cref="System.Security.Permissions.StrongNameIdentityPermissionAttribute"/>.
307 private static Action<object, object> CreatePropertySetter(RuntimeTypeHandle entityDeclaringType, RuntimeMethodHandle rmh, bool allowNull)
311 ValidateSetterProperty(entityDeclaringType, rmh, out mi, out realType);
313 // the setter always skips visibility so that we can call our internal method to handle errors
314 // because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
315 DynamicMethod method = CreateDynamicMethod(mi.Name, typeof(void), new Type[] { typeof(object), typeof(object) });
316 ILGenerator gen = method.GetILGenerator();
317 GenerateNecessaryPermissionDemands(gen, mi);
319 Type elementType = realType;
320 Label labelContinueNull = gen.DefineLabel();
321 Label labelContinueValue = gen.DefineLabel();
322 Label labelInvalidValue = gen.DefineLabel();
323 if (realType.IsValueType)
325 if (realType.IsGenericType && (typeof(Nullable<>) == realType.GetGenericTypeDefinition()))
327 elementType = realType.GetGenericArguments()[0];
330 { // force allowNull false for non-nullable value types
335 // ((TargetType)instance)
336 gen.Emit(OpCodes.Ldarg_0);
337 gen.Emit(OpCodes.Castclass, mi.DeclaringType);
339 // if (value is elementType) {
340 gen.Emit(OpCodes.Ldarg_1);
341 gen.Emit(OpCodes.Isinst, elementType);
344 { // reference type or nullable type
345 gen.Emit(OpCodes.Ldarg_1);
346 if (elementType == realType)
348 gen.Emit(OpCodes.Brfalse_S, labelContinueNull); // if (null ==
352 gen.Emit(OpCodes.Brtrue, labelContinueValue);
353 gen.Emit(OpCodes.Pop); // pop Isinst
355 LocalBuilder local = gen.DeclareLocal(realType);
356 gen.Emit(OpCodes.Ldloca_S, local); // load valuetype&
357 gen.Emit(OpCodes.Initobj, realType); // init &
358 gen.Emit(OpCodes.Ldloc_0); // load valuetype
359 gen.Emit(OpCodes.Br_S, labelContinueNull);
360 gen.MarkLabel(labelContinueValue);
363 gen.Emit(OpCodes.Dup);
364 gen.Emit(OpCodes.Brfalse_S, labelInvalidValue); // (arg1 is Inst)
366 if (elementType.IsValueType)
368 gen.Emit(OpCodes.Unbox_Any, elementType); // ((PropertyType)value)
370 if (elementType != realType)
371 { // new Nullable<PropertyType>
372 gen.Emit(OpCodes.Newobj, realType.GetConstructor(new Type[] { elementType }));
375 gen.MarkLabel(labelContinueNull);
376 gen.Emit(mi.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi); // .Property =
377 gen.Emit(OpCodes.Ret);
379 // ThrowInvalidValue(value, typeof(PropertyType), DeclaringType.Name, PropertyName
380 gen.MarkLabel(labelInvalidValue);
381 gen.Emit(OpCodes.Pop); // pop Ldarg_0
382 gen.Emit(OpCodes.Pop); // pop IsInst'
383 gen.Emit(OpCodes.Ldarg_1); // determine if InvalidCast or NullReference
384 gen.Emit(OpCodes.Ldtoken, elementType);
385 gen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
386 gen.Emit(OpCodes.Ldstr, mi.DeclaringType.Name);
387 gen.Emit(OpCodes.Ldstr, mi.Name.Substring(4)); // substring to strip "set_"
388 Debug.Assert(null != (Action<Object,Type,String,String>)EntityUtil.ThrowSetInvalidValue, "missing method ThrowSetInvalidValue(object,Type,string,string)");
389 gen.Emit(OpCodes.Call, typeof(EntityUtil).GetMethod("ThrowSetInvalidValue", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(object),typeof(Type),typeof(string),typeof(string)},null));
390 gen.Emit(OpCodes.Ret);
391 return (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>));
394 internal static void ValidateSetterProperty(RuntimeTypeHandle entityDeclaringType, RuntimeMethodHandle setterMethodHandle, out MethodInfo setterMethodInfo, out Type realType)
396 if (default(RuntimeMethodHandle).Equals(setterMethodHandle))
398 ThrowPropertyNoSetter();
401 Debug.Assert(!default(RuntimeTypeHandle).Equals(entityDeclaringType), "Type handle of entity should always be known.");
402 setterMethodInfo = (MethodInfo)MethodBase.GetMethodFromHandle(setterMethodHandle, entityDeclaringType);
404 if (setterMethodInfo.IsStatic)
406 ThrowPropertyIsStatic();
408 if (setterMethodInfo.DeclaringType.IsValueType)
410 ThrowPropertyDeclaringTypeIsValueType();
413 ParameterInfo[] parameters = setterMethodInfo.GetParameters();
414 if ((null == parameters) || (1 != parameters.Length))
415 { // if no parameters (i.e. not a set_Property method), will still throw this message
416 ThrowPropertyIsIndexed();
418 realType = setterMethodInfo.ReturnType;
419 if ((null != realType) && (typeof(void) != realType))
421 ThrowPropertyUnsupportedForm();
424 realType = parameters[0].ParameterType;
425 if (realType.IsPointer)
427 ThrowPropertyUnsupportedType();
431 /// <summary>Determines if the specified method requires permission demands to be invoked safely.</summary>
432 /// <param name="mi">Method instance to check.</param>
433 /// <returns>true if the specified method requires permission demands to be invoked safely, false otherwise.</returns>
434 internal static bool RequiresPermissionDemands(MethodBase mi)
436 System.Diagnostics.Debug.Assert(mi != null);
437 return !IsPublic(mi);
440 private static void GenerateNecessaryPermissionDemands(ILGenerator gen, MethodBase mi)
444 gen.Emit(OpCodes.Ldsfld, typeof(LightweightCodeGenerator).GetField("MemberAccessReflectionPermission", BindingFlags.Static | BindingFlags.NonPublic));
445 gen.Emit(OpCodes.Callvirt, typeof(ReflectionPermission).GetMethod("Demand"));
449 internal static bool IsPublic(MethodBase method)
451 return (method.IsPublic && IsPublic(method.DeclaringType));
454 internal static bool IsPublic(Type type)
456 return ((null == type) || (type.IsPublic && IsPublic(type.DeclaringType)));
460 /// Create delegate used to invoke either the GetRelatedReference or GetRelatedCollection generic method on the RelationshipManager.
462 /// <param name="sourceMember">source end of the relationship for the requested navigation</param>
463 /// <param name="targetMember">target end of the relationship for the requested navigation</param>
464 /// <returns>Delegate that can be used to invoke the corresponding method.</returns>
465 [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
466 private static Func<RelationshipManager, RelatedEnd, RelatedEnd> CreateGetRelatedEndMethod(AssociationEndMember sourceMember, AssociationEndMember targetMember)
468 Debug.Assert(sourceMember.DeclaringType == targetMember.DeclaringType, "Source and Target members must be in the same DeclaringType");
470 EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceMember);
471 EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetMember);
472 NavigationPropertyAccessor sourceAccessor = MetadataHelper.GetNavigationPropertyAccessor(targetEntityType, targetMember, sourceMember);
473 NavigationPropertyAccessor targetAccessor = MetadataHelper.GetNavigationPropertyAccessor(sourceEntityType, sourceMember, targetMember);
475 MethodInfo genericCreateRelatedEndMethod = typeof(LightweightCodeGenerator).GetMethod("CreateGetRelatedEndMethod", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(AssociationEndMember), typeof(AssociationEndMember), typeof(NavigationPropertyAccessor), typeof(NavigationPropertyAccessor) }, null);
476 Debug.Assert(genericCreateRelatedEndMethod != null, "Could not find method LightweightCodeGenerator.CreateGetRelatedEndMethod");
478 MethodInfo createRelatedEndMethod = genericCreateRelatedEndMethod.MakeGenericMethod(sourceEntityType.ClrType, targetEntityType.ClrType);
479 object getRelatedEndDelegate = createRelatedEndMethod.Invoke(null, new object[] { sourceMember, targetMember, sourceAccessor, targetAccessor });
481 return (Func<RelationshipManager, RelatedEnd, RelatedEnd>)getRelatedEndDelegate;
484 private static Func<RelationshipManager, RelatedEnd, RelatedEnd> CreateGetRelatedEndMethod<TSource, TTarget>(AssociationEndMember sourceMember, AssociationEndMember targetMember, NavigationPropertyAccessor sourceAccessor, NavigationPropertyAccessor targetAccessor)
485 where TSource : class
486 where TTarget : class
488 Func<RelationshipManager, RelatedEnd, RelatedEnd> getRelatedEnd;
490 // Get the appropriate method, either collection or reference depending on the target multiplicity
491 switch (targetMember.RelationshipMultiplicity)
493 case RelationshipMultiplicity.ZeroOrOne:
494 case RelationshipMultiplicity.One:
496 getRelatedEnd = (manager, relatedEnd) =>
497 manager.GetRelatedReference<TSource, TTarget>(sourceMember.DeclaringType.FullName,
502 sourceMember.RelationshipMultiplicity,
507 case RelationshipMultiplicity.Many:
509 getRelatedEnd = (manager, relatedEnd) =>
510 manager.GetRelatedCollection<TSource, TTarget>(sourceMember.DeclaringType.FullName,
515 sourceMember.RelationshipMultiplicity,
521 throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)targetMember.RelationshipMultiplicity);
524 return getRelatedEnd;
527 private static void ThrowConstructorNoParameterless(Type type)
529 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_ConstructorNoParameterless(type.FullName));
531 private static void ThrowPropertyDeclaringTypeIsValueType()
533 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyDeclaringTypeIsValueType);
535 private static void ThrowPropertyUnsupportedForm()
537 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyUnsupportedForm);
539 private static void ThrowPropertyUnsupportedType()
541 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyUnsupportedType);
543 private static void ThrowPropertyStrongNameIdentity()
545 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyStrongNameIdentity);
547 private static void ThrowPropertyIsIndexed()
549 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyIsIndexed);
551 private static void ThrowPropertyIsStatic()
553 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyIsStatic);
555 private static void ThrowPropertyNoGetter()
557 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyNoGetter);
559 private static void ThrowPropertyNoSetter()
561 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyNoSetter);
566 #region Lightweight code generation
568 internal static readonly ReflectionPermission MemberAccessReflectionPermission = new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);
570 internal static bool HasMemberAccessReflectionPermission()
574 MemberAccessReflectionPermission.Demand();
577 catch (SecurityException)
583 // we could cache more, like 'new Type[] { ... }' and 'typeof(object)'
584 // but pruned as much as possible for the workingset helps, even little things
586 // Assert MemberAccess to skip visibility check & ReflectionEmit so we can generate the method (make calls to EF internals).
587 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2128")]
588 [System.Security.SecuritySafeCritical]
589 [ReflectionPermission(SecurityAction.Assert, MemberAccess = true)]
590 internal static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes)
592 // Create a transparent dynamic method (Module not specified) to ensure we do not satisfy any link demands
593 // in method callees.
594 return new DynamicMethod(name, returnType, parameterTypes, true);