/// <summary> /// Emits most of the body of the shared state setter. /// </summary> /// <param name="processor">The ILProcessor to use.</param> /// <param name="sharedStateBackingField">The backing field for the shared state.</param> /// <param name="methodDefinition">The inner definition of the action method.</param> /// <param name="actionType">The action type.</param> /// <param name="actionFieldDefinition">The field definition.</param> private void EmitSharedStateSetter( ILProcessor processor, FieldReference sharedStateBackingField, MethodDefinition methodDefinition, TypeReference actionType, FieldDefinition actionFieldDefinition) { var moduleDefinition = sharedStateBackingField.Module; // determine the name of the action. var attribute = methodDefinition.CustomAttributes.Single(x => x.AttributeType.FullName == this.actionAttributeReference.FullName); var actionName = methodDefinition.Name; var attributeArgument = attribute.ConstructorArguments.FirstOrDefault(); if (!string.IsNullOrEmpty(attributeArgument.Value as string)) { actionName = attributeArgument.Value as string; } var actionExtensions = this.actionExtensionsReference; var voidType = moduleDefinition.ImportReference(typeof(void)); MethodReference createActionMethod; MethodReference actionTypeConstructorReference = actionType.Resolve().Methods.Single(x => x.IsConstructor); actionTypeConstructorReference = actionTypeConstructorReference.GetGenericMethodOnInstantance(actionType); if (actionType is GenericInstanceType) { actionTypeConstructorReference = moduleDefinition.ImportReference(actionTypeConstructorReference, (GenericInstanceType)actionType); createActionMethod = actionExtensions.Resolve().Methods.Single(x => x.Name.Contains("CreateAction") && x.GenericParameters.Count == ((GenericInstanceType)actionType).GenericArguments.Count); var createActionInstanceMethod = new GenericInstanceMethod(createActionMethod); foreach (var parameter in ((GenericInstanceType)actionType).GenericArguments) { createActionInstanceMethod.GenericArguments.Add(parameter.Resolve()); } createActionMethod = moduleDefinition.ImportReference(createActionInstanceMethod); } else { actionTypeConstructorReference = moduleDefinition.ImportReference(actionTypeConstructorReference); createActionMethod = moduleDefinition.ImportReference(actionExtensions.Resolve().Methods.Single(x => x.Name.Contains("CreateAction") && !x.GenericParameters.Any())); } processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldfld, sharedStateBackingField); processor.Emit(OpCodes.Ldstr, actionName); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldftn, methodDefinition); processor.Emit(OpCodes.Newobj, actionTypeConstructorReference); processor.Emit(OpCodes.Call, createActionMethod); processor.Emit(OpCodes.Stfld, actionFieldDefinition); }
/// <summary> /// Emits most of the body of the shared state setter. /// </summary> /// <param name="processor">The ILProcessor to use.</param> /// <param name="sharedStateBackingField">The backing field for the shared state.</param> /// <param name="methodDefinition">The inner definition of the action method.</param> /// <param name="actionName">The name of the action.</param> /// <param name="actionType">The action type.</param> /// <param name="actionFieldDefinition">The field definition.</param> private void EmitSharedStateSetter( ILProcessor processor, FieldReference sharedStateBackingField, MethodDefinition methodDefinition, string actionName, TypeReference actionType, FieldDefinition actionFieldDefinition) { var moduleDefinition = sharedStateBackingField.Module; var actionExtensions = this.weavingContext.CortexNetApiActionExtensions; var voidType = moduleDefinition.TypeSystem.Void; MethodReference createActionMethod; MethodReference actionTypeConstructorReference = actionType.Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic); actionTypeConstructorReference = actionTypeConstructorReference.GetGenericMethodOnInstantance(actionType); if (actionType is GenericInstanceType) { actionTypeConstructorReference = moduleDefinition.ImportReference(actionTypeConstructorReference, (GenericInstanceType)actionType); createActionMethod = actionExtensions.Resolve().Methods.Single(x => x.Name.Contains("CreateAction") && x.GenericParameters.Count == ((GenericInstanceType)actionType).GenericArguments.Count && x.Parameters.Count == 4); var createActionInstanceMethod = new GenericInstanceMethod(createActionMethod); foreach (var parameter in ((GenericInstanceType)actionType).GenericArguments) { createActionInstanceMethod.GenericArguments.Add(parameter.Resolve()); } createActionMethod = moduleDefinition.ImportReference(createActionInstanceMethod); } else { actionTypeConstructorReference = moduleDefinition.ImportReference(actionTypeConstructorReference); createActionMethod = moduleDefinition.ImportReference(actionExtensions.Resolve().Methods.Single(x => x.Name.Contains("CreateAction") && !x.GenericParameters.Any() && x.Parameters.Count == 4)); } processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldfld, sharedStateBackingField); processor.Emit(OpCodes.Ldstr, actionName); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldftn, methodDefinition); processor.Emit(OpCodes.Newobj, actionTypeConstructorReference); processor.Emit(OpCodes.Call, createActionMethod); processor.Emit(OpCodes.Stfld, actionFieldDefinition); }
/// <summary> /// Emits IL code for adding a computed member on this observable object. /// </summary> /// <param name="processor">The ILProcessor to use.</param> /// <param name="computedName">The name of the computed member.</param> /// <param name="methodDefinition">The methodDefinition of the member.</param> /// <param name="setMethodDefinition">The methodDefinition of the setter.</param> /// <param name="equalityComparerType">The equality comparer type.</param> /// <param name="requiresReaction">Whether the computed value requires a reaction.</param> /// <param name="keepAlive">Whether to keep the computed value alive.</param> /// <param name="observableObjectField">A <see cref="FieldDefinition"/> for the field where the ObservableObject instance is kept.</param> private void EmitComputedMemberAdd( ILProcessor processor, string computedName, MethodDefinition methodDefinition, MethodDefinition setMethodDefinition, TypeReference equalityComparerType, bool requiresReaction, bool keepAlive, FieldDefinition observableObjectField) { var module = methodDefinition.Module; var functionType = methodDefinition.GetFunctionType(this.WeavingContext); var observableObjectType = this.WeavingContext.CortexNetTypesObservableObject.Resolve(); var observableObjectAddComputedMethod = new GenericInstanceMethod(observableObjectType.Methods.FirstOrDefault(m => m.Name == "AddComputedMember")); observableObjectAddComputedMethod.GenericArguments.Add(methodDefinition.ReturnType); MethodReference functionTypeConstructorReference = functionType.Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic); functionTypeConstructorReference = module.ImportReference(functionTypeConstructorReference.GetGenericMethodOnInstantance(functionType)); var computedValueOptionsType = this.WeavingContext.CortexNetComputedValueOptions.Resolve(); var computedValueOptionsConstructor = computedValueOptionsType.Methods.Single(x => x.IsConstructor && !x.IsStatic); var computedValueOptionsInstanceType = new GenericInstanceType(computedValueOptionsType); computedValueOptionsInstanceType.GenericArguments.Add(methodDefinition.ReturnType); var computedValueOptionsConstructorReference = module.ImportReference(computedValueOptionsConstructor.GetGenericMethodOnInstantance(computedValueOptionsInstanceType)); var setContextMethod = computedValueOptionsType.Methods.Single(x => x.Name == "set_Context"); var setContextReference = module.ImportReference(setContextMethod.GetGenericMethodOnInstantance(computedValueOptionsInstanceType)); var setRequiresReactionMethod = computedValueOptionsType.Methods.Single(x => x.Name == "set_RequiresReaction"); var setRequiresReactionReference = module.ImportReference(setRequiresReactionMethod.GetGenericMethodOnInstantance(computedValueOptionsInstanceType)); var setKeepAliveMethod = computedValueOptionsType.Methods.Single(x => x.Name == "set_KeepAlive"); var setKeepAliveReference = module.ImportReference(setKeepAliveMethod.GetGenericMethodOnInstantance(computedValueOptionsInstanceType)); // reference to this. processor.Emit(OpCodes.Ldarg_0); // load observable object to call "AddComputedMember"; processor.Emit(OpCodes.Ldfld, observableObjectField); // first argument, name of computed. processor.Emit(OpCodes.Ldstr, computedName); // first argument of ComputedValueOptions, getter definition. processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldftn, methodDefinition); processor.Emit(OpCodes.Newobj, functionTypeConstructorReference); // second argument of ComputedValueOptions, name again (kind of double. Is the same in Mobx. Maybe fix this?) processor.Emit(OpCodes.Ldstr, computedName); processor.Emit(OpCodes.Newobj, computedValueOptionsConstructorReference); // object initializers for computedvalueoptions. We dup the reference to call setters. First set context. processor.Emit(OpCodes.Dup); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Callvirt, setContextReference); // set requiresReaction. processor.Emit(OpCodes.Dup); processor.Emit(requiresReaction ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); processor.Emit(OpCodes.Callvirt, setRequiresReactionReference); // set keepAlive. processor.Emit(OpCodes.Dup); processor.Emit(keepAlive ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); processor.Emit(OpCodes.Callvirt, setKeepAliveReference); // custom equality comparer in the attribute. if (equalityComparerType != null) { var setEqualityComparerMethod = computedValueOptionsType.Methods.Single(x => x.Name == "set_EqualityComparer"); var setEqualityComparerReference = module.ImportReference(setEqualityComparerMethod.GetGenericMethodOnInstantance(computedValueOptionsInstanceType)); MethodReference equalityComparerConstructorReference = equalityComparerType.Resolve().Methods.SingleOrDefault(x => x.IsConstructor && !x.IsStatic && x.Parameters.Count == 0); // The equalitycomparer needs to have a parameterless constructor. if (equalityComparerConstructorReference == null) { this.ParentWeaver.WriteWarning(string.Format( CultureInfo.CurrentCulture, Resources.NoParameterLessConstructorForEqualityComparer, equalityComparerType.Name, computedName, methodDefinition.DeclaringType.Name)); } else { if (equalityComparerType.IsGenericInstance) { equalityComparerConstructorReference = equalityComparerConstructorReference.GetGenericMethodOnInstantance(equalityComparerType); } processor.Emit(OpCodes.Dup); processor.Emit(OpCodes.Newobj, module.ImportReference(equalityComparerConstructorReference)); processor.Emit(OpCodes.Callvirt, setEqualityComparerReference); } } // Add setter to the computed value. if (setMethodDefinition != null) { var setSetterMethod = computedValueOptionsType.Methods.Single(x => x.Name == "set_Setter"); var setSetterReference = module.ImportReference(setSetterMethod.GetGenericMethodOnInstantance(computedValueOptionsInstanceType)); var actionType = setMethodDefinition.GetActionType(this.WeavingContext); MethodReference actionTypeConstructorReference = actionType.Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic); actionTypeConstructorReference = module.ImportReference(actionTypeConstructorReference.GetGenericMethodOnInstantance(actionType)); processor.Emit(OpCodes.Dup); processor.Emit(OpCodes.Ldarg_0); processor.Emit(OpCodes.Ldftn, setMethodDefinition); processor.Emit(OpCodes.Newobj, actionTypeConstructorReference); processor.Emit(OpCodes.Callvirt, setSetterReference); } // call AddComputedMember. processor.Emit(OpCodes.Callvirt, module.ImportReference(observableObjectAddComputedMethod)); }
/// <summary> /// Emits IL code for assignment of the inner observer object field. /// </summary> /// <param name="processor">The processor to use.</param> /// <param name="observerName">The name of the observer.</param> /// <param name="innerObserverObjectField">The inner observer object field to assign.</param> /// <param name="buildRenderTreeMethod">The buildRenderTreeMethod on the compoent.</param> /// <param name="stateChangedMethod">The method to call to set the state to changed.</param> private void EmitObserverObjectInit(ILProcessor processor, string observerName, FieldDefinition innerObserverObjectField, MethodDefinition buildRenderTreeMethod, MethodDefinition stateChangedMethod) { var module = this.parentWeaver.ModuleDefinition; var observableObjectConstructor = module.ImportReference(this.parentWeaver.ModuleDefinition.ImportReference(this.weavingContext.CortexNetBlazorObserverObject).Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic)); var buildRenderTreeReference = module.ImportReference(buildRenderTreeMethod); var stateChangedReference = module.ImportReference(stateChangedMethod); var renderActionType = buildRenderTreeMethod.GetActionType(this.weavingContext); MethodReference renderActionConstructorType = renderActionType.Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic); var renderActionConstructorReference = module.ImportReference(renderActionConstructorType.GetGenericMethodOnInstantance(renderActionType)); var stateChangedActionType = stateChangedMethod.GetActionType(this.weavingContext); MethodReference stateChangedActionConstructorType = stateChangedActionType.Resolve().Methods.Single(x => x.IsConstructor && !x.IsStatic); var stateChangedActionConstructorReference = module.ImportReference(stateChangedActionConstructorType); var instructions = new List <Instruction> { // this.observerObject = new ObserverObject(sharedState, name, (Action<RenderTreeBuilder>)buildRenderTreeAction, (Action)stateChangedAction); processor.Create(OpCodes.Ldarg_0), processor.Create(OpCodes.Ldarg_1), processor.Create(OpCodes.Ldstr, observerName), processor.Create(OpCodes.Ldarg_0), processor.Create(OpCodes.Dup), processor.Create(OpCodes.Ldvirtftn, buildRenderTreeReference), processor.Create(OpCodes.Newobj, renderActionConstructorReference), processor.Create(OpCodes.Ldarg_0), processor.Create(OpCodes.Ldftn, stateChangedReference), processor.Create(OpCodes.Newobj, stateChangedActionConstructorReference), processor.Create(OpCodes.Newobj, observableObjectConstructor), processor.Create(OpCodes.Stfld, innerObserverObjectField), }; foreach (var instruction in instructions) { processor.Append(instruction); } }