コード例 #1
0
ファイル: IlInjector.cs プロジェクト: thedeveus/NObservable
        public static FieldReference FindOrInjectTrackerField(WeavingContext context, TypeDefinition type)
        {
            var found = FindTrackerField(type);

            if (found != null)
            {
                return(found);
            }
            var field = new FieldDefinition(FieldName, FieldAttributes.FamANDAssem,
                                            context.PropertyTrackerReference);

            type.Fields.Add(field);
            foreach (var ctor in type.GetConstructors())
            {
                using (var il = new Helper(ctor))
                {
                    var first = ctor.Body.Instructions.First();
                    il.AddBefore(first, new Instructions
                    {
                        OpCodes.Ldarg_0,
                        { OpCodes.Ldflda, field },
                        { OpCodes.Call, context.PropertyTrackerInitReference }
                    });
                }
            }



            return(field);
        }
コード例 #2
0
 static FieldReference FindOrInjectTrackerField(WeavingContext context, TypeDefinition type)
 {
     return(FindOrInjectField(context, type, TrackerFieldName, context.PropertyTrackerReference, (il, field) =>
     {
         var first = il.Body.Instructions.First();
         il.AddBefore(first, new Instructions
         {
             OpCodes.Ldarg_0,
             { OpCodes.Ldflda, field },
             { OpCodes.Call, context.PropertyTrackerInitReference }
         });
     }));
 }
コード例 #3
0
 static FieldReference FindOrInjectBlazorHelperField(WeavingContext context, TypeDefinition type)
 {
     return(FindOrInjectField(context, type, BlazorHelperFieldName, context.BlazorComponentHelperReference,
                              (il, field) =>
     {
         il.AddBefore(il.Body.Instructions.First(), new Instructions
         {
             OpCodes.Ldarg_0,
             { OpCodes.Ldfld, field },
             { OpCodes.Brtrue, il.Body.Instructions.First() },
             OpCodes.Ldarg_0,
             OpCodes.Ldarg_0,
             { OpCodes.Newobj, context.BlazorComponentHelperCtorReference },
             { OpCodes.Stfld, field }
         });
     }));
 }
コード例 #4
0
        static FieldReference FindOrInjectField(WeavingContext context, TypeDefinition type,
                                                string fieldName, TypeReference fieldType, Action <Helper, FieldDefinition> builder)

        {
            var found = FindTrackerField(type, fieldName);

            if (found != null)
            {
                return(found);
            }
            var field = new FieldDefinition(fieldName, FieldAttributes.FamANDAssem, fieldType);

            type.Fields.Add(field);
            foreach (var ctor in type.GetConstructors())
            {
                using (var il = new Helper(ctor))
                {
                    builder(il, field);
                }
            }
            return(field);
        }
コード例 #5
0
ファイル: IlInjector.cs プロジェクト: thedeveus/NObservable
        static int SetNextPropertyId(WeavingContext context, TypeDefinition typeDef)
        {
            var existing =
                typeDef.CustomAttributes.FirstOrDefault(a =>
                                                        a.AttributeType.AreEqual(context.PropertyCountAttributeReference));

            if (existing != null)
            {
                var nextId = (int)existing.ConstructorArguments[0].Value + 1;
                existing.ConstructorArguments[0] = new CustomAttributeArgument(existing.AttributeType, nextId);
                return(nextId);
            }
            else
            {
                var nextId = FindBaseTypeObservablePropertyCount(typeDef) + 1;
                typeDef.CustomAttributes.Add(new CustomAttribute(context.PropertyCountAttributeCtorReference)
                {
                    ConstructorArguments =
                    { new CustomAttributeArgument(context.TypeSystem.Int32Reference, nextId) }
                });
                return(nextId);
            }
        }
コード例 #6
0
ファイル: IlInjector.cs プロジェクト: thedeveus/NObservable
        public static void InstrumentProperty(WeavingContext context, PropertyDefinition prop)
        {
            var getter       = prop.GetMethod;
            var setter       = prop.SetMethod;
            var propId       = SetNextPropertyId(context, getter.DeclaringType);
            var trackerField = FindOrInjectTrackerField(context, getter.DeclaringType);

            using (var getterIl = new Helper(getter))
            {
                var getterFirst = getter.Body.Instructions.First();
                getterIl.AddBefore(getterFirst, new Instructions
                {
                    OpCodes.Ldarg_0,
                    { OpCodes.Ldflda, trackerField },
                    { OpCodes.Ldc_I4, propId },
                    { OpCodes.Call, context.PropertyTrackerTrackGetReference }
                });
            }

            using (var setterIl = new Helper(setter))
            {
                var oldValueVariable = new VariableDefinition(prop.PropertyType);
                var oldValueIsSet    = new VariableDefinition(context.TypeSystem.BooleanReference);
                setter.Body.Variables.Add(oldValueVariable);
                setter.Body.Variables.Add(oldValueIsSet);


                var originalSetterFirst = setter.Body.Instructions.First();

                // Prologue


                setterIl.AddBefore(originalSetterFirst, new Instructions
                {
                    // var oldValueIsSet = false
                    { OpCodes.Ldc_I4_0 },
                    { OpCodes.Stloc, oldValueIsSet },

                    // this._tracker.EnterTrackSet();
                    { OpCodes.Ldarg_0 },
                    { OpCodes.Ldflda, trackerField },
                    { OpCodes.Call, context.PropertyTrackerEnterTrackSetReference },

                    // var old = this.Property;
                    { OpCodes.Ldarg_0 },
                    { OpCodes.Callvirt, getter },
                    { OpCodes.Stloc, oldValueVariable },

                    // oldValueIsSet = true
                    { OpCodes.Ldc_I4_1 },
                    { OpCodes.Stloc, oldValueIsSet }
                });


                // New final return instruction

                var lastRet = Instruction.Create(OpCodes.Ret);
                setterIl.Append(lastRet);

                var leaveTry = Instruction.Create(OpCodes.Leave_S, lastRet);
                if (lastRet.Previous.OpCode == OpCodes.Ret)
                {
                    setterIl.Replace(lastRet.Previous, leaveTry);
                }
                else
                {
                    setterIl.AddBefore(lastRet, leaveTry);
                }


                var genericTrackSet = new GenericInstanceMethod(context.PropertyTrackerTrackSetReference)
                {
                    GenericArguments = { prop.PropertyType },
                };


                //var leaveFinally = setterIl.Create(OpCodes.Leave_S, lastRet);
                var leaveFinally = Instruction.Create(OpCodes.Endfinally);


                var fastFinallyInstructions = new Instructions
                {
                    OpCodes.Ldarg_0,
                    { OpCodes.Ldflda, trackerField },
                    { OpCodes.Call, context.PropertyTrackerLeaveTrackSetReference }
                };

                var finallyInstructions = new Instructions
                {
                    // if(!oldValueIsSet) goto: fastFinally
                    { OpCodes.Ldloc, oldValueIsSet },
                    { OpCodes.Brfalse, fastFinallyInstructions.First() },

                    // this._tracker.TrackSet({id}, oldValue, Prop ({this._tracker.LeaveTrackSet()}))
                    { OpCodes.Ldarg_0 },
                    { OpCodes.Ldflda, trackerField },

                    { OpCodes.Ldc_I4, propId },

                    { OpCodes.Ldloc, oldValueVariable },

                    { OpCodes.Ldarg_0 },
                    { OpCodes.Callvirt, getter },

                    // this._tracker.LeaveTrackSet()
                    { OpCodes.Ldarg_0 },
                    { OpCodes.Ldflda, trackerField },
                    { OpCodes.Call, context.PropertyTrackerLeaveTrackSetReference },

                    { OpCodes.Call, genericTrackSet },
                    { OpCodes.Br, leaveFinally }
                };


                setterIl.AddBefore(lastRet, finallyInstructions);
                setterIl.AddBefore(lastRet, fastFinallyInstructions);
                setterIl.AddBefore(lastRet, leaveFinally);


                foreach (var i in setter.Body.Instructions.Where(x => x.OpCode == OpCodes.Ret && x != lastRet).ToList())
                {
                    setterIl.Replace(i, Instruction.Create(OpCodes.Leave_S, lastRet));
                }

                setter.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Finally)
                {
                    TryStart     = setter.Body.Instructions.First(),
                    TryEnd       = leaveTry.Next,
                    HandlerStart = leaveTry.Next,
                    HandlerEnd   = leaveFinally.Next
                });
                setter.Body.MaxStackSize += 2;
            }
        }
コード例 #7
0
        public static void InstrumentBlazorComponent(WeavingContext context, TypeDefinition type)
        {
            var field  = FindOrInjectBlazorHelperField(context, type);
            var method = type.Methods.First(m => m.Name == "BuildRenderTree");

            using (var il = new Helper(method))
            {
                var originalFirst = il.Body.Instructions.First();

                il.AddBefore(originalFirst, new Instructions()
                {
                    OpCodes.Ldarg_0,
                    { OpCodes.Ldfld, field },
                    { OpCodes.Callvirt, context.BlazorComponentHelperOnRenderEnterReference }
                });


                var lastRet = Instruction.Create(OpCodes.Ret);
                il.Append(lastRet);

                var leaveTry = Instruction.Create(OpCodes.Leave_S, lastRet);
                if (lastRet.Previous.OpCode == OpCodes.Ret)
                {
                    il.Replace(lastRet.Previous, leaveTry);
                }
                else
                {
                    il.AddBefore(lastRet, leaveTry);
                }
                foreach (var i in method.Body.Instructions.Where(x => x.OpCode == OpCodes.Ret && x != lastRet).ToList())
                {
                    il.Replace(i, Instruction.Create(OpCodes.Leave_S, lastRet));
                }

                var leaveFinally = Instruction.Create(OpCodes.Endfinally);
                il.AddBefore(lastRet, leaveFinally);


                il.AddBefore(leaveFinally, new Instructions()
                {
                    OpCodes.Ldarg_0,
                    { OpCodes.Ldfld, field },
                    { OpCodes.Callvirt, context.BlazorComponentHelperOnRenderLeaveReference }
                });

                il.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Finally)
                {
                    TryStart     = originalFirst,
                    TryEnd       = leaveTry.Next,
                    HandlerStart = leaveTry.Next,
                    HandlerEnd   = leaveFinally.Next
                });
            }
            var blazorComponent = type.FindBaseType(t =>
                                                    t.FullName == "Microsoft.AspNetCore.Blazor.Components.BlazorComponent");

            if (!(type.Methods.Any(m => m.Name == "ShouldRender" && m.DeclaringType == type)))
            {
                var baseMethod = blazorComponent.Methods.First(m =>
                                                               m.Name == "ShouldRender" && m.Attributes.HasFlag(MethodAttributes.Virtual));
                var shouldRender = CreateMethodOverride(type, baseMethod);
                using (var il = new Helper(shouldRender))
                {
                    il.Append(new Instructions
                    {
                        OpCodes.Ldarg_0,
                        { OpCodes.Ldfld, field },
                        { OpCodes.Callvirt, context.BlazorComponentHelperShouldRenderReference },
                        OpCodes.Ret
                    });
                }
            }

            var onParametersSetBase = FindClosestOverride(type, "OnParametersSet");
            var onParametersSet     = CreateMethodOverride(type, onParametersSetBase);

            if (onParametersSetBase.DeclaringType == type)
            {
                onParametersSetBase.Attributes &= ~MethodAttributes.MemberAccessMask;
                onParametersSetBase.Attributes |= MethodAttributes.NewSlot | MethodAttributes.Private;
                onParametersSetBase.Name       += "_Original_" + Guid.NewGuid().ToString().Replace("-", "");
            }

            using (var il = new Helper(onParametersSet))
            {
                var baseRef = onParametersSetBase.DeclaringType.Module == type.Module
                    ? onParametersSetBase
                      : type.Module.ImportReference(onParametersSetBase);
                il.Append(new Instructions
                {
                    OpCodes.Ldarg_0,
                    { OpCodes.Ldfld, field },
                    { OpCodes.Callvirt, context.BlazorComponentHelperOnParametersSetReference },
                    OpCodes.Ldarg_0,
                    { OpCodes.Call, baseRef },
                    OpCodes.Ret
                });
            }
        }