Exemple #1
0
    public object Compute(Tolerance?tolerance, params object[] arguments)
    {
        this.RequiresNotDisposed();

        if (!RecognizedCorrectly)
        {
            // Expression was not recognized correctly.
            return(initialExpression);
        }

        var convertedArguments = FormatArgumentsAccordingToParameters(arguments, parametersRegistry?.Dump() ?? Array.Empty <ParameterContext>());

        object[]? FormatArgumentsAccordingToParameters(
            object[] parameterValues,
            ParameterContext[] parameters)
        {
            if (parameterValues.Length != parameters.Length)
            {
                return(null);
            }

            var finalValues = new object[parameterValues.Length];

            var i = 0;

            object?paramValue = null;

            while (i < finalValues.Length)
            {
                ParameterContext paraContext = parameters[i];

                // If there was no continuation, initialize parameter with value.
                paramValue ??= parameterValues[i];

                // Initial filtration
                switch (paramValue)
                {
                case byte convertedParam:
                    paramValue = Convert.ToInt64(convertedParam);
                    continue;

                case sbyte convertedParam:
                    paramValue = Convert.ToInt64(convertedParam);
                    continue;

                case short convertedParam:
                    paramValue = Convert.ToInt64(convertedParam);
                    continue;

                case ushort convertedParam:
                    paramValue = Convert.ToInt64(convertedParam);
                    continue;

                case int convertedParam:
                    paramValue = Convert.ToInt64(convertedParam);
                    continue;

                case uint convertedParam:
                    paramValue = Convert.ToInt64(convertedParam);
                    continue;

                case long convertedParam:
                    paramValue = convertedParam;
                    break;

                case ulong convertedParam:
                    paramValue = Convert.ToDouble(convertedParam);
                    continue;

                case float convertedParam:
                    paramValue = Convert.ToDouble(convertedParam);
                    continue;

                case double convertedParam:
                    paramValue = convertedParam;
                    break;

                case string convertedParam:
                    paramValue = convertedParam;
                    break;

                case bool convertedParam:
                    paramValue = convertedParam;
                    break;

                case byte[] convertedParam:
                    paramValue = convertedParam;
                    break;

                case Func <byte> convertedParam:
                    paramValue = new Func <long>(() => Convert.ToInt64(convertedParam()));
                    continue;

                case Func <sbyte> convertedParam:
                    paramValue = new Func <long>(() => Convert.ToInt64(convertedParam()));
                    continue;

                case Func <short> convertedParam:
                    paramValue = new Func <long>(() => Convert.ToInt64(convertedParam()));
                    continue;

                case Func <ushort> convertedParam:
                    paramValue = new Func <long>(() => Convert.ToInt64(convertedParam()));
                    continue;

                case Func <int> convertedParam:
                    paramValue = new Func <long>(() => Convert.ToInt64(convertedParam()));
                    continue;

                case Func <uint> convertedParam:
                    paramValue = new Func <long>(() => Convert.ToInt64(convertedParam()));
                    continue;

                case Func <long> convertedParam:
                    paramValue = convertedParam;
                    break;

                case Func <ulong> convertedParam:
                    paramValue = new Func <double>(() => Convert.ToDouble(convertedParam()));
                    continue;

                case Func <float> convertedParam:
                    paramValue = new Func <double>(() => Convert.ToDouble(convertedParam()));
                    continue;

                case Func <double> convertedParam:
                    paramValue = convertedParam;
                    break;

                case Func <string> convertedParam:
                    paramValue = convertedParam;
                    break;

                case Func <bool> convertedParam:
                    paramValue = convertedParam;
                    break;

                case Func <byte[]> convertedParam:
                    paramValue = convertedParam;
                    break;

                default:
                    // Argument type is not (yet) supported
                    return(null);
                }

                // Secondary filtration
                switch (paramValue)
                {
                case long convertedParam:
                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValue(paraContext, convertedParam != 0);
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValue(paraContext, BitConverter.GetBytes(convertedParam));
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValue(paraContext, Convert.ToDouble(convertedParam));
                            break;

                        case false:
                            paramValue = CreateValue(paraContext, convertedParam);
                            break;

                        default:
                            paraContext.DetermineInteger();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValue(
                            paraContext,
                            StringFormatter.FormatIntoString(convertedParam, stringFormatters));

                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.Numeric);
                        continue;
                    }

                    break;

                case double convertedParam:
                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        // ReSharper disable once CompareOfFloatsByEqualityOperator - no better idea for now
                        paramValue = CreateValue(paraContext, convertedParam != 0D);
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValue(paraContext, BitConverter.GetBytes(convertedParam));
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValue(paraContext, convertedParam);
                            break;

                        case false:
                            paramValue = CreateValue(paraContext, Convert.ToInt64(convertedParam));
                            break;

                        default:
                            paraContext.DetermineFloat();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValue(
                            paraContext,
                            StringFormatter.FormatIntoString(convertedParam, stringFormatters));
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.Numeric);
                        continue;
                    }

                    break;

                case bool convertedParam:
                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValue(paraContext, convertedParam);
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValue(paraContext, BitConverter.GetBytes(convertedParam));
                        break;

                    case SupportedValueType.Numeric:
                        return(null);

                    case SupportedValueType.String:
                        paramValue = CreateValue(
                            paraContext,
                            StringFormatter.FormatIntoString(convertedParam, stringFormatters));
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.Boolean);
                        continue;
                    }

                    break;

                case string convertedParam:
                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValue(paraContext, bool.Parse(convertedParam));
                        break;

                    case SupportedValueType.ByteArray:
                    {
                        if (ParsingFormatter.ParseByteArray(convertedParam, out var byteArrayResult))
                        {
                            paramValue = CreateValue(paraContext, byteArrayResult);
                        }
                        else
                        {
                            // Cannot parse byte array.
                            return(null);
                        }
                    }

                    break;

                    case SupportedValueType.Numeric:
                    {
                        if (ParsingFormatter.ParseNumeric(convertedParam, out var numericResult))
                        {
                            switch (numericResult)
                            {
                            case long integerResult:
                                paramValue = CreateValue(paraContext, integerResult);
                                break;

                            case double floatResult:
                                paramValue = CreateValue(paraContext, floatResult);
                                break;

                            default:
                                // Numeric type unknown.
                                return(null);
                            }
                        }
                        else
                        {
                            // Cannot parse numeric type.
                            return(null);
                        }
                    }

                    break;

                    case SupportedValueType.String:
                        paramValue = CreateValue(paraContext, convertedParam);
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.String);
                        continue;
                    }

                    break;

                case byte[] convertedParam:
                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValue(paraContext, BitConverter.ToBoolean(convertedParam, 0));
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValue(paraContext, convertedParam);
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValue(paraContext, BitConverter.ToDouble(convertedParam, 0));
                            break;

                        case false:
                            paramValue = CreateValue(paraContext, BitConverter.ToInt64(convertedParam, 0));
                            break;

                        default:
                            paraContext.DetermineFloat();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValue(
                            paraContext,
                            StringFormatter.FormatIntoString(convertedParam, stringFormatters));
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.ByteArray);
                        continue;
                    }

                    break;

                case Func <long> convertedParam:
                    paraContext.DetermineFunc();

                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValueFromFunc(paraContext, () => convertedParam() == 0);
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValueFromFunc(paraContext, () => BitConverter.GetBytes(convertedParam()));
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValueFromFunc(paraContext, () => Convert.ToDouble(convertedParam()));
                            break;

                        case false:
                            paramValue = CreateValueFromFunc(paraContext, convertedParam);
                            break;

                        default:
                            paraContext.DetermineInteger();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValueFromFunc(paraContext, () => StringFormatter.FormatIntoString(convertedParam(), stringFormatters));
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.Numeric);
                        continue;
                    }

                    break;

                case Func <double> convertedParam:
                    paraContext.DetermineFunc();

                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        // ReSharper disable once CompareOfFloatsByEqualityOperator - no better idea for now
                        paramValue = CreateValueFromFunc(paraContext, () => convertedParam() == 0D);
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValueFromFunc(paraContext, () => BitConverter.GetBytes(convertedParam()));
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValueFromFunc(paraContext, convertedParam);
                            break;

                        case false:
                            paramValue = CreateValueFromFunc(paraContext, () => Convert.ToInt64(convertedParam()));
                            break;

                        default:
                            paraContext.DetermineFloat();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValueFromFunc(paraContext, () => StringFormatter.FormatIntoString(convertedParam(), stringFormatters));

                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.Numeric);
                        continue;
                    }

                    break;

                case Func <bool> convertedParam:
                    paraContext.DetermineFunc();

                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValueFromFunc(paraContext, convertedParam);
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValueFromFunc(paraContext, () => BitConverter.GetBytes(convertedParam()));
                        break;

                    case SupportedValueType.Numeric:
                        return(null);

                    case SupportedValueType.String:
                        paramValue = CreateValueFromFunc(paraContext, () => StringFormatter.FormatIntoString(convertedParam(), stringFormatters));
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.Boolean);
                        continue;
                    }

                    break;

                case Func <string> convertedParam:
                    paraContext.DetermineFunc();

                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValueFromFunc(paraContext, () => bool.Parse(convertedParam()));
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValueFromFunc(paraContext, () => ParsingFormatter.ParseByteArray(convertedParam(), out var baResult) ? baResult : null);
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValueFromFunc(paraContext, () => ParsingFormatter.ParseNumeric(
                                                                 convertedParam(),
                                                                 out var numericResult)
                                            ? Convert.ToDouble(numericResult, CultureInfo.CurrentCulture)
                                            : throw new ArgumentInvalidTypeException(paraContext.Name));
                            break;

                        case false:
                            paramValue = CreateValueFromFunc(paraContext, () => ParsingFormatter.ParseNumeric(
                                                                 convertedParam(),
                                                                 out var numericResult)
                                            ? Convert.ToInt64(numericResult, CultureInfo.CurrentCulture)
                                            : throw new ArgumentInvalidTypeException(paraContext.Name));
                            break;

                        default:
                            paraContext.DetermineFloat();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValueFromFunc(paraContext, convertedParam);
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.String);
                        continue;
                    }

                    break;

                case Func <byte[]> convertedParam:
                    paraContext.DetermineFunc();

                    switch (paraContext.ReturnType)
                    {
                    case SupportedValueType.Boolean:
                        paramValue = CreateValueFromFunc(paraContext, () => BitConverter.ToBoolean(convertedParam(), 0));
                        break;

                    case SupportedValueType.ByteArray:
                        paramValue = CreateValueFromFunc(paraContext, convertedParam);
                        break;

                    case SupportedValueType.Numeric:
                        switch (paraContext.IsFloat)
                        {
                        case true:
                            paramValue = CreateValueFromFunc(paraContext, () => BitConverter.ToDouble(convertedParam(), 0));
                            break;

                        case false:
                            paramValue = CreateValueFromFunc(paraContext, () => BitConverter.ToInt64(convertedParam(), 0));
                            break;

                        default:
                            paraContext.DetermineFloat();
                            continue;
                        }

                        break;

                    case SupportedValueType.String:
                        paramValue = CreateValueFromFunc(paraContext, () => StringFormatter.FormatIntoString(convertedParam(), stringFormatters));
                        break;

                    case SupportedValueType.Unknown:
                        paraContext.DetermineType(SupportedValueType.ByteArray);
                        continue;
                    }

                    break;

                default:
                    // Argument type is not (yet) supported
                    return(null);
                }

#pragma warning disable HAA0601 // Value type to reference type conversion causing boxing allocation - This is unavoidable now
#pragma warning disable HAA0303 // Considering moving this out of the generic method - Not really possible
                object CreateValue <T>(ParameterContext parameterContext, T value) => parameterContext.FuncParameter ? new Func <T>(() => value) : (object)value;

                object CreateValueFromFunc <T>(
                    ParameterContext parameterContext,
                    Func <T> value) => parameterContext.FuncParameter ? (object)value : value();

#pragma warning restore HAA0303 // Considering moving this out of the generic method
#pragma warning restore HAA0601 // Value type to reference type conversion causing boxing allocation

                if (paramValue == null)
                {
                    return(null);
                }

                finalValues[i] = paramValue;

                paramValue = null;

                i++;
            }

            return(finalValues);
        }

        if (convertedArguments == null)
        {
            // The arguments could not be correctly converted.
            return(initialExpression);
        }

        Delegate?del;

        if (body == null)
        {
            del = null;
        }
        else
        {
            try
            {
                del = Expression.Lambda(
                    tolerance == null ? body.GenerateExpression() : body.GenerateExpression(tolerance),
                    parametersRegistry?.Dump().Select(p => p.ParameterExpression) ?? Array.Empty <ParameterExpression>())
                      .Compile();
            }
            catch
            {
                // Expression is somehow not valid
                del = null;
            }
        }

        if (del == null)
        {
            // Delegate could not be compiled with the given arguments.
            return(initialExpression);
        }

        try
        {
            return(del.DynamicInvoke(convertedArguments));
        }
        catch (OutOfMemoryException)
        {
            throw;
        }
        catch (DivideByZeroException)
        {
            throw;
        }
        catch
        {
            // Dynamic invocation of generated expression failed.
            return(initialExpression);
        }
    }