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); }
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 } }); })); }
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 } }); })); }
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); }
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); } }
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; } }
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 }); } }