Exemple #1
0
 private static void DispatcherVoid(
     MethodAccess methodAccess,
     object instance,
     params object[] args)
 {
     m_DispatcherCalls.Add(methodAccess);
 }
        public static DynamicMethod CreateStandIn(MethodAccess methodAccess)
        {
            List <Type> parameters = methodAccess.MemberInfo.GetParameters()
                                     .Select(info => info.ParameterType)
                                     .ToList();

            if (!methodAccess.MemberInfo.IsStatic)
            {
                parameters.Insert(
                    0,
                    methodAccess.MemberInfo.DeclaringType); // First argument is the instance
            }

            DynamicMethod dyn = new DynamicMethod(
                "Original",
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard,
                methodAccess.MemberInfo.ReturnType,
                parameters.ToArray(),
                methodAccess.MemberInfo.DeclaringType,
                true);

            // The standin as it is will never be called. But it still needs a body for the reverse patching.
            ILGenerator il = dyn.GetILGenerator();

            il.ThrowException(typeof(StandInNotPatchedException));

            return(dyn);
        }
Exemple #3
0
 private static bool DispatchPrefixExecution(
     MethodAccess methodAccess,
     object instance,
     params object[] args)
 {
     return(methodAccess.InvokeOnBeforeCallHandler(instance, args));
 }
Exemple #4
0
 private static bool DispatcherFalse(
     MethodAccess methodAccess,
     object instance,
     params object[] args)
 {
     m_DispatcherCalls.Add(methodAccess);
     return(true);
 }
Exemple #5
0
 private static bool DispatcherCallOriginal(
     MethodAccess methodAccess,
     object instance,
     params object[] args)
 {
     m_DispatcherCalls.Add(methodAccess);
     methodAccess.CallOriginal(instance, args);
     return(false);
 }
Exemple #6
0
        private static void DisplayMethodRegistry()
        {
            if (!Imgui.TreeNode("Patched method registry"))
            {
                return;
            }

            foreach (KeyValuePair <MethodId, MethodAccess> registrar in MethodRegistry.IdToMethod)
            {
                MethodAccess access = registrar.Value;
                string       sName  = $"{registrar.Key} {access}";
                if (!Imgui.TreeNode(sName))
                {
                    continue;
                }

#if DEBUG
                Imgui.Columns(2);
                Imgui.Separator();
                Imgui.Text("Instance");

                // first line: global handler
                Imgui.Text("global");

                // instance specific handlers
                foreach (KeyValuePair <object, Action <object> > handler in access
                         .InstanceSpecificHandlers)
                {
                    Imgui.Text(handler.Key.ToString());
                }

                Imgui.NextColumn();
                Imgui.Text("Handler");
                Imgui.Separator();

                // first line: global handler
                Imgui.Text(
                    access.GlobalHandler != null ?
                    access.GlobalHandler.Target + "." + access.GlobalHandler.Method.Name :
                    "-");

                // instance specific handlers
                foreach (KeyValuePair <object, Action <object> > handler in access
                         .InstanceSpecificHandlers)
                {
                    Imgui.Text(handler.Value.Target + "." + handler.Value.Method.Name);
                }

                Imgui.Columns();
                Imgui.TreePop();
#else
                DisplayDebugDisabledText();
#endif
            }

            Imgui.TreePop();
        }
 public ByteCodeEventHandlerInfo(ComponentSystem handler, Method method, int priority, ICollection <Type> filterComponents, ICollection <Type> componentParams)
 {
     this.handler          = handler;
     this.methodAccess     = MethodAccess.get(handler.GetType());
     methodIndex           = methodAccess.getIndex(method.Name, method.ParameterTypes);
     this.filterComponents = ImmutableList.copyOf(filterComponents);
     this.componentParams  = ImmutableList.copyOf(componentParams);
     this.priority         = priority;
 }
 public static MethodAccess AddPrefix(
     MethodInfo original,
     MethodInfo dispatcher,
     EPatchBehaviour eBehaviour)
 {
     lock (Patcher.HarmonyLock)
     {
         MethodAccess sync = new MethodAccess(original);
         AddPrefix(sync, dispatcher, eBehaviour);
         return(sync);
     }
 }
        public static MethodId Register([NotNull] MethodAccess methodAccess)
        {
            lock (m_Lock)
            {
                if (MethodIds.ContainsKey(methodAccess))
                {
                    throw new ArgumentException($"Duplicate register for: {methodAccess}");
                }

                MethodId id = MethodId.GetNextId();
                Ids.Add(id, methodAccess);
                MethodIds.Add(methodAccess, id);
                return(id);
            }
        }
Exemple #10
0
        private void GeneratePrefixWithMultipleArgsWorks()
        {
            // Generate prefix for SingleArg
            MethodInfo method     = AccessTools.Method(typeof(Foo), nameof(Foo.ThreeArgs));
            MethodInfo dispatcher = AccessTools.Method(
                typeof(MethodPatchFactory_Test),
                nameof(DispatcherVoid));
            MethodAccess  access = new MethodAccess(method);
            DynamicMethod prefix = MethodPatchFactory.GeneratePrefix(
                access,
                dispatcher,
                EPatchBehaviour.AlwaysCallOriginal);

            // Call
            object ret = prefix.Invoke(m_Foo, new object[] { m_Foo, 42, 43.0f, 44.0 });

            Assert.IsType <bool>(ret);
            Assert.True((bool)ret);  // AlwaysCallOriginal
        }
Exemple #11
0
        private void ArgumentsAreForwarded()
        {
            // Generate prefix for ThreeArgs
            MethodInfo method     = AccessTools.Method(typeof(Foo), nameof(Foo.ThreeArgs));
            MethodInfo dispatcher = AccessTools.Method(
                typeof(MethodPatchFactory_Test),
                nameof(DispatcherCallOriginal));
            MethodAccess  access = new MethodAccess(method);
            DynamicMethod prefix = MethodPatchFactory.GeneratePrefix(
                access,
                dispatcher,
                EPatchBehaviour.NeverCallOriginal);

            // Call
            int    iArg = 42;
            float  fArg = 43.0f;
            double dArg = 44.0;
            object ret  = prefix.Invoke(m_Foo, new object[] { m_Foo, iArg, fArg, dArg });

            Assert.IsType <bool>(ret);
            Assert.False((bool)ret);  // NeverCallOriginal
            Assert.Single(m_DispatcherCalls);
            Assert.NotNull(m_DispatcherCalls[0]);
            Assert.Same(access, m_DispatcherCalls[0]);

            // Verify original was called
            Assert.Single(m_Foo.CallHistory);
            Assert.Equal(Foo.EMethod.ThreeArgs, m_Foo.CallHistory[0]);
            Assert.Single(m_Foo.ArgsHistory);
            Assert.Equal(3, m_Foo.ArgsHistory[0].Length);

            // Verify individual args where forwarded
            Assert.NotNull(m_Foo.ArgsHistory[0][0]);
            Assert.IsType <int>(m_Foo.ArgsHistory[0][0]);
            Assert.Equal(iArg, (int)m_Foo.ArgsHistory[0][0]);
            Assert.NotNull(m_Foo.ArgsHistory[0][1]);
            Assert.IsType <float>(m_Foo.ArgsHistory[0][1]);
            Assert.Equal(fArg, (float)m_Foo.ArgsHistory[0][1]);
            Assert.NotNull(m_Foo.ArgsHistory[0][2]);
            Assert.IsType <double>(m_Foo.ArgsHistory[0][2]);
            Assert.Equal(dArg, (double)m_Foo.ArgsHistory[0][2]);
        }
Exemple #12
0
        private void GeneratePrefixForVoidDispatcherWorks()
        {
            // Generate prefix for SingleArg
            MethodInfo method     = AccessTools.Method(typeof(Foo), nameof(Foo.SingleArg));
            MethodInfo dispatcher = AccessTools.Method(
                typeof(MethodPatchFactory_Test),
                nameof(DispatcherVoid));
            MethodAccess  access = new MethodAccess(method);
            DynamicMethod prefix = MethodPatchFactory.GeneratePrefix(
                access,
                dispatcher,
                EPatchBehaviour.AlwaysCallOriginal);

            // Call
            object ret = prefix.Invoke(m_Foo, new object[] { m_Foo, 42 });

            Assert.IsType <bool>(ret);
            Assert.True((bool)ret);  // AlwaysCallOriginal
            Assert.Single(m_DispatcherCalls);
            Assert.NotNull(m_DispatcherCalls[0]);
            Assert.Same(access, m_DispatcherCalls[0]);
        }
        public static void AddPrefix(
            MethodAccess access,
            MethodInfo dispatcher,
            EPatchBehaviour eBehaviour)
        {
            lock (Patcher.HarmonyLock)
            {
                if (Prefixes.ContainsKey(access.MemberInfo))
                {
                    throw new Exception("Patch already initialized.");
                }

                Prefixes[access.MemberInfo] = GeneratePrefix(access, dispatcher, eBehaviour);

                MethodInfo factoryMethod = typeof(MethodPatchFactory).GetMethod(nameof(GetPrefix));

                HarmonyMethod patch = new HarmonyMethod(factoryMethod)
                {
                    priority = SyncPriority.MethodPatchGeneratedPrefix
                };
                Patcher.HarmonyInstance.Patch(access.MemberInfo, patch);
            }
        }
Exemple #14
0
        protected override void OnCreateXType <T>(XType <T> xType)
        {
            base.OnCreateXType(xType);

            Type type = typeof(T);

            if (!ConstructorEligible(xType))
            {
                return;
            }

            if (type.GetConstructor(Type.EmptyTypes) != null)
            {
                xType.Register(new XConstructor <T>(xType,
                                                    (Func <T>)makePublicParameterlessConstructorDelegate.MakeGenericMethod(typeof(T)).Invoke(this, null)));
                return;
            }

            MethodAccess ctorAccess = GetAccessLevel <T>();

            if (ctorAccess != MethodAccess.Public)
            {
                ConstructorInfo ci = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)
                                     .FirstOrDefault(x =>
                                                     x.GetParameters().Length == 0 &&
                                                     ((x.IsFamilyOrAssembly && ctorAccess.HasFlag(MethodAccess.ProtectedInternal)) ||
                                                      (x.IsAssembly && ctorAccess.HasFlag(MethodAccess.Internal)) ||
                                                      (x.IsFamily && ctorAccess.HasFlag(MethodAccess.Protected)) ||
                                                      (x.IsFamilyAndAssembly && ctorAccess.HasFlag(MethodAccess.PrivateProtected)) ||
                                                      (x.IsPrivate && ctorAccess.HasFlag(MethodAccess.Private))));

                if (ci != null)
                {
                    xType.Register(new XConstructor <T>(xType, () => (T)ci.Invoke(null)));
                }
            }
        }
 public void Register(MethodAccess method)
 {
     m_Handlers.Add(new MethodCallSyncHandler(method));
 }
Exemple #16
0
 public bool TryGetMethod(string sMethodName, out MethodAccess methodAccess)
 {
     return(TryGetMethod(AccessTools.Method(m_Declaring, sMethodName), out methodAccess));
 }
Exemple #17
0
 public bool TryGetMethod(MethodInfo methodInfo, out MethodAccess methodAccess)
 {
     methodAccess = m_Access.FirstOrDefault(m => m.MemberInfo.Equals(methodInfo));
     return(methodAccess != null);
 }
 public MethodCallSyncHandler([NotNull] MethodAccess methodAccess)
 {
     MethodAccess = methodAccess;
     Register();
 }
        /// <summary>
        ///     Generates a <see cref="DynamicMethod" /> to be used as a harmony prefix. The method
        ///     signature exactly matches the original method with an additional and automatically
        ///     captures the instance for non-static functions.
        ///     The generated Prefix captures the <paramref name="method" /> and calls the
        ///     <paramref name="dispatcher" /> with the following arguments:
        ///     `dispatcher(MethodAccess access, object instance, object [] args)`.
        ///     With `args` containing the original method arguments (excluding __instance).
        /// </summary>
        /// <param name="methodAccess">Method that is to be prefixed.</param>
        /// <param name="dispatcher">Dispatcher to be called in the prefix.</param>
        /// <param name="eBehaviour">Return value behaviour of the generated prefix.</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static DynamicMethod GeneratePrefix(
            MethodAccess methodAccess,
            MethodInfo dispatcher,
            EPatchBehaviour eBehaviour)
        {
            List <SMethodParameter> parameters = methodAccess.MemberInfo.GetParameters()
                                                 .Select(
                p => new SMethodParameter
            {
                Info          = p,
                ParameterType =
                    p.ParameterType,
                Name = p.Name
            })
                                                 .ToList();

            if (!methodAccess.MemberInfo.IsStatic)
            {
                parameters.Insert(
                    0,
                    new SMethodParameter
                {
                    Info          = null,
                    ParameterType = methodAccess.MemberInfo.DeclaringType,
                    Name          = "__instance"
                });     // Inject an __instance
            }

            DynamicMethod dyn = new DynamicMethod(
                "Prefix",
                typeof(bool),
                parameters.Select(p => p.ParameterType).ToArray(),
                methodAccess.MemberInfo.DeclaringType,
                true);

            for (int i = 0; i < parameters.Count; ++i)
            {
                SMethodParameter    parameter = parameters[i];
                ParameterAttributes attr;
                if (parameter.Info != null)
                {
                    attr = parameter.Info.Attributes;
                }
                else
                {
                    // Injected parameter
                    attr = ParameterAttributes.In;
                }

                int iArgIndex = i + 1; // +1 because 0 is the return value
                dyn.DefineParameter(iArgIndex, attr, parameter.Name);
            }

            // Generate a dispatcher call
            ILGenerator il = dyn.GetILGenerator();

            // We want to embed the SyncMethod instance into the DynamicMethod. Unsafe code ahead!
            // https://stackoverflow.com/questions/4989681/place-an-object-on-top-of-stack-in-ilgenerator
            GCHandle gcHandle = GCHandle.Alloc(methodAccess);
            IntPtr   pMethod  = GCHandle.ToIntPtr(gcHandle);

            // Arg0: SyncMethod instance
            if (IntPtr.Size == 4)
            {
                il.Emit(OpCodes.Ldc_I4, pMethod.ToInt32());
            }
            else
            {
                il.Emit(OpCodes.Ldc_I8, pMethod.ToInt64());
            }

            il.Emit(OpCodes.Ldobj, typeof(MethodAccess));

            // Arg1: The instance.
            bool isStatic = methodAccess.MemberInfo.IsStatic;

            if (isStatic)
            {
                il.Emit(OpCodes.Ldnull);
            }
            else
            {
                // Forwarded the injected "__instance" field
                il.Emit(OpCodes.Ldarg_0);

                // Remove the injected instance from the parameters
                parameters.RemoveAt(0);
            }

            // Arg2: object[] of all args. Prepare the array
            LocalBuilder args = il.DeclareLocal(typeof(object[]));

            // start off by creating an object[] with correct size
            il.Emit(OpCodes.Ldc_I4, parameters.Count);
            il.Emit(OpCodes.Newarr, typeof(object));
            il.Emit(OpCodes.Stloc, args); // store into local var `args`

            // Place argument in array
            for (int i = 0; i < parameters.Count; ++i)
            {
                int iArgIndex = isStatic ? i : i + 1; // +1 because of the injected __instance
                il.Emit(OpCodes.Ldloc, args);         // Object reference to `args`
                il.Emit(OpCodes.Ldc_I4, i);           // Array index into `args`
                il.Emit(OpCodes.Ldarg, iArgIndex);    // value to put at index
                if (parameters[i].ParameterType.IsValueType)
                {
                    il.Emit(OpCodes.Box, parameters[i].ParameterType);
                }

                il.Emit(OpCodes.Stelem_Ref); // pops value, index and array reference from stack.
            }

            // Arg2 done, push it to the stack
            il.Emit(OpCodes.Ldloc, args); // Object reference to `args`

            // Call dispatcher
            il.EmitCall(OpCodes.Call, dispatcher, null);

            switch (eBehaviour)
            {
            case EPatchBehaviour.AlwaysCallOriginal:
                if (dispatcher.ReturnType != typeof(void))
                {
                    il.Emit(OpCodes.Pop);
                }

                il.Emit(OpCodes.Ldc_I4_1);
                break;

            case EPatchBehaviour.NeverCallOriginal:
                if (dispatcher.ReturnType != typeof(void))
                {
                    il.Emit(OpCodes.Pop);
                }

                il.Emit(OpCodes.Ldc_I4_0);
                break;

            case EPatchBehaviour.CallOriginalBaseOnDispatcherReturn:
                if (dispatcher.ReturnType != typeof(bool))
                {
                    throw new Exception(
                              "Invalid dispatcher. Dispatcher function required to return a bool to decided if the original function should be called.");
                }

                // Correct value is already on the stack
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(eBehaviour), eBehaviour, null);
            }

            il.Emit(OpCodes.Ret);

            return(dyn);
        }