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));
 }
Exemple #6
0
 internal LoadedLuaScript(LuaScript original, byte[] hash)
 {
     Original = original;
     Hash     = hash;
 }
Exemple #7
0
        /// <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());
        }
Exemple #8
0
        /// <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);
        }