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);
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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);
        }
Example #4
0
    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)));
        }