public static Func<T, F> CreateFastFieldGetter<T, F>( FieldInfo fieldInfo ) { if( fieldInfo == null ) throw new ArgumentNullException( nameof( fieldInfo ) ); if( !typeof( F ).IsAssignableFrom( fieldInfo.FieldType ) ) throw new ArgumentException( "FieldInfo type does not match return type." ); if( typeof( T ) != typeof( object ) ) if( fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom( typeof( T ) ) ) throw new MissingFieldException( typeof( T ).Name, fieldInfo.Name ); var name = $"FastReflection<{typeof( T ).FullName}.Get_{fieldInfo.Name}>"; var dm = new DynamicMethodDefinition( name, typeof( F ), new[] { typeof( T ) } ); var il = dm.GetILProcessor(); if( !fieldInfo.IsStatic ) { il.Emit( OpCodes.Ldarg_0 ); il.Emit( OpCodes.Castclass, fieldInfo.DeclaringType ); } il.Emit( fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo ); if( fieldInfo.FieldType.IsValueType != typeof( F ).IsValueType ) { il.Emit( OpCodes.Box, fieldInfo.FieldType ); } il.Emit( OpCodes.Ret ); return (Func<T, F>)dm.Generate().CreateDelegate( typeof( Func<T, F> ) ); }
public MethodBase GetDetourTarget(MethodBase from, MethodBase to) { Type context = to.DeclaringType; MethodInfo dm = null; if (GlueThiscallStructRetPtr != GlueThiscallStructRetPtrOrder.Original && from is MethodInfo fromInfo && !from.IsStatic && to is MethodInfo toInfo && to.IsStatic && fromInfo.ReturnType == toInfo.ReturnType && fromInfo.ReturnType.IsValueType) { int size = fromInfo.ReturnType.GetManagedSize(); if (size == 3 || size == 5 || size == 6 || size == 7 || size >= 9) { Type thisType = from.GetThisParamType(); Type retType = fromInfo.ReturnType.MakeByRefType(); // Refs are shiny pointers. int thisPos = 0; int retPos = 1; if (GlueThiscallStructRetPtr == GlueThiscallStructRetPtrOrder.RetThisArgs) { thisPos = 1; retPos = 0; } List<Type> argTypes = new List<Type> { thisType }; argTypes.Insert(retPos, retType); argTypes.AddRange(from.GetParameters().Select(p => p.ParameterType)); using (DynamicMethodDefinition dmd = new DynamicMethodDefinition( $"Glue:ThiscallStructRetPtr<{from.GetID(simple: true)},{to.GetID(simple: true)}>", typeof(void), argTypes.ToArray() )) { ILProcessor il = dmd.GetILProcessor(); // Load the return buffer address. il.Emit(OpCodes.Ldarg, retPos); // Invoke the target method with all remaining arguments. { il.Emit(OpCodes.Ldarg, thisPos); for (int i = 2; i < argTypes.Count; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Call, il.Body.Method.Module.ImportReference(to)); } // Store the returned object to the return buffer. il.Emit(OpCodes.Stobj, il.Body.Method.Module.ImportReference(fromInfo.ReturnType)); il.Emit(OpCodes.Ret); dm = dmd.Generate(); } } } return dm ?? to; }
/// <summary>Returns the methods unmodified list of CodeInstructions</summary> /// <param name="original">The original method</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <returns>A list containing all the original CodeInstructions</returns> public static List<CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator) { // Create a copy var dmd = new DynamicMethodDefinition(original); // Create a manipulator to obtain the instructions var manipulator = new ILManipulator(dmd.Definition.Body); generator = new CecilILGenerator(dmd.GetILProcessor()).GetProxy(); return manipulator.GetInstructions(generator); }
internal static void Initialize() { foreach (var field in AccessTools.GetDeclaredFields(typeof(OS)).Where(x => x.FieldType == typeof(Color))) { var dynMethod = new DynamicMethodDefinition(field.Name, typeof(Color).MakeByRefType(), new Type[] { typeof(OS) }); var p = dynMethod.GetILProcessor(); p.Emit(OpCodes.Ldarg_0); p.Emit(OpCodes.Ldflda, field); p.Emit(OpCodes.Ret); OSColorFieldsFast.Add(field.Name, dynMethod.Generate().CreateDelegate <RefColorFieldDelegate>()); } }
public static Delegate GenerateDelegate(MethodBase method, bool useCallVirt) { var parameterTypes = GetActualParameters(method); var actionParameters = GetActionParameters(method); var returnType = method is MethodInfo mi ? mi.ReturnType : typeof(void); var dynamicMethod = new DynamicMethodDefinition("DynamicMethod_" + method.Name, returnType, actionParameters); var il = dynamicMethod.GetILProcessor(); if (actionParameters.Length >= 1) { il.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_0); il.Emit(Mono.Cecil.Cil.OpCodes.Castclass, parameterTypes[0]); if (actionParameters.Length >= 2) { il.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_1); il.Emit(Mono.Cecil.Cil.OpCodes.Castclass, parameterTypes[1]); if (actionParameters.Length >= 3) { il.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_2); il.Emit(Mono.Cecil.Cil.OpCodes.Castclass, parameterTypes[2]); if (actionParameters.Length >= 4) { il.Emit(Mono.Cecil.Cil.OpCodes.Ldarg_3); il.Emit(Mono.Cecil.Cil.OpCodes.Castclass, parameterTypes[3]); } } } } il.Emit(useCallVirt ? Mono.Cecil.Cil.OpCodes.Callvirt : Mono.Cecil.Cil.OpCodes.Call, method); il.Emit(Mono.Cecil.Cil.OpCodes.Ret); var delegateType = returnType == typeof(void) ? GetGenericActionType(actionParameters) : GetGenericFuncType(actionParameters, returnType); var actionOrFunc = dynamicMethod.Generate().CreateDelegate(delegateType); return(actionOrFunc); }
public MethodInfo GetDelegateInvoker <T>() where T : Delegate { Type t = typeof(T); MethodInfo invoker; if (invokerCache.TryGetValue(t, out WeakReference invokerRef)) { if (invokerRef == null) { return(null); } invoker = invokerRef.Target as MethodInfo; if (invokerRef.IsAlive) { return(invoker); } } MethodInfo delInvoke = t.GetMethod("Invoke"); ParameterInfo[] args = delInvoke.GetParameters(); if (args.Length == 0) { invokerCache[t] = null; return(null); } invoker = FastDelegateInvokers.GetInvoker(delInvoke); if (invoker != null) { invokerCache[t] = new WeakReference(invoker); return(invoker); } Type[] argTypes = new Type[args.Length + 1]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } argTypes[args.Length] = typeof(T); using (DynamicMethodDefinition dmdInvoke = new DynamicMethodDefinition( $"MMIL:Invoke<{delInvoke.DeclaringType.FullName}>", delInvoke.ReturnType, argTypes )) { ILProcessor il = dmdInvoke.GetILProcessor(); // Load the delegate reference first. il.Emit(OpCodes.Ldarg, args.Length); // Load the rest of the args for (int i = 0; i < args.Length; i++) { il.Emit(OpCodes.Ldarg, i); } // Invoke the delegate and return its result. il.Emit(OpCodes.Callvirt, delInvoke); il.Emit(OpCodes.Ret); invoker = dmdInvoke.Generate(); invokerCache[t] = new WeakReference(invoker); return(invoker); } }
public static FastReflectionDelegate CreateFastDelegate( MethodBase method, bool directBoxValueAccess, bool forceNonVirtcall ) { DynamicMethodDefinition dmd = new DynamicMethodDefinition( $"FastReflection<{method.DeclaringType.FullName + "." + method.Name}>", typeof( object ), DynamicMethodDelegateArgs ); ILProcessor il = dmd.GetILProcessor(); ParameterInfo[] args = method.GetParameters(); bool generateLocalBoxValuePtr = true; if( !method.IsStatic ) { il.Emit( OpCodes.Ldarg_0 ); if( method.DeclaringType.IsValueType ) { il.Emit( OpCodes.Unbox_Any, method.DeclaringType ); } } for( int i = 0; i < args.Length; i++ ) { Type argType = args[ i ].ParameterType; bool argIsByRef = argType.IsByRef; if( argIsByRef ) argType = argType.GetElementType(); bool argIsValueType = argType.IsValueType; if( argIsByRef && argIsValueType && !directBoxValueAccess ) { // Used later when storing back the reference to the new box in the array. il.Emit( OpCodes.Ldarg_1 ); il.Emit( OpCodes.Ldc_I4, i ); } il.Emit( OpCodes.Ldarg_1 ); il.Emit( OpCodes.Ldc_I4, i ); if( argIsByRef && !argIsValueType ) { il.Emit( OpCodes.Ldelema, typeof( object ) ); } else { il.Emit( OpCodes.Ldelem_Ref ); if( argIsValueType ) { if( !argIsByRef || !directBoxValueAccess ) { // if !directBoxValueAccess, create a new box if required il.Emit( OpCodes.Unbox_Any, argType ); if( argIsByRef ) { // box back il.Emit( OpCodes.Box, argType ); // store new box value address to local 0 il.Emit( OpCodes.Dup ); il.Emit( OpCodes.Unbox, argType ); if( generateLocalBoxValuePtr ) { generateLocalBoxValuePtr = false; dmd.Definition.Body.Variables.Add( new VariableDefinition( new PinnedType( new PointerType( dmd.Definition.Module.TypeSystem.Void ) ) ) ); } il.Emit( OpCodes.Stloc_0 ); // arr and index set up already il.Emit( OpCodes.Stelem_Ref ); // load address back to stack il.Emit( OpCodes.Ldloc_0 ); } } else { // if directBoxValueAccess, emit unbox (get value address) il.Emit( OpCodes.Unbox, argType ); } } } } if( method.IsConstructor ) { il.Emit( OpCodes.Newobj, method as ConstructorInfo ); } else if( method.IsFinal || !method.IsVirtual || forceNonVirtcall ) { il.Emit( OpCodes.Call, method as MethodInfo ); } else { il.Emit( OpCodes.Callvirt, method as MethodInfo ); } Type returnType = method.IsConstructor ? method.DeclaringType : ( method as MethodInfo ).ReturnType; if( returnType != typeof( void ) ) { if( returnType.IsValueType ) { il.Emit( OpCodes.Box, returnType ); } } else { il.Emit( OpCodes.Ldnull ); } il.Emit( OpCodes.Ret ); return (FastReflectionDelegate)dmd.Generate().CreateDelegate( typeof( FastReflectionDelegate ) ); }
public Hook(MethodBase from, MethodInfo to, object target, ref HookConfig config) { Method = from; Target = to; DelegateTarget = target; // Check if hook ret -> method ret is valid. Don't check for method ret -> hook ret, as that's too strict. Type returnType = (from as MethodInfo)?.ReturnType ?? typeof(void); if (to.ReturnType != returnType && !to.ReturnType.IsCompatible(returnType)) { throw new InvalidOperationException($"Return type of hook for {from} doesn't match, must be {((from as MethodInfo)?.ReturnType ?? typeof(void)).FullName}"); } if (target == null && !to.IsStatic) { throw new InvalidOperationException($"Hook for method {from} must be static, or you must pass a target instance."); } ParameterInfo[] hookArgs = Target.GetParameters(); // Check if the parameters match. // If the delegate has got an extra first parameter that itself is a delegate, it's the orig trampoline passthrough. ParameterInfo[] args = Method.GetParameters(); Type[] argTypes; if (!Method.IsStatic) { argTypes = new Type[args.Length + 1]; argTypes[0] = Method.GetThisParamType(); for (int i = 0; i < args.Length; i++) { argTypes[i + 1] = args[i].ParameterType; } } else { argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } } Type origType = null; if (hookArgs.Length == argTypes.Length + 1 && typeof(Delegate).IsAssignableFrom(hookArgs[0].ParameterType)) { _OrigDelegateType = origType = hookArgs[0].ParameterType; } else if (hookArgs.Length != argTypes.Length) { throw new InvalidOperationException($"Parameter count of hook for {from} doesn't match, must be {argTypes.Length}"); } for (int i = 0; i < argTypes.Length; i++) { Type argMethod = argTypes[i]; Type argHook = hookArgs[i + (origType == null ? 0 : 1)].ParameterType; if (!argMethod.IsCompatible(argHook)) { throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related"); } } MethodInfo origInvoke = _OrigDelegateInvoke = origType?.GetMethod("Invoke"); // TODO: Check origType Invoke arguments. DynamicMethodDefinition dmd; ILProcessor il; using (dmd = new DynamicMethodDefinition( $"Hook<{Method.GetID(simple: true)}>?{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes )) { il = dmd.GetILProcessor(); if (target != null) { _RefTarget = il.EmitReference(target); } if (origType != null) { _RefTrampoline = il.EmitReference <Delegate>(null); } for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, Target); il.Emit(OpCodes.Ret); TargetReal = dmd.Generate().Pin(); } // Temporarily provide a trampoline that waits for the proper trampoline. if (origType != null) { ParameterInfo[] origArgs = origInvoke.GetParameters(); Type[] origArgTypes = new Type[origArgs.Length]; for (int i = 0; i < origArgs.Length; i++) { origArgTypes[i] = origArgs[i].ParameterType; } using (dmd = new DynamicMethodDefinition( $"Chain:TMP<{Method.GetID(simple: true)}>?{GetHashCode()}", (origInvoke as MethodInfo)?.ReturnType ?? typeof(void), origArgTypes )) { il = dmd.GetILProcessor(); // while (ref == null) { } _RefTrampolineTmp = il.EmitReference <Delegate>(null); il.Emit(OpCodes.Brfalse, il.Body.Instructions[0]); // Invoke the generated delegate. il.EmitGetReference <Delegate>(_RefTrampolineTmp.Value); for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Callvirt, origInvoke); il.Emit(OpCodes.Ret); DynamicMethodHelper.SetReference(_RefTrampoline.Value, dmd.Generate().CreateDelegate(origType)); } } _Detour = new Detour(Method, TargetReal, new DetourConfig() { ManualApply = config.ManualApply, Priority = config.Priority, ID = config.ID, Before = config.Before, After = config.After }); _UpdateOrig(null); }
/// <summary> /// Generate a new DynamicMethod with which you can invoke the previous state. /// If the NativeDetour holds a reference to a managed method, a copy of the original method is returned. /// If the NativeDetour holds a reference to a native function, an "undo-call-redo" trampoline with a matching signature is returned. /// </summary> public MethodBase GenerateTrampoline(MethodBase signature = null) { MethodBase remoteTrampoline = OnGenerateTrampoline?.InvokeWhileNull <MethodBase>(this, signature); if (remoteTrampoline != null) { return(remoteTrampoline); } if (_IsFree) { throw new InvalidOperationException("Free() has been called on this detour."); } if (_BackupMethod != null) { // If we're detouring an IL method and have an IL copy, invoke the IL copy. // Note that this ignores the passed signature. return(_BackupMethod); } /* * if (signature == null) * signature = _BackupMethod; */ if (signature == null) { throw new ArgumentNullException("A signature must be given if the NativeDetour doesn't hold a reference to a managed method."); } // Otherwise, undo the detour, call the method and reapply the detour. MethodBase methodCallable = Method; if (methodCallable == null) { methodCallable = DetourHelper.GenerateNativeProxy(Data.Method, signature); } Type returnType = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] args = signature.GetParameters(); Type[] argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } using (DynamicMethodDefinition dmd = new DynamicMethodDefinition( $"Trampoline:Native<{Method?.GetFindableID(simple: true) ?? ((long) Data.Method).ToString("X16")}>?{GetHashCode()}", returnType, argTypes )) { ILProcessor il = dmd.GetILProcessor(); ExceptionHandler eh = new ExceptionHandler(ExceptionHandlerType.Finally); il.Body.ExceptionHandlers.Add(eh); il.EmitDetourCopy(_BackupNative, Data.Method, Data.Type); // Store the return value in a local as we can't preserve the stack across exception block boundaries. VariableDefinition localResult = null; if (returnType != typeof(void)) { il.Body.Variables.Add(localResult = new VariableDefinition(il.Import(returnType))); } // Label blockTry = il.BeginExceptionBlock(); int instriTryStart = il.Body.Instructions.Count; for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } if (methodCallable is MethodInfo) { il.Emit(OpCodes.Call, (MethodInfo)methodCallable); } else if (methodCallable is ConstructorInfo) { il.Emit(OpCodes.Call, (ConstructorInfo)methodCallable); } else { throw new NotSupportedException($"Method type {methodCallable.GetType().FullName} not supported."); } if (localResult != null) { il.Emit(OpCodes.Stloc, localResult); } il.Emit(OpCodes.Leave, (object)null); Instruction instrLeave = il.Body.Instructions[il.Body.Instructions.Count - 1]; // il.BeginFinallyBlock(); int instriTryEnd = il.Body.Instructions.Count; int instriFinallyStart = il.Body.Instructions.Count; // Reapply the detour even if the method threw an exception. il.EmitDetourApply(Data); // il.EndExceptionBlock(); int instriFinallyEnd = il.Body.Instructions.Count; Instruction instrLeaveTarget = null; if (localResult != null) { il.Emit(OpCodes.Ldloc, localResult); instrLeaveTarget = il.Body.Instructions[il.Body.Instructions.Count - 1]; } il.Emit(OpCodes.Ret); instrLeaveTarget = instrLeaveTarget ?? il.Body.Instructions[il.Body.Instructions.Count - 1]; instrLeave.Operand = instrLeaveTarget; // TODO: Are the exception handler indices correct? eh.TryStart = il.Body.Instructions[instriTryStart]; eh.TryEnd = il.Body.Instructions[instriTryEnd]; eh.HandlerStart = il.Body.Instructions[instriTryEnd]; eh.HandlerEnd = il.Body.Instructions[instriFinallyEnd]; return(dmd.Generate().Pin()); } }
public static DynamicMethodDefinition ToDynamicMethod(this MethodDefinition method) { List <Type> parameters = new List <Type>(); foreach (var param in method.Parameters) { string[] typeArr = param.ParameterType.FullName.Split('.'); Array.Resize(ref typeArr, typeArr.Length - 1); string nameSpace = string.Join(".", typeArr); Assembly asm = null; if (nameSpace.StartsWith("System")) { asm = typeof(object).Assembly; } else if (nameSpace.StartsWith("Terraria")) { asm = typeof(Main).Assembly; } else if (nameSpace.StartsWith("Microsoft")) { asm = typeof(Vector2).Assembly; } Type type = asm.GetType(param.ParameterType.FullName); if (param.ParameterType.IsByReference) { type = type.MakeByRefType(); } parameters.Add(type); } DynamicMethodDefinition dynamicMethod = new DynamicMethodDefinition($"{method.DeclaringType.Name}_{method.Name}", Type.GetType(method.ReturnType.FullName), parameters.ToArray()); ILProcessor il = dynamicMethod.GetILProcessor(); foreach (Instruction instruction in method.Body.Instructions) { if (instruction.Operand is FieldReference reference) { TypeReference fieldType = reference.FieldType; if (fieldType.Scope.ToString().StartsWith("tConfig")) { fieldType = new TypeReference(fieldType.Namespace, fieldType.Name, TerrariaModule, TerrariaModule); } TypeReference declaringType = reference.DeclaringType; if (declaringType.Scope.ToString().StartsWith("tConfig")) { declaringType = new TypeReference(declaringType.Namespace, declaringType.Name, TerrariaModule, TerrariaModule); } il.Emit(instruction.OpCode, new FieldReference(reference.Name, fieldType, declaringType)); } else { il.Emit(instruction.OpCode, instruction.Operand); } } foreach (Instruction inst in dynamicMethod.Definition.Body.Instructions) { if (inst.Operand is Instruction targetOrig) { inst.Operand = dynamicMethod.Definition.Body.Instructions[method.Body.Instructions.IndexOf(targetOrig)]; } } return(dynamicMethod); }