internal LoadedLuaScript(LuaScript original, byte[] hash) { Original = original; Hash = hash; }
/// <summary> /// Determines whether or not the given type can be used to provide parameters for the given LuaScript. /// </summary> public static bool IsValidParameterHash(Type t, LuaScript script, out string missingMember, out string badTypeMember) { for (var i = 0; i < script.Arguments.Length; i++) { var argName = script.Arguments[i]; var member = t.GetMember(argName).SingleOrDefault(m => m is PropertyInfo || m is FieldInfo); if (member == null) { missingMember = argName; badTypeMember = null; return false; } var memberType = member is FieldInfo ? ((FieldInfo)member).FieldType : ((PropertyInfo)member).PropertyType; if(!ConvertableTypes.Contains(memberType)){ missingMember = null; badTypeMember = argName; return false; } } missingMember = badTypeMember = null; return true; }
/// <summary> /// Creates a Func that extracts parameters from the given type for use by a LuaScript. /// /// Members that are RedisKey's get extracted to be passed in as keys to redis; all members that /// appear in the script get extracted as RedisValue arguments to be sent up as args. /// /// We send all values as arguments so we don't have to prepare the same script for different parameter /// types. /// /// The created Func takes a RedisKey, which will be prefixed to all keys (and arguments of type RedisKey) for /// keyspace isolation. /// </summary> public static Func<object, RedisKey?, ScriptParameters> GetParameterExtractor(Type t, LuaScript script) { string ignored; if (!IsValidParameterHash(t, script, out ignored, out ignored)) throw new Exception("Shouldn't be possible"); var keys = new List<MemberInfo>(); var args = new List<MemberInfo>(); for (var i = 0; i < script.Arguments.Length; i++) { var argName = script.Arguments[i]; var member = t.GetMember(argName).SingleOrDefault(m => m is PropertyInfo || m is FieldInfo); var memberType = member is FieldInfo ? ((FieldInfo)member).FieldType : ((PropertyInfo)member).PropertyType; if (memberType == typeof(RedisKey)) { keys.Add(member); } args.Add(member); } var nullableRedisKeyHasValue = typeof(RedisKey?).GetProperty("HasValue").GetGetMethod(); var dyn = new DynamicMethod("ParameterExtractor_" + t.FullName + "_" + script.OriginalScript.GetHashCode(), typeof(ScriptParameters), new[] { typeof(object), typeof(RedisKey?) }, restrictedSkipVisibility: true); var il = dyn.GetILGenerator(); // only init'd if we use it LocalBuilder redisKeyLoc = null; var loc = il.DeclareLocal(t); il.Emit(OpCodes.Ldarg_0); // object #if !CORE_CLR if (t.IsValueType) #else if (t.GetTypeInfo().IsValueType) #endif { il.Emit(OpCodes.Unbox_Any, t); // T } else { il.Emit(OpCodes.Castclass, t); // T } il.Emit(OpCodes.Stloc, loc); // --empty-- var needsKeyPrefixLoc = il.DeclareLocal(typeof(bool)); il.Emit(OpCodes.Ldarga_S, 1); // RedisKey?* il.Emit(OpCodes.Call, nullableRedisKeyHasValue); // bool il.Emit(OpCodes.Stloc, needsKeyPrefixLoc); // --empty-- if (keys.Count == 0) { // if there are no keys, don't allocate il.Emit(OpCodes.Ldnull); // null } else { il.Emit(OpCodes.Ldc_I4, keys.Count); // int il.Emit(OpCodes.Newarr, typeof(RedisKey)); // RedisKey[] } for (var i = 0; i < keys.Count; i++) { il.Emit(OpCodes.Dup); // RedisKey[] RedisKey[] il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisKey[] int #if !CORE_CLR if (t.IsValueType) #else if (t.GetTypeInfo().IsValueType) #endif { il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisKey[] int T* } else { il.Emit(OpCodes.Ldloc, loc); // RedisKey[] RedisKey[] int T } LoadMember(il, keys[i]); // RedisKey[] RedisKey[] int RedisKey PrefixIfNeeded(il, needsKeyPrefixLoc, ref redisKeyLoc); // RedisKey[] RedisKey[] int RedisKey il.Emit(OpCodes.Stelem, typeof(RedisKey)); // RedisKey[] } if (args.Count == 0) { // if there are no args, don't allocate il.Emit(OpCodes.Ldnull); // RedisKey[] null } else { il.Emit(OpCodes.Ldc_I4, args.Count); // RedisKey[] int il.Emit(OpCodes.Newarr, typeof(RedisValue)); // RedisKey[] RedisValue[] } for (var i = 0; i < args.Count; i++) { il.Emit(OpCodes.Dup); // RedisKey[] RedisValue[] RedisValue[] il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisValue[] RedisValue[] int #if !CORE_CLR if (t.IsValueType) #else if (t.GetTypeInfo().IsValueType) #endif { il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisValue[] RedisValue[] int T* } else { il.Emit(OpCodes.Ldloc, loc); // RedisKey[] RedisValue[] RedisValue[] int T } var member = args[i]; LoadMember(il, member); // RedisKey[] RedisValue[] RedisValue[] int memberType ConvertToRedisValue(member, il, needsKeyPrefixLoc, ref redisKeyLoc); // RedisKey[] RedisValue[] RedisValue[] int RedisValue il.Emit(OpCodes.Stelem, typeof(RedisValue)); // RedisKey[] RedisValue[] } il.Emit(OpCodes.Newobj, ScriptParameters.Cons); // ScriptParameters il.Emit(OpCodes.Ret); // --empty-- var ret = (Func<object, RedisKey?, ScriptParameters>)dyn.CreateDelegate(typeof(Func<object, RedisKey?, ScriptParameters>)); return ret; }
public Task <LoadedLuaScript> ScriptLoadAsync(LuaScript script, CommandFlags flags = CommandFlags.None) { return(script.LoadAsync(this, flags)); }
public LoadedLuaScript ScriptLoad(LuaScript script, CommandFlags flags = CommandFlags.None) { return(script.Load(this, flags)); }
/// <summary> /// <para>Creates a Func that extracts parameters from the given type for use by a LuaScript.</para> /// <para> /// Members that are RedisKey's get extracted to be passed in as keys to redis; all members that /// appear in the script get extracted as RedisValue arguments to be sent up as args. /// </para> /// <para> /// We send all values as arguments so we don't have to prepare the same script for different parameter /// types. /// </para> /// <para> /// The created Func takes a RedisKey, which will be prefixed to all keys (and arguments of type RedisKey) for /// keyspace isolation. /// </para> /// </summary> /// <param name="t">The type to extract for.</param> /// <param name="script">The script to extract for.</param> public static Func <object, RedisKey?, ScriptParameters> GetParameterExtractor(Type t, LuaScript script) { if (!IsValidParameterHash(t, script, out _, out _)) { throw new Exception("Shouldn't be possible"); } Expression GetMember(Expression root, MemberInfo member) { switch (member.MemberType) { case MemberTypes.Property: return(Expression.Property(root, (PropertyInfo)member)); case MemberTypes.Field: return(Expression.Field(root, (FieldInfo)member)); default: throw new ArgumentException(nameof(member)); } } var keys = new List <MemberInfo>(); var args = new List <MemberInfo>(); for (var i = 0; i < script.Arguments.Length; i++) { var argName = script.Arguments[i]; var member = t.GetMember(argName).SingleOrDefault(m => m is PropertyInfo || m is FieldInfo); var memberType = member is FieldInfo ? ((FieldInfo)member).FieldType : ((PropertyInfo)member).PropertyType; if (memberType == typeof(RedisKey)) { keys.Add(member); } else if (memberType != typeof(RedisValue) && !_conversionOperators.ContainsKey(memberType)) { throw new InvalidCastException($"There is no conversion available from {memberType.Name} to {nameof(RedisValue)}"); } args.Add(member); } var objUntyped = Expression.Parameter(typeof(object), "obj"); var objTyped = Expression.Convert(objUntyped, t); var keyPrefix = Expression.Parameter(typeof(RedisKey?), "keyPrefix"); Expression keysResult, valuesResult; MethodInfo asRedisValue = null; Expression[] keysResultArr = null; if (keys.Count == 0) { // if there are no keys, don't allocate keysResult = Expression.Constant(null, typeof(RedisKey[])); } else { var needsKeyPrefix = Expression.Property(keyPrefix, nameof(Nullable <RedisKey> .HasValue)); var keyPrefixValueArr = new[] { Expression.Call(keyPrefix, nameof(Nullable <RedisKey> .GetValueOrDefault), null, null) }; var prepend = typeof(RedisKey).GetMethod(nameof(RedisKey.Prepend), BindingFlags.Public | BindingFlags.Instance); asRedisValue = typeof(RedisKey).GetMethod(nameof(RedisKey.AsRedisValue), BindingFlags.NonPublic | BindingFlags.Instance); keysResultArr = new Expression[keys.Count]; for (int i = 0; i < keysResultArr.Length; i++) { var member = GetMember(objTyped, keys[i]); keysResultArr[i] = Expression.Condition(needsKeyPrefix, Expression.Call(member, prepend, keyPrefixValueArr), member); } keysResult = Expression.NewArrayInit(typeof(RedisKey), keysResultArr); } if (args.Count == 0) { // if there are no args, don't allocate valuesResult = Expression.Constant(null, typeof(RedisValue[])); } else { valuesResult = Expression.NewArrayInit(typeof(RedisValue), args.Select(arg => { var member = GetMember(objTyped, arg); if (member.Type == typeof(RedisValue)) { return(member); // pass-thru } if (member.Type == typeof(RedisKey)) { // need to apply prefix (note we can re-use the body from earlier) var val = keysResultArr[keys.IndexOf(arg)]; return(Expression.Call(val, asRedisValue)); } // otherwise: use the conversion operator var conversion = _conversionOperators[member.Type]; return(Expression.Call(conversion, member)); })); } var body = Expression.Lambda <Func <object, RedisKey?, ScriptParameters> >( Expression.New(ScriptParameters.Cons, keysResult, valuesResult), objUntyped, keyPrefix); return(body.Compile()); }
/// <summary> /// Creates a Func that extracts parameters from the given type for use by a LuaScript. /// /// Members that are RedisKey's get extracted to be passed in as keys to redis; all members that /// appear in the script get extracted as RedisValue arguments to be sent up as args. /// /// We send all values as arguments so we don't have to prepare the same script for different parameter /// types. /// /// The created Func takes a RedisKey, which will be prefixed to all keys (and arguments of type RedisKey) for /// keyspace isolation. /// </summary> public static Func <object, RedisKey?, ScriptParameters> GetParameterExtractor(Type t, LuaScript script) { string ignored; if (!IsValidParameterHash(t, script, out ignored, out ignored)) { throw new Exception("Shouldn't be possible"); } var keys = new List <MemberInfo>(); var args = new List <MemberInfo>(); for (var i = 0; i < script.Arguments.Length; i++) { var argName = script.Arguments[i]; var member = t.GetMember(argName).SingleOrDefault(m => m is PropertyInfo || m is FieldInfo); var memberType = member is FieldInfo ? ((FieldInfo)member).FieldType : ((PropertyInfo)member).PropertyType; if (memberType == typeof(RedisKey)) { keys.Add(member); } args.Add(member); } var nullableRedisKeyHasValue = typeof(RedisKey?).GetProperty("HasValue").GetGetMethod(); var dyn = new DynamicMethod("ParameterExtractor_" + t.FullName + "_" + script.OriginalScript.GetHashCode(), typeof(ScriptParameters), new[] { typeof(object), typeof(RedisKey?) }, restrictedSkipVisibility: true); var il = dyn.GetILGenerator(); // only init'd if we use it LocalBuilder redisKeyLoc = null; var loc = il.DeclareLocal(t); il.Emit(OpCodes.Ldarg_0); // object #if !CORE_CLR if (t.IsValueType) #else if (t.GetTypeInfo().IsValueType) #endif { il.Emit(OpCodes.Unbox_Any, t); // T } else { il.Emit(OpCodes.Castclass, t); // T } il.Emit(OpCodes.Stloc, loc); // --empty-- var needsKeyPrefixLoc = il.DeclareLocal(typeof(bool)); il.Emit(OpCodes.Ldarga_S, 1); // RedisKey?* il.Emit(OpCodes.Call, nullableRedisKeyHasValue); // bool il.Emit(OpCodes.Stloc, needsKeyPrefixLoc); // --empty-- if (keys.Count == 0) { // if there are no keys, don't allocate il.Emit(OpCodes.Ldnull); // null } else { il.Emit(OpCodes.Ldc_I4, keys.Count); // int il.Emit(OpCodes.Newarr, typeof(RedisKey)); // RedisKey[] } for (var i = 0; i < keys.Count; i++) { il.Emit(OpCodes.Dup); // RedisKey[] RedisKey[] il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisKey[] int #if !CORE_CLR if (t.IsValueType) #else if (t.GetTypeInfo().IsValueType) #endif { il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisKey[] int T* } else { il.Emit(OpCodes.Ldloc, loc); // RedisKey[] RedisKey[] int T } LoadMember(il, keys[i]); // RedisKey[] RedisKey[] int RedisKey PrefixIfNeeded(il, needsKeyPrefixLoc, ref redisKeyLoc); // RedisKey[] RedisKey[] int RedisKey il.Emit(OpCodes.Stelem, typeof(RedisKey)); // RedisKey[] } if (args.Count == 0) { // if there are no args, don't allocate il.Emit(OpCodes.Ldnull); // RedisKey[] null } else { il.Emit(OpCodes.Ldc_I4, args.Count); // RedisKey[] int il.Emit(OpCodes.Newarr, typeof(RedisValue)); // RedisKey[] RedisValue[] } for (var i = 0; i < args.Count; i++) { il.Emit(OpCodes.Dup); // RedisKey[] RedisValue[] RedisValue[] il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisValue[] RedisValue[] int #if !CORE_CLR if (t.IsValueType) #else if (t.GetTypeInfo().IsValueType) #endif { il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisValue[] RedisValue[] int T* } else { il.Emit(OpCodes.Ldloc, loc); // RedisKey[] RedisValue[] RedisValue[] int T } var member = args[i]; LoadMember(il, member); // RedisKey[] RedisValue[] RedisValue[] int memberType ConvertToRedisValue(member, il, needsKeyPrefixLoc, ref redisKeyLoc); // RedisKey[] RedisValue[] RedisValue[] int RedisValue il.Emit(OpCodes.Stelem, typeof(RedisValue)); // RedisKey[] RedisValue[] } il.Emit(OpCodes.Newobj, ScriptParameters.Cons); // ScriptParameters il.Emit(OpCodes.Ret); // --empty-- var ret = (Func <object, RedisKey?, ScriptParameters>)dyn.CreateDelegate(typeof(Func <object, RedisKey?, ScriptParameters>)); return(ret); }