public CatelType(TypeDefinition typeDefinition) { Mappings = new List <MemberMapping>(); Properties = new List <CatelTypeProperty>(); Ignore = true; TypeDefinition = typeDefinition; Name = typeDefinition.FullName; DetermineCatelType(); if (Type == CatelTypeType.Unknown) { FodyEnvironment.LogWarning($"Cannot determine the Catel type used for '{Name}', type will be ignored for weaving"); return; } try { DetermineTypes(); if (!DetermineMethods()) { return; } Properties = DetermineProperties(); DetermineMappings(); Ignore = false; } catch (Exception) { FodyEnvironment.LogWarning($"Failed to get additional information about type '{Name}', type will be ignored for weaving"); } }
private void EnsureStaticConstructor(TypeDefinition type) { var staticConstructor = type.Constructor(true); if (staticConstructor is null) { FodyEnvironment.WriteDebug($"\t\t\t{type.Name} - adding static constructor"); var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void"); staticConstructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static | MethodAttributes.RTSpecialName, type.Module.ImportReference(voidType)); var body = staticConstructor.Body; body.SimplifyMacros(); body.Instructions.Add(Instruction.Create(OpCodes.Ret)); body.OptimizeMacros(); type.Methods.Add(staticConstructor); FodyEnvironment.WriteDebug($"\t\t\t{type.Name} - added static constructor"); } }
public void Execute() { var xmlDefinition = _assemblyResolver.Resolve("System.Xml"); if (xmlDefinition != null) { var xmlTypes = xmlDefinition.MainModule.Types; XmlQualifiedName = (from t in xmlTypes where string.Equals(t.FullName, "System.Xml.XmlQualifiedName") select t).FirstOrDefault(); XmlSchemaSet = (from t in xmlTypes where string.Equals(t.FullName, "System.Xml.Schema.XmlSchemaSet") select t).FirstOrDefault(); } else { FodyEnvironment.WriteInfo("System.Xml not referenced, disabling xml-related features"); } GeneratedCodeAttribute = GetCoreTypeReference(GeneratedCodeAttributeTypeName); CompilerGeneratedAttribute = GetCoreTypeReference(CompilerGeneratedAttributeTypeName); DebuggerNonUserCodeAttribute = GetCoreTypeReference(DebuggerNonUserCodeAttributeTypeName); }
private void AddCatelTypeIfRequired(TypeDefinition typeDefinition) { if (typeDefinition?.BaseType is null) { return; } if (typeDefinition.IsDecoratedWithAttribute("Catel.Fody.NoWeavingAttribute")) { FodyEnvironment.WriteDebug($"\t{typeDefinition.FullName} is decorated with the NoWeaving attribute, type will be ignored."); typeDefinition.RemoveAttribute("Catel.Fody.NoWeavingAttribute"); return; } if (!typeDefinition.ImplementsCatelModel()) { return; } var typeNode = new CatelType(typeDefinition, _msCoreReferenceFinder); if (typeNode.Ignore || CatelTypes.Contains(typeNode)) { return; } CatelTypes.Add(typeNode); }
private void DetermineMethods() { string methodName = $"On{PropertyDefinition.Name}Changed"; var declaringType = PropertyDefinition.DeclaringType; var callbackReferences = (from method in declaringType.Methods where method.Name == methodName select method).ToList(); foreach (var callbackReference in callbackReferences) { if (callbackReference != null) { if (callbackReference.HasParameters) { FodyEnvironment.LogWarning($"Method '{declaringType.FullName}.{callbackReference.Name}' matches automatic change method name but has parameters and will not be used as automatic change callback. Rename the method to remove this warning or remove parameters to use as automatic callback method."); continue; } MethodReference finalCallbackReference = callbackReference; if (declaringType.HasGenericParameters) { finalCallbackReference = finalCallbackReference.MakeGeneric(declaringType); } ChangeCallbackReference = finalCallbackReference; break; } } }
private void AddChangeNotificationHandlerField(PropertyDefinition property, CatelTypeProperty propertyData) { if (propertyData.ChangeCallbackReference == null) { return; } FodyEnvironment.LogDebug($"\t\t\t{property.Name} - adding On{property.Name}Changed invocation"); var declaringType = property.DeclaringType; var fieldName = GetChangeNotificationHandlerFieldName(property); var handlerType = GetEventHandlerAdvancedPropertyChangedEventArgs(property); var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs"); //.field private static class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> CS$<>9__CachedAnonymousMethodDelegate1 var field = new FieldDefinition(fieldName, FieldAttributes.Private | FieldAttributes.Static, declaringType.Module.ImportReference(handlerType)); declaringType.Fields.Add(field); field.MarkAsCompilerGenerated(_msCoreReferenceFinder); //.method private hidebysig static void <.cctor>b__0(object s, class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs e) cil managed //{ // .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() // .maxstack 8 // L_0000: ldarg.0 // L_0001: castclass Catel.Fody.TestAssembly.ModelBaseTest // L_0006: callvirt instance void Catel.Fody.TestAssembly.ModelBaseTest::OnLastNameChanged() // L_000b: nop // L_000c: ret //} var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void"); var objectType = _msCoreReferenceFinder.GetCoreTypeReference("Object"); var initializationMethodName = GetChangeNotificationHandlerConstructorName(property); var initializationMethod = new MethodDefinition(initializationMethodName, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, declaringType.Module.ImportReference(voidType)); initializationMethod.Parameters.Add(new ParameterDefinition("s", ParameterAttributes.None, declaringType.Module.ImportReference(objectType))); initializationMethod.Parameters.Add(new ParameterDefinition("e", ParameterAttributes.None, declaringType.Module.ImportReference(advancedPropertyChangedEventArgsType))); var body = initializationMethod.Body; body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Castclass, declaringType.MakeGenericIfRequired()), Instruction.Create(OpCodes.Callvirt, propertyData.ChangeCallbackReference), Instruction.Create(OpCodes.Nop), Instruction.Create(OpCodes.Ret)); declaringType.Methods.Add(initializationMethod); initializationMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder); }
public void Execute(bool force = false) { var property = _propertyData.PropertyDefinition; if (property == null) { FodyEnvironment.LogWarning("Skipping an unknown property because it has no property definition"); return; } if (!force && !HasBackingField(property)) { FodyEnvironment.LogDebug($"\t\tSkipping '{property.Name}' because it has no backing field"); return; } if (ImplementsICommand(property)) { FodyEnvironment.LogDebug($"\t\tSkipping '{property.Name}' because it implements ICommand"); return; } FodyEnvironment.LogDebug("\t\t" + property.Name); try { EnsureStaticConstructor(property.DeclaringType); AddChangeNotificationHandlerField(property, _propertyData); var fieldDefinition = AddPropertyFieldDefinition(property); if (!AddPropertyRegistration(property, _propertyData)) { return; } var fieldReference = GetFieldReference(property.DeclaringType, fieldDefinition.Name, true); AddGetValueCall(property, fieldReference); AddSetValueCall(property, fieldReference, _propertyData.IsReadOnly); RemoveBackingField(property); } catch (Exception ex) { FodyEnvironment.LogError($"\t\tFailed to handle property '{property.DeclaringType.Name}.{property.Name}'\n{ex.Message}\n{ex.StackTrace}"); #if DEBUG Debugger.Launch(); #endif } }
private GenericInstanceType GetEventHandlerAdvancedPropertyChangedEventArgs(PropertyDefinition property) { var genericHandlerType = _msCoreReferenceFinder.GetCoreTypeReference("System.EventHandler`1"); if (genericHandlerType == null) { FodyEnvironment.LogError("Expected to find EventHandler<T>, but type was not found"); return(null); } var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs"); var handlerType = new GenericInstanceType(genericHandlerType); handlerType.GenericArguments.Add(advancedPropertyChangedEventArgsType); return(handlerType); }
public CatelType(TypeDefinition typeDefinition, MsCoreReferenceFinder msCoreReferenceFinder) { Mappings = new List <MemberMapping>(); Properties = new List <CatelTypeProperty>(); Ignore = true; TypeDefinition = typeDefinition; Name = typeDefinition.FullName; _msCoreReferenceFinder = msCoreReferenceFinder; DetermineCatelType(); if (Type == CatelTypeType.Unknown) { FodyEnvironment.WriteWarning($"Cannot determine the Catel type used for '{Name}', type will be ignored for weaving"); return; } Version = TypeDefinition.GetCatelVersion(); if (Version == CatelVersion.Unknown) { FodyEnvironment.WriteWarning($"Cannot determine the Catel version used for '{Name}', type will be ignored for weaving"); return; } try { DetermineTypes(); if (!DetermineMethods()) { FodyEnvironment.WriteWarning($"Cannot determine the Catel methods used for '{Name}', type will be ignored for weaving"); return; } DetermineProperties(); DetermineMappings(); Ignore = false; } catch (Exception ex) { FodyEnvironment.WriteWarning($"Failed to get additional information about type '{Name}', type will be ignored for weaving: '{ex.Message}'"); } }
private int AddGetValueCall(PropertyDefinition property, FieldReference fieldReference) { FodyEnvironment.WriteDebug($"\t\t\t{property.Name} - adding GetValue call"); var genericGetValue = new GenericInstanceMethod(_catelType.GetValueInvoker); foreach (var genericParameter in _catelType.GetValueInvoker.GenericParameters) { genericGetValue.GenericParameters.Add(genericParameter); } genericGetValue.GenericArguments.Add(property.PropertyType.Import()); if (property.GetMethod is null) { var getMethod = new MethodDefinition($"get_{property.Name}", MethodAttributes.Public, property.PropertyType.Import()); property.DeclaringType.Methods.Add(getMethod); getMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder); property.GetMethod = getMethod; } var body = property.GetMethod.Body; body.SimplifyMacros(); var instructions = body.Instructions; instructions.Clear(); var finalIndex = instructions.Insert(0, Instruction.Create(OpCodes.Nop), Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldsfld, fieldReference), Instruction.Create(OpCodes.Call, genericGetValue), Instruction.Create(OpCodes.Ret)); body.OptimizeMacros(); return(finalIndex); }
private void AddChangeNotificationHandlerField(PropertyDefinition property, CatelTypeProperty propertyData) { if (propertyData.ChangeCallbackReference is null) { return; } FodyEnvironment.WriteDebug($"\t\t\t{property.Name} - adding On{property.Name}Changed invocation"); switch (_catelType.Version) { case CatelVersion.v5: AddChangeNotificationHandlerField_Catel5(property, propertyData); break; case CatelVersion.v6: AddChangeNotificationHandlerField_Catel6(property, propertyData); break; } }
public void Execute(bool force = false) { var property = _propertyData.PropertyDefinition; if (property is null) { FodyEnvironment.WriteWarning("Skipping an unknown property because it has no property definition"); return; } if (!force && !HasBackingField(property)) { FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it has no backing field"); return; } if (!IsCleanSetter(property)) { FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it has no clean setter (custom implementation?)"); return; } FodyEnvironment.WriteDebug("\t\t" + property.Name); try { UpdateSetValueCall(property); } catch (Exception ex) { FodyEnvironment.WriteError($"\t\tFailed to handle property '{property.DeclaringType.Name}.{property.Name}'\n{ex.Message}\n{ex.StackTrace}"); #if DEBUG Debugger.Launch(); #endif } }
public TypeReference GetCoreTypeReference(string typeName) { if (!_typeReferencesByFullName.ContainsKey(typeName)) { var types = GetTypes(); foreach (var type in types) { _typeReferencesByFullName[type.FullName] = type; _typeReferencesByShortName[type.Name] = type; } } if (!_typeReferencesByFullName.TryGetValue(typeName, out var resolvedType)) { if (!_typeReferencesByShortName.TryGetValue(typeName, out resolvedType)) { FodyEnvironment.WriteError($"Type '{typeName}' cannot be found, please report this bug"); return(null); } } return(resolvedType); }
private int AddSetValueCall(PropertyDefinition property, FieldReference fieldReference, bool isReadOnly) { FodyEnvironment.WriteDebug($"\t\t\t{property.Name} - adding SetValue call"); //string fieldName = string.Format("{0}Property", property.Name); //var declaringType = property.DeclaringType; //var fieldReference = GetField(declaringType, fieldName); // Catel v5: SetValue<TValue>(PropertyData propertyName, TValue value) // Catel v6: SetValue<TValue>(IPropertyData propertyName, TValue value) if (property.SetMethod is null) { var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void"); var setMethod = new MethodDefinition($"set_{property.Name}", MethodAttributes.Public, property.DeclaringType.Module.ImportReference(voidType)); setMethod.Parameters.Add(new ParameterDefinition(property.PropertyType.Import())); property.DeclaringType.Methods.Add(setMethod); setMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder); property.SetMethod = setMethod; } var finalSetMethod = property.SetMethod; if (isReadOnly) { finalSetMethod.IsPrivate = true; finalSetMethod.IsPublic = false; } var body = property.SetMethod.Body; body.SimplifyMacros(); var instructions = body.Instructions; instructions.Clear(); var instructionsToAdd = new List <Instruction>(); instructionsToAdd.AddRange(new[] { Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldsfld, fieldReference), Instruction.Create(OpCodes.Ldarg_1) }); // Check if Catel 5.12+ SetValue<TValue> is available var preferredSetValueInvoker = _catelType.PreferredSetValueInvoker; if (preferredSetValueInvoker.HasGenericParameters) { // Generic, no boxing required, but we need to make it generic var genericSetValue = new GenericInstanceMethod(preferredSetValueInvoker); foreach (var genericParameter in preferredSetValueInvoker.GenericParameters) { genericSetValue.GenericParameters.Add(genericParameter); } genericSetValue.GenericArguments.Add(property.PropertyType.Import()); preferredSetValueInvoker = genericSetValue; } else { // Non-generic, requires boxing if (property.PropertyType.IsBoxingRequired(_catelType.SetValueInvoker.Parameters[1].ParameterType)) { instructionsToAdd.Add(Instruction.Create(OpCodes.Box, property.PropertyType.Import())); } } if (preferredSetValueInvoker.Parameters.Count > 2) { // Catel v5 is a new signature: // protected internal void SetValue(string name, object value, bool notifyOnChange = true) instructionsToAdd.Add(Instruction.Create(OpCodes.Ldc_I4_1)); } instructionsToAdd.Add(Instruction.Create(OpCodes.Call, preferredSetValueInvoker)); instructionsToAdd.Add(Instruction.Create(OpCodes.Ret)); var finalIndex = instructions.Insert(0, instructionsToAdd.ToArray()); body.OptimizeMacros(); return(finalIndex); }
public void Execute(bool force = false) { var preferredSetValueInvoker = _catelType.PreferredSetValueInvoker; if (_catelType.GetValueInvoker is null || preferredSetValueInvoker is null) { return; } var property = _propertyData.PropertyDefinition; if (property is null) { FodyEnvironment.WriteWarning("Skipping an unknown property because it has no property definition"); return; } if (AlreadyContainsCallToMember(property.GetMethod, _catelType.GetValueInvoker.Name) || AlreadyContainsCallToMember(property.SetMethod, preferredSetValueInvoker.Name)) { FodyEnvironment.WriteDebug($"\t{property.GetName()} already has GetValue and/or SetValue functionality. Property will be ignored."); return; } if (!force && !HasBackingField(property)) { FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it has no backing field"); return; } if (ImplementsICommand(property)) { FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it implements ICommand"); return; } FodyEnvironment.WriteDebug("\t\t" + property.GetName()); try { EnsureStaticConstructor(property.DeclaringType); AddChangeNotificationHandlerField(property, _propertyData); var fieldDefinition = AddPropertyFieldDefinition(property); if (!AddPropertyRegistration(property, _propertyData)) { return; } var fieldReference = GetFieldReference(property.DeclaringType, fieldDefinition.Name, true); AddGetValueCall(property, fieldReference); AddSetValueCall(property, fieldReference, _propertyData.IsReadOnly); RemoveBackingField(property); } catch (Exception ex) { FodyEnvironment.WriteError($"\t\tFailed to handle property '{property.DeclaringType.Name}.{property.Name}'\n{ex.Message}\n{ex.StackTrace}"); #if DEBUG Debugger.Launch(); #endif } }
private bool AddPropertyRegistration(PropertyDefinition property, CatelTypeProperty propertyData) { var fieldName = $"{property.Name}Property"; var declaringType = property.DeclaringType; var fieldReference = GetFieldReference(declaringType, fieldName, true); if (fieldReference is null) { FodyEnvironment.WriteWarning($"\t\tCannot handle property '{_catelType.Name}.{property.Name}' because backing field is not found"); return(false); } var staticConstructor = declaringType.Constructor(true); var body = staticConstructor.Body; body.SimplifyMacros(); var instructions = body.Instructions; // Always inject before the first try block, or just before the last return statement var instructionToInsert = (from instruction in instructions where instruction.OpCode == OpCodes.Ret select instruction).FirstOrDefault(); var exceptionHandler = body.ExceptionHandlers.FirstOrDefault(); if (exceptionHandler != null) { instructionToInsert = exceptionHandler.TryStart; } var index = (instructionToInsert != null) ? instructions.IndexOf(instructionToInsert) : instructions.Count; var instructionsToInsert = new List <Instruction>(); switch (_catelType.Version) { case CatelVersion.v5: instructionsToInsert.AddRange(CreatePropertyRegistration_Catel5(property, propertyData)); break; case CatelVersion.v6: instructionsToInsert.AddRange(CreatePropertyRegistration_Catel6(property, propertyData)); break; } var registerPropertyInvoker = (propertyData.DefaultValue is null) ? _catelType.RegisterPropertyWithoutDefaultValueInvoker : _catelType.RegisterPropertyWithDefaultValueInvoker; // Fill up the final booleans: // Catel v5: RegisterProperty([0] string name, // [1] Type type, // [2] Func<object> createDefaultValue = null, // [3] EventHandler<AdvancedPropertyChangedEventArgs> propertyChangedEventHandler = null, // [4] bool includeInSerialization = true, // [5] bool includeInBackup = true) // // Catel v6: RegisterProperty<TValue>([0] string name, // [1] Func<TValue> createDefaultValue = null, // [2] EventHandler<APropertyChangedEventArgs> propertyChangedEventHandler = null, // [3] bool includeInSerialization = true, // [4] bool includeInBackup = true) var parameters = registerPropertyInvoker.Parameters.Skip(3).ToList(); var boolCount = 0; for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; if (string.CompareOrdinal(parameter.ParameterType.FullName, _moduleWeaver.TypeSystem.BooleanDefinition.FullName) != 0) { continue; } boolCount++; // includeInBackup == 2nd bool value if (!propertyData.IncludeInBackup && boolCount == 2) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_0)); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_1)); } } // Make call to register property generic var finalRegisterPropertyMethod = registerPropertyInvoker; if (registerPropertyInvoker.HasGenericParameters) { var genericRegisterProperty = new GenericInstanceMethod(registerPropertyInvoker); if (registerPropertyInvoker.HasGenericParameters) { foreach (var genericParameter in registerPropertyInvoker.GenericParameters) { genericRegisterProperty.GenericParameters.Add(genericParameter); } genericRegisterProperty.GenericArguments.Add(property.PropertyType.Import(false)); } finalRegisterPropertyMethod = genericRegisterProperty; } instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Call, finalRegisterPropertyMethod), Instruction.Create(OpCodes.Stsfld, fieldReference) }); instructions.Insert(index, instructionsToInsert); body.OptimizeMacros(); return(true); }
public void Execute() { try { #if DEBUG if (!Debugger.IsAttached) { Debugger.Launch(); FodyEnvironment.LogDebug = CreateLoggingCallback(LogDebug); FodyEnvironment.LogInfo = CreateLoggingCallback(LogInfo); FodyEnvironment.LogWarning = CreateLoggingCallback(LogWarning); FodyEnvironment.LogError = CreateLoggingCallback(LogError); } #endif // Clear cache because static members will be re-used over multiple builds over multiple systems CacheHelper.ClearAllCaches(); var configuration = new Configuration(Config); InitializeEnvironment(); LogInfo($"Catel.Fody v{GetType().Assembly.GetName().Version}"); // 1st step: set up the basics var msCoreReferenceFinder = new MsCoreReferenceFinder(this, ModuleDefinition.AssemblyResolver); msCoreReferenceFinder.Execute(); // Validate if Catel.Core is referenced var isRunningAgainstCatelCore = false; var catelCoreReference = AssemblyResolver.Resolve("Catel.Core"); if (catelCoreReference == null) { if (!ModuleDefinition.Name.StartsWith("Catel.Core")) { LogWarning("No reference to Catel.Core found, this weaver is useless without referencing Catel"); return; } isRunningAgainstCatelCore = true; LogInfo("No reference to Catel.Core found, but continuing because this is running against Catel.Core itself"); } // Note: nested types not supported because we only list actual types (thus not nested) var types = ModuleDefinition.GetTypes().Where(x => x.IsClass && x.BaseType != null).ToList(); var typeNodeBuilder = new CatelTypeNodeBuilder(types); typeNodeBuilder.Execute(); // Remove any code generated types from the list of types to process var codeGenTypeCleaner = new CodeGenTypeCleaner(typeNodeBuilder); codeGenTypeCleaner.Execute(); // 2nd step: Auto property weaving if (!isRunningAgainstCatelCore && configuration.WeaveProperties) { FodyEnvironment.LogInfo("Weaving properties"); var propertyWeaverService = new AutoPropertiesWeaverService(configuration, typeNodeBuilder, msCoreReferenceFinder); propertyWeaverService.Execute(); } else { FodyEnvironment.LogInfo("Weaving properties is disabled"); } // 3rd step: Exposed properties weaving if (!isRunningAgainstCatelCore && configuration.WeaveExposedProperties) { FodyEnvironment.LogInfo("Weaving exposed properties"); var exposedPropertiesWeaverService = new ExposedPropertiesWeaverService(typeNodeBuilder, msCoreReferenceFinder); exposedPropertiesWeaverService.Execute(); } else { FodyEnvironment.LogInfo("Weaving exposed properties is disabled"); } // 4th step: Argument weaving if (!isRunningAgainstCatelCore && configuration.WeaveArguments) { FodyEnvironment.LogInfo("Weaving arguments"); var argumentWeaverService = new ArgumentWeaverService(types, msCoreReferenceFinder); argumentWeaverService.Execute(); } else { FodyEnvironment.LogInfo("Weaving arguments is disabled"); } // 5th step: Logging weaving (we will run this against Catel.Core) if (configuration.WeaveLogging) { FodyEnvironment.LogInfo("Weaving logging"); var loggingWeaver = new LoggingWeaverService(types); loggingWeaver.Execute(); } else { FodyEnvironment.LogInfo("Weaving logging is disabled"); } // 6th step: Xml schema weaving if (!isRunningAgainstCatelCore && configuration.GenerateXmlSchemas) { FodyEnvironment.LogInfo("Weaving xml schemas"); var xmlSchemasWeaverService = new XmlSchemasWeaverService(msCoreReferenceFinder, typeNodeBuilder); xmlSchemasWeaverService.Execute(); } else { FodyEnvironment.LogInfo("Weaving xml schemas is disabled"); } // Last step: clean up var referenceCleaner = new ReferenceCleaner(this); referenceCleaner.Execute(); } catch (Exception ex) { LogError(ex.Message); #if DEBUG if (!Debugger.IsAttached) { Debugger.Launch(); } #endif } }
public override void Execute() { try { //#if DEBUG // if (!Debugger.IsAttached) // { // Debugger.Launch(); // } //#endif // First of all, set the assembly resolver if (AssemblyResolver is null) { AssemblyResolver = ModuleDefinition.AssemblyResolver; } if (TypeSystem is null) { var typeCache = new global::Fody.TypeCache(x => AssemblyResolver.Resolve(x)); typeCache.BuildAssembliesToScan(this); TypeSystem = new global::Fody.TypeSystem(x => typeCache.FindType(x), ModuleDefinition); } // Clear cache because static members will be re-used over multiple builds over multiple systems CacheHelper.ClearAllCaches(); var configuration = new Configuration(Config); InitializeEnvironment(); WriteInfo($"Catel.Fody v{GetType().Assembly.GetName().Version}"); // 1st step: set up the basics var msCoreReferenceFinder = new MsCoreReferenceFinder(this, ModuleDefinition.AssemblyResolver); msCoreReferenceFinder.Execute(); // Validate if Catel.Core is referenced configuration.IsRunningAgainstCatel = false; if (ModuleDefinition.Name.StartsWith("Catel.Core") || ModuleDefinition.Name.StartsWith("Catel.MVVM") || ModuleDefinition.Name.StartsWith("Catel.Serialization.") || ModuleDefinition.Name.StartsWith("Catel.Tests")) { configuration.IsRunningAgainstCatel = true; WriteInfo("Running against Catel itself, most features will be disabled"); } var catelCoreReference = AssemblyResolver.Resolve("Catel.Core"); if (!configuration.IsRunningAgainstCatel && catelCoreReference is null) { WriteWarning("No reference to Catel.Core found, this weaver is useless without referencing Catel"); return; } // Note: nested types not supported because we only list actual types (thus not nested) var types = ModuleDefinition.GetTypes().Where(x => x.IsClass && x.BaseType != null).ToList(); var typeNodeBuilder = new CatelTypeNodeBuilder(types, msCoreReferenceFinder); typeNodeBuilder.Execute(); // Remove any code generated types from the list of types to process var codeGenTypeCleaner = new CodeGenTypeCleaner(typeNodeBuilder); codeGenTypeCleaner.Execute(); // 2nd step: Auto property weaving if (!configuration.IsRunningAgainstCatel && configuration.WeaveProperties) { FodyEnvironment.WriteInfo("Weaving properties"); var propertyWeaverService = new AutoPropertiesWeaverService(configuration, this, typeNodeBuilder, msCoreReferenceFinder); propertyWeaverService.Execute(); } else { FodyEnvironment.WriteInfo("Weaving properties is disabled"); } // 3rd step: Exposed properties weaving if (!configuration.IsRunningAgainstCatel && configuration.WeaveExposedProperties) { FodyEnvironment.WriteInfo("Weaving exposed properties"); var exposedPropertiesWeaverService = new ExposedPropertiesWeaverService(this, configuration, typeNodeBuilder, msCoreReferenceFinder); exposedPropertiesWeaverService.Execute(); } else { FodyEnvironment.WriteInfo("Weaving exposed properties is disabled"); } // 4th step: Argument weaving if (configuration.WeaveArguments) { FodyEnvironment.WriteInfo("Weaving arguments"); var argumentWeaverService = new ArgumentWeaverService(types, msCoreReferenceFinder, configuration); argumentWeaverService.Execute(); } else { FodyEnvironment.WriteInfo("Weaving arguments is disabled"); } // 5th step: Logging weaving (we will run this against Catel.Core) if (configuration.WeaveLogging) { FodyEnvironment.WriteInfo("Weaving logging"); var loggingWeaver = new LoggingWeaverService(types); loggingWeaver.Execute(); } else { FodyEnvironment.WriteInfo("Weaving logging is disabled"); } // 6th step: Xml schema weaving if (!configuration.IsRunningAgainstCatel && configuration.GenerateXmlSchemas) { FodyEnvironment.WriteInfo("Weaving xml schemas"); var xmlSchemasWeaverService = new XmlSchemasWeaverService(this, msCoreReferenceFinder, typeNodeBuilder); xmlSchemasWeaverService.Execute(); } else { FodyEnvironment.WriteInfo("Weaving xml schemas is disabled"); } // Validate that nothing has been left out var validationService = new ValidationService(this); validationService.Validate(); // Last step: clean up var referenceCleaner = new ReferenceCleaner(this); referenceCleaner.Execute(); } catch (Exception ex) { WriteError(ex.Message); #if DEBUG if (!Debugger.IsAttached) { Debugger.Launch(); } #endif } }
private bool AddPropertyRegistration(PropertyDefinition property, CatelTypeProperty propertyData) { var fieldName = $"{property.Name}Property"; var declaringType = property.DeclaringType; var fieldReference = GetFieldReference(declaringType, fieldName, true); if (fieldReference == null) { FodyEnvironment.LogWarning($"\t\tCannot handle property '{_catelType.Name}.{property.Name}' because backing field is not found"); return(false); } var staticConstructor = declaringType.Constructor(true); var body = staticConstructor.Body; body.SimplifyMacros(); var instructions = body.Instructions; // Always inject before the first try block, or just before the last return statement var instructionToInsert = (from instruction in instructions where instruction.OpCode == OpCodes.Ret select instruction).FirstOrDefault(); var exceptionHandler = body.ExceptionHandlers.FirstOrDefault(); if (exceptionHandler != null) { instructionToInsert = exceptionHandler.TryStart; } var index = (instructionToInsert != null) ? instructions.IndexOf(instructionToInsert) : instructions.Count; //L_0000: ldstr "FullName" //L_0005: ldtoken string // note that this is the property type //L_000a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) //L_000f: ldnull //L_0010: ldnull //L_0011: ldc.i4.1 //L_0012: call class [Catel.Core]Catel.Data.PropertyData [Catel.Core]Catel.Data.ModelBase::RegisterProperty(string, class [mscorlib]System.Type, class [mscorlib]System.Func`1<object>, class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs>, bool) //L_0017: stsfld class [Catel.Core]Catel.Data.PropertyData Catel.Fody.TestAssembly.ViewModelBaseTest::FullNameProperty var getTypeFromHandle = property.Module.GetMethodAndImport("GetTypeFromHandle"); var instructionsToInsert = new List <Instruction>(); instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Ldstr, property.Name), Instruction.Create(OpCodes.Ldtoken, property.PropertyType.Import()), Instruction.Create(OpCodes.Call, getTypeFromHandle), }); var resolvedPropertyType = propertyData.PropertyDefinition.PropertyType.Resolve(); // Default value if (propertyData.DefaultValue is string) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldstr, (string)propertyData.DefaultValue)); } else if (propertyData.DefaultValue is bool) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (bool)propertyData.DefaultValue ? 1 : 0)); } else if (propertyData.DefaultValue is int) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)propertyData.DefaultValue)); } else if (propertyData.DefaultValue is long) { if ((long)propertyData.DefaultValue <= int.MaxValue) { // Note: don't use Ldc_I8 here, although it is a long instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)(long)propertyData.DefaultValue)); instructionsToInsert.Add(Instruction.Create(OpCodes.Conv_I8)); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I8, (long)propertyData.DefaultValue)); } } else if (propertyData.DefaultValue is float) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_R4, (float)propertyData.DefaultValue)); } else if (propertyData.DefaultValue is double) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_R8, (double)propertyData.DefaultValue)); } else if (resolvedPropertyType != null && resolvedPropertyType.IsEnum && propertyData.DefaultValue != null) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)((CustomAttributeArgument)propertyData.DefaultValue).Value)); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldnull)); } if (propertyData.ChangeCallbackReference != null) { //L_0040: ldsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1 //L_0045: brtrue.s L_005a //L_0047: ldnull //L_0048: ldftn void Catel.Fody.TestAssembly.ModelBaseTest::<.cctor>b__0(object, class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs) //L_004e: newobj instance void [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs>::.ctor(object, native int) //L_0053: stsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1 //L_0058: br.s L_005a //L_005a: ldsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1 var handlerFieldName = GetChangeNotificationHandlerFieldName(property); var handlerConstructorFieldName = GetChangeNotificationHandlerConstructorName(property); var handlerField = GetFieldReference(property.DeclaringType, handlerFieldName, true); var handlerConstructor = GetMethodReference(property.DeclaringType, handlerConstructorFieldName, true); var handlerType = GetEventHandlerAdvancedPropertyChangedEventArgs(property); var importedHandlerType = handlerType.Resolve(); var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs"); var handlerTypeConstructor = declaringType.Module.ImportReference(importedHandlerType.Constructor(false)); var genericConstructor = handlerTypeConstructor.MakeHostInstanceGeneric(declaringType.Module.ImportReference(advancedPropertyChangedEventArgsType)); var finalInstruction = Instruction.Create(OpCodes.Ldsfld, handlerField); instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Ldsfld, handlerField), Instruction.Create(OpCodes.Brtrue_S, finalInstruction), Instruction.Create(OpCodes.Ldnull), Instruction.Create(OpCodes.Ldftn, handlerConstructor), Instruction.Create(OpCodes.Newobj, genericConstructor), Instruction.Create(OpCodes.Stsfld, handlerField), Instruction.Create(OpCodes.Br_S, finalInstruction), finalInstruction }); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldnull)); } var registerPropertyInvoker = (propertyData.DefaultValue == null) ? _catelType.RegisterPropertyWithoutDefaultValueInvoker : _catelType.RegisterPropertyWithDefaultValueInvoker; var parameters = registerPropertyInvoker.Parameters.Reverse().ToList(); for (int i = 0; i < parameters.Count; i++) { var parameterType = parameters[i]; if (string.CompareOrdinal(parameterType.ParameterType.FullName, FodyEnvironment.ModuleDefinition.TypeSystem.Boolean.FullName) != 0) { break; } instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_1)); } // Make call to register property generic var finalRegisterPropertyMethod = registerPropertyInvoker; if (registerPropertyInvoker.HasGenericParameters) { var genericRegisterProperty = new GenericInstanceMethod(registerPropertyInvoker); if (registerPropertyInvoker.HasGenericParameters) { foreach (var genericParameter in registerPropertyInvoker.GenericParameters) { genericRegisterProperty.GenericParameters.Add(genericParameter); } genericRegisterProperty.GenericArguments.Add(property.PropertyType.Import(true)); } finalRegisterPropertyMethod = genericRegisterProperty; } instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Call, finalRegisterPropertyMethod), Instruction.Create(OpCodes.Stsfld, fieldReference) }); instructions.Insert(index, instructionsToInsert); body.OptimizeMacros(); return(true); }
private void RemoveBackingField(PropertyDefinition property) { var fieldName = GetBackingFieldName(property); var declaringType = property.DeclaringType; var field = GetFieldDefinition(declaringType, fieldName, false); if (field != null) { foreach (var ctor in declaringType.GetConstructors()) { var ctorBody = ctor.Body; ctorBody.SimplifyMacros(); var instructions = ctorBody.Instructions; var validInstructionCounter = 0; for (var i = 0; i < instructions.Count; i++) { var instruction = instructions[i]; if (instruction.IsOpCode(OpCodes.Nop)) { continue; } validInstructionCounter++; if (instruction.UsesField(field)) { FodyEnvironment.WriteDebug($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Converting field usage to property usage to maintain compatibility with Catel generated properties."); if (instruction.IsOpCode(OpCodes.Stfld)) { // Setter instruction.OpCode = OpCodes.Call; // Note: make sure to support generic types MethodReference setter = property.SetMethod; if (declaringType.MakeGenericIfRequired() is GenericInstanceType genericInstanceType) { setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray()); } instruction.Operand = declaringType.Module.ImportReference(setter); // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready) var baseIndex = ctor.FindBaseConstructorIndex(); if (baseIndex >= 0) { // After a call to a ctor, a double nop is required var indexToInsert = baseIndex + 1; if (instructions.IsNextInstructionOpCode(baseIndex, OpCodes.Nop)) { indexToInsert++; if (instructions.IsNextInstructionOpCode(baseIndex + 1, OpCodes.Nop)) { indexToInsert++; } } instructions.MoveInstructionsToPosition(i - 2, 3, indexToInsert); } } else if (instruction.IsOpCode(OpCodes.Ldfld)) { // Getter instruction.OpCode = OpCodes.Call; // Note: make sure to support generic types MethodReference getter = property.GetMethod; if (declaringType.MakeGenericIfRequired() is GenericInstanceType genericInstanceType) { getter = getter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray()); } instruction.Operand = declaringType.Module.ImportReference(getter); // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready) var baseIndex = ctor.FindBaseConstructorIndex(); if (baseIndex >= 0) { // After a call to a ctor, a double nop is required var indexToInsert = baseIndex + 1; if (instructions.IsNextInstructionOpCode(baseIndex, OpCodes.Nop)) { indexToInsert++; if (instructions.IsNextInstructionOpCode(baseIndex + 1, OpCodes.Nop)) { indexToInsert++; } } instructions.MoveInstructionsToPosition(i - 2, 3, indexToInsert); } } else if (instruction.IsOpCode(OpCodes.Ldflda)) { // Probably setting a generic field value to a value by directly using an address. Since this was code like this: // // call instance !0 MyCompany.Models.Base.ItemsModel`1 < !T >::get_SelectedItem() // initobj !T // // We need to generate code like this: // // ldloca.s local // initobj !T // ldloc.0 // call instance void Catel.Fody.TestAssembly.CSharp6_AutoPropertyInitializer_Generic_ExpectedCode`1 < !T >::set_SelectedItem(!0) // Note: make sure to support generic types MethodReference setter = property.SetMethod; if (declaringType.MakeGenericIfRequired() is GenericInstanceType genericInstanceType) { setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray()); } var variable = new VariableDefinition(property.PropertyType.MakeGenericIfRequired()); ctorBody.Variables.Add(variable); ctorBody.InitLocals = true; var newInstructions = new List <Instruction> { Instruction.Create(OpCodes.Ldloca, variable), instructions[i + 1], // Just copy this initobj !T instruction Instruction.Create(OpCodes.Ldloc, variable), Instruction.Create(OpCodes.Call, setter) }; // Remove 3 instructions // ldarg // ldflda // init T instructions.RemoveAt(i); instructions.RemoveAt(i); if (instructions[i - 1].IsOpCode(OpCodes.Ldarg, OpCodes.Ldarg_0)) { newInstructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0)); instructions.RemoveAt(i - 1); } // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready) var baseIndex = ctor.FindBaseConstructorIndex(); if (baseIndex >= 0) { // After a call to a ctor, a double nop is required var indexToInsert = baseIndex + 1; if (instructions.IsNextInstructionOpCode(baseIndex, OpCodes.Nop)) { indexToInsert++; if (instructions.IsNextInstructionOpCode(baseIndex + 1, OpCodes.Nop)) { indexToInsert++; } } instructions.Insert(indexToInsert, newInstructions); } else { FodyEnvironment.WriteError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. A rare condition occurred (no base ctor found), please contact support"); } } else { FodyEnvironment.WriteError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Tried to convert it to property usage, but OpCode '{instruction.OpCode}' is not supported. Please raise an issue."); } } } ctorBody.OptimizeMacros(); ctor.UpdateDebugInfo(); } declaringType.Fields.Remove(field); } }
private int AddSetValueCall(PropertyDefinition property, FieldReference fieldReference, bool isReadOnly) { FodyEnvironment.LogDebug($"\t\t\t{property.Name} - adding SetValue call"); //string fieldName = string.Format("{0}Property", property.Name); //var declaringType = property.DeclaringType; //var fieldReference = GetField(declaringType, fieldName); // Writes SetValue(PropertyData propertyName, object value) if (property.SetMethod == null) { var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void"); var setMethod = new MethodDefinition($"set_{property.Name}", MethodAttributes.Public, property.DeclaringType.Module.ImportReference(voidType)); setMethod.Parameters.Add(new ParameterDefinition(property.PropertyType.Import())); property.DeclaringType.Methods.Add(setMethod); setMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder); property.SetMethod = setMethod; } var finalSetMethod = property.SetMethod; if (isReadOnly) { finalSetMethod.IsPrivate = true; finalSetMethod.IsPublic = false; } var body = property.SetMethod.Body; body.SimplifyMacros(); var instructions = body.Instructions; instructions.Clear(); var instructionsToAdd = new List <Instruction>(); instructionsToAdd.AddRange(new[] { Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldsfld, fieldReference), Instruction.Create(OpCodes.Ldarg_1) }); if (property.PropertyType.IsBoxingRequired(_catelType.SetValueInvoker.Parameters[1].ParameterType)) { instructionsToAdd.Add(Instruction.Create(OpCodes.Box, property.PropertyType.Import())); } if (_catelType.SetValueInvoker.Parameters.Count > 2) { // Catel v5 is a new signature: // protected internal void SetValue(string name, object value, bool notifyOnChange = true) instructionsToAdd.Add(Instruction.Create(OpCodes.Ldc_I4_1)); } instructionsToAdd.Add(Instruction.Create(OpCodes.Call, _catelType.SetValueInvoker)); instructionsToAdd.Add(Instruction.Create(OpCodes.Ret)); var finalIndex = instructions.Insert(0, instructionsToAdd.ToArray()); body.OptimizeMacros(); return(finalIndex); }
private void RemoveBackingField(PropertyDefinition property) { var fieldName = GetBackingFieldName(property); var declaringType = property.DeclaringType; var field = GetFieldDefinition(declaringType, fieldName, false); if (field != null) { foreach (var ctor in declaringType.GetConstructors()) { var ctorBody = ctor.Body; ctorBody.SimplifyMacros(); var instructions = ctorBody.Instructions; var validInstructionCounter = 0; for (int i = 0; i < instructions.Count; i++) { var instruction = instructions[i]; if (instruction.IsOpCode(OpCodes.Nop)) { continue; } validInstructionCounter++; if (instruction.UsesField(field)) { FodyEnvironment.LogDebug($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Converting field usage to property usage to maintain compatibility with Catel generated properties."); if (instruction.IsOpCode(OpCodes.Stfld)) { // Setter instruction.OpCode = OpCodes.Call; // Note: make sure to support generic types MethodReference setter = property.SetMethod; var genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType; if (genericInstanceType != null) { setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray()); } instruction.Operand = declaringType.Module.Import(setter); // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready) instructions.MoveInstructionsToEnd(i - 2, 3); } else if (instruction.IsOpCode(OpCodes.Ldfld)) { // Getter instruction.OpCode = OpCodes.Call; // Note: make sure to support generic types MethodReference getter = property.GetMethod; var genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType; if (genericInstanceType != null) { getter = getter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray()); } instruction.Operand = declaringType.Module.Import(getter); // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready) instructions.MoveInstructionsToEnd(i - 2, 3); } else if (instruction.IsOpCode(OpCodes.Ldflda)) { // Probably setting a generic field value to a value by directly using an address. Since this was code like this: // // call instance !0 MyCompany.Models.Base.ItemsModel`1 < !T >::get_SelectedItem() // initobj !T // // We need to generate code like this: // // ldloca.s local // initobj !T // ldloc.0 // call instance void Catel.Fody.TestAssembly.CSharp6_AutoPropertyInitializer_Generic_ExpectedCode`1 < !T >::set_SelectedItem(!0) // Note: make sure to support generic types MethodReference setter = property.SetMethod; var genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType; if (genericInstanceType != null) { setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray()); } var variable = new VariableDefinition(property.PropertyType.MakeGenericIfRequired()); ctorBody.Variables.Add(variable); ctorBody.InitLocals = true; var newInstructions = new List <Instruction>(); newInstructions.Add(Instruction.Create(OpCodes.Ldloca, variable)); newInstructions.Add(instructions[i + 1]); // Just copy this instruction newInstructions.Add(Instruction.Create(OpCodes.Ldloc, variable)); newInstructions.Add(Instruction.Create(OpCodes.Call, setter)); // Remove 2 instructions instructions.RemoveAt(i); instructions.RemoveAt(i); // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready) instructions.Insert(instructions.Count - 1, newInstructions); } else { FodyEnvironment.LogError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Tried to convert it to property usage, but OpCode '{instruction.OpCode}' is not supported. Please raise an issue."); } } } ctorBody.OptimizeMacros(); } declaringType.Fields.Remove(field); } }