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