/// <summary> /// Evaluates a string representing a condition from a "condition" attribute. /// If the condition is a malformed string, it throws an InvalidProjectFileException. /// This method uses cached expression trees to avoid generating them from scratch every time it's called. /// This method is thread safe and is called from engine and task execution module threads /// </summary> internal static bool EvaluateCondition <P, I> ( string condition, ParserOptions options, Expander <P, I> expander, ExpanderOptions expanderOptions, string evaluationDirectory, ElementLocation elementLocation, ILoggingService loggingServices, BuildEventContext buildEventContext, IFileSystem fileSystem, ProjectRootElementCacheBase projectRootElementCache = null) where P : class, IProperty where I : class, IItem { return(EvaluateConditionCollectingConditionedProperties( condition, options, expander, expanderOptions, null /* do not collect conditioned properties */, evaluationDirectory, elementLocation, loggingServices, buildEventContext, fileSystem, projectRootElementCache)); }
internal ConditionEvaluationState ( string condition, Expander <P, I> expander, ExpanderOptions expanderOptions, Dictionary <string, List <string> > conditionedPropertiesInProject, string evaluationDirectory, ElementLocation elementLocation, IFileSystem fileSystem, ProjectRootElementCacheBase projectRootElementCache = null ) { ErrorUtilities.VerifyThrowArgumentNull(condition, nameof(condition)); ErrorUtilities.VerifyThrowArgumentNull(expander, nameof(expander)); ErrorUtilities.VerifyThrowArgumentNull(evaluationDirectory, nameof(evaluationDirectory)); ErrorUtilities.VerifyThrowArgumentNull(elementLocation, nameof(elementLocation)); Condition = condition; _expander = expander; _expanderOptions = expanderOptions; ConditionedPropertiesInProject = conditionedPropertiesInProject; // May be null EvaluationDirectory = evaluationDirectory; ElementLocation = elementLocation; LoadedProjectsCache = projectRootElementCache; FileSystem = fileSystem; }
/// <summary> /// Evaluates a string representing a condition from a "condition" attribute. /// If the condition is a malformed string, it throws an InvalidProjectFileException. /// This method uses cached expression trees to avoid generating them from scratch every time it's called. /// This method is thread safe and is called from engine and task execution module threads /// Logging service may be null. /// </summary> internal static bool EvaluateConditionCollectingConditionedProperties <P, I> ( string condition, ParserOptions options, Expander <P, I> expander, ExpanderOptions expanderOptions, Dictionary <string, List <string> > conditionedPropertiesTable, string evaluationDirectory, ElementLocation elementLocation, ILoggingService loggingServices, BuildEventContext buildEventContext, IFileSystem fileSystem, ProjectRootElementCacheBase projectRootElementCache = null ) where P : class, IProperty where I : class, IItem { ErrorUtilities.VerifyThrowArgumentNull(condition, "condition"); ErrorUtilities.VerifyThrowArgumentNull(expander, "expander"); ErrorUtilities.VerifyThrowArgumentLength(evaluationDirectory, "evaluationDirectory"); ErrorUtilities.VerifyThrowArgumentNull(buildEventContext, "buildEventContext"); // An empty condition is equivalent to a "true" condition. if (condition.Length == 0) { return(true); } // If the condition wasn't empty, there must be a location for it ErrorUtilities.VerifyThrowArgumentNull(elementLocation, "elementLocation"); // Get the expression tree cache for the current parsing options. var cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees.GetOrAdd( (int)options, _ => new ExpressionTreeForCurrentOptionsWithSize(new ConcurrentDictionary <string, ConcurrentStack <GenericExpressionNode> >(StringComparer.Ordinal))); cachedExpressionTreesForCurrentOptions = FlushCacheIfLargerThanThreshold(options, cachedExpressionTreesForCurrentOptions); // Get the pool of expressions for this condition. var expressionPool = cachedExpressionTreesForCurrentOptions.GetOrAdd(condition, _ => new ConcurrentStack <GenericExpressionNode>()); // Try and see if there's an available expression tree in the pool. // If not, parse a new expression tree and add it back to the pool. if (!expressionPool.TryPop(out var parsedExpression)) { var conditionParser = new Parser(); #region REMOVE_COMPAT_WARNING conditionParser.LoggingServices = loggingServices; conditionParser.LogBuildEventContext = buildEventContext; #endregion parsedExpression = conditionParser.Parse(condition, options, elementLocation); } bool result; var state = new ConditionEvaluationState <P, I>( condition, expander, expanderOptions, conditionedPropertiesTable, evaluationDirectory, elementLocation, fileSystem, projectRootElementCache); // We are evaluating this expression now and it can cache some state for the duration, // so we don't want multiple threads working on the same expression lock (parsedExpression) { try { result = parsedExpression.Evaluate(state); } finally { parsedExpression.ResetState(); if (!s_disableExpressionCaching) { // Finished using the expression tree. Add it back to the pool so other threads can use it. expressionPool.Push(parsedExpression); } } } return(result); }