File: Evaluation\Expander\WellKnownFunctions.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;
 
using ParseArgs = Microsoft.Build.Evaluation.Expander.ArgumentParser;
 
 
namespace Microsoft.Build.Evaluation.Expander
{
    internal class WellKnownFunctions
    {
 
        internal static bool ElementsOfType(object[] args, Type type)
        {
            for (var i = 0; i < args.Length; i++)
            {
                if (args[i].GetType() != type)
                {
                    return false;
                }
            }
 
            return true;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static void LogFunctionCall(Type receiverType, string methodName, string fileName, object? objectInstance, object[] args)
        {
            var logFile = Path.Combine(Directory.GetCurrentDirectory(), fileName);
 
            var argSignature = args != null
                ? string.Join(", ", args.Select(a => a?.GetType().Name ?? "null"))
                : string.Empty;
 
            File.AppendAllText(logFile, $"ReceiverType={receiverType?.FullName}; ObjectInstanceType={objectInstance?.GetType().FullName}; MethodName={methodName}({argSignature})\n");
        }
 
        internal static bool TryExecutePathFunction(string methodName, out object? returnVal, object[] args)
        {
            returnVal = default;
            if (string.Equals(methodName, nameof(Path.Combine), StringComparison.OrdinalIgnoreCase))
            {
                string? arg0, arg1, arg2, arg3;
 
                // Combine has fast implementations for up to 4 parameters: https://github.com/dotnet/corefx/blob/2c55db90d622fa6279184e6243f0470a3755d13c/src/Common/src/CoreLib/System/IO/Path.cs#L293-L317
                switch (args.Length)
                {
                    case 0:
                        return false;
                    case 1:
                        if (ParseArgs.TryGetArg(args, out arg0) && arg0 != null)
                        {
                            returnVal = Path.Combine(arg0);
                            return true;
                        }
                        break;
                    case 2:
                        if (ParseArgs.TryGetArgs(args, out arg0, out arg1) && arg0 != null && arg1 != null)
                        {
                            returnVal = Path.Combine(arg0, arg1);
                            return true;
                        }
                        break;
                    case 3:
                        if (ParseArgs.TryGetArgs(args, out arg0, out arg1, out arg2) && arg0 != null && arg1 != null && arg2 != null)
                        {
                            returnVal = Path.Combine(arg0, arg1, arg2);
                            return true;
                        }
                        break;
                    case 4:
                        if (ParseArgs.TryGetArgs(args, out arg0, out arg1, out arg2, out arg3) && arg0 != null && arg1 != null && arg2 != null && arg3 != null)
                        {
                            returnVal = Path.Combine(arg0, arg1, arg2, arg3);
                            return true;
                        }
                        break;
                    default:
                        if (ElementsOfType(args, typeof(string)))
                        {
                            returnVal = Path.Combine(Array.ConvertAll(args, o => (string)o));
                            return true;
                        }
                        break;
                }
            }
            else if (string.Equals(methodName, nameof(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = Path.DirectorySeparatorChar;
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Path.GetFullPath), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = Path.GetFullPath(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Path.IsPathRooted), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = Path.IsPathRooted(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Path.GetTempPath), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = Path.GetTempPath();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Path.GetFileName), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = Path.GetFileName(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Path.GetDirectoryName), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = Path.GetDirectoryName(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Path.GetFileNameWithoutExtension), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = Path.GetFileNameWithoutExtension(arg0);
                    return true;
                }
            }
            return false;
        }
 
        /// <summary>
        /// Handler for executing well known string functions
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="returnVal"></param>
        /// <param name="text"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        internal static bool TryExecuteStringFunction(string methodName, out object? returnVal, string text, object[] args)
        {
            returnVal = null;
            if (string.Equals(methodName, nameof(string.StartsWith), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = text.StartsWith(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.Replace), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1) && arg0 != null)
                {
                    returnVal = text.Replace(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.Contains), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = text.Contains(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.ToUpperInvariant), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = text.ToUpperInvariant();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.ToLowerInvariant), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = text.ToLowerInvariant();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.EndsWith), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = text.EndsWith(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.ToLower), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = text.ToLower();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.IndexOf), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out StringComparison arg1) && arg0 != null)
                {
                    returnVal = text.IndexOf(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.IndexOfAny), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = text.IndexOfAny(arg0.ToCharArray());
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.LastIndexOf), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = text.LastIndexOf(arg0);
                    return true;
                }
                else if (ParseArgs.TryGetArgs(args, out arg0, out int startIndex) && arg0 != null)
                {
                    returnVal = text.LastIndexOf(arg0, startIndex);
                    return true;
                }
                else if (ParseArgs.TryGetArgs(args, out arg0, out StringComparison arg1) && arg0 != null)
                {
                    returnVal = text.LastIndexOf(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.LastIndexOfAny), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = text.LastIndexOfAny(arg0.ToCharArray());
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.Length), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = text.Length;
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.Substring), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out int startIndex))
                {
                    returnVal = text.Substring(startIndex);
                    return true;
                }
                else if (ParseArgs.TryGetArgs(args, out startIndex, out int length))
                {
                    returnVal = text.Substring(startIndex, length);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.Split), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? separator) && separator?.Length == 1)
                {
                    returnVal = text.Split(separator[0]);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.PadLeft), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out int totalWidth))
                {
                    returnVal = text.PadLeft(totalWidth);
                    return true;
                }
                else if (ParseArgs.TryGetArgs(args, out totalWidth, out string? paddingChar) && paddingChar?.Length == 1)
                {
                    returnVal = text.PadLeft(totalWidth, paddingChar[0]);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.PadRight), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out int totalWidth))
                {
                    returnVal = text.PadRight(totalWidth);
                    return true;
                }
                else if (ParseArgs.TryGetArgs(args, out totalWidth, out string? paddingChar) && paddingChar?.Length == 1)
                {
                    returnVal = text.PadRight(totalWidth, paddingChar[0]);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.TrimStart), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? trimChars) && trimChars?.Length > 0)
                {
                    returnVal = text.TrimStart(trimChars.ToCharArray());
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.TrimEnd), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? trimChars) && trimChars?.Length > 0)
                {
                    returnVal = text.TrimEnd(trimChars.ToCharArray());
                    return true;
                }
            }
            else if (string.Equals(methodName, "get_Chars", StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out int index))
                {
                    returnVal = text[index];
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(string.Equals), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = text.Equals(arg0);
                    return true;
                }
            }
            return false;
        }
 
        internal static bool TryExecuteIntrinsicFunction(string methodName, out object? returnVal, IFileSystem fileSystem, object[] args)
        {
            returnVal = default;
            if (string.Equals(methodName, nameof(IntrinsicFunctions.EnsureTrailingSlash), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.EnsureTrailingSlash(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.ValueOrDefault), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.ValueOrDefault(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.NormalizePath), StringComparison.OrdinalIgnoreCase))
            {
                if (ElementsOfType(args, typeof(string)))
                {
                    returnVal = IntrinsicFunctions.NormalizePath(Array.ConvertAll(args, o => (string)o));
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetDirectoryNameOfFileAbove), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.GetDirectoryNameOfFileAbove(arg0, arg1, fileSystem);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetRegistryValueFromView), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length >= 4 &&
                    ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.GetRegistryValueFromView(arg0, arg1, args[2], new ArraySegment<object>(args, 3, args.Length - 3));
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.IsRunningFromVisualStudio), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.IsRunningFromVisualStudio();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Escape), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.Escape(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Unescape), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.Unescape(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetPathOfFileAbove), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.GetPathOfFileAbove(arg0, arg1, fileSystem);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Add), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryExecuteArithmeticOverload(args, IntrinsicFunctions.Add, IntrinsicFunctions.Add, out returnVal))
                {
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Subtract), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryExecuteArithmeticOverload(args, IntrinsicFunctions.Subtract, IntrinsicFunctions.Subtract, out returnVal))
                {
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Multiply), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryExecuteArithmeticOverload(args, IntrinsicFunctions.Multiply, IntrinsicFunctions.Multiply, out returnVal))
                {
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Divide), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryExecuteArithmeticOverload(args, IntrinsicFunctions.Divide, IntrinsicFunctions.Divide, out returnVal))
                {
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.Modulo), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryExecuteArithmeticOverload(args, IntrinsicFunctions.Modulo, IntrinsicFunctions.Modulo, out returnVal))
                {
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetCurrentToolsDirectory), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetCurrentToolsDirectory();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetToolsDirectory32), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetToolsDirectory32();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetToolsDirectory64), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetToolsDirectory64();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetMSBuildSDKsPath), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetMSBuildSDKsPath();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetVsInstallRoot), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetVsInstallRoot();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetMSBuildExtensionsPath), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetMSBuildExtensionsPath();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetProgramFiles32), StringComparison.OrdinalIgnoreCase))
            {
                if (args.Length == 0)
                {
                    returnVal = IntrinsicFunctions.GetProgramFiles32();
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.VersionEquals), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.VersionEquals(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.VersionNotEquals), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.VersionNotEquals(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.VersionGreaterThan), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.VersionGreaterThan(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.VersionGreaterThanOrEquals), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.VersionGreaterThanOrEquals(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.VersionLessThan), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.VersionLessThan(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.VersionLessThanOrEquals), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.VersionLessThanOrEquals(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetTargetFrameworkIdentifier), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.GetTargetFrameworkIdentifier(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetTargetFrameworkVersion), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.GetTargetFrameworkVersion(arg0);
                    return true;
                }
                if (ParseArgs.TryGetArgs(args, out string? arg1, out int arg2))
                {
                    returnVal = IntrinsicFunctions.GetTargetFrameworkVersion(arg1, arg2);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.IsTargetFrameworkCompatible), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1))
                {
                    returnVal = IntrinsicFunctions.IsTargetFrameworkCompatible(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetTargetPlatformIdentifier), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.GetTargetPlatformIdentifier(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.GetTargetPlatformVersion), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.GetTargetPlatformVersion(arg0);
                    return true;
                }
                if (ParseArgs.TryGetArgs(args, out string? arg1, out int arg2))
                {
                    returnVal = IntrinsicFunctions.GetTargetPlatformVersion(arg1, arg2);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.ConvertToBase64), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.ConvertToBase64(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.ConvertFromBase64), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    returnVal = IntrinsicFunctions.ConvertFromBase64(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.StableStringHash), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0))
                {
                    // Prevent loading methods refs from StringTools if ChangeWave opted out.
                    returnVal = ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_10)
                        ? IntrinsicFunctions.StableStringHash(arg0)
                        : IntrinsicFunctions.StableStringHashLegacy(arg0);
                    return true;
                }
                else if (ParseArgs.TryGetArgs(args, out string? arg1, out string? arg2) && Enum.TryParse<IntrinsicFunctions.StringHashingAlgorithm>(arg2, true, out var hashAlgorithm) && arg1 != null && arg2 != null)
                {
                    returnVal = IntrinsicFunctions.StableStringHash(arg1, hashAlgorithm);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.AreFeaturesEnabled), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out Version? arg0) && arg0 != null)
                {
                    returnVal = IntrinsicFunctions.AreFeaturesEnabled(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.SubstringByAsciiChars), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out string? arg0, out int arg1, out int arg2) && arg0 != null)
                {
                    returnVal = IntrinsicFunctions.SubstringByAsciiChars(arg0, arg1, arg2);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.CheckFeatureAvailability), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = IntrinsicFunctions.CheckFeatureAvailability(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.BitwiseOr), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out int arg0, out int arg1))
                {
                    returnVal = IntrinsicFunctions.BitwiseOr(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.BitwiseAnd), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out int arg0, out int arg1))
                {
                    returnVal = IntrinsicFunctions.BitwiseAnd(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.BitwiseXor), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out int arg0, out int arg1))
                {
                    returnVal = IntrinsicFunctions.BitwiseXor(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.BitwiseNot), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out int arg0))
                {
                    returnVal = IntrinsicFunctions.BitwiseNot(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.LeftShift), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out int arg0, out int arg1))
                {
                    returnVal = IntrinsicFunctions.LeftShift(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.RightShift), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out int arg0, out int arg1))
                {
                    returnVal = IntrinsicFunctions.RightShift(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.RightShiftUnsigned), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArgs(args, out int arg0, out int arg1))
                {
                    returnVal = IntrinsicFunctions.RightShiftUnsigned(arg0, arg1);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.NormalizeDirectory), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = IntrinsicFunctions.NormalizeDirectory(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(IntrinsicFunctions.IsOSPlatform), StringComparison.OrdinalIgnoreCase))
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = IntrinsicFunctions.IsOSPlatform(arg0);
                    return true;
                }
            }
            return false;
        }
 
        /// <summary>
        /// Shortcut to avoid calling into binding if we recognize some most common functions.
        /// Binding is expensive and throws first-chance MissingMethodExceptions, which is
        /// bad for debugging experience and has a performance cost.
        /// A typical binding operation with exception can take ~1.500 ms; this call is ~0.050 ms
        /// (rough numbers just for comparison).
        /// See https://github.com/dotnet/msbuild/issues/2217.
        /// </summary>
        /// <param name="methodName"> </param>
        /// <param name="receiverType"> </param>
        /// <param name="fileSystem"> </param>
        /// <param name="returnVal">The value returned from the function call.</param>
        /// <param name="objectInstance">Object that the function is called on.</param>
        /// <param name="args">arguments.</param>
        /// <returns>True if the well known function call binding was successful.</returns>
        internal static bool TryExecuteWellKnownFunction(string methodName, Type receiverType, IFileSystem fileSystem, out object? returnVal, object objectInstance, object[] args)
        {
            returnVal = null;
 
            if (objectInstance is string text)
            {
                return TryExecuteStringFunction(methodName, out returnVal, text, args);
            }
            else if (objectInstance is string[] stringArray)
            {
                if (string.Equals(methodName, "GetValue", StringComparison.OrdinalIgnoreCase))
                {
                    if (ParseArgs.TryGetArg(args, out int index))
                    {
                        returnVal = stringArray[index];
                        return true;
                    }
                }
            }
            else if (objectInstance == null) // Calling a well-known static function
            {
                if (receiverType == typeof(string))
                {
                    if (string.Equals(methodName, nameof(string.IsNullOrWhiteSpace), StringComparison.OrdinalIgnoreCase))
                    {
                        if (ParseArgs.TryGetArg(args, out string? arg0))
                        {
                            returnVal = string.IsNullOrWhiteSpace(arg0);
                            return true;
                        }
                    }
                    else if (string.Equals(methodName, nameof(string.IsNullOrEmpty), StringComparison.OrdinalIgnoreCase))
                    {
                        if (ParseArgs.TryGetArg(args, out string? arg0))
                        {
                            returnVal = string.IsNullOrEmpty(arg0);
                            return true;
                        }
                    }
                    else if (string.Equals(methodName, nameof(string.Copy), StringComparison.OrdinalIgnoreCase))
                    {
                        if (ParseArgs.TryGetArg(args, out string? arg0))
                        {
                            returnVal = arg0;
                            return true;
                        }
                    }
                }
                else if (receiverType == typeof(Math))
                {
                    if (string.Equals(methodName, nameof(Math.Max), StringComparison.OrdinalIgnoreCase))
                    {
                        if (ParseArgs.TryGetArgs(args, out double arg0, out double arg1))
                        {
                            returnVal = Math.Max(arg0, arg1);
                            return true;
                        }
                    }
                    else if (string.Equals(methodName, nameof(Math.Min), StringComparison.OrdinalIgnoreCase))
                    {
                        if (ParseArgs.TryGetArgs(args, out double arg0, out double arg1))
                        {
                            returnVal = Math.Min(arg0, arg1);
                            return true;
                        }
                    }
                }
                else if (receiverType == typeof(IntrinsicFunctions))
                {
                    return TryExecuteIntrinsicFunction(methodName, out returnVal, fileSystem, args);
                }
                else if (receiverType == typeof(Path))
                {
                    return TryExecutePathFunction(methodName, out returnVal, args);
                }
                else if (receiverType == typeof(Version))
                {
                    if (string.Equals(methodName, nameof(Version.Parse), StringComparison.OrdinalIgnoreCase))
                    {
                        if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                        {
                            returnVal = Version.Parse(arg0);
                            return true;
                        }
                    }
                }
                else if (receiverType == typeof(Guid))
                {
                    if (string.Equals(methodName, nameof(Guid.NewGuid), StringComparison.OrdinalIgnoreCase))
                    {
                        if (args.Length == 0)
                        {
                            returnVal = Guid.NewGuid();
                            return true;
                        }
                    }
                }
                else if (string.Equals(methodName, nameof(Regex.Replace), StringComparison.OrdinalIgnoreCase) && args.Length == 3)
                {
                    if (ParseArgs.TryGetArgs(args, out string? arg1, out string? arg2, out string? arg3) && arg1 != null && arg2 != null && arg3 != null)
                    {
                        returnVal = Regex.Replace(arg1, arg2, arg3);
                        return true;
                    }
                }
            }
            else if (string.Equals(methodName, nameof(Version.ToString), StringComparison.OrdinalIgnoreCase) && objectInstance is Version v)
            {
                if (ParseArgs.TryGetArg(args, out int arg0))
                {
                    returnVal = v.ToString(arg0);
                    return true;
                }
            }
            else if (string.Equals(methodName, nameof(Int32.ToString), StringComparison.OrdinalIgnoreCase) && objectInstance is int i)
            {
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = i.ToString(arg0);
                    return true;
                }
            }
            if (Traits.Instance.LogPropertyFunctionsRequiringReflection)
            {
                LogFunctionCall(receiverType, methodName, "PropertyFunctionsRequiringReflection", objectInstance, args);
            }
 
            return false;
        }
 
        internal static bool TryExecuteWellKnownFunctionWithPropertiesParam<T>(string methodName, Type receiverType, LoggingContext loggingContext,
                                                                            IPropertyProvider<T> properties, out object? returnVal, object objectInstance, object[] args)
            where T : class, IProperty
        {
            returnVal = null;
 
            if (receiverType == typeof(IntrinsicFunctions))
            {
                if (string.Equals(methodName, nameof(IntrinsicFunctions.RegisterBuildCheck), StringComparison.OrdinalIgnoreCase))
                {
                    string projectPath = properties.GetProperty("MSBuildProjectFullPath")?.EvaluatedValue ?? string.Empty;
                    ErrorUtilities.VerifyThrow(loggingContext != null, $"The logging context is missed. {nameof(IntrinsicFunctions.RegisterBuildCheck)} can not be invoked.");
                    if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                    {
                        returnVal = IntrinsicFunctions.RegisterBuildCheck(projectPath, arg0, loggingContext);
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Shortcut to avoid calling into binding if we recognize some most common constructors.
        /// Analogous to TryExecuteWellKnownFunction but guaranteed to not throw.
        /// </summary>
        /// <param name="receiverType"> Receiver type for the constructor. </param>
        /// <param name="returnVal">The instance as created by the constructor call.</param>
        /// <param name="args">Arguments.</param>
        /// <returns>True if the well known constructor call binding was successful.</returns>
        internal static bool TryExecuteWellKnownConstructorNoThrow(Type? receiverType, out object? returnVal, object[] args)
        {
            returnVal = null;
 
            if (receiverType == typeof(string))
            {
                if (args.Length == 0)
                {
                    returnVal = String.Empty;
                    return true;
                }
                if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null)
                {
                    returnVal = arg0;
                    return true;
                }
            }
            return false;
        }
    }
}