/// <summary>
 /// Decodes the parameter expression.
 /// </summary>
 /// <param name="expression">The expression to be decoded.</param>
 /// <param name="state">The current state of the decode operation.</param>
 /// <param name="options">The options for the decode operation.</param>
 /// <returns>An <see cref="Expression"/> object.</returns>
 internal static Expression DecodeParameterExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
 {
     return(state.GetOrAddParameter(expression));
 }
        /// <summary>
        /// Decodes the method call expression.
        /// </summary>
        /// <param name="expression">The expression to be decoded.</param>
        /// <param name="state">The current state of the decode operation.</param>
        /// <param name="options">The options for the decode operation.</param>
        /// <returns>An <see cref="Expression"/> object.</returns>
        internal static Expression DecodeMethodCallExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            var        arguments        = new List <Expression>();
            Expression objectExpression = null;

            if (expression.Expressions["Object"] != null)
            {
                objectExpression = DecodeExpression(expression.Expressions["Object"], state, options);
            }

            int argumentCount = Convert.ToInt32(expression.Values["ArgumentCount"]);

            for (int i = 0; i < argumentCount; i++)
            {
                arguments.Add(DecodeExpression(expression.Expressions[$"a{i}"], state, options));
            }

            var methodInfo = state.SignatureHelper.GetMethodInfoFromSignature(expression.Values["Method"]);

            if (methodInfo == null)
            {
                throw new MethodNotFoundException("A matching method was not found.");
            }

            if (!options.IsMethodSafe(methodInfo))
            {
                throw new UnsafeMethodCallException("Attempted to decode an unsafe method call.", methodInfo);
            }

            return(Expression.Call(objectExpression, methodInfo, arguments));
        }
        /// <summary>
        /// Decodes the lambda expression.
        /// </summary>
        /// <param name="expression">The expression to be decoded.</param>
        /// <param name="state">The current state of the decode operation.</param>
        /// <param name="options">The options for the decode operation.</param>
        /// <returns>An <see cref="Expression"/> object.</returns>
        internal static Expression DecodeLambdaExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            var body           = DecodeExpression(expression.Expressions["Body"], state, options);
            var parameterCount = Convert.ToInt32(expression.Values["ParameterCount"]);
            var parameters     = new List <ParameterExpression>();

            for (int i = 0; i < parameterCount; i++)
            {
                parameters.Add(( ParameterExpression )DecodeExpression(expression.Expressions[$"p{i}"], state, options));
            }

            return(Expression.Lambda(body, parameters.ToArray()));
        }
        /// <summary>
        /// Decodes the member expression.
        /// </summary>
        /// <param name="expression">The expression to be decoded.</param>
        /// <param name="state">The current state of the decode operation.</param>
        /// <param name="options">The options for the decode operation.</param>
        /// <returns>An <see cref="Expression"/> object.</returns>
        internal static Expression DecodeMemberExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            MemberInfo memberInfo;
            Type       type = state.SignatureHelper.GetTypeFromSignature(expression.Values["Type"]);

            if (bool.Parse(expression.Values["IsProperty"]))
            {
                memberInfo = type.GetProperty(expression.Values["Member"]);
            }
            else
            {
                memberInfo = type.GetField(expression.Values["Member"]);
            }

            var expr = DecodeExpression(expression.Expressions["Expression"], state, options);

            return(Expression.MakeMemberAccess(expr, memberInfo));
        }
        /// <summary>
        /// Decodes the constant expression.
        /// </summary>
        /// <param name="expression">The expression to be decoded.</param>
        /// <param name="state">The current state of the decode operation.</param>
        /// <param name="options">The options for the decode operation.</param>
        /// <returns>An <see cref="Expression"/> object.</returns>
        internal static Expression DecodeConstantExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            var type  = state.SignatureHelper.GetTypeFromSignature(expression.Values["Type"]);
            var Value = expression.Values["Value"];

            return(Expression.Constant(Convert.ChangeType(Value, type)));
        }
        /// <summary>
        /// Decodes the unary expression.
        /// </summary>
        /// <param name="expression">The expression to be decoded.</param>
        /// <param name="state">The current state of the decode operation.</param>
        /// <param name="options">The options for the decode operation.</param>
        /// <returns>An <see cref="Expression"/> object.</returns>
        internal static Expression DecodeUnaryExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            var operand = DecodeExpression(expression.Expressions["Operand"], state, options);
            var type    = state.SignatureHelper.GetTypeFromSignature(expression.Values["Type"]);

            return(Expression.MakeUnary(expression.NodeType, operand, type));
        }
        /// <summary>
        /// Decodes the binary expression.
        /// </summary>
        /// <param name="expression">The expression to be decoded.</param>
        /// <param name="state">The current state of the decode operation.</param>
        /// <param name="options">The options for the decode operation.</param>
        /// <returns>An <see cref="Expression"/> object.</returns>
        internal static Expression DecodeBinaryExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            var left  = DecodeExpression(expression.Expressions["Left"], state, options);
            var right = DecodeExpression(expression.Expressions["Right"], state, options);

            if (expression.Expressions["Conversion"] == null)
            {
                return(Expression.MakeBinary(expression.NodeType, left, right));
            }
            else
            {
                var conversion = ( LambdaExpression )DecodeExpression(expression.Expressions["Conversion"], state, options);

                return(Expression.MakeBinary(expression.NodeType, left, right, false, null, conversion));
            }
        }
        /// <summary>
        /// Decodes the expression back into a LINQ expression.
        /// </summary>
        /// <param name="expression">The encoded expression to be decoded.</param>
        /// <param name="state">The state of the decode operation.</param>
        /// <param name="options">The options that will be used during decoding.</param>
        /// <returns>A LINQ <see cref="Expression"/> instance.</returns>
        internal static Expression DecodeExpression(EncodedExpression expression, DecodeState state, DecodeOptions options)
        {
            if (!ExpressionTypeMapping.ContainsKey(expression.NodeType))
            {
                throw new ArgumentException($"Encountered unknown node type {expression.NodeType} when decoding expression.", nameof(expression));
            }

            return(ExpressionTypeMapping[expression.NodeType].Decode(expression, state, options));
        }
 /// <summary>
 /// Decodes the expression back into a LINQ expression.
 /// </summary>
 /// <param name="expression">The encoded expression to be decoded.</param>
 /// <param name="options">The options that will be used during decoding.</param>
 /// <returns>A LINQ <see cref="Expression"/> instance.</returns>
 public static Expression DecodeExpression(EncodedExpression expression, DecodeOptions options)
 {
     return(DecodeExpression(expression, new DecodeState(), options));
 }