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); }
private static bool DispatchPrefixExecution( MethodAccess methodAccess, object instance, params object[] args) { return(methodAccess.InvokeOnBeforeCallHandler(instance, args)); }
private static bool DispatcherFalse( MethodAccess methodAccess, object instance, params object[] args) { m_DispatcherCalls.Add(methodAccess); return(true); }
private static bool DispatcherCallOriginal( MethodAccess methodAccess, object instance, params object[] args) { m_DispatcherCalls.Add(methodAccess); methodAccess.CallOriginal(instance, args); return(false); }
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); } }
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 }
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]); }
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); } }
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)); }
public bool TryGetMethod(string sMethodName, out MethodAccess methodAccess) { return(TryGetMethod(AccessTools.Method(m_Declaring, sMethodName), out methodAccess)); }
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); }