예제 #1
0
        /// <summary>
        /// Generates the mixin type.
        /// </summary>
        /// <param name="mixinType">The interface type to implement.</param>
        /// <param name="baseTypes">The base types The mixin uses.</param>
        /// <param name="serviceProvider">The dependency injection scope.</param>
        /// <returns>A <see cref="Type"/> that represents the mixin.</returns>
        private Type GenerateMixinType(Type mixinType, Type[] baseTypes, IServiceProvider serviceProvider)
        {
            var typeBuilder = TypeFactory
                              .Default
                              .NewType(TypeName(mixinType, baseTypes))
                              .Class()
                              .Public()
                              .Implements <IMixinObject>()
                              .ImplementsInterfaces(mixinType);

            var instancesField = typeBuilder
                                 .NewField <object[]>("baseTypes")
                                 .Private();

            var serviceProviderField = typeBuilder
                                       .NewField <IServiceProvider>("serviceProvider")
                                       .Private();

            var context = new TypeFactoryContext(
                typeBuilder,
                mixinType,
                baseTypes,
                serviceProvider,
                instancesField,
                serviceProviderField);

            this.EmitMixinObjectInterface(typeBuilder, instancesField);

            this.ImplementInterfaces(context);

            // Add a constructor to the type.
            this.EmitConstructor(
                typeBuilder,
                mixinType,
                instancesField,
                serviceProviderField);

            // Create the type.
            return(typeBuilder
                   .CreateType());
        }
예제 #2
0
        /// <summary>
        /// Implements a method.
        /// </summary>
        /// <param name="context">The mixin factories current context.</param>
        /// <param name="methodInfo">The <see cref="MethodInfo"/> of the method being implemented.</param>
        /// <param name="methodIL">The methods <see cref="ILGenerator"/>.</param>
        /// <param name="methodArgs">An array containing the methods argument types.</param>
        private void BuildMethod(TypeFactoryContext context, MethodInfo methodInfo, IEmitter methodIL, Type[] methodArgs)
        {
            string targetMemberName = methodInfo.Name;
            Type   targetStaticType = null;

            // Are we proxying a property?
            PropertyInfo propertyInfo = methodInfo.GetProperty();

            if (propertyInfo != null)
            {
                MixinImplAttribute implAttr = propertyInfo.GetCustomAttribute <MixinImplAttribute>();
                if (implAttr != null)
                {
                    targetMemberName = implAttr.TargetMemberName.IsNullOrEmpty() == false?methodInfo.Name.Substring(0, 4) + implAttr.TargetMemberName : targetMemberName;

                    targetStaticType = implAttr.TargetStaticType;
                }
            }
            else
            {
                // The is a method.
                MixinImplAttribute implAttr = methodInfo.GetCustomAttribute <MixinImplAttribute>();
                if (implAttr != null)
                {
                    targetMemberName = implAttr.TargetMemberName.IsNullOrEmpty() == false ? implAttr.TargetMemberName : targetMemberName;
                    targetStaticType = implAttr.TargetStaticType;
                }
            }

            // Get the method being proxied.
            int        index         = -1;
            MethodInfo proxiedMethod = null;

            if (targetStaticType == null)
            {
                for (int i = 0; i < context.BaseTypes.Length; i++)
                {
                    proxiedMethod = context.BaseTypes[i].GetMethodWithParameters(targetMemberName, BindingFlags.Public | BindingFlags.Instance, methodInfo.GetParameters());
                    if (proxiedMethod != null)
                    {
                        index = i;
                        break;
                    }
                }
            }
            else
            {
                proxiedMethod = targetStaticType.GetMethodWithParameters(targetMemberName, BindingFlags.Public | BindingFlags.Static, methodInfo.GetParameters());
            }

            // Was the method found?
            if (proxiedMethod == null)
            {
                // No method found
                FieldInfo field = null;

                // Is the desired method is a property getter and there is public field with same name?
                if (methodInfo.IsPropertyGet() == true &&
                    (field = context.NewType.GetField(methodInfo.Name.Substring(4))) != null)
                {
                    methodIL
                    .DeclareLocal(proxiedMethod.ReturnType, out ILocal sourceValue)
                    .DeclareLocal(field.FieldType, out ILocal fieldValue)
                    .DeclareLocal(methodInfo.ReturnType, out ILocal returnValue)

                    .LdArg0()
                    .LdFld(context.BaseObjectField)
                    .LdcI4(index)
                    .LdElemRef()
                    .StLoc(sourceValue)

                    .LdLoc(sourceValue)
                    .LdFld(field)
                    .StLoc(fieldValue);

                    // Are the return types different?
                    if (field.FieldType != methodInfo.ReturnType)
                    {
                        // try casting...
                        methodIL
                        .LdLoc(fieldValue)
                        .CastClass(methodInfo.ReturnType)
                        .StLoc(returnValue);
                    }

                    methodIL
                    .LdLoc(fieldValue)
                    .Ret();
                }

                // Is the desired method is a property setter and there is public field with same name?
                else if (methodInfo.IsPropertySet() == true &&
                         (field = context.NewType.GetField(methodInfo.Name.Substring(4))) != null)
                {
                    methodIL
                    .LdArg0()
                    .LdFld(context.BaseObjectField)
                    .LdcI4(index)
                    .LdElemRef()

                    .LdArg1()
                    .StFld(field)
                    .Ret();
                }
                else
                {
                    // Unable to implement the desired method.
                    methodIL.ThrowException(typeof(NotImplementedException));
                }

                return;
            }

            ILocal methodReturn = null;

            if (methodInfo.ReturnType != typeof(void))
            {
                // Do the return types match?
                if (methodInfo.ReturnType != proxiedMethod.ReturnType)
                {
                    throw new MixinGenerationException("The returns types do not match or cannot be adapted");
                }

                // Declare locals
                methodIL.DeclareLocal(methodInfo.ReturnType, out methodReturn);

                // hmm
                // If this is a generic method changes it to a generic method of return type.
                // This is NOT robust enough needs to only change the signature if the proxied return
                // type is actual required in the generic definition.
                if (proxiedMethod.IsGenericMethodDefinition == true)
                {
                    proxiedMethod = proxiedMethod.MakeGenericMethod(methodInfo.ReturnType);
                }
            }

            // Is the proxied method static?
            if (proxiedMethod.IsStatic == true)
            {
                methodIL
                .EmitLoadParameters(methodInfo)
                .Call(proxiedMethod);
            }
            else
            {
                // Is the adapted type a class?
                if (context.BaseTypes[index].IsClass == true)
                {
                    methodIL
                    .LdArg0()
                    .LdFld(context.BaseObjectField)
                    .LdcI4(index)
                    .LdElemRef()
                    .EmitLoadParameters(methodInfo)
                    .CallVirt(proxiedMethod);
                }

                // Is the adapted type a value type?
                else if (context.BaseTypes[index].IsValueType == true)
                {
                    methodIL
                    .LdArg0()
                    .LdFlda(context.BaseObjectField)
                    .LdcI4(index)
                    .LdElemRef()
                    .EmitLoadParameters(methodInfo)
                    .Call(proxiedMethod);
                }
            }

            // Does the method expect a return value?
            if (methodReturn != null)
            {
                methodIL
                .StLoc(methodReturn)
                .LdLoc(methodReturn);
            }

            methodIL.Emit(OpCodes.Ret);
        }
예제 #3
0
        /// <summary>
        /// Implements the mixin types interfaces on the new type.
        /// </summary>
        /// <param name="context">The current type factory context.</param>
        private void ImplementInterfaces(TypeFactoryContext context)
        {
            var propertyMethods = new Dictionary <string, IMethodBuilder>();

            foreach (var memberInfo in context.NewType.GetMembers())
            {
                if (memberInfo.MemberType == MemberTypes.Method)
                {
                    var methodInfo = (MethodInfo)memberInfo;
                    var methodArgs = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();

                    if (methodInfo.ContainsGenericParameters == true)
                    {
                        var genericArguments = methodInfo.GetGenericArguments();

                        var methodBuilder = context
                                            .TypeBuilder
                                            .NewMethod(methodInfo.Name)
                                            .Public()
                                            .Virtual()
                                            .Params(methodArgs)
                                            .Returns(methodInfo.ReturnType);

                        methodBuilder
                        .NewGenericParameters(
                            genericArguments.Select(t => t.Name).ToArray(),
                            (gps) =>
                        {
                            for (int m = 0; m < gps.Length; m++)
                            {
                                gps[m].Attributes = genericArguments[m].GetTypeInfo().GenericParameterAttributes;
                            }
                        });

                        var methodIL = methodBuilder.Body();

                        if (context.NewType.GetMethod(methodInfo.Name, methodInfo.GetGenericArguments()) == null)
                        {
                            // Throw NotImplementedException
                            methodIL.ThrowException <NotImplementedException>("Not Implemented");
                            continue;
                        }

                        ILocal methodReturn = null;
                        if (methodInfo.ReturnType != typeof(void))
                        {
                            methodIL.DeclareLocal(methodInfo.ReturnType, out methodReturn);
                        }

                        methodIL
                        .LdArg0()
                        .LdFld(context.BaseObjectField)
                        .EmitLoadParameters(methodInfo);

                        MethodInfo callMethod1 = context.BaseObjectField.FieldType.GetMethod(memberInfo.Name, genericArguments);
                        MethodInfo callMethod  = context.BaseObjectField.FieldType.GetMethod(memberInfo.Name, methodArgs).MakeGenericMethod(genericArguments);
                        methodIL
                        .CallVirt(callMethod1);

                        if (methodReturn != null)
                        {
                            methodIL
                            .StLoc(methodReturn)
                            .LdLoc(methodReturn);
                        }

                        methodIL.Ret();
                    }
                    else
                    {
                        string           name  = methodInfo.Name;
                        MethodAttributes attrs = methodInfo.Attributes & ~MethodAttributes.Abstract;
                        var methodBuilder      = context
                                                 .TypeBuilder
                                                 .NewMethod(
                            methodInfo.Name,
                            attrs,
                            CallingConventions.HasThis,
                            methodInfo.ReturnType)
                                                 .Params(methodArgs);

                        var methodIL = methodBuilder.Body();

                        this.BuildMethod(context, methodInfo, methodIL, methodArgs);

                        if (methodInfo.IsProperty() == true)
                        {
                            propertyMethods.Add(methodInfo.Name, methodBuilder);
                        }
                    }
                }
                else if (memberInfo.MemberType == MemberTypes.Property)
                {
                    var propertyBuilder = context
                                          .TypeBuilder
                                          .NewProperty(memberInfo.Name, ((PropertyInfo)memberInfo).PropertyType)
                                          .Attributes(PropertyAttributes.SpecialName);

                    if (propertyMethods.TryGetValue(memberInfo.PropertyGetName(), out IMethodBuilder getMethod) == true)
                    {
                        propertyBuilder.GetMethod = getMethod;
                    }

                    if (propertyMethods.TryGetValue(memberInfo.PropertySetName(), out IMethodBuilder setMethod) == true)
                    {
                        propertyBuilder.SetMethod = setMethod;
                    }
                }
            }
        }