Пример #1
0
    public void Execute()
    {
        // UNCOMMENT THIS DEBUGGER LAUNCH TO BE ABLE TO RUN A SEPARATE VS INSTANCE TO DEBUG WEAVING WHILST BUILDING
        // Debugger.Launch();

        var submitAnalytics = System.Threading.Tasks.Task.Factory.StartNew(() => {
            var analytics = new RealmWeaver.Analytics(ModuleDefinition);
            try {
                analytics.SubmitAnalytics();
            } catch (Exception e) {
                LogDebug("Error submitting analytics: " + e.Message);
            }
        });

        RealmAssembly = ModuleDefinition.AssemblyResolver.Resolve("Realm");  // Note that the assembly is Realm but the namespace Realms with the s

        RealmObject = RealmAssembly.MainModule.GetTypes().First(x => x.Name == "RealmObject");
        RealmObjectIsManagedGetter = ModuleDefinition.ImportReference(RealmObject.Properties.Single(x => x.Name == "IsManaged").GetMethod);

        var typeTable = new Dictionary <string, string>()
        {
            { "System.String", "String" },
            { "System.Char", "Char" },
            { "System.Byte", "Byte" },
            { "System.Int16", "Int16" },
            { "System.Int32", "Int32" },
            { "System.Int64", "Int64" },
            { "System.Single", "Single" },
            { "System.Double", "Double" },
            { "System.Boolean", "Boolean" },
            { "System.DateTimeOffset", "DateTimeOffset" },
            { "System.Byte[]", "ByteArray" },
            { "System.Nullable`1<System.Char>", "NullableChar" },
            { "System.Nullable`1<System.Byte>", "NullableByte" },
            { "System.Nullable`1<System.Int16>", "NullableInt16" },
            { "System.Nullable`1<System.Int32>", "NullableInt32" },
            { "System.Nullable`1<System.Int64>", "NullableInt64" },
            { "System.Nullable`1<System.Single>", "NullableSingle" },
            { "System.Nullable`1<System.Double>", "NullableDouble" },
            { "System.Nullable`1<System.Boolean>", "NullableBoolean" },
            { "System.Nullable`1<System.DateTimeOffset>", "NullableDateTimeOffset" }
        };

        // Cache of getter and setter methods for the various types.
        var methodTable = new Dictionary <string, Tuple <MethodReference, MethodReference> >();

        var objectIdTypes = new List <string>
        {
            "System.String",
            "System.Char",
            "System.Byte",
            "System.Int16",
            "System.Int32",
            "System.Int64",
        };

        var indexableTypes = new List <string>(objectIdTypes);

        indexableTypes.Add("System.Boolean");
        indexableTypes.Add("System.DateTimeOffset");

        var genericGetObjectValueReference = LookupMethodAndImport(RealmObject, "GetObjectValue");
        var genericSetObjectValueReference = LookupMethodAndImport(RealmObject, "SetObjectValue");
        var genericGetListValueReference   = LookupMethodAndImport(RealmObject, "GetListValue");

        var wovenAttributeClass       = RealmAssembly.MainModule.GetTypes().First(x => x.Name == "WovenAttribute");
        var wovenAttributeConstructor = ModuleDefinition.ImportReference(wovenAttributeClass.GetConstructors().First());

        var wovenPropertyAttributeClass       = RealmAssembly.MainModule.GetTypes().First(x => x.Name == "WovenPropertyAttribute");
        var wovenPropertyAttributeConstructor = ModuleDefinition.ImportReference(wovenPropertyAttributeClass.GetConstructors().First());

        CorLib         = ModuleDefinition.AssemblyResolver.Resolve((AssemblyNameReference)ModuleDefinition.TypeSystem.CoreLibrary);
        System_Object  = CorLib.MainModule.GetType("System.Object");
        System_Boolean = CorLib.MainModule.GetType("System.Boolean");
        System_String  = ModuleDefinition.ImportReference(CorLib.MainModule.GetType("System.String"));
        System_Type    = ModuleDefinition.ImportReference(CorLib.MainModule.GetType("System.Type"));
        // WARNING the GetType("System.Collections.Generic.List`1") below RETURNS NULL WHEN COMPILING A PCL
        // UNUSED SO COMMENT OUT var listType = ModuleDefinition.ImportReference(CorLib.MainModule.GetType("System.Collections.Generic.List`1"));

        var systemAssembly            = ModuleDefinition.AssemblyResolver.Resolve("System");
        var systemObjectModelAssembly = TryResolveAssembly("System.ObjectModel");

        var propertyChangedEventArgs = LookupType("PropertyChangedEventArgs", systemObjectModelAssembly, systemAssembly);

        PropChangedEventArgsConstructor = ModuleDefinition.ImportReference(propertyChangedEventArgs.GetConstructors().First());

        var propChangedEventHandlerDefinition = LookupType("PropertyChangedEventHandler", systemObjectModelAssembly, systemAssembly);

        PropChangedEventHandlerReference       = ModuleDefinition.ImportReference(propChangedEventHandlerDefinition);
        PropChangedEventHandlerInvokeReference = ModuleDefinition.ImportReference(propChangedEventHandlerDefinition.Methods.First(x => x.Name == "Invoke"));

        // If the solution has a reference to PropertyChanged.Fody, let's look up the DoNotNotifyAttribute for use later.
        var usesPropertyChangedFody = ModuleDefinition.AssemblyReferences.Any(X => X.Name == "PropertyChanged");

        if (usesPropertyChangedFody)
        {
            var propChangedAssembly            = ModuleDefinition.AssemblyResolver.Resolve("PropertyChanged");
            var doNotNotifyAttributeDefinition = propChangedAssembly.MainModule.GetTypes().First(X => X.Name == "DoNotNotifyAttribute");
            PropChangedDoNotNotifyAttributeConstructorDefinition = ModuleDefinition.ImportReference(doNotNotifyAttributeDefinition.GetConstructors().First());
        }

        Debug.WriteLine("Weaving file: " + ModuleDefinition.FullyQualifiedName);

        foreach (var type in GetMatchingTypes())
        {
            if (type == null)
            {
                Debug.WriteLine("Weaving skipping null type from GetMatchingTypes");
                continue;
            }
            Debug.WriteLine("Weaving " + type.Name);

            var typeImplementsPropertyChanged = type.Interfaces.Any(t => t.FullName == "System.ComponentModel.INotifyPropertyChanged");

            EventDefinition propChangedEventDefinition = null;
            FieldDefinition propChangedFieldDefinition = null;

            if (typeImplementsPropertyChanged)
            {
                propChangedEventDefinition = type.Events.First(X => X.FullName.EndsWith("::PropertyChanged"));
                propChangedFieldDefinition = type.Fields.First(X => X.FullName.EndsWith("::PropertyChanged"));
            }

            foreach (var prop in type.Properties.Where(x => x.HasThis && !x.CustomAttributes.Any(a => a.AttributeType.Name == "IgnoredAttribute")))
            {
                var sequencePoint = prop.GetMethod.Body.Instructions.First().SequencePoint;

                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 = GetBackingField(prop);

                Debug.Write("  - " + prop.PropertyType.FullName + " " + prop.Name + " (Column: " + columnName + ").. ");

                var isIndexed = prop.CustomAttributes.Any(a => a.AttributeType.Name == "IndexedAttribute");
                if (isIndexed && (!indexableTypes.Contains(prop.PropertyType.FullName)))
                {
                    LogErrorPoint($"{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}", sequencePoint);
                    continue;
                }

                var isObjectId = prop.CustomAttributes.Any(a => a.AttributeType.Name == "ObjectIdAttribute");
                if (isObjectId && (!objectIdTypes.Contains(prop.PropertyType.FullName)))
                {
                    LogErrorPoint($"{type.Name}.{prop.Name} is marked as [ObjectId] which is only allowed on integral and string types, not on {prop.PropertyType.FullName}", sequencePoint);
                    continue;
                }

                if (!prop.IsAutomatic())
                {
                    if (IsRealmObject(prop.PropertyType))
                    {
                        LogWarningPoint($"{type.Name}.{columnName} is not an automatic property but its type is a RealmObject which normally indicates a relationship", sequencePoint);
                    }

                    Debug.WriteLine("Skipped because it's not automatic.");
                    continue;
                }
                if (typeTable.ContainsKey(prop.PropertyType.FullName))
                {
                    var typeId = prop.PropertyType.FullName + (isObjectId ? " unique" : "");
                    if (!methodTable.ContainsKey(typeId))
                    {
                        var getter = LookupMethodAndImport(RealmObject, "Get" + typeTable[prop.PropertyType.FullName] + "Value");
                        var setter = LookupMethodAndImport(RealmObject, "Set" + typeTable[prop.PropertyType.FullName] + "Value" + (isObjectId ? "Unique": ""));
                        methodTable[typeId] = Tuple.Create(getter, setter);
                    }

                    ReplaceGetter(prop, columnName, methodTable[typeId].Item1);
                    ReplaceSetter(prop, backingField, columnName, methodTable[typeId].Item2, typeImplementsPropertyChanged, propChangedEventDefinition, propChangedFieldDefinition);
                }
//                else if (prop.PropertyType.Name == "IList`1" && prop.PropertyType.Namespace == "System.Collections.Generic")
                else if (prop.PropertyType.Name == "RealmList`1" && prop.PropertyType.Namespace == "Realms")
                {
                    // RealmList allows people to declare lists only of RealmObject due to the class definition
                    if (!prop.IsAutomatic())
                    {
                        LogErrorPoint($"{type.Name}.{columnName} is not an automatic property but its type is a RealmList which normally indicates a relationship", sequencePoint);
                        continue;
                    }
                    if (prop.SetMethod != null)
                    {
                        LogErrorPoint($"{type.Name}.{columnName} has a setter but its type is a RealmList which only supports getters", sequencePoint);
                        continue;
                    }

                    var elementType = ((GenericInstanceType)prop.PropertyType).GenericArguments.Single();
                    ReplaceGetter(prop, columnName, new GenericInstanceMethod(genericGetListValueReference)
                    {
                        GenericArguments = { elementType }
                    });
                }
                else if (IsRealmObject(prop.PropertyType))
                {
                    if (!prop.IsAutomatic())
                    {
                        LogWarningPoint($"{type.Name}.{columnName} is not an automatic property but its type is a RealmObject which normally indicates a relationship", sequencePoint);
                        continue;
                    }

                    ReplaceGetter(prop, columnName, new GenericInstanceMethod(genericGetObjectValueReference)
                    {
                        GenericArguments = { prop.PropertyType }
                    });
                    ReplaceSetter(prop, backingField, columnName, new GenericInstanceMethod(genericSetObjectValueReference)
                    {
                        GenericArguments = { prop.PropertyType }
                    }, typeImplementsPropertyChanged, propChangedEventDefinition, propChangedFieldDefinition);                                                                                                                                                     // with casting in the RealmObject methods, should just work
                }
                else if (prop.PropertyType.FullName == "System.DateTime")
                {
                    LogErrorPoint($"class '{type.Name}' field '{prop.Name}' is a DateTime which is not supported - use DateTimeOffset instead.", sequencePoint);
                }
                else
                {
                    LogErrorPoint($"class '{type.Name}' field '{columnName}' is a '{prop.PropertyType}' which is not yet supported", sequencePoint);
                }

                var wovenPropertyAttribute = new CustomAttribute(wovenPropertyAttributeConstructor);
                wovenPropertyAttribute.ConstructorArguments.Add(new CustomAttributeArgument(System_String, backingField.Name));
                prop.CustomAttributes.Add(wovenPropertyAttribute);

                Debug.WriteLine("");
            }

            var           wovenAttribute = new CustomAttribute(wovenAttributeConstructor);
            TypeReference helperType     = WeaveRealmObjectHelper(type);
            wovenAttribute.ConstructorArguments.Add(new CustomAttributeArgument(System_Type, helperType));
            type.CustomAttributes.Add(wovenAttribute);
            Debug.WriteLine("");
        }

        submitAnalytics.Wait();
        return;
    }
Пример #2
0
    public void Execute()
    {
        // UNCOMMENT THIS DEBUGGER LAUNCH TO BE ABLE TO RUN A SEPARATE VS INSTANCE TO DEBUG WEAVING WHILST BUILDING
        // Debugger.Launch();  

        Debug.WriteLine("Weaving file: " + ModuleDefinition.FullyQualifiedName);

        var submitAnalytics = System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            var analytics = new RealmWeaver.Analytics(ModuleDefinition);
            try
            {
                analytics.SubmitAnalytics();
            }
            catch (Exception e)
            {
                LogDebug("Error submitting analytics: " + e.Message);
            }
        });

        _references = RealmWeaver.ImportedReferences.Create(ModuleDefinition);

        // Cache of getter and setter methods for the various types.
        var methodTable = new Dictionary<string, Tuple<MethodReference, MethodReference>>();

        foreach (var type in GetMatchingTypes())
        {
            try
            {
                WeaveType(type, methodTable);
            }
            catch (Exception e)
            {
                LogError($"Unexpected error caught weaving type '{type.Name}': {e.Message}.\r\nCallstack:\r\n{e.StackTrace}");
            }
        }

        submitAnalytics.Wait();
    }
Пример #3
0
    public void Execute()
    {
        // UNCOMMENT THIS DEBUGGER LAUNCH TO BE ABLE TO RUN A SEPARATE VS INSTANCE TO DEBUG WEAVING WHILST BUILDING
        // Debugger.Launch();

        Debug.WriteLine("Weaving file: " + ModuleDefinition.FullyQualifiedName);

        var submitAnalytics = System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            var analytics = new RealmWeaver.Analytics(ModuleDefinition);
            try
            {
                analytics.SubmitAnalytics();
            }
            catch (Exception e)
            {
                LogDebug("Error submitting analytics: " + e.Message);
            }
        });

        _realmAssembly = ModuleDefinition.AssemblyResolver.Resolve("Realm");  // Note that the assembly is Realm but the namespace Realms with the s

        _realmObject = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "RealmObject");
        _realmObjectIsManagedGetter = ModuleDefinition.ImportReference(_realmObject.Properties.Single(x => x.Name == "IsManaged").GetMethod);

        // Cache of getter and setter methods for the various types.
        var methodTable = new Dictionary <string, Tuple <MethodReference, MethodReference> >();

        _genericGetObjectValueReference = LookupMethodAndImport(_realmObject, "GetObjectValue");
        _genericSetObjectValueReference = LookupMethodAndImport(_realmObject, "SetObjectValue");
        _genericGetListValueReference   = LookupMethodAndImport(_realmObject, "GetListValue");

        var wovenAttributeClass = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "WovenAttribute");

        _wovenAttributeConstructor = ModuleDefinition.ImportReference(wovenAttributeClass.GetConstructors().First());

        var wovenPropertyAttributeClass = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "WovenPropertyAttribute");

        _wovenPropertyAttributeConstructor = ModuleDefinition.ImportReference(wovenPropertyAttributeClass.GetConstructors().First());

        _corLib        = ModuleDefinition.AssemblyResolver.Resolve((AssemblyNameReference)ModuleDefinition.TypeSystem.CoreLibrary);
        System_Object  = _corLib.MainModule.GetType("System.Object");
        System_Boolean = _corLib.MainModule.GetType("System.Boolean");
        System_String  = ModuleDefinition.ImportReference(_corLib.MainModule.GetType("System.String"));
        System_Type    = ModuleDefinition.ImportReference(_corLib.MainModule.GetType("System.Type"));
        // WARNING the GetType("System.Collections.Generic.List`1") below RETURNS NULL WHEN COMPILING A PCL
        // UNUSED SO COMMENT OUT var listType = ModuleDefinition.ImportReference(_corLib.MainModule.GetType("System.Collections.Generic.List`1"));

        var systemAssembly            = ModuleDefinition.AssemblyResolver.Resolve("System");
        var systemObjectModelAssembly = TryResolveAssembly("System.ObjectModel");

        var propertyChangedEventArgs = LookupType("PropertyChangedEventArgs", systemObjectModelAssembly, systemAssembly);

        _propChangedEventArgsConstructor = ModuleDefinition.ImportReference(propertyChangedEventArgs.GetConstructors().First());

        var propChangedEventHandlerDefinition = LookupType("PropertyChangedEventHandler", systemObjectModelAssembly, systemAssembly);

        _propChangedEventHandlerReference       = ModuleDefinition.ImportReference(propChangedEventHandlerDefinition);
        _propChangedEventHandlerInvokeReference = ModuleDefinition.ImportReference(propChangedEventHandlerDefinition.Methods.First(x => x.Name == "Invoke"));

        // If the solution has a reference to PropertyChanged.Fody, let's look up the DoNotNotifyAttribute for use later.
        var usesPropertyChangedFody = ModuleDefinition.AssemblyReferences.Any(X => X.Name == "PropertyChanged");

        if (usesPropertyChangedFody)
        {
            var propChangedAssembly            = ModuleDefinition.AssemblyResolver.Resolve("PropertyChanged");
            var doNotNotifyAttributeDefinition = propChangedAssembly.MainModule.GetTypes().First(X => X.Name == "DoNotNotifyAttribute");
            _propChangedDoNotNotifyAttributeConstructorDefinition = ModuleDefinition.ImportReference(doNotNotifyAttributeDefinition.GetConstructors().First());
        }

        foreach (var type in GetMatchingTypes())
        {
            try
            {
                WeaveType(type, methodTable);
            }
            catch (Exception e)
            {
                LogError($"Unexpected error caught weaving type '{type.Name}': {e.Message}.\r\nCallstack:\r\n{e.StackTrace}");
            }
        }

        submitAnalytics.Wait();
    }
Пример #4
0
    public void Execute()
    {
        // UNCOMMENT THIS DEBUGGER LAUNCH TO BE ABLE TO RUN A SEPARATE VS INSTANCE TO DEBUG WEAVING WHILST BUILDING
        // Debugger.Launch();

        Debug.WriteLine("Weaving file: " + ModuleDefinition.FullyQualifiedName);

        var submitAnalytics = System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            var analytics = new RealmWeaver.Analytics(ModuleDefinition);
            try
            {
                analytics.SubmitAnalytics();
            }
            catch (Exception e)
            {
                LogDebug("Error submitting analytics: " + e.Message);
            }
        });

        _realmAssembly = AssemblyResolver.Resolve("Realm");  // Note that the assembly is Realm but the namespace Realms with the s

        _realmObject = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "RealmObject");
        _realmObjectIsManagedGetter      = ModuleDefinition.ImportReference(_realmObject.Properties.Single(x => x.Name == "IsManaged").GetMethod);
        _realmObjectRealmGetter          = ModuleDefinition.ImportReference(_realmObject.Properties.Single(p => p.Name == "Realm").GetMethod);
        _realmObjectRaisePropertyChanged = ModuleDefinition.ImportReference(_realmObject.Methods.Single(m => m.Name == "RaisePropertyChanged"));

        var realm = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "Realm");

        _realmAddGenericReference = ModuleDefinition.ImportReference(realm.Methods.First(x => x.Name == "Add" && x.HasGenericParameters));

        // Cache of getter and setter methods for the various types.
        var methodTable = new Dictionary <string, Tuple <MethodReference, MethodReference> >();

        _genericGetObjectValueReference = _realmObject.LookupMethodReference("GetObjectValue", ModuleDefinition);
        _genericSetObjectValueReference = _realmObject.LookupMethodReference("SetObjectValue", ModuleDefinition);
        _genericGetListValueReference   = _realmObject.LookupMethodReference("GetListValue", ModuleDefinition);

        var preserveAttributeClass = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "PreserveAttribute");

        _preserveAttributeConstructor           = ModuleDefinition.ImportReference(preserveAttributeClass.GetConstructors().Single(c => c.Parameters.Count == 0));
        _preserveAttributeConstructorWithParams = ModuleDefinition.ImportReference(preserveAttributeClass.GetConstructors().Single(c => c.Parameters.Count == 2));

        var wovenAttributeClass = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "WovenAttribute");

        _wovenAttributeConstructor = ModuleDefinition.ImportReference(wovenAttributeClass.GetConstructors().First());

        var wovenPropertyAttributeClass = _realmAssembly.MainModule.GetTypes().First(x => x.Name == "WovenPropertyAttribute");

        _wovenPropertyAttributeConstructor = ModuleDefinition.ImportReference(wovenPropertyAttributeClass.GetConstructors().First());

        _corLib         = AssemblyResolver.Resolve((AssemblyNameReference)ModuleDefinition.TypeSystem.CoreLibrary);
        _system_Object  = ModuleDefinition.TypeSystem.Object;
        _system_Boolean = ModuleDefinition.TypeSystem.Boolean;
        _system_Int32   = ModuleDefinition.TypeSystem.Int32;

        var dateTimeOffsetType = GetTypeFromSystemAssembly("System.DateTimeOffset");

        _system_DateTimeOffset = ModuleDefinition.ImportReference(dateTimeOffsetType);
        _system_DatetimeOffset_Op_Inequality = ModuleDefinition.ImportReference(dateTimeOffsetType.GetMethods().Single(m => m.Name == "op_Inequality"));

        _system_Type = ModuleDefinition.ImportReference(GetTypeFromSystemAssembly("System.Type"));

        var listTypeDefinition = _corLib.MainModule.GetType("System.Collections.Generic.List`1");

        if (listTypeDefinition == null)
        {
            _system_IList = ModuleDefinition.ImportReference(typeof(System.Collections.Generic.List <>));
        }
        else
        {
            _system_IList = ModuleDefinition.ImportReference(listTypeDefinition);
        }

        // If the solution has a reference to PropertyChanged.Fody, let's look up the DoNotNotifyAttribute for use later.
        var usesPropertyChangedFody = ModuleDefinition.AssemblyReferences.Any(X => X.Name == "PropertyChanged");

        if (usesPropertyChangedFody)
        {
            var propChangedAssembly            = AssemblyResolver.Resolve("PropertyChanged");
            var doNotNotifyAttributeDefinition = propChangedAssembly.MainModule.GetTypes().First(X => X.Name == "DoNotNotifyAttribute");
            _propChangedDoNotNotifyAttributeConstructorDefinition = ModuleDefinition.ImportReference(doNotNotifyAttributeDefinition.GetConstructors().First());
        }

        foreach (var type in GetMatchingTypes())
        {
            try
            {
                WeaveType(type, methodTable);
            }
            catch (Exception e)
            {
                LogError($"Unexpected error caught weaving type '{type.Name}': {e.Message}.\r\nCallstack:\r\n{e.StackTrace}");
            }
        }

        submitAnalytics.Wait();
    }