Beispiel #1
0
        //
        // Top node of grammar
        //    See grammar for how the following methods relate to each
        //    other.
        //
        private GenericExpressionNode Expr(string expression)
        {
            GenericExpressionNode node = BooleanTerm(expression);

            if (!_lexer.IsNext(Token.TokenType.EndOfInput))
            {
                node = ExprPrime(expression, node);
            }

            #region REMOVE_COMPAT_WARNING
            // Check for potential change in behavior
            if (LoggingServices != null && !_warnedForExpression &&
                node.PotentialAndOrConflict())
            {
                // We only want to warn once even if there multiple () sub expressions
                _warnedForExpression = true;

                // Log a warning regarding the fact the expression may have been evaluated
                // incorrectly in earlier version of MSBuild
                LoggingServices.LogWarning(_logBuildEventContext, null, new BuildEventFileInfo(_elementLocation), "ConditionMaybeEvaluatedIncorrectly", expression);
            }
            #endregion

            return(node);
        }
Beispiel #2
0
        //
        // Main entry point for parser.
        // You pass in the expression you want to parse, and you get an
        // ExpressionTree out the back end.
        //
        internal GenericExpressionNode Parse(string expression, ParserOptions optionSettings, ElementLocation elementLocation)
        {
            // We currently have no support (and no scenarios) for disallowing property references
            // in Conditions.
            ErrorUtilities.VerifyThrow(0 != (optionSettings & ParserOptions.AllowProperties),
                                       "Properties should always be allowed.");

            _options         = optionSettings;
            _elementLocation = elementLocation;

            _lexer = new Scanner(expression, _options);
            if (!_lexer.Advance())
            {
                errorPosition = _lexer.GetErrorPosition();
                ProjectErrorUtilities.ThrowInvalidProject(elementLocation, _lexer.GetErrorResource(), expression, errorPosition, _lexer.UnexpectedlyFound);
            }
            GenericExpressionNode node = Expr(expression);

            if (!_lexer.IsNext(Token.TokenType.EndOfInput))
            {
                errorPosition = _lexer.GetErrorPosition();
                ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
            }
            return(node);
        }
Beispiel #3
0
        private GenericExpressionNode BooleanTermPrime(string expression, GenericExpressionNode lhs)
        {
            if (_lexer.IsNext(Token.TokenType.EndOfInput))
            {
                return(lhs);
            }
            else if (Same(expression, Token.TokenType.And))
            {
                GenericExpressionNode rhs = RelationalExpr(expression);
                if (rhs == null)
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
                }

                OperatorExpressionNode andNode = new AndExpressionNode();
                andNode.LeftChild  = lhs;
                andNode.RightChild = rhs;
                return(BooleanTermPrime(expression, andNode));
            }
            else
            {
                // Should this be error case?
                return(lhs);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Expands properties and items in the argument, and verifies that the result is consistent
        /// with a scalar parameter type.
        /// </summary>
        /// <param name="function">Function name for errors</param>
        /// <param name="argumentNode">Argument to be expanded</param>
        /// <returns>Scalar result</returns>
        private string ExpandArgumentForScalarParameter(string function, GenericExpressionNode argumentNode, ConditionEvaluator.IConditionEvaluationState state)
        {
            string argument = argumentNode.GetUnexpandedValue(state);

            IList <TaskItem> items;

            items = state.ExpandIntoTaskItems(argument);

            string expandedValue = String.Empty;

            if (items.Count == 0)
            {
                // Empty argument, that's fine.
            }
            else if (items.Count == 1)
            {
                expandedValue = items[0].ItemSpec;
            }
            else // too many items for the function
            {
                // We only allow a single item to be passed into a scalar parameter.
                ProjectErrorUtilities.ThrowInvalidProject(
                    state.ElementLocation,
                    "CannotPassMultipleItemsIntoScalarFunction", function, argument,
                    state.ExpandIntoString(argument));
            }

            return(expandedValue);
        }
        private List <string> ExpandArgumentAsFileList(GenericExpressionNode argumentNode, ConditionEvaluator.IConditionEvaluationState state, bool isFilePath = true)
        {
            string argument = argumentNode.GetUnexpandedValue(state);

            // Fix path before expansion
            if (isFilePath)
            {
                argument = FileUtilities.FixFilePath(argument);
            }

            IList <TaskItem> expanded = state.ExpandIntoTaskItems(argument);
            var expandedCount         = expanded.Count;

            if (expandedCount == 0)
            {
                return(null);
            }

            var list = new List <string>(capacity: expandedCount);

            for (var i = 0; i < expandedCount; i++)
            {
                var item = expanded[i];
                if (state.EvaluationDirectory != null && !Path.IsPathRooted(item.ItemSpec))
                {
                    list.Add(Path.GetFullPath(Path.Combine(state.EvaluationDirectory, item.ItemSpec)));
                }
                else
                {
                    list.Add(item.ItemSpec);
                }
            }

            return(list);
        }
Beispiel #6
0
        private void Args(string expression, List <GenericExpressionNode> arglist)
        {
            GenericExpressionNode arg = Arg(expression);

            arglist.Add(arg);
            if (Same(expression, Token.TokenType.Comma))
            {
                Args(expression, arglist);
            }
        }
Beispiel #7
0
        private GenericExpressionNode BooleanTerm(string expression)
        {
            GenericExpressionNode node = RelationalExpr(expression);

            if (node == null)
            {
                errorPosition = _lexer.GetErrorPosition();
                ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
            }

            if (!_lexer.IsNext(Token.TokenType.EndOfInput))
            {
                node = BooleanTermPrime(expression, node);
            }
            return(node);
        }
Beispiel #8
0
        private IEnumerable <string> ExpandArgumentAsFileList(GenericExpressionNode argumentNode, ConditionEvaluator.IConditionEvaluationState state, bool isFilePath = true)
        {
            string argument = argumentNode.GetUnexpandedValue(state);

            // Fix path before expansion
            if (isFilePath)
            {
                argument = FileUtilities.FixFilePath(argument);
            }

            return(state.ExpandIntoTaskItems(argument)
                   .Select(i =>
                           state.EvaluationDirectory != null && !Path.IsPathRooted(i.ItemSpec)
                        ? Path.GetFullPath(Path.Combine(state.EvaluationDirectory, i.ItemSpec))
                        : i.ItemSpec));
        }
Beispiel #9
0
        private GenericExpressionNode RelationalExpr(string expression)
        {
            {
                GenericExpressionNode lhs = Factor(expression);
                if (lhs == null)
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
                }

                OperatorExpressionNode node = RelationalOperation(expression);
                if (node == null)
                {
                    return(lhs);
                }
                GenericExpressionNode rhs = Factor(expression);
                node.LeftChild  = lhs;
                node.RightChild = rhs;
                return(node);
            }
        }
Beispiel #10
0
 private GenericExpressionNode ExprPrime(string expression, GenericExpressionNode lhs)
 {
     if (Same(expression, Token.TokenType.EndOfInput))
     {
         return(lhs);
     }
     else if (Same(expression, Token.TokenType.Or))
     {
         OperatorExpressionNode orNode = new OrExpressionNode();
         GenericExpressionNode  rhs    = BooleanTerm(expression);
         orNode.LeftChild  = lhs;
         orNode.RightChild = rhs;
         return(ExprPrime(expression, orNode));
     }
     else
     {
         // I think this is ok.  ExprPrime always shows up at
         // the rightmost side of the grammar rhs, the EndOfInput case
         // takes care of things
         return(lhs);
     }
 }
Beispiel #11
0
        private GenericExpressionNode Factor(string expression)
        {
            // Checks for TokenTypes String, Numeric, Property, ItemMetadata, and ItemList.
            GenericExpressionNode arg = this.Arg(expression);

            // If it's one of those, return it.
            if (arg != null)
            {
                return(arg);
            }

            // If it's not one of those, check for other TokenTypes.
            Token current = _lexer.CurrentToken;

            if (Same(expression, Token.TokenType.Function))
            {
                if (!Same(expression, Token.TokenType.LeftParenthesis))
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", _lexer.IsNextString(), errorPosition);
                    return(null);
                }
                var arglist = new List <GenericExpressionNode>();
                Arglist(expression, arglist);
                if (!Same(expression, Token.TokenType.RightParenthesis))
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
                    return(null);
                }
                return(new FunctionCallExpressionNode(current.String, arglist));
            }
            else if (Same(expression, Token.TokenType.LeftParenthesis))
            {
                GenericExpressionNode child = Expr(expression);
                if (Same(expression, Token.TokenType.RightParenthesis))
                {
                    return(child);
                }
                else
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
                }
            }
            else if (Same(expression, Token.TokenType.Not))
            {
                OperatorExpressionNode notNode = new NotExpressionNode();
                GenericExpressionNode  expr    = Factor(expression);
                if (expr == null)
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
                }
                notNode.LeftChild = expr;
                return(notNode);
            }
            else
            {
                errorPosition = _lexer.GetErrorPosition();
                ProjectErrorUtilities.ThrowInvalidProject(_elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
            }
            return(null);
        }
        /// <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,
            ProjectRootElementCache 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");

            Hashtable cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees[(int)options];

            // We only need to lock on writes to the table
            if (cachedExpressionTreesForCurrentOptions == null)
            {
                // Given property functions, casing in conditional expressions isn't necessarily ignored.
                cachedExpressionTreesForCurrentOptions = new Hashtable(50, StringComparer.Ordinal);

                lock (s_cachedExpressionTrees)
                {
                    s_cachedExpressionTrees[(int)options] = cachedExpressionTreesForCurrentOptions;
                }
            }

            // VS stress tests could fill up this cache without end, for example if they use
            // random configuration names - those appear in conditional expressions.
            // So if we hit a limit that we should never hit in normal circumstances in VS,
            // and rarely, periodically hit in normal circumstances in large tree builds,
            // just clear out the cache. It can start repopulating again. Some kind of prioritized
            // aging isn't worth it: although the hit rate of these caches is excellent (nearly 100%)
            // the cost of reparsing expressions should the cache be cleared is not particularly large.
            // Loading Australian Government in VS, there are 3 of these tables, two with about 50
            // entries and one with about 650 entries. So 3000 seems large enough.
            if (cachedExpressionTreesForCurrentOptions.Count > 3000) // threadsafe
            {
                lock (cachedExpressionTreesForCurrentOptions)
                {
                    cachedExpressionTreesForCurrentOptions.Clear();
                }
            }

            // 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, options, elementLocation);

                // 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)
                {
                    if (!s_disableExpressionCaching)
                    {
                        cachedExpressionTreesForCurrentOptions[condition] = parsedExpression;
                    }
                }
            }

            bool result;

            ConditionEvaluationState <P, I> state = new ConditionEvaluationState <P, I>(condition, expander, expanderOptions, conditionedPropertiesTable, evaluationDirectory, elementLocation, 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();
                }
            }

            return(result);
        }
Beispiel #13
0
        private GenericExpressionNode BooleanTermPrime(string expression, GenericExpressionNode lhs)
        {
            if (_lexer.IsNext(Token.TokenType.EndOfInput))
            {
                return lhs;
            }
            else if (Same(expression, Token.TokenType.And))
            {
                GenericExpressionNode rhs = RelationalExpr(expression);
                if (null == rhs)
                {
                    errorPosition = _lexer.GetErrorPosition();
                    ProjectErrorUtilities.VerifyThrowInvalidProject(false, _elementLocation, "UnexpectedTokenInCondition", expression, _lexer.IsNextString(), errorPosition);
                }

                OperatorExpressionNode andNode = new AndExpressionNode();
                andNode.LeftChild = lhs;
                andNode.RightChild = rhs;
                return BooleanTermPrime(expression, andNode);
            }
            else
            {
                // Should this be error case?
                return lhs;
            }
        }
Beispiel #14
0
 private GenericExpressionNode ExprPrime(string expression, GenericExpressionNode lhs)
 {
     if (Same(expression, Token.TokenType.EndOfInput))
     {
         return lhs;
     }
     else if (Same(expression, Token.TokenType.Or))
     {
         OperatorExpressionNode orNode = new OrExpressionNode();
         GenericExpressionNode rhs = BooleanTerm(expression);
         orNode.LeftChild = lhs;
         orNode.RightChild = rhs;
         return ExprPrime(expression, orNode);
     }
     else
     {
         // I think this is ok.  ExprPrime always shows up at
         // the rightmost side of the grammar rhs, the EndOfInput case
         // takes care of things
         return lhs;
     }
 }
        /// <summary>
        /// Expands properties and items in the argument, and verifies that the result is consistent
        /// with a scalar parameter type.
        /// </summary>
        /// <param name="function">Function name for errors</param>
        /// <param name="argumentNode">Argument to be expanded</param>
        /// <returns>Scalar result</returns>
        private string ExpandArgumentForScalarParameter(string function, GenericExpressionNode argumentNode, ConditionEvaluator.IConditionEvaluationState state)
        {
            string argument = argumentNode.GetUnexpandedValue(state);

            IList<TaskItem> items;

            items = state.ExpandIntoTaskItems(argument);

            string expandedValue = String.Empty;

            if (items.Count == 0)
            {
                // Empty argument, that's fine.
            }
            else if (items.Count == 1)
            {
                expandedValue = items[0].ItemSpec;
            }
            else // too many items for the function
            {
                // We only allow a single item to be passed into a scalar parameter.
                ProjectErrorUtilities.ThrowInvalidProject(
                    state.ElementLocation,
                    "CannotPassMultipleItemsIntoScalarFunction", function, argument,
                    state.ExpandIntoString(argument));
            }

            return expandedValue;
        }