/// <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> /// <param name="condition">Can be null</param> /// <param name="conditionAttribute">XML attribute on which the condition is evaluated</param> /// <param name="expander">All the data available for expanding embedded properties, metadata, and items</param> /// <param name="conditionedPropertiesTable">Can be null</param> /// <param name="itemListOptions"></param> /// <param name="loggingServices">Can be null</param> /// <param name="buildEventContext"> contains contextual information for logging events</param> /// <returns>true, if the expression evaluates to true, otherwise false</returns> internal static bool EvaluateCondition ( string condition, XmlAttribute conditionAttribute, Expander expander, Hashtable conditionedPropertiesTable, ParserOptions itemListOptions, EngineLoggingServices loggingServices, BuildEventContext buildEventContext ) { ErrorUtilities.VerifyThrow((conditionAttribute != null) || (string.IsNullOrEmpty(condition)), "If condition is non-empty, you must provide the XML node representing the condition."); // An empty condition is equivalent to a "true" condition. if (string.IsNullOrEmpty(condition)) { return(true); } Hashtable cachedExpressionTreesForCurrentOptions = cachedExpressionTrees[(int)itemListOptions]; // Try and see if we have an expression tree for this condition already GenericExpressionNode parsedExpression = (GenericExpressionNode)cachedExpressionTreesForCurrentOptions[condition]; if (parsedExpression == null) { Parser conditionParser = new Parser(); #region REMOVE_COMPAT_WARNING conditionParser.LoggingServices = loggingServices; conditionParser.LogBuildEventContext = buildEventContext; #endregion parsedExpression = conditionParser.Parse(condition, conditionAttribute, itemListOptions); // It's possible two threads will add a different tree to the same entry in the hashtable, // but it should be rare and it's not a problem - the previous entry will be thrown away. // We could ensure no dupes with double check locking but it's not really necessary here. // Also, we don't want to lock on every read. lock (cachedExpressionTreesForCurrentOptions) { cachedExpressionTreesForCurrentOptions[condition] = parsedExpression; } } ConditionEvaluationState state = new ConditionEvaluationState(conditionAttribute, expander, conditionedPropertiesTable, condition); bool result; // 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) { result = parsedExpression.Evaluate(state); parsedExpression.ResetState(); } return(result); }
/// <summary> /// If any expression nodes cache any state for the duration of evaluation, /// now's the time to clean it up /// </summary> internal override void ResetState() { if (leftChild != null) { leftChild.ResetState(); } if (rightChild != null) { rightChild.ResetState(); } }