public static Instruction CreateSetInstruction(this PropertyDefinition property) { if (property.SetMethod != null) { return(Instruction.Create(OpCodes.Call, property.SetMethod)); } var field = property.GetBackingField(); return(field != null?Instruction.Create(OpCodes.Stfld, field) : null); }
private bool TryCopy(PropertyDefinition property, out IEnumerable <Instruction> instructions) { if (property.AnyAttribute(IgnoreDuringDeepCopyAttribute)) { property.CustomAttributes.Remove(property.SingleAttribute(IgnoreDuringDeepCopyAttribute)); instructions = null; return(false); } if (property.GetMethod == null || property.SetMethod == null && property.GetBackingField() == null) { instructions = null; return(false); } if (property.AnyAttribute(DeepCopyByReferenceAttribute)) { property.CustomAttributes.Remove(property.SingleAttribute(DeepCopyByReferenceAttribute)); instructions = new[] { Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Ldarg_1), Instruction.Create(OpCodes.Callvirt, property.GetMethod), property.CreateSetInstruction() }; return(true); } var source = ValueSource.New().Property(property); var target = ValueTarget.New().Property(property); var list = new List <Instruction>(); instructions = list; if (property.PropertyType.IsArray) { using (new IfNotNull(list, source)) list.AddRange(CopyArray(property)); return(true); } instructions = Copy(property.PropertyType, source, target); return(true); }
/// <summary> /// Weaves a property on an observable object. /// </summary> /// <param name="property">The property to make observable.</param> /// <param name="defaultEnhancer">The type of the default Enhancer.</param> private void WeaveProperty(PropertyDefinition property, TypeReference defaultEnhancer) { var module = property.Module; var getMethod = property.GetMethod; var setMethod = property.SetMethod; var declaringType = property.DeclaringType; if (!getMethod.CustomAttributes.Any(x => x.AttributeType.FullName == typeof(CompilerGeneratedAttribute).FullName)) { this.ParentWeaver.LogWarning(string.Format(CultureInfo.CurrentCulture, Resources.PropertyNotAutogenerated, property.Name, declaringType.Name)); return; } if (setMethod == null) { this.ParentWeaver.LogWarning(string.Format(CultureInfo.CurrentCulture, Resources.NoSetterForObservable, property.Name, declaringType.Name)); return; } // property name var propertyName = property.Name; var observableAttribute = property.CustomAttributes.SingleOrDefault(x => x.AttributeType.FullName == this.observableAttributeReference.FullName); // default enhancer var defaultEhancerType = defaultEnhancer; var enhancerType = defaultEhancerType; if (observableAttribute != null) { foreach (var constructorArgument in observableAttribute.ConstructorArguments) { if (constructorArgument.Type.FullName == typeof(string).FullName) { propertyName = constructorArgument.Value as string; } if (constructorArgument.Type.FullName == typeof(Type).FullName) { enhancerType = module.ImportReference(constructorArgument.Value as Type); } } } // Get the backing field and remove it. var backingField = property.GetBackingField(); module.ImportReference(backingField); declaringType.Fields.Remove(backingField); // get or create the ObservableObjectField. FieldDefinition observableObjectField = declaringType.Fields.FirstOrDefault(x => x.FieldType.FullName == this.observableObjectReference.FullName); if (observableObjectField is null) { var observableFieldTypeReference = this.observableObjectReference; observableObjectField = declaringType.CreateField(observableFieldTypeReference, InnerObservableObjectFieldName); // push IL code for initialization of observableObject to the queue to emit in the ISharedState setter. this.ProcessorQueue.SharedStateAssignmentQueue.Enqueue( (declaringType, false, (processor, sharedStateBackingField) => this.EmitObservableObjectInit( processor, declaringType.Name, defaultEhancerType, sharedStateBackingField, observableObjectField))); } // push IL code for initialization of a property to the queue to emit in the ISharedState setter. this.ProcessorQueue.SharedStateAssignmentQueue.Enqueue( (declaringType, false, (processor, sharedStateBackingField) => this.EmitObservablePropertyAdd( processor, propertyName, property.PropertyType, enhancerType, sharedStateBackingField, observableObjectField))); this.RewriteGetMethod(getMethod, observableObjectField, propertyName, property.PropertyType); this.RewriteSetMethod(setMethod, observableObjectField, propertyName, property.PropertyType); }
private WeaveResult WeaveProperty(PropertyDefinition prop, TypeDefinition type, Dictionary <string, Tuple <MethodReference, MethodReference> > methodTable) { var columnName = prop.Name; var mapToAttribute = prop.CustomAttributes.FirstOrDefault(a => a.AttributeType.Name == "MapToAttribute"); if (mapToAttribute != null) { columnName = (string)mapToAttribute.ConstructorArguments[0].Value; } var backingField = prop.GetBackingField(); var isIndexed = prop.CustomAttributes.Any(a => a.AttributeType.Name == "IndexedAttribute"); if (isIndexed && (!_indexableTypes.Contains(prop.PropertyType.FullName))) { return(WeaveResult.Error($"{type.Name}.{prop.Name} is marked as [Indexed] which is only allowed on integral types as well as string, bool and DateTimeOffset, not on {prop.PropertyType.FullName}.")); } var isPrimaryKey = prop.CustomAttributes.Any(a => a.AttributeType.Name == "PrimaryKeyAttribute"); if (isPrimaryKey && (!_primaryKeyTypes.Contains(prop.PropertyType.FullName))) { return(WeaveResult.Error($"{type.Name}.{prop.Name} is marked as [PrimaryKey] which is only allowed on integral and string types, not on {prop.PropertyType.FullName}.")); } if (!prop.IsAutomatic()) { if (prop.PropertyType.Resolve().BaseType.IsSameAs(_realmObject)) { return(WeaveResult.Warning($"{type.Name}.{columnName} is not an automatic property but its type is a RealmObject which normally indicates a relationship.")); } return(WeaveResult.Skipped()); } if (_typeTable.ContainsKey(prop.PropertyType.FullName)) { // If the property is automatic but doesn't have a setter, we should still ignore it. if (prop.SetMethod == null) { return(WeaveResult.Skipped()); } var typeId = prop.PropertyType.FullName + (isPrimaryKey ? " unique" : string.Empty); if (!methodTable.ContainsKey(typeId)) { var getter = _realmObject.LookupMethodReference("Get" + _typeTable[prop.PropertyType.FullName] + "Value", ModuleDefinition); var setter = _realmObject.LookupMethodReference("Set" + _typeTable[prop.PropertyType.FullName] + "Value" + (isPrimaryKey ? "Unique" : string.Empty), ModuleDefinition); methodTable[typeId] = Tuple.Create(getter, setter); } ReplaceGetter(prop, columnName, methodTable[typeId].Item1); ReplaceSetter(prop, backingField, columnName, methodTable[typeId].Item2); } // treat IList and RealmList similarly but IList gets a default so is useable as standalone // IList or RealmList allows people to declare lists only of _realmObject due to the class definition else if (prop.IsIList()) { var elementType = ((GenericInstanceType)prop.PropertyType).GenericArguments.Single(); if (!elementType.Resolve().BaseType.IsSameAs(_realmObject)) { return(WeaveResult.Warning($"SKIPPING {type.Name}.{columnName} because it is an IList but its generic type is not a RealmObject subclass, so will not persist.")); } if (prop.SetMethod != null) { return(WeaveResult.Error($"{type.Name}.{columnName} has a setter but its type is a IList which only supports getters.")); } var concreteListType = new GenericInstanceType(_system_IList) { GenericArguments = { elementType } }; var listConstructor = concreteListType.Resolve().GetConstructors().Single(c => c.IsPublic && c.Parameters.Count == 0); var concreteListConstructor = listConstructor.MakeHostInstanceGeneric(elementType); // weaves list getter which also sets backing to List<T>, forcing it to accept us setting it post-init var backingDef = backingField as FieldDefinition; if (backingDef != null) { backingDef.Attributes &= ~FieldAttributes.InitOnly; // without a set; auto property has this flag we must clear } ReplaceListGetter(prop, backingField, columnName, new GenericInstanceMethod(_genericGetListValueReference) { GenericArguments = { elementType } }, ModuleDefinition.ImportReference(concreteListConstructor)); } else if (prop.PropertyType.Resolve().BaseType.IsSameAs(_realmObject)) { if (!prop.IsAutomatic()) { return(WeaveResult.Warning($"{type.Name}.{columnName} is not an automatic property but its type is a RealmObject which normally indicates a relationship.")); } // with casting in the _realmObject methods, should just work ReplaceGetter(prop, columnName, new GenericInstanceMethod(_genericGetObjectValueReference) { GenericArguments = { prop.PropertyType } }); ReplaceSetter(prop, backingField, columnName, new GenericInstanceMethod(_genericSetObjectValueReference) { GenericArguments = { prop.PropertyType } }); } else if (prop.PropertyType.FullName == "System.DateTime") { return(WeaveResult.Error($"Class '{type.Name}' field '{prop.Name}' is a DateTime which is not supported - use DateTimeOffset instead.")); } else if (prop.PropertyType.FullName == "System.Nullable`1<System.DateTime>") { return(WeaveResult.Error($"Class '{type.Name}' field '{prop.Name}' is a DateTime? which is not supported - use DateTimeOffset? instead.")); } else { return(WeaveResult.Error($"Class '{type.Name}' field '{columnName}' is a '{prop.PropertyType}' which is not yet supported.")); } var preserveAttribute = new CustomAttribute(_preserveAttributeConstructor); prop.CustomAttributes.Add(preserveAttribute); var wovenPropertyAttribute = new CustomAttribute(_wovenPropertyAttributeConstructor); prop.CustomAttributes.Add(wovenPropertyAttribute); Debug.WriteLine(string.Empty); var primaryKeyMsg = isPrimaryKey ? "[PrimaryKey]" : string.Empty; var indexedMsg = isIndexed ? "[Indexed]" : string.Empty; LogDebug($"Woven {type.Name}.{prop.Name} as a {prop.PropertyType.FullName} {primaryKeyMsg} {indexedMsg}."); return(WeaveResult.Success(prop, backingField, isPrimaryKey)); }
/// <summary> /// Reassign an Enumerable property by calling a constructor of an observable enumerable type. /// </summary> /// <param name="propertyDefinition">The property definition to use to replace.</param> /// <param name="observableEnumerableType">The observable enumerable type.</param> /// <param name="defaultEnhancer">The default enhancer to use.</param> private void ReassignEnumerable(PropertyDefinition propertyDefinition, TypeReference observableEnumerableType, TypeReference defaultEnhancer) { var module = propertyDefinition.Module; var declaringType = propertyDefinition.DeclaringType; var importedType = observableEnumerableType; var genricConstructor = importedType.Resolve().Methods.Single(x => x.IsConstructor); MethodReference constructorReference; // property name var propertyName = propertyDefinition.Name; var observableAttribute = propertyDefinition.CustomAttributes.SingleOrDefault(x => x.AttributeType.FullName == this.observableAttributeReference.FullName); // default enhancer var defaultEhancerType = module.ImportReference(defaultEnhancer); var enhancerType = defaultEhancerType; var propertyBackingField = propertyDefinition.GetBackingField(); if (observableAttribute != null) { foreach (var constructorArgument in observableAttribute.ConstructorArguments) { if (constructorArgument.Type.FullName == typeof(string).FullName) { propertyName = constructorArgument.Value as string; } if (constructorArgument.Type.FullName == typeof(Type).FullName) { enhancerType = module.ImportReference(constructorArgument.Value as Type); } } } if (propertyDefinition.PropertyType.IsGenericInstance) { var instantiatedType = new GenericInstanceType(importedType); foreach (var argument in (propertyDefinition.PropertyType as GenericInstanceType).GenericArguments) { instantiatedType.GenericArguments.Add(module.ImportReference(argument)); } constructorReference = genricConstructor.GetGenericMethodOnInstantance(module.ImportReference(instantiatedType)); } else { var instantiatedType = new GenericInstanceType(importedType); instantiatedType.GenericArguments.Add(module.TypeSystem.Object); constructorReference = genricConstructor.GetGenericMethodOnInstantance(module.ImportReference(instantiatedType)); } // push IL code for initialization of a property to the queue to emit in the ISharedState setter. this.processorQueue.SharedStateAssignmentQueue.Enqueue( (declaringType, false, (processor, sharedStateBackingField) => this.EmitObservableEnumerableCreation( processor, propertyName, constructorReference, enhancerType, sharedStateBackingField, propertyBackingField))); }