private static void Cast(ILGenerator il, CacheMethod method, int locals) { if (method.AsyncType == null) { return; } var returnType = (method.RawType ?? method.ValueType) == typeof(void) ? typeof(ValueTask) : typeof(ValueTask <>).MakeGenericType(method.RawType ?? method.ValueType); if (returnType == method.Method.ReturnType && !(method.Method.ReturnType == typeof(ValueTask) && method.RawType != null)) { return; } if (method.Method.ReturnType == typeof(ValueTask)) { il.Emit(OpCodes.Call, ConvertMethod.MakeGenericMethod(method.RawType)); } else { il.DeclareLocal(returnType); Stloc(il, locals); il.Emit(OpCodes.Ldloca_S, locals); il.Emit(OpCodes.Call, returnType.GetMethod(nameof(ValueTask.AsTask))); } }
private static void BuildSet(CacheMethod method, ILGenerator il, int defaultTtl) { Ldarg(il, method.Value + 1); var locals = 0; if (LdTtl(method, il, locals, defaultTtl)) { locals++; } if (method.When > 0) { Ldarg(il, method.When + 1); } else { il.Emit(OpCodes.Ldc_I4_0); } if (InitValue <CancellationToken>(method.CancellationToken, il, locals)) { locals++; } BuildCacheMethod(method, locals, il, nameof(CacheHelper.Set), nameof(CacheHelper.SetAsync), method.Method.GetParameters()[0].ParameterType, method.Method.GetParameters()[method.Value].ParameterType); }
private static void BuildGet(CacheMethod method, ILGenerator il, int defaultTtl) { var keyType = method.Method.GetParameters()[0].ParameterType; if (method.Method.IsAbstract && method.Value < 1) { var locals = 0; if (InitValue <CancellationToken>(method.CancellationToken, il, locals)) { locals++; } BuildCacheMethod(method, locals, il, nameof(CacheHelper.Get), nameof(CacheHelper.GetAsync), keyType, method.ValueType !); } else { BuildFunc(method, il, keyType, method.ValueType !); var locals = 0; if (LdTtl(method, il, locals, defaultTtl)) { locals++; } if (InitValue <CancellationToken>(method.CancellationToken, il, locals)) { locals++; } BuildCacheMethod(method, locals, il, nameof(CacheHelper.GetOrSet), nameof(CacheHelper.GetOrSetAsync), keyType, method.ValueType !); } }
private void BuildGet(CacheMethod method, ILGenerator il, int defaultTtl) { var keyType = method.Method.GetParameters()[0].ParameterType; #if BuildTask if (method.Method.Resolve().IsAbstract&& method.Value < 1) #else if (method.Method.IsAbstract && method.Value < 1) #endif { var locals = 0; if (InitValue <CancellationToken>(method.CancellationToken, il, locals)) { locals++; } BuildCacheMethod(method, locals, il, nameof(CacheHelper.Get), keyType, method.WarpedValue ? method.ValueType.GetGenericArguments()[0] : method.ValueType); } else { BuildFunc(method, il, keyType, method.ValueType); var locals = 0; if (LdTtl(method, il, locals, defaultTtl)) { locals++; } if (InitValue <CancellationToken>(method.CancellationToken, il, locals)) { locals++; } BuildCacheMethod(method, locals, il, nameof(CacheHelper.GetOrSet), keyType, method.WarpedValue ? method.ValueType.GetGenericArguments()[0] : method.ValueType); } }
private static void BuildRemove(CacheMethod method, ILGenerator il) { var locals = 0; if (InitValue <CancellationToken>(method.CancellationToken, il, locals)) { locals++; } BuildCacheMethod(method, locals, il, nameof(CacheHelper.Remove), nameof(CacheHelper.RemoveAsync), method.Method.GetParameters()[0].ParameterType); }
private static void BuildCacheMethod(CacheMethod method, int locals, ILGenerator il, string syncMethod, string asyncMethod, params Type[] typeArguments) { if (method.AsyncType == null) { il.Emit(OpCodes.Callvirt, CacheHelperMethods[syncMethod].MakeGenericMethod(typeArguments)); if (method.Method.ReturnType == typeof(void)) { il.Emit(OpCodes.Pop); } } else { il.Emit(OpCodes.Callvirt, CacheHelperMethods[asyncMethod].MakeGenericMethod(typeArguments)); Cast(il, method, locals); } }
private static void BuildCacheMethod(CacheMethod method, int locals, ILGenerator il, string methodName, params Type[] typeArguments) { if (method.WarpedValue) { methodName += "2"; } if (method.AsyncType == null) { il.Emit(OpCodes.Callvirt, CacheHelperMethods[methodName].MakeGenericMethod(typeArguments)); if (method.Method.ReturnType == typeof(void)) { il.Emit(OpCodes.Pop); } } else { il.Emit(OpCodes.Callvirt, CacheHelperMethods[methodName + "Async"].MakeGenericMethod(typeArguments)); Cast(il, method, locals); } }
private static bool LdTtl(CacheMethod method, ILGenerator il, int locals, int defaultTtl) { if (method.Ttl < 1) { if (defaultTtl < 1) { return(InitValue <TimeSpan>(method.Ttl, il, locals)); } il.Emit(OpCodes.Ldc_R8, (double)defaultTtl); } else { var type = method.Method.GetParameters()[method.Ttl].ParameterType; var rawType = Nullable.GetUnderlyingType(type); if (rawType == null) { Ldarg(il, method.Ttl + 1); } else { il.Emit(OpCodes.Ldarga_S, method.Ttl + 1); } if (type == typeof(DateTime) || type == typeof(DateTimeOffset)) { il.Emit(OpCodes.Call, type.GetProperty(nameof(DateTime.Now)).GetMethod); il.Emit(OpCodes.Call, type.GetMethod("op_Subtraction", new[] { type, type })); return(false); } if (rawType == typeof(DateTime) || rawType == typeof(DateTimeOffset)) { var falseLabel = il.DefineLabel(); var endLabel = il.DefineLabel(); il.Emit(OpCodes.Call, type.GetProperty(nameof(Nullable <TimeSpan> .HasValue)).GetMethod); il.Emit(OpCodes.Brfalse_S, falseLabel); il.Emit(OpCodes.Ldarga_S, method.Ttl + 1); il.Emit(OpCodes.Call, type.GetProperty(nameof(Nullable <TimeSpan> .Value)).GetMethod); il.Emit(OpCodes.Call, rawType.GetProperty(nameof(DateTime.Now)).GetMethod); il.Emit(OpCodes.Call, rawType.GetMethod("op_Subtraction", new[] { rawType, rawType })); il.Emit(OpCodes.Br_S, endLabel); il.MarkLabel(falseLabel); il.DeclareLocal(typeof(TimeSpan)); il.Emit(OpCodes.Ldloca_S, locals); il.Emit(OpCodes.Initobj, typeof(TimeSpan)); Ldloc(il, locals); il.MarkLabel(endLabel); return(true); } if (rawType != null) { il.Emit(OpCodes.Call, type.GetMethod(nameof(Nullable <TimeSpan> .GetValueOrDefault), #if NET45 new Type[0])); } #else Array.Empty <Type>())); #endif if ((rawType ?? type) == typeof(TimeSpan)) { return(false); } if ((rawType ?? type) != typeof(double)) { if ((rawType ?? type) == typeof(uint) || (rawType ?? type) == typeof(ulong)) { il.Emit(OpCodes.Conv_R_Un); } else if ((rawType ?? type) == typeof(decimal)) { il.Emit(OpCodes.Call, typeof(decimal).GetMethods(BindingFlags.Public | BindingFlags.Static) .First(m => m.Name == "op_Explicit" && m.ReturnType == typeof(double))); } il.Emit(OpCodes.Conv_R8); } } il.Emit(OpCodes.Call, typeof(TimeSpan).GetMethod(nameof(TimeSpan.FromSeconds))); return(false); }
private static void BuildFunc(CacheMethod method, ILGenerator il, Type keyType, Type valueType) { Type funcType; if (method.Method.IsAbstract) { Ldarg(il, method.Value + 1); funcType = method.Method.GetParameters()[method.Value].ParameterType; } else { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldftn, method.Method); var result = method.Method.GetParameters().Select(p => p.ParameterType).Union(new[] { valueType }).ToArray(); funcType = result.Length switch { 1 => typeof(Func <>).MakeGenericType(result), 2 => typeof(Func <,>).MakeGenericType(result), 3 => typeof(Func <, ,>).MakeGenericType(result), 4 => typeof(Func <, , ,>).MakeGenericType(result), _ => throw CacheTypeResolver.ParameterException(method.Method.DeclaringType, method.Method) }; il.Emit(OpCodes.Newobj, funcType.GetConstructors()[0]); } var args = funcType.GetGenericArguments(); Type?arg1 = null, arg2 = null, arg3 = null, returnArg = null; if (args.Length > 1) { arg1 = args[0] == keyType ? typeof(object) : args[0]; } if (args.Length > 2) { arg2 = args[1] == keyType ? typeof(object) : args[1]; } if (args.Length > 3) { arg3 = args[2] == keyType ? typeof(object) : args[2]; } if (args.Length > 4) { throw CacheTypeResolver.ParameterException(method.Method.DeclaringType, method.Method); } if (args[args.Length - 1].IsGenericType) { var type = args[args.Length - 1].GetGenericTypeDefinition(); if (type == typeof(Task <>) || type == typeof(ValueTask <>)) { returnArg = type; } } if (arg1 != typeof(object) || arg2 != typeof(TimeSpan) || arg3 != typeof(CancellationToken) || method.AsyncType != null && returnArg != typeof(ValueTask <>)) { il.Emit(OpCodes.Call, (method.AsyncType == null ? FuncHelper.GetWrapMethod(arg1, arg2, arg3, returnArg) : FuncHelper.GetWrapAsyncMethod(arg1, arg2, arg3, returnArg)) .MakeGenericMethod(keyType, method.ValueType)); } }
private void BuildFunc(CacheMethod method, ILGenerator il, Type keyType, Type valueType) { #if BuildTask GenericInstanceType funcType; if (method.Method.Resolve().IsAbstract) #else Type funcType; if (method.Method.IsAbstract) #endif { Ldarg(il, method.Value + 1); #if BuildTask funcType = (GenericInstanceType)method.Method.GetParameters()[method.Value].ParameterType; #else funcType = method.Method.GetParameters()[method.Value].ParameterType; #endif } else { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldftn, method.Method); var result = method.Method.GetParameters().Select(p => p.ParameterType).Union(new[] { valueType }).ToArray(); funcType = result.Length switch { #if BuildTask 1 => new GenericInstanceType(ImportType(typeof(Func <>))), 2 => new GenericInstanceType(ImportType(typeof(Func <,>))), 3 => new GenericInstanceType(ImportType(typeof(Func <, ,>))), 4 => new GenericInstanceType(ImportType(typeof(Func <, , ,>))), #else 1 => typeof(Func <>).MakeGenericType(result), 2 => typeof(Func <,>).MakeGenericType(result), 3 => typeof(Func <, ,>).MakeGenericType(result), 4 => typeof(Func <, , ,>).MakeGenericType(result), #endif _ => throw CacheTypeResolver.ParameterException(method.Method.DeclaringType !, method.Method) }; #if BuildTask foreach (var p in result) { funcType.GenericArguments.Add(p); } il.Emit(OpCodes.Newobj, Module.ImportReference(funcType.Resolve().GetConstructors().First()).CopyTo(funcType)); #else il.Emit(OpCodes.Newobj, funcType.GetConstructors()[0]); #endif } var args = funcType.GetGenericArguments(); Type?arg1 = null, arg2 = null, arg3 = null, returnArg = null; #if BuildTask if (args.Length > 1) { arg1 = args[0] == keyType ? TypeSystem.Object : args[0]; } if (args.Length > 2) { arg2 = args[1] == keyType ? TypeSystem.Object : args[1]; } if (args.Length > 3) { arg3 = args[2] == keyType ? TypeSystem.Object : args[2]; } if (args.Length > 4) { throw CacheTypeResolver.ParameterException(method.Method.DeclaringType !, method.Method); } if (args[args.Length - 1].IsGenericInstance) { var type = args[args.Length - 1].GetElementType(); if (type.IsType(typeof(Task <>)) || type.IsType(typeof(ValueTask <>))) { returnArg = type; } } if (arg1 != null && arg1.IsType <object>() && arg2 != null && arg2.IsType <TimeSpan>() && arg3 != null && arg3.IsType <CancellationToken>() && (method.AsyncType == null || returnArg != null && returnArg.IsType(typeof(ValueTask <>)))) { return; } var fat = _helper.FuncAdapterType.MakeGenericInstanceType(keyType, method.ValueType); il.Emit(OpCodes.Newobj, new MethodInfo(".ctor", TypeSystem.Void, fat) { Parameters = { new ParameterBuilder(TypeSystem.Object) }, HasThis = true }); il.Emit(OpCodes.Ldftn, Module.ImportReference(_helper.GetWrapMethod(method.AsyncType == null, arg1, arg2, arg3, returnArg)).CopyTo(fat)); il.Emit(OpCodes.Newobj, Module.ImportReference(typeof(Func <, , ,>).GetConstructors()[0]).CopyTo(ImportType(typeof(Func <, , ,>)).MakeGenericInstanceType(keyType.GetElementType(), ImportType <TimeSpan>(), ImportType <CancellationToken>(), method.AsyncType == null ? method.ValueType : ImportType(typeof(ValueTask <>)).MakeGenericInstanceType(method.ValueType.GetElementType())))); #else if (args.Length > 1) { arg1 = args[0] == keyType ? typeof(object) : args[0]; } if (args.Length > 2) { arg2 = args[1] == keyType ? typeof(object) : args[1]; } if (args.Length > 3) { arg3 = args[2] == keyType ? typeof(object) : args[2]; } if (args.Length > 4) { throw CacheTypeResolver.ParameterException(method.Method.DeclaringType !, method.Method); } if (args[args.Length - 1].IsGenericType) { var type = args[args.Length - 1].GetGenericTypeDefinition(); if (type == typeof(Task <>) || type == typeof(ValueTask <>)) { returnArg = type; } } if (arg1 == typeof(object) && arg2 == typeof(TimeSpan) && arg3 == typeof(CancellationToken) && (method.AsyncType == null || returnArg == typeof(ValueTask <>))) { return; } var fat = _helper.FuncAdapterType.MakeGenericType(keyType, method.ValueType); il.Emit(OpCodes.Newobj, fat.GetConstructors()[0]); il.Emit(OpCodes.Ldftn, fat.GetMethod(_helper.GetWrapMethod(method.AsyncType == null, arg1, arg2, arg3, returnArg).Name) !); il.Emit(OpCodes.Newobj, typeof(Func <, , ,>).MakeGenericType(keyType, typeof(TimeSpan), typeof(CancellationToken), method.AsyncType == null ? method.ValueType : typeof(ValueTask <>).MakeGenericType(method.ValueType)).GetConstructors()[0]); #endif }