internal List<CodeInstruction> GetResult(ILGenerator generator, MethodBase method) { IEnumerable instructions = codeInstructions; transpilers.ForEach(transpiler => { // before calling some transpiler, convert the input to 'their' CodeInstruction type // also remember any unassignable values that otherwise would be lost instructions = ConvertToGeneralInstructions(transpiler, instructions, out var unassignedValues); // remember the order of the original input (for detection of dupped code instructions) var originalInstructions = new List<object>(); originalInstructions.AddRange(instructions.Cast<object>()); // call the transpiler var parameter = GetTranspilerCallParameters(generator, transpiler, method, instructions); instructions = transpiler.Invoke(null, parameter.ToArray()) as IEnumerable; // convert result back to 'our' CodeInstruction and re-assign otherwise lost fields if (unassignedValues != null) instructions = ConvertToOurInstructions(instructions, typeof(CodeInstruction), originalInstructions, unassignedValues); }); var result = instructions.Cast<CodeInstruction>().ToList(); if (argumentShift) StructReturnBuffer.ArgumentShifter(result); return result; }
internal static List <CodeInstruction> GetInstructions(ILGenerator generator, MethodBase method, int maxTranspilers) { if (generator == null) { throw new ArgumentNullException(nameof(generator)); } if (method == null) { throw new ArgumentNullException(nameof(method)); } var originalVariables = MethodPatcher.DeclareLocalVariables(generator, method); var useStructReturnBuffer = StructReturnBuffer.NeedsFix(method); var copier = new MethodCopier(method, generator, originalVariables); copier.SetArgumentShift(useStructReturnBuffer); var info = Harmony.GetPatchInfo(method); if (info != null) { var sortedTranspilers = PatchFunctions.GetSortedPatchMethods(method, info.Transpilers.ToArray(), false); for (var i = 0; i < maxTranspilers && i < sortedTranspilers.Count; i++) { copier.AddTranspiler(sortedTranspilers[i]); } } return(copier.Finalize(null, null, out var _)); }
static bool HasStructReturnBuffer() { if (AccessTools.IsMonoRuntime) { if (hasTestResult_Mono is false) { Sandbox.hasStructReturnBuffer_Mono = false; var self = new StructReturnBuffer(); var original = AccessTools.DeclaredMethod(typeof(Sandbox), nameof(Sandbox.GetStruct_Mono)); var replacement = AccessTools.DeclaredMethod(typeof(Sandbox), nameof(Sandbox.GetStructReplacement_Mono)); _ = Memory.DetourMethod(original, replacement); _ = new Sandbox().GetStruct_Mono(Sandbox.magicValue, Sandbox.magicValue); hasTestResult_Mono = true; } return(Sandbox.hasStructReturnBuffer_Mono); } if (hasTestResult_Net is false) { Sandbox.hasStructReturnBuffer_Net = false; var self = new StructReturnBuffer(); var original = AccessTools.DeclaredMethod(typeof(Sandbox), nameof(Sandbox.GetStruct_Net)); var replacement = AccessTools.DeclaredMethod(typeof(Sandbox), nameof(Sandbox.GetStructReplacement_Net)); _ = Memory.DetourMethod(original, replacement); _ = new Sandbox().GetStruct_Net(Sandbox.magicValue, Sandbox.magicValue); hasTestResult_Net = true; } return(Sandbox.hasStructReturnBuffer_Net); }
internal static List <CodeInstruction> GetInstructions(ILGenerator generator, MethodBase method, int maxTranspilers) { if (generator == null) { throw new ArgumentNullException(nameof(generator)); } if (method == null) { throw new ArgumentNullException(nameof(method)); } var originalVariables = MethodPatcher.DeclareLocalVariables(generator, method); var useStructReturnBuffer = StructReturnBuffer.NeedsFix(method); var copier = new MethodCopier(method, generator, originalVariables); copier.SetArgumentShift(useStructReturnBuffer); var info = Harmony.GetPatchInfo(method); if (info != null) { var sortedTranspilers = PatchFunctions.GetSortedPatchMethods(method, info.Transpilers.ToArray(), false); for (var i = 0; i < maxTranspilers && i < sortedTranspilers.Count; i++) { copier.AddTranspiler(sortedTranspilers[i]); } } var endLabels = new List <Label>(); var emitter = new Emitter(generator, false); copier.Finalize(emitter, endLabels, out var hasReturnCode); return(emitter.GetInstructions().OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList()); }
static bool HasStructReturnBuffer() { if (hasTestResult == false) { Sandbox.hasStructReturnBuffer = false; var self = new StructReturnBuffer(); var original = AccessTools.DeclaredMethod(typeof(Sandbox), nameof(Sandbox.GetStruct)); var replacement = AccessTools.DeclaredMethod(typeof(Sandbox), nameof(Sandbox.GetStructReplacement)); _ = Memory.DetourMethod(original, replacement); _ = new Sandbox().GetStruct(Sandbox.magicValue, Sandbox.magicValue); hasTestResult = true; } return(Sandbox.hasStructReturnBuffer); }
internal MethodPatcher(MethodBase original, MethodBase source, List <MethodInfo> prefixes, List <MethodInfo> postfixes, List <MethodInfo> transpilers, List <MethodInfo> finalizers, bool debug) { if (original is null) { throw new ArgumentNullException(nameof(original)); } this.debug = debug; this.original = original; this.source = source; this.prefixes = prefixes; this.postfixes = postfixes; this.transpilers = transpilers; this.finalizers = finalizers; Memory.MarkForNoInlining(original); if (debug) { FileLog.LogBuffered($"### Patch: {original.FullDescription()}"); FileLog.FlushBuffer(); } idx = prefixes.Count() + postfixes.Count() + finalizers.Count(); useStructReturnBuffer = StructReturnBuffer.NeedsFix(original); if (debug && useStructReturnBuffer) { FileLog.Log($"### Note: A buffer for the returned struct is used. That requires an extra IntPtr argument before the first real argument"); } returnType = AccessTools.GetReturnedType(original); patch = CreateDynamicMethod(original, $"_Patch{idx}", debug); if (patch is null) { throw new Exception("Could not create replacement method"); } il = patch.GetILGenerator(); emitter = new Emitter(il, debug); }
internal static DynamicMethodDefinition CreateDynamicMethod(MethodBase original, string suffix, bool debug) { if (original is null) { throw new ArgumentNullException(nameof(original)); } var useStructReturnBuffer = StructReturnBuffer.NeedsFix(original); var patchName = $"{original.DeclaringType?.FullName}.{original.Name}{suffix}"; patchName = patchName.Replace("<>", ""); var parameters = original.GetParameters(); var parameterTypes = new List <Type>(); parameterTypes.AddRange(parameters.Types()); if (useStructReturnBuffer) { parameterTypes.Insert(0, typeof(IntPtr)); } if (original.IsStatic is false) { if (AccessTools.IsStruct(original.DeclaringType)) { parameterTypes.Insert(0, original.DeclaringType.MakeByRefType()); } else { parameterTypes.Insert(0, original.DeclaringType); } } var returnType = useStructReturnBuffer ? typeof(void) : AccessTools.GetReturnedType(original); var method = new DynamicMethodDefinition( patchName, returnType, parameterTypes.ToArray() ) { OwnerType = original.DeclaringType }; var offset = (original.IsStatic ? 0 : 1) + (useStructReturnBuffer ? 1 : 0); if (useStructReturnBuffer) { method.Definition.Parameters[original.IsStatic ? 0 : 1].Name = "retbuf"; } if (!original.IsStatic) { method.Definition.Parameters[0].Name = "this"; } for (var i = 0; i < parameters.Length; i++) { var param = method.Definition.Parameters[i + offset]; param.Attributes = (Mono.Cecil.ParameterAttributes)parameters[i].Attributes; param.Name = parameters[i].Name; } if (debug) { var parameterStrings = parameterTypes.Select(p => p.FullDescription()).ToList(); if (parameterTypes.Count == method.Definition.Parameters.Count) { for (var i = 0; i < parameterTypes.Count; i++) { parameterStrings[i] += $" {method.Definition.Parameters[i].Name}"; } } FileLog.Log($"### Replacement: static {returnType.FullDescription()} {original.DeclaringType.FullName}::{patchName}({parameterStrings.Join()})"); } return(method); }
internal static DynamicMethodDefinition CreateDynamicMethod(MethodBase original, string suffix, bool debug) { if (original == null) { throw new ArgumentNullException(nameof(original)); } var patchName = original.Name + suffix; patchName = patchName.Replace("<>", ""); var parameters = original.GetParameters(); var parameterTypes = parameters.Types().ToList(); var useStructReturnBuffer = StructReturnBuffer.NeedsFix(original); if (useStructReturnBuffer) { parameterTypes.Insert(0, typeof(IntPtr)); } if (original.IsStatic == false) { if (AccessTools.IsStruct(original.DeclaringType)) { parameterTypes.Insert(0, original.DeclaringType.MakeByRefType()); } else { parameterTypes.Insert(0, original.DeclaringType); } } var returnType = useStructReturnBuffer ? typeof(void) : AccessTools.GetReturnedType(original); var method = new DynamicMethodDefinition( patchName, returnType, parameterTypes.ToArray() ) { OwnerType = original.DeclaringType }; #if NETSTANDARD2_0 || NETCOREAPP2_0 #else var offset = (original.IsStatic ? 0 : 1) + (useStructReturnBuffer ? 1 : 0); if (useStructReturnBuffer) { method.Definition.Parameters[original.IsStatic ? 0 : 1].Name = "retbuf"; } if (!original.IsStatic) { method.Definition.Parameters[0].Name = "this"; } for (var i = 0; i < parameters.Length; i++) { var param = method.Definition.Parameters[i + offset]; param.Attributes = (Mono.Cecil.ParameterAttributes)parameters[i].Attributes; param.Name = parameters[i].Name; } #endif if (debug) { FileLog.LogBuffered($"### Replacement: static {returnType.FullDescription()} {original.DeclaringType.FullName}::{patchName}{parameterTypes.ToArray().Description()}"); } return(method); }