private Expression ProduceComparatorExpressionForSingleFieldCondition()

in sdk/monitor/Azure.Monitor.OpenTelemetry.LiveMetrics/src/Internals/Filtering/Filter.cs [459:780]


        private Expression ProduceComparatorExpressionForSingleFieldCondition(Expression fieldExpression, Type fieldType, bool isFieldTypeNullable = false)
        {
            // this must determine an appropriate runtime comparison given the field type, the predicate, and the comparand
            TypeCode fieldTypeCode = Type.GetTypeCode(fieldType);
            switch (fieldTypeCode)
            {
                case TypeCode.Boolean:
                    {
                        ThrowOnInvalidFilter(fieldType, !comparandBoolean.HasValue);

                        switch (predicate)
                        {
                            case Predicate.Equal:
                                // fieldValue == this.comparandBoolean.Value;
                                return Expression.Equal(fieldExpression, Expression.Constant(comparandBoolean.Value, isFieldTypeNullable ? typeof(bool?) : typeof(bool)));
                            case Predicate.NotEqual:
                                // fieldValue != this.comparandBoolean.Value;
                                return Expression.NotEqual(fieldExpression, Expression.Constant(comparandBoolean.Value, isFieldTypeNullable ? typeof(bool?) : typeof(bool)));
                            default:
                                ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }

                    break;
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.Byte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                    {
                        if (fieldType.GetTypeInfo().IsEnum)
                        {
                            // this is actually an Enum
                            object enumValue = null;
                            try
                            {
                                enumValue = Enum.Parse(fieldType, comparand, true);
                            }
                            catch (System.Exception)
                            {
                                // we must throw unless this.predicate is either Contains or DoesNotContain, in which case it's ok
                                ThrowOnInvalidFilter(fieldType, predicate != Predicate.Contains && predicate != Predicate.DoesNotContain);
                            }

                            Type enumUnderlyingType = fieldType.GetTypeInfo().GetEnumUnderlyingType();

                            // This block matches the case statements just above.
                            static Type GetNullableType(Type inputType) => inputType switch
                            {
                                _ when inputType == typeof(sbyte) => typeof(sbyte?),
                                _ when inputType == typeof(short) => typeof(short?),
                                _ when inputType == typeof(int) => typeof(int?),
                                _ when inputType == typeof(long) => typeof(long?),
                                _ when inputType == typeof(byte) => typeof(byte?),
                                _ when inputType == typeof(ushort) => typeof(ushort?),
                                _ when inputType == typeof(uint) => typeof(uint?),
                                _ when inputType == typeof(ulong) => typeof(ulong?),
                                _ when inputType == typeof(float) => typeof(float?),
                                _ when inputType == typeof(double) => typeof(double?),
                                _ => throw new ArgumentException($"Cannot create a nullable type for {inputType.FullName}."),
                            };

                            switch (predicate)
                            {
                                case Predicate.Equal:
                                    // fieldValue == enumValue
                                    if (isFieldTypeNullable)
                                    {
                                        // For nullable enums, we need to use a different approach
                                        // First, check if the field is null, then check for equality if not null
                                        // (fieldExpression == null ? false : fieldExpression.Value == enumValue)
                                        return Expression.Condition(
                                            Expression.Equal(fieldExpression, Expression.Constant(null, fieldExpression.Type)),
                                            Expression.Constant(false),
                                            Expression.Equal(
                                                Expression.Convert(fieldExpression, fieldType),
                                                Expression.Constant(enumValue, fieldType)));
                                    }
                                    else
                                    {
                                        return Expression.Equal(fieldExpression, Expression.Constant(enumValue, fieldType));
                                    }
                                case Predicate.NotEqual:
                                    // fieldValue != enumValue
                                    if (isFieldTypeNullable)
                                    {
                                        // For nullable enums: (fieldExpression == null ? true : fieldExpression.Value != enumValue)
                                        return Expression.Condition(
                                            Expression.Equal(fieldExpression, Expression.Constant(null, fieldExpression.Type)),
                                            Expression.Constant(true),
                                            Expression.NotEqual(
                                                Expression.Convert(fieldExpression, fieldType),
                                                Expression.Constant(enumValue, fieldType)));
                                    }
                                    else
                                    {
                                        return Expression.NotEqual(fieldExpression, Expression.Constant(enumValue, fieldType));
                                    }
                                case Predicate.LessThan:
                                    // (int)fieldValue < (int)enumValue
                                    // (int?)fieldValue < (int?)enumValue
                                    Type underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.LessThan(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.GreaterThan:
                                    // (int)fieldValue > (int)enumValue
                                    // (int?)fieldValue > (int?)enumValue
                                    underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.GreaterThan(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.LessThanOrEqual:
                                    // (int)fieldValue <= (int)enumValue
                                    // (int?)fieldValue <= (int?)enumValue
                                    underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.LessThanOrEqual(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.GreaterThanOrEqual:
                                    // (int)fieldValue >= (int)enumValue
                                    // (int?)fieldValue >= (int?)enumValue
                                    underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
                                    return Expression.GreaterThanOrEqual(
                                        Expression.Convert(fieldExpression, underlyingType),
                                        Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
                                case Predicate.Contains:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1
                                    Expression toStringCall = Expression.Call(fieldExpression, isFieldTypeNullable ? ValueTypeToStringMethodInfo : ObjectToStringMethodInfo);
                                    Expression indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                                case Predicate.DoesNotContain:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1
                                    toStringCall = Expression.Call(fieldExpression, isFieldTypeNullable ? ValueTypeToStringMethodInfo : ObjectToStringMethodInfo);
                                    indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.Equal(indexOfCall, Expression.Constant(-1));
                                default:
                                    ThrowOnInvalidFilter(fieldType);
                                    break;
                            }
                        }
                        else
                        {
                            // this is a regular numerical type
                            // in order for the expression to compile, we must cast to double unless it's already double
                            // we're using double as the lowest common denominator for all numerical types
                            Expression fieldConvertedExpression = fieldTypeCode == TypeCode.Double
                                                                      ? fieldExpression
                                                                      : Expression.ConvertChecked(fieldExpression, isFieldTypeNullable ? typeof(double?) : typeof(double));

                            switch (predicate)
                            {
                                case Predicate.Equal:
                                    ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                    return Expression.Equal(
                                        fieldConvertedExpression,
                                        Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
                                case Predicate.NotEqual:
                                    ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                    return Expression.NotEqual(
                                        fieldConvertedExpression,
                                        Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
                                case Predicate.LessThan:
                                    ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                    return Expression.LessThan(
                                        fieldConvertedExpression,
                                        Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
                                case Predicate.GreaterThan:
                                    ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                    return Expression.GreaterThan(
                                        fieldConvertedExpression,
                                        Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
                                case Predicate.LessThanOrEqual:
                                    ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                    return Expression.LessThanOrEqual(
                                        fieldConvertedExpression,
                                        Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
                                case Predicate.GreaterThanOrEqual:
                                    ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                    return Expression.GreaterThanOrEqual(
                                        fieldConvertedExpression,
                                        Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
                                case Predicate.Contains:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1
                                    Expression toStringCall = isFieldTypeNullable
                                                                  ? Expression.Call(fieldConvertedExpression, NullableDoubleToStringMethodInfo)
                                                                  : Expression.Call(fieldConvertedExpression, DoubleToStringMethodInfo, Expression.Constant(CultureInfo.InvariantCulture));
                                    Expression indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                                case Predicate.DoesNotContain:
                                    // fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1
                                    toStringCall = isFieldTypeNullable
                                                       ? Expression.Call(fieldConvertedExpression, NullableDoubleToStringMethodInfo)
                                                       : Expression.Call(fieldConvertedExpression, DoubleToStringMethodInfo, Expression.Constant(CultureInfo.InvariantCulture));
                                    indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                                    return Expression.Equal(indexOfCall, Expression.Constant(-1));
                                default:
                                    ThrowOnInvalidFilter(fieldType);
                                    break;
                            }
                        }
                    }

                    break;
                case TypeCode.String:
                    {
                        Expression fieldValueOrEmptyString = Expression.Condition(Expression.Equal(fieldExpression, Expression.Constant(null)), Expression.Constant(string.Empty), fieldExpression);

                        Expression indexOfCall = Expression.Call(fieldValueOrEmptyString, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));

                        switch (predicate)
                        {
                            case Predicate.Equal:
                                // (fieldValue ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                            case Predicate.NotEqual:
                                // !(fieldValue ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Not(Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase)));
                            case Predicate.LessThan:
                            case Predicate.GreaterThan:
                            case Predicate.LessThanOrEqual:
                            case Predicate.GreaterThanOrEqual:
                                // double.TryParse(fieldValue, out temp) && temp {<, <=, >, >=} comparandDouble
                                ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
                                return CreateStringToDoubleComparisonBlock(fieldExpression, predicate);
                            case Predicate.Contains:
                                // fieldValue => (fieldValue ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1;
                                return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                            case Predicate.DoesNotContain:
                                // fieldValue => (fieldValue ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1;
                                return Expression.Equal(indexOfCall, Expression.Constant(-1));
                            default:
                                ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }

                    break;
                default:
                    Type nullableUnderlyingType;
                    if (fieldType == typeof(TimeSpan))
                    {
                        ThrowOnInvalidFilter(fieldType, !comparandTimeSpan.HasValue);
                        if (fieldExpression.Type == typeof(string))
                        {
                            MethodInfo parseMethod = typeof(TimeSpan).GetMethod("Parse", new[] { typeof(string) });
                            fieldExpression = Expression.Call(parseMethod, fieldExpression);
                        }

                        switch (predicate)
                        {
                            case Predicate.Equal:
                                Func<TimeSpan, bool> comparator = fieldValue => fieldValue == comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.NotEqual:
                                comparator = fieldValue => fieldValue != comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.LessThan:
                                comparator = fieldValue => fieldValue < comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.GreaterThan:
                                comparator = fieldValue => fieldValue > comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.LessThanOrEqual:
                                comparator = fieldValue => fieldValue <= comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            case Predicate.GreaterThanOrEqual:
                                comparator = fieldValue => fieldValue >= comparandTimeSpan.Value;
                                return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
                            default:
                                ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }
                    else if (fieldType == typeof(Uri))
                    {
                        Expression toStringCall = Expression.Call(fieldExpression, UriToStringMethodInfo);

                        Expression fieldValueOrEmptyString = Expression.Condition(Expression.Equal(fieldExpression, Expression.Constant(null)), Expression.Constant(string.Empty), toStringCall);

                        Expression indexOfCall = Expression.Call(fieldValueOrEmptyString, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));

                        switch (predicate)
                        {
                            case Predicate.Equal:
                                // (fieldValue?.ToString() ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
                            case Predicate.NotEqual:
                                // !(fieldValue?.ToString() ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
                                return Expression.Not(Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase)));
                            case Predicate.Contains:
                                // fieldValue => (fieldValue?.ToString() ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1;
                                return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
                            case Predicate.DoesNotContain:
                                // fieldValue => (fieldValue?.ToString() ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1;
                                return Expression.Equal(indexOfCall, Expression.Constant(-1));
                            default:
                                ThrowOnInvalidFilter(fieldType);
                                break;
                        }
                    }
                    else if ((nullableUnderlyingType = Nullable.GetUnderlyingType(fieldType)) != null)
                    {
                        // make a recursive call for the underlying type
                        return ProduceComparatorExpressionForSingleFieldCondition(fieldExpression, nullableUnderlyingType, true);
                    }
                    else
                    {
                        ThrowOnInvalidFilter(fieldType);
                    }

                    break;
            }

            return null;
        }