Ejemplo n.º 1
0
        /// <summary>
        /// Generates instructions to load arguments or default values onto the stack in a
        /// detour method.
        /// </summary>
        /// <param name="generator">The method where the calls will be added.</param>
        /// <param name="actualParams">The actual parameters required.</param>
        /// <param name="expectedParams">The parameters provided.</param>
        /// <param name="offset">The offset to start loading (0 = static, 1 = instance).</param>
        private static void LoadParameters(ILGenerator generator, ParameterInfo[] actualParams,
                                           Type[] expectedParams, int offset)
        {
            // Load the known method arguments onto the stack
            int n = expectedParams.Length, need = actualParams.Length + offset;

            if (n > 0)
            {
                generator.Emit(OpCodes.Ldarg_0);
            }
            if (n > 1)
            {
                generator.Emit(OpCodes.Ldarg_1);
            }
            if (n > 2)
            {
                generator.Emit(OpCodes.Ldarg_2);
            }
            if (n > 3)
            {
                generator.Emit(OpCodes.Ldarg_3);
            }
            for (int i = 4; i < n; i++)
            {
                generator.Emit(OpCodes.Ldarg_S, i);
            }
            // Load the rest as defaults
            for (int i = n; i < need; i++)
            {
                var param = actualParams[i - offset];
                PTranspilerTools.GenerateDefaultLoad(generator, param.ParameterType, param.
                                                     DefaultValue);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Verifies that the delegate signature provided in dst can be dynamically mapped to
        /// the method provided by src, with the possible addition of optional parameters set
        /// to their default values.
        /// </summary>
        /// <param name="expected">The method return type and parameter types expected.</param>
        /// <param name="actual">The method to be called.</param>
        /// <param name="actualReturn">The type of the method or constructor's return value.</param>
        /// <returns>The parameters used in the call to the actual method.</returns>
        /// <exception cref="DetourException">If the delegate does not match the target.</exception>
        private static ParameterInfo[] ValidateDelegate(DelegateInfo expected,
                                                        MethodBase actual, Type actualReturn)
        {
            var parameterTypes = expected.ParameterTypes;
            var returnType     = expected.ReturnType;

            // Validate return types
            if (!returnType.IsAssignableFrom(actualReturn))
            {
                throw new DetourException("Return type {0} cannot be converted to type {1}".
                                          F(actualReturn.FullName, returnType.FullName));
            }
            // Do not allow methods declared in not yet closed generic types
            var baseType = actual.DeclaringType;

            if (baseType.ContainsGenericParameters)
            {
                throw new DetourException(("Method parent type {0} must have all " +
                                           "generic parameters defined").F(baseType.FullName));
            }
            // Validate parameter types
            string actualName = baseType.FullName + "." + actual.Name;
            var    actualParams = actual.GetParameters();
            int    n = actualParams.Length, check = parameterTypes.Length;

            Type[] actualParamTypes, currentTypes = new Type[n];
            bool   noThisPointer = actual.IsStatic || actual.IsConstructor;

            for (int i = 0; i < n; i++)
            {
                currentTypes[i] = actualParams[i].ParameterType;
            }
            if (noThisPointer)
            {
                actualParamTypes = currentTypes;
            }
            else
            {
                actualParamTypes = PTranspilerTools.PushDeclaringType(currentTypes, baseType);
                n++;
            }
            if (check > n)
            {
                throw new DetourException(("Method {0} has only {1:D} parameters, but " +
                                           "{2:D} were supplied").F(actual.ToString(), n, check));
            }
            // Check up to the number we have
            for (int i = 0; i < check; i++)
            {
                Type have = actualParamTypes[i], want = parameterTypes[i];
                if (!have.IsAssignableFrom(want))
                {
                    throw new DetourException(("Argument {0:D} for method {3} cannot be " +
                                               "converted from {1} to {2}").F(i, have.FullName, want.FullName,
                                                                              actualName));
                }
            }
            // Any remaining parameters must be optional
            int offset = noThisPointer ? 0 : 1;

            for (int i = check; i < n; i++)
            {
                var cParam = actualParams[i - offset];
                if (!cParam.IsOptional)
                {
                    throw new DetourException(("New argument {0:D} for method {1} ({2}) " +
                                               "is not optional").F(i, actualName, cParam.ParameterType.FullName));
                }
            }
            return(actualParams);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Creates a dynamic detour method of the specified delegate type to wrap a base game
        /// method with the specified name. The dynamic method will automatically adapt if
        /// optional parameters are added, filling in their default values.
        /// </summary>
        /// <typeparam name="D">The delegate type to be used to call the detour.</typeparam>
        /// <param name="target">The target method to be called.</param>
        /// <returns>The detour that will call that method.</returns>
        /// <exception cref="DetourException">If the delegate does not match the target.</exception>
        public static D Detour <D>(this MethodInfo target) where D : Delegate
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            if (target.ContainsGenericParameters)
            {
                throw new ArgumentException("Generic types must have all parameters defined");
            }
            var expected           = DelegateInfo.Create(typeof(D));
            var parentType         = target.DeclaringType;
            var expectedParamTypes = expected.ParameterTypes;
            var actualParams       = ValidateDelegate(expected, target);
            int offset             = target.IsStatic ? 0 : 1;
            // Method will be "declared" in the type of the target, as we are detouring around
            // a method of that type
            var caller = new DynamicMethod(target.Name + "_Detour", expected.ReturnType,
                                           expectedParamTypes, parentType, true);
            var generator = caller.GetILGenerator();
            // Load the known method arguments onto the stack
            int n = expectedParamTypes.Length, need = actualParams.Length + offset;

            if (n > 0)
            {
                generator.Emit(OpCodes.Ldarg_0);
            }
            if (n > 1)
            {
                generator.Emit(OpCodes.Ldarg_1);
            }
            if (n > 2)
            {
                generator.Emit(OpCodes.Ldarg_2);
            }
            if (n > 3)
            {
                generator.Emit(OpCodes.Ldarg_3);
            }
            for (int i = 4; i < n; i++)
            {
                generator.Emit(OpCodes.Ldarg_S, i);
            }
            // Load the rest as defaults
            for (int i = n; i < need; i++)
            {
                var param = actualParams[i - offset];
                PTranspilerTools.GenerateDefaultLoad(generator, param.ParameterType, param.
                                                     DefaultValue);
            }
            if (parentType.IsValueType || target.IsStatic)
            {
                generator.Emit(OpCodes.Call, target);
            }
            else
            {
                generator.Emit(OpCodes.Callvirt, target);
            }
            generator.Emit(OpCodes.Ret);
            // Define the parameter names, parameter indexes start at 1
            if (offset > 0)
            {
                caller.DefineParameter(1, ParameterAttributes.None, "this");
            }
            for (int i = offset; i < n; i++)
            {
                var oldParam = actualParams[i - offset];
                caller.DefineParameter(i + 1, oldParam.Attributes, oldParam.Name);
            }
#if DEBUG
            PUtil.LogDebug("Created delegate {0} for method {1}.{2} with parameters [{3}]".
                           F(caller.Name, parentType.FullName, target.Name, actualParams.Join(",")));
#endif
            return(caller.CreateDelegate(typeof(D)) as D);
        }