using System; using System.Collections.Generic; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; namespace NzbDrone.RuntimePatches.Mono { // Mono 6.0 - 6.x bug 16122 // Unimplemented method used in GzipStream initiated via the http stack, the method existed as far back as 5.10 public class DeflateStreamAsyncPatch : MonoRuntimePatchBase { private static DeflateStreamAsyncPatch Instance; public override Version MonoMinVersion => new Version(6, 0); public override Version MonoMaxVersion => new Version(6, 0, 0, 334); protected override void Patch() { Instance = this; TryPatchMethod(typeof(DeflateStream), "ReadAsyncMemory", "Memory", "CancellationToken"); TryPatchMethod(typeof(DeflateStream), "WriteAsyncMemory", "ReadOnlyMemory", "CancellationToken"); } // We need a Transpiler coz these methods are for net4.7.2 so we cannot access the types directly // internal ValueTask ReadAsyncMemory(Memory destination, CancellationToken cancellationToken) // { // - throw new NotImplementedException(); // + return base.ReadAsync(destination, cancellationToken); // } static IEnumerable Transpiler_ReadAsyncMemory(IEnumerable instructions, MethodBase method) { var codes = instructions.ToList(); var patchable = codes.Matches(OpCodes.Newobj, OpCodes.Throw); var readAsync = method.DeclaringType.BaseType.GetMethod("ReadAsync", method.GetParameterTypes()); if (patchable && readAsync != null) { codes.Clear(); codes.Add(new CodeInstruction(OpCodes.Ldarg_0)); codes.Add(new CodeInstruction(OpCodes.Ldarg_1)); codes.Add(new CodeInstruction(OpCodes.Ldarg_2)); codes.Add(new CodeInstruction(OpCodes.Call, readAsync)); codes.Add(new CodeInstruction(OpCodes.Ret)); Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}"); } else { Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected"); } return codes; } // internal ValueTask WriteAsyncMemory(ReadOnlyMemory source, CancellationToken cancellationToken) // { // - throw new NotImplementedException(); // + return base.WriteAsync(source, cancellationToken); // } static IEnumerable Transpiler_WriteAsyncMemory(IEnumerable instructions, MethodBase method) { var codes = instructions.ToList(); var patchable = codes.Matches(OpCodes.Newobj, OpCodes.Throw); var writeAsync = method.DeclaringType.BaseType.GetMethod("WriteAsync", method.GetParameterTypes()); if (patchable && writeAsync != null) { codes.Clear(); codes.Add(new CodeInstruction(OpCodes.Ldarg_0)); codes.Add(new CodeInstruction(OpCodes.Ldarg_1)); codes.Add(new CodeInstruction(OpCodes.Ldarg_2)); codes.Add(new CodeInstruction(OpCodes.Call, writeAsync)); codes.Add(new CodeInstruction(OpCodes.Ret)); Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}"); } else { Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected"); } return codes; } } }