csharp/actions/runner/src/Sdk/DTExpressions2/Expressions2/Sdk/ExpressionUtility.cs

ExpressionUtility.cs
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using GitHub.DistributedTask.Logging;

namespace GitHub.DistributedTask.Expressions2.Sdk
{
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal static clast ExpressionUtility
    {
        internal static Object ConvertToCanonicalValue(
            Object val,
            out ValueKind kind,
            out Object raw)
        {
            raw = null;

            if (Object.ReferenceEquals(val, null))
            {
                kind = ValueKind.Null;
                return null;
            }
            else if (val is Boolean)
            {
                kind = ValueKind.Boolean;
                return val;
            }
            else if (val is Double)
            {
                kind = ValueKind.Number;
                return val;
            }
            else if (val is String)
            {
                kind = ValueKind.String;
                return val;
            }
            else if (val is INull n)
            {
                kind = ValueKind.Null;
                raw = val;
                return null;
            }
            else if (val is IBoolean boolean)
            {
                kind = ValueKind.Boolean;
                raw = val;
                return boolean.GetBoolean();
            }
            else if (val is INumber number)
            {
                kind = ValueKind.Number;
                raw = val;
                return number.GetNumber();
            }
            else if (val is IString str)
            {
                kind = ValueKind.String;
                raw = val;
                return str.GetString();
            }
            else if (val is IReadOnlyObject)
            {
                kind = ValueKind.Object;
                return val;
            }
            else if (val is IReadOnlyArray)
            {
                kind = ValueKind.Array;
                return val;
            }
            else if (!val.GetType().GetTypeInfo().IsClast)
            {
                if (val is Decimal || val is Byte || val is SByte || val is Int16 || val is UInt16 || val is Int32 || val is UInt32 || val is Int64 || val is UInt64 || val is Single)
                {
                    kind = ValueKind.Number;
                    return Convert.ToDouble(val);
                }
                else if (val is Enum)
                {
                    var strVal = String.Format(CultureInfo.InvariantCulture, "{0:G}", val);
                    if (Double.TryParse(strVal, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out Double doubleValue))
                    {
                        kind = ValueKind.Number;
                        return doubleValue;
                    }

                    kind = ValueKind.String;
                    return strVal;
                }
            }

            kind = ValueKind.Object;
            return val;
        }

        internal static String FormatValue(
            ISecretMasker secretMasker,
            EvaluationResult evaluationResult)
        {
            return FormatValue(secretMasker, evaluationResult.Value, evaluationResult.Kind);
        }

        internal static String FormatValue(
            ISecretMasker secretMasker,
            Object value,
            ValueKind kind)
        {
            switch (kind)
            {
                case ValueKind.Null:
                    return ExpressionConstants.Null;

                case ValueKind.Boolean:
                    return ((Boolean)value) ? ExpressionConstants.True : ExpressionConstants.False;

                case ValueKind.Number:
                    var strNumber = ((Double)value).ToString(ExpressionConstants.NumberFormat, CultureInfo.InvariantCulture);
                    return secretMasker != null ? secretMasker.MaskSecrets(strNumber) : strNumber;

                case ValueKind.String:
                    // Mask secrets before string-escaping.
                    var strValue = secretMasker != null ? secretMasker.MaskSecrets(value as String) : value as String;
                    return $"'{StringEscape(strValue)}'";

                case ValueKind.Array:
                case ValueKind.Object:
                    return kind.ToString();

                default: // Should never reach here.
                    throw new NotSupportedException($"Unable to convert to realized expression. Unexpected value kind: {kind}");
            }
        }

        internal static bool IsLegalKeyword(String str)
        {
            if (String.IsNullOrEmpty(str))
            {
                return false;
            }

            var first = str[0];
            if ((first >= 'a' && first = 'A' && first = 'a' && c = 'A' && c = '0' && c  2 &&
                str[1] == 'x' &&
                str.Skip(2).All(x => (x >= '0' && x = 'a' && x = 'A' && x  2 &&
                str[1] == 'o' &&
                str.Skip(2).All(x => x >= '0' && x