internal FieldDefinition ImplementMappingField(PropertyDefinition property) { // Load the property mapping type. TypeReference mappingType = MainModule.Import(typeof(PropertyMapping <>)); Assert.IsNotNull(mappingType, "{0}: Failed to import type {1}.", Assembly.FullName, mappingType.FullName); // Add the property type as generic parameter. GenericInstanceType fieldType = new GenericInstanceType(mappingType); fieldType.GenericArguments.Add(property.PropertyType); // Generate the name of the private backing field. string fieldName = "_" + Char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1) + "Mapping"; // Implement the field. FieldDefinition mappingField = new FieldDefinition(fieldName, FieldAttributes.Family, fieldType); Type.Fields.Add(mappingField); FieldReference backingField = property.TryGetBackingField(); if (backingField != null) { Type.Fields.Remove(backingField.Resolve()); } PropertyGeneratorTaskHelper p = new PropertyGeneratorTaskHelper(property, mappingField); if (!Uri.IsWellFormedUriString(p.Uri, UriKind.Absolute)) { throw new UriFormatException("Annotated URI must be in absolute format."); } // Finally, implement the field initializers in the constructors of the class. foreach (MethodDefinition ctor in Type.GetConstructors()) { // Implementing mapping fields in static constructors results in invalid byte code, // since there is no ldarg.0 (this) variable. if (ctor.IsStatic) { continue; } MethodGeneratorTask g = new MethodGeneratorTask(ctor); g.Instructions.AddRange(GetFieldInitializationInstructions(g.Processor, p, fieldType)); g.Instructions.AddRange(ctor.Body.Instructions); if (g.CanExecute()) { g.Execute(); } } return(mappingField); }
private bool ImplementPropertyMapping(PropertyDefinition property, MethodDefinition raisePropertyChanged) { ImplementRdfPropertyTask subtask = new ImplementRdfPropertyTask(Generator, Type); FieldDefinition mappingField = subtask.ImplementMappingField(property); PropertyGeneratorTaskHelper p = new PropertyGeneratorTaskHelper(property, mappingField); MethodGeneratorTask getValueGenerator = subtask.GetGetValueGenerator(p); MethodGeneratorTask setValueGenerator = subtask.GetSetValueGenerator(p); if (!getValueGenerator.CanExecute()) { string msg = "{0}.{1}: Failed to implement property getter."; throw new Exception(string.Format(msg, property.DeclaringType.FullName, property.Name)); } if (!setValueGenerator.CanExecute()) { string msg = "{0}.{1}: Failed to implement property setter."; throw new Exception(string.Format(msg, property.DeclaringType.FullName, property.Name)); } Instruction ret = setValueGenerator.Processor.Create(OpCodes.Ret); // Instructions for calling SetValue() IList <Instruction> sv = new List <Instruction>(setValueGenerator.Instructions); sv.RemoveAt(0); // Remove the first nop-Instruction.. sv.RemoveAt(sv.Count - 1); // Remove the ret-Instruction.. // Instructions for return on equal values. MethodReference getValue = Type.TryGetGetValueMethod(Assembly, property.PropertyType); IList <Instruction> roe = GetReturnOnEqualsInstructionsForMapping(setValueGenerator.Processor, mappingField, getValue, sv.First(), ret).ToList(); Assert.Greater(roe.Count, 0, "{0}.{1}: Failed to generate byte code for return on equality.", Type.FullName, property.Name); // Instructions for calling RaisePropertyChanged() IList <Instruction> rpc = GetRaisePropertyChangedInstructions(setValueGenerator.Processor, property, raisePropertyChanged).ToList(); Assert.Greater(rpc.Count, 0, "{0}: Failed to generate byte code for calling the RaisePropertyChanged() method.", Type.FullName); // Re-write the instructions for the SetValue() method generator. setValueGenerator.Processor.Body.MaxStackSize = IsMappedProperty ? 4 : 2; setValueGenerator.Instructions.Clear(); setValueGenerator.Instructions.AddRange(roe); setValueGenerator.Instructions.AddRange(sv); setValueGenerator.Instructions.AddRange(rpc); setValueGenerator.Instructions.Add(ret); setValueGenerator.Execute(); getValueGenerator.Execute(); return(true); }
public override bool Execute(object parameter = null) { PropertyDefinition property = parameter as PropertyDefinition; if (property == null) { return(false); } FieldDefinition mappingField = ImplementMappingField(property); PropertyGeneratorTaskHelper p = new PropertyGeneratorTaskHelper(property, mappingField); if (p.Property.GetMethod != null) { if (p.Property.GetMethod.HasCustomAttribute <CompilerGeneratedAttribute>() || p.Property.GetMethod.IsCompilerControlled) { MethodGeneratorTask getValueGenerator = GetGetValueGenerator(p); if (getValueGenerator.CanExecute()) { getValueGenerator.Execute(); } else { string msg = "{0}.{1}: Failed to implement property getter."; throw new Exception(string.Format(msg, property.DeclaringType.FullName, property.Name)); } } } if (p.Property.SetMethod != null) { if (p.Property.SetMethod.HasCustomAttribute <CompilerGeneratedAttribute>() || p.Property.SetMethod.IsCompilerControlled) { MethodGeneratorTask setValueGenerator = GetSetValueGenerator(p); if (setValueGenerator.CanExecute()) { setValueGenerator.Execute(); } else { string msg = "{0}.{1}: Failed to implement property setter."; throw new Exception(string.Format(msg, property.DeclaringType.FullName, property.Name)); } } } Log.LogMessage("{0}.{1} -> <{2}>", Type.FullName, property.Name, p.Uri); return(true); }
internal MethodGeneratorTask GetSetValueGenerator(PropertyGeneratorTaskHelper p) { // Load a reference to the SetValue method for the property mapping. MethodReference setValue = Type.TryGetSetValueMethod(Assembly, p.Property.PropertyType); Assert.IsNotNull(setValue, "{0}: Type has no SetValue() method.", p.Property.DeclaringType.FullName); MethodGeneratorTask generator = new MethodGeneratorTask(p.Property.SetMethod); generator.Instructions.AddRange(GetCallSetValueInstructions(generator.Processor, p, setValue)); return(generator); }
internal MethodGeneratorTask GetGetValueGenerator(PropertyGeneratorTaskHelper p) { // Load a reference to the GetValue method for the property mapping. MethodReference getValue = Type.TryGetGetValueMethod(Assembly, p.Property.PropertyType); if (getValue == null) { throw new ArgumentException("{0}: Type has no GetValue() method.", p.Property.DeclaringType.FullName); } MethodGeneratorTask generator = new MethodGeneratorTask(p.Property.GetMethod); generator.Instructions.AddRange(GetReturnGetValueInstructions(generator.Processor, p, getValue)); return(generator); }
private IEnumerable <Instruction> GetFieldInitializationInstructions(ILProcessor processor, PropertyGeneratorTaskHelper p, TypeReference mappingType) { MethodDefinition ctorDef; if (p.HasDefaultValue) { ctorDef = GetPropertyMappingConstructorDefinition(mappingType, p.DefaultValue); } else { ctorDef = GetPropertyMappingConstructorDefinition(mappingType); } if (ctorDef == null) { yield break; } MethodReference ctor = Generator.Assembly.MainModule.ImportReference(ctorDef); // Thanks to: http://stackoverflow.com/questions/4968755/mono-cecil-call-generic-base-class-method-from-other-assembly if (mappingType.IsGenericInstance) { var genericType = mappingType as GenericInstanceType; ctor = MakeGeneric(ctor, genericType.GenericArguments.ToArray()); } yield return(processor.Create(OpCodes.Ldarg_0)); yield return(processor.Create(OpCodes.Ldstr, p.Property.Name)); yield return(processor.Create(OpCodes.Ldstr, p.Uri)); if (p.HasDefaultValue) { if (p.Initializer == null) { foreach (Instruction i in GetLdX(processor, p.DefaultValue)) { yield return(i); } } else { foreach (var inst in p.Initializer) { yield return(inst); } } } if (p.LanguageInvariant) { yield return(processor.Create(OpCodes.Ldc_I4_1)); } else { yield return(processor.Create(OpCodes.Ldc_I4_0)); } yield return(processor.Create(OpCodes.Newobj, ctor)); yield return(processor.Create(OpCodes.Stfld, p.BackingField)); }
private IEnumerable <Instruction> GetReturnGetValueInstructions(ILProcessor processor, PropertyGeneratorTaskHelper p, MethodReference getValue) { processor.Create(OpCodes.Ldloc_0); foreach (Instruction i in GetCallGetValueInstructions(processor, p, getValue)) { yield return(i); } yield return(processor.Create(OpCodes.Ret)); }
private IEnumerable <Instruction> GetCallGetValueInstructions(ILProcessor processor, PropertyGeneratorTaskHelper p, MethodReference getValue) { yield return(processor.Create(OpCodes.Nop)); yield return(processor.Create(OpCodes.Ldarg_0)); yield return(processor.Create(OpCodes.Ldarg_0)); yield return(processor.Create(OpCodes.Ldfld, p.BackingField)); yield return(processor.Create(OpCodes.Callvirt, getValue)); }
internal FieldDefinition ImplementMappingField(PropertyDefinition property) { // Load the property mapping type. TypeReference mappingType = MainModule.ImportReference(ILGenerator.PropertyMapping); if (mappingType == null) { throw new ArgumentException(string.Format("{0}: Failed to import type {1}.", Assembly.FullName, mappingType.FullName)); } // Add the property type as generic parameter. GenericInstanceType fieldType = new GenericInstanceType(mappingType); fieldType.GenericArguments.Add(property.PropertyType); // Generate the name of the private backing field. string fieldName = "<" + Char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1) + ">" + "k__" + "MappingField"; var mappingField = Type.TryGetField(fieldName); if (mappingField == null) { // Implement the field. mappingField = new FieldDefinition(fieldName, FieldAttributes.Family, fieldType); Type.Fields.Add(mappingField); } FieldReference backingField = property.TryGetBackingField(); if (backingField != null) { Type.Fields.Remove(backingField.Resolve()); } PropertyGeneratorTaskHelper p = new PropertyGeneratorTaskHelper(property, mappingField); if (!Uri.IsWellFormedUriString(p.Uri, UriKind.Absolute)) { throw new UriFormatException("Annotated URI must be in absolute format."); } // Finally, implement the field initializers in the constructors of the class. foreach (MethodDefinition ctor in Type.GetConstructors()) { // Implementing mapping fields in static constructors results in invalid byte code, // since there is no ldarg.0 (this) variable. if (ctor.IsStatic) { continue; } MethodGeneratorTask g = new MethodGeneratorTask(ctor); List <Instruction> Omit = new List <Instruction>(); bool alreadyInitialized = false; foreach (var x in ctor.Body.Instructions) { if (backingField != null && x.Operand == backingField) { p.Initializer = new List <Instruction>(); Omit.Add(x); var inst = x; while (inst.Previous.OpCode.Code != Code.Ldarg_0) { inst = inst.Previous; p.Initializer.Insert(0, inst); // Remove this initializiation from constructor Omit.Add(inst); } Omit.Add(inst.Previous); } if (mappingField != null && x.Operand != null && x.Operand is FieldDefinition && (x.Operand as FieldDefinition).FullName == mappingField.FullName) { alreadyInitialized = true; continue; } } if (alreadyInitialized) { continue; } g.Instructions.AddRange(GetFieldInitializationInstructions(g.Processor, p, fieldType)); g.Instructions.AddRange(ctor.Body.Instructions.Where((x) => !Omit.Contains(x))); if (g.CanExecute()) { g.Execute(); } } return(mappingField); }