// Emit a surrogate property for a CLR property. // The surrogate code we'll generate will look like this: // public <PropertyType> <PropertyName> { // get { // // For normal types. // return _$wrapper.<PropertyName>; // // // For Binary. // Binary value = _$wrapper.<PropertyName>; // if (value == null) { // return null; // } // return value.ToArray(); // } // set { // if (value == null) { // return; // } // // // For normal types. // _$wrapper.<PropertyName> = value; // // // For Binary. // Binary valueToStore; // if (value == null) { // valueToStore = null; // } // else { // valueToStore = new Binary(value); // } // _$wrapper.<PropertyName> = valueToStore; // } // } private static void EmitClrProperty(TypeBuilder typeBuilder, FieldInfo wrapperField, PropertyDescriptor pd, PropertyInfo pi, string name) { Type propertyType = SerializationUtility.GetClientType(pi.PropertyType); PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None, propertyType, null); CustomAttributeBuilder dataMemberAtt = DataContractSurrogateGenerator.GetDataMemberAttributeBuilder( pd.Attributes.OfType <DataMemberAttribute>().FirstOrDefault()); propertyBuilder.SetCustomAttribute(dataMemberAtt); // get { // return ((Entity)$wrapper).Property; // } MethodBuilder getPropertyMethodBuilder = typeBuilder.DefineMethod("get_" + name, MethodAttributes.Public, propertyType, Type.EmptyTypes); ILGenerator generator = getPropertyMethodBuilder.GetILGenerator(); // Push the wrapper object onto the stack. generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, wrapperField); generator.Emit(OpCodes.Castclass, pi.DeclaringType); // Call the property getter. generator.Emit(OpCodes.Callvirt, pi.GetGetMethod()); // Deal with client-server type conversions. if (propertyType != pi.PropertyType) { EmitToClientConversion(generator, pi.PropertyType, propertyType); } generator.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getPropertyMethodBuilder); MethodBuilder setPropertyMethodBuilder = typeBuilder.DefineMethod("set_" + name, MethodAttributes.Public, null, new Type[] { propertyType }); generator = setPropertyMethodBuilder.GetILGenerator(); Label returnLabel = generator.DefineLabel(); // Data members require a getter and setter. However, if the real property is read-only, make sure // our surrogate property setter is a no-op. MethodInfo setMethod = pi.GetSetMethod(); if (setMethod != null && setMethod.IsPublic) { // NOTE: We don't ever set null values, because a property may be required. For // original objects however it's possible that required properties are not // roundtripped, as they may not have RoundtripOriginalAttribute. // set { // if (value != null) { // _$wrapper.Property = value; // } // } // If the value is null, return. if (!propertyType.IsValueType) { generator.Emit(OpCodes.Ldarg_1); EmitBranchIfNull(generator, propertyType, returnLabel); } else if (TypeUtility.IsNullableType(propertyType)) { generator.Emit(OpCodes.Ldarga_S, 1); EmitBranchIfNull(generator, propertyType, returnLabel); } // Push the wrapper object onto the stack. generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, wrapperField); generator.Emit(OpCodes.Castclass, pi.DeclaringType); // Push the value onto the stack. generator.Emit(OpCodes.Ldarg_1); // Deal with client-server type conversions. if (propertyType != pi.PropertyType) { EmitToServerConversion(generator, propertyType, pi.PropertyType); } // Call the property setter. generator.Emit(OpCodes.Callvirt, setMethod); } generator.MarkLabel(returnLabel); generator.Emit(OpCodes.Ret); propertyBuilder.SetSetMethod(setPropertyMethodBuilder); }
// Emit a surrogate property for a virtual property (a property that doesn't exist on the physical CLR type). // The PropertyDescriptor for each virtual property is initialized in the type initializer of a surrogate type. // The surrogate code we'll generate will look like this: // public <PropertyType> <PropertyName> { // get { // // For reference types. // return (<PropertyType>)$<PropertyName>.GetValue(_$wrapper); // // // For value types. // object value = $<PropertyName>.GetValue(_$wrapper); // if (value == null) { // return default(value); // } // return (<PropertyType>)value; // // // For Binary. // Binary value = (Binary)$<PropertyName>.GetValue(_$wrapper); // if (value == null) { // return null; // } // return value.ToArray(); // } // set { // if (value == null) { // return; // } // // // For normal types. // $<PropertyName>.SetValue(_$wrapper, value); // // // For value types. // $<PropertyName>.SetValue(_$wrapper, (object)value); // // // For Binary. // Binary valueToStore; // if (value == null) { // valueToStore = null; // } // else { // valueToStore = new Binary(value); // } // $<PropertyName>.SetValue(_$wrapper, valueToStore); // } // } private static void EmitAttachedProperty(TypeBuilder typeBuilder, FieldInfo wrapperField, Lazy <ILGenerator> typeInitializerFactory, PropertyDescriptor pd, string name) { // private static PropertyDescriptor $property; FieldBuilder propertyDescFieldBuilder = typeBuilder.DefineField("$" + name, typeof(PropertyDescriptor), FieldAttributes.Private | FieldAttributes.Static); EmitPropertyInitializer(propertyDescFieldBuilder, typeInitializerFactory, name); Type propertyType = SerializationUtility.GetClientType(pd.PropertyType); PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None, propertyType, null); CustomAttributeBuilder dataMemberAtt = DataContractSurrogateGenerator.GetDataMemberAttributeBuilder( pd.Attributes[typeof(DataMemberAttribute)] as DataMemberAttribute); propertyBuilder.SetCustomAttribute(dataMemberAtt); // get { // return $property.GetValue(_$wrapper); // } MethodBuilder getPropertyMethodBuilder = typeBuilder.DefineMethod("get_" + name, MethodAttributes.Public, propertyType, Type.EmptyTypes); ILGenerator generator = getPropertyMethodBuilder.GetILGenerator(); // Get the PropertyDescriptor. generator.Emit(OpCodes.Ldsfld, propertyDescFieldBuilder); // Push the wrapper object onto the stack. We'll use it as an argument for our // call to GetValue later on. generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, wrapperField); // PropertyDescriptor.GetValue(_$wrapper). generator.Emit(OpCodes.Callvirt, typeof(PropertyDescriptor).GetMethod("GetValue")); // Unbox/cast. DynamicMethodUtility.EmitFromObjectConversion(generator, pd.PropertyType); // Deal with client-server type conversions. if (propertyType != pd.PropertyType) { EmitToClientConversion(generator, pd.PropertyType, propertyType); } generator.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getPropertyMethodBuilder); MethodBuilder setPropertyMethodBuilder = typeBuilder.DefineMethod("set_" + name, MethodAttributes.Public, null, new Type[] { propertyType }); generator = setPropertyMethodBuilder.GetILGenerator(); Label returnLabel = generator.DefineLabel(); // Data members require a getter and setter. However, if the real property is read-only, make sure // our surrogate property setter is a no-op. if (!pd.IsReadOnly) { // NOTE: We don't ever set null values, because a property may be required. For // original objects however it's possible that required properties are not // roundtripped, as they may not have RoundtripOriginalAttribute. // set { // if (value != null) { // $property.SetValue(_$wrapper, value); // } // } // If the value is null, return. if (!propertyType.IsValueType) { generator.Emit(OpCodes.Ldarg_1); EmitBranchIfNull(generator, propertyType, returnLabel); } else if (TypeUtility.IsNullableType(propertyType)) { generator.Emit(OpCodes.Ldarga_S, 1); EmitBranchIfNull(generator, propertyType, returnLabel); } // Get the PropertyDescriptor. generator.Emit(OpCodes.Ldsfld, propertyDescFieldBuilder); // Push the wrapper object onto the stack. We'll use it as an argument for our // call to SetValue later on. generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, wrapperField); // Push the value onto the stack. We'll use it as the 2nd argument for // our call to SetValue. generator.Emit(OpCodes.Ldarg_1); // Deal with client-server type conversions. if (propertyType != pd.PropertyType) { EmitToServerConversion(generator, propertyType, pd.PropertyType); } // Box value types. DynamicMethodUtility.EmitToObjectConversion(generator, pd.PropertyType); // PropertyDescriptor.SetValue(_$wrapper, value). generator.Emit(OpCodes.Callvirt, typeof(PropertyDescriptor).GetMethod("SetValue")); } generator.MarkLabel(returnLabel); generator.Emit(OpCodes.Ret); propertyBuilder.SetSetMethod(setPropertyMethodBuilder); }