/// <summary>
        /// Returns true if this node evaluates to an empty string,
        /// otherwise false.
        /// It may be cheaper to determine whether an expression will evaluate
        /// to empty than to fully evaluate it.
        /// Implementations should cache the result so that calls after the first are free.
        /// </summary>
        internal override bool EvaluatesToEmpty(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (_cachedExpandedValue == null)
            {
                if (_expandable)
                {
                    string expandBreakEarly = state.ExpandIntoStringBreakEarly(_value);

                    if (expandBreakEarly == null)
                    {
                        // It broke early: we can't store the value, we just
                        // know it's non empty
                        return(false);
                    }

                    // It didn't break early, the result is accurate,
                    // so store it so the work isn't done again.
                    _cachedExpandedValue = expandBreakEarly;
                }
                else
                {
                    _cachedExpandedValue = _value;
                }
            }

            return(_cachedExpandedValue.Length == 0);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Evaluate as boolean
        /// </summary>
        internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            bool isLeftNum      = LeftChild.TryNumericEvaluate(state, out double leftNum);
            bool isLeftVersion  = LeftChild.TryVersionEvaluate(state, out Version leftVersion);
            bool isRightNum     = RightChild.TryNumericEvaluate(state, out double rightNum);
            bool isRightVersion = RightChild.TryVersionEvaluate(state, out Version rightVersion);

            if ((!isLeftNum && !isLeftVersion) || (!isRightNum && !isRightVersion))
            {
                ProjectErrorUtilities.ThrowInvalidProject(
                    state.ElementLocation,
                    "ComparisonOnNonNumericExpression",
                    state.Condition,
                    /* helpfully display unexpanded token and expanded result in error message */
                    isLeftNum ? RightChild.GetUnexpandedValue(state) : LeftChild.GetUnexpandedValue(state),
                    isLeftNum ? RightChild.GetExpandedValue(state) : LeftChild.GetExpandedValue(state));
            }

            return((isLeftNum, isLeftVersion, isRightNum, isRightVersion) switch
            {
                (true, _, true, _) => Compare(leftNum, rightNum),
                (_, true, _, true) => Compare(leftVersion, rightVersion),
                (true, _, _, true) => Compare(leftNum, rightVersion),
                (_, true, true, _) => Compare(leftVersion, rightNum),

                _ => false
            });
Exemplo n.º 3
0
        /// <summary>
        /// Evaluate as boolean
        /// </summary>
        internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            ProjectErrorUtilities.VerifyThrowInvalidProject
                (LeftChild.CanBoolEvaluate(state),
                state.ElementLocation,
                "ExpressionDoesNotEvaluateToBoolean",
                LeftChild.GetUnexpandedValue(state),
                LeftChild.GetExpandedValue(state),
                state.Condition);

            if (LeftChild.BoolEvaluate(state))
            {
                // Short circuit
                return(true);
            }
            else
            {
                ProjectErrorUtilities.VerifyThrowInvalidProject
                    (RightChild.CanBoolEvaluate(state),
                    state.ElementLocation,
                    "ExpressionDoesNotEvaluateToBoolean",
                    RightChild.GetUnexpandedValue(state),
                    RightChild.GetExpandedValue(state),
                    state.Condition);

                return(RightChild.BoolEvaluate(state));
            }
        }
Exemplo n.º 4
0
        private void UpdateConditionedProperties(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (!_conditionedPropertiesUpdated && state.ConditionedPropertiesInProject != null)
            {
                string leftUnexpandedValue  = LeftChild.GetUnexpandedValue(state);
                string rightUnexpandedValue = RightChild.GetUnexpandedValue(state);

                if (leftUnexpandedValue != null)
                {
                    ConditionEvaluator.UpdateConditionedPropertiesTable
                        (state.ConditionedPropertiesInProject,
                        leftUnexpandedValue,
                        RightChild.GetExpandedValue(state));
                }

                if (rightUnexpandedValue != null)
                {
                    ConditionEvaluator.UpdateConditionedPropertiesTable
                        (state.ConditionedPropertiesInProject,
                        rightUnexpandedValue,
                        LeftChild.GetExpandedValue(state));
                }

                _conditionedPropertiesUpdated = true;
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Evaluate as boolean
        /// </summary>
        internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (!LeftChild.TryBoolEvaluate(state, out bool leftBool))
            {
                ProjectErrorUtilities.ThrowInvalidProject(
                    state.ElementLocation,
                    "ExpressionDoesNotEvaluateToBoolean",
                    LeftChild.GetUnexpandedValue(state),
                    LeftChild.GetExpandedValue(state),
                    state.Condition);
            }

            if (!leftBool)
            {
                // Short circuit
                return(false);
            }
            else
            {
                if (!RightChild.TryBoolEvaluate(state, out bool rightBool))
                {
                    ProjectErrorUtilities.ThrowInvalidProject(
                        state.ElementLocation,
                        "ExpressionDoesNotEvaluateToBoolean",
                        RightChild.GetUnexpandedValue(state),
                        RightChild.GetExpandedValue(state),
                        state.Condition);
                }

                return(rightBool);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Whether it can be evaluated as a Version
        /// </summary>
        internal override bool CanVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            // Check if the value can be formatted as a Version number
            // This is needed for nodes that identify as Numeric but can't be parsed as numbers (e.g. 8.1.1.0 vs 8.1)
            Version unused;

            return(Version.TryParse(_value, out unused));
        }
Exemplo n.º 7
0
        internal override bool CanVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (ShouldBeTreatedAsVisualStudioVersion(state))
            {
                return(true);
            }

            return(Version.TryParse(GetExpandedValue(state), out _));
        }
Exemplo n.º 8
0
        internal override bool CanNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (ShouldBeTreatedAsVisualStudioVersion(state))
            {
                return(true);
            }

            return(ConversionUtilities.ValidDecimalOrHexNumber(GetExpandedValue(state)));
        }
Exemplo n.º 9
0
        internal override Version VersionEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (ShouldBeTreatedAsVisualStudioVersion(state))
            {
                return(Version.Parse(MSBuildConstants.CurrentVisualStudioVersion));
            }

            return(Version.Parse(GetExpandedValue(state)));
        }
Exemplo n.º 10
0
        /// <summary>
        /// Evaluate as numeric
        /// </summary>
        internal override double NumericEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (ShouldBeTreatedAsVisualStudioVersion(state))
            {
                return(ConversionUtilities.ConvertDecimalOrHexToDouble(MSBuildConstants.CurrentVisualStudioVersion));
            }

            return(ConversionUtilities.ConvertDecimalOrHexToDouble(GetExpandedValue(state)));
        }
Exemplo n.º 11
0
 /// <summary>
 /// Check that the number of function arguments is correct.
 /// </summary>
 /// <param name="expected"></param>
 private void VerifyArgumentCount(int expected, ConditionEvaluator.IConditionEvaluationState state)
 {
     ProjectErrorUtilities.VerifyThrowInvalidProject
         (_arguments.Count == expected,
         state.ElementLocation,
         "IncorrectNumberOfFunctionArguments",
         state.Condition,
         _arguments.Count,
         expected);
 }
Exemplo n.º 12
0
        /// <summary>
        /// The main evaluate entry point for expression trees
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        internal bool Evaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            ProjectErrorUtilities.VerifyThrowInvalidProject(
                CanBoolEvaluate(state),
                state.ElementLocation,
                "ConditionNotBooleanDetail",
                state.Condition,
                GetExpandedValue(state));

            return(BoolEvaluate(state));
        }
Exemplo n.º 13
0
 /// <summary>
 /// Evaluate as boolean
 /// </summary>
 internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
 {
     ProjectErrorUtilities.VerifyThrowInvalidProject
             (LeftChild.TryBoolEvaluate(state, out bool boolValue),
              state.ElementLocation,
              "ExpressionDoesNotEvaluateToBoolean",
              LeftChild.GetUnexpandedValue(state),
              LeftChild.GetExpandedValue(state),
              state.Condition);
     return !boolValue;
 }
Exemplo n.º 14
0
 internal override bool TryNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state, out double result)
 {
     if (ShouldBeTreatedAsVisualStudioVersion(state))
     {
         result = ConversionUtilities.ConvertDecimalOrHexToDouble(MSBuildConstants.CurrentVisualStudioVersion);
         return(true);
     }
     else
     {
         return(ConversionUtilities.TryConvertDecimalOrHexToDouble(GetExpandedValue(state), out result));
     }
 }
Exemplo n.º 15
0
 internal override bool TryVersionEvaluate(ConditionEvaluator.IConditionEvaluationState state, out Version result)
 {
     if (ShouldBeTreatedAsVisualStudioVersion(state))
     {
         result = Version.Parse(MSBuildConstants.CurrentVisualStudioVersion);
         return(true);
     }
     else
     {
         return(Version.TryParse(GetExpandedValue(state), out result));
     }
 }
Exemplo n.º 16
0
        internal bool Evaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (!TryBoolEvaluate(state, out bool boolValue))
            {
                ProjectErrorUtilities.ThrowInvalidProject(
                    state.ElementLocation,
                    "ConditionNotBooleanDetail",
                    state.Condition,
                    GetExpandedValue(state));
            }

            return(boolValue);
        }
Exemplo n.º 17
0
        internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            ProjectErrorUtilities.VerifyThrowInvalidProject
                (LeftChild != null && RightChild != null,
                state.ElementLocation,
                "IllFormedCondition",
                state.Condition);

            // It's sometimes possible to bail out of expansion early if we just need to know whether
            // the result is empty string.
            // If at least one of the left or the right hand side will evaluate to empty,
            // and we know which do, then we already have enough information to evaluate this expression.
            // That means we don't have to fully expand a condition like " '@(X)' == '' "
            // which is a performance advantage if @(X) is a huge item list.
            bool leftEmpty  = LeftChild.EvaluatesToEmpty(state);
            bool rightEmpty = RightChild.EvaluatesToEmpty(state);

            if (leftEmpty || rightEmpty)
            {
                UpdateConditionedProperties(state);

                return(Compare(leftEmpty, rightEmpty));
            }
            else if (LeftChild.TryNumericEvaluate(state, out double leftNumericValue) && RightChild.TryNumericEvaluate(state, out double rightNumericValue))
            {
                // The left child evaluating to a number and the right child not evaluating to a number
                // is insufficient to say they are not equal because $(MSBuildToolsVersion) evaluates to
                // the string "Current" most of the time but when doing numeric comparisons, is treated
                // as a version and returns "17.0" (or whatever the current tools version is). This means
                // that if '$(MSBuildToolsVersion)' is "equal" to BOTH '17.0' and 'Current' (if 'Current'
                // is 17.0).
                return(Compare(leftNumericValue, rightNumericValue));
            }
            else if (LeftChild.TryBoolEvaluate(state, out bool leftBoolValue) && RightChild.TryBoolEvaluate(state, out bool rightBoolValue))
            {
                return(Compare(leftBoolValue, rightBoolValue));
            }

            string leftExpandedValue  = LeftChild.GetExpandedValue(state);
            string rightExpandedValue = RightChild.GetExpandedValue(state);

            ProjectErrorUtilities.VerifyThrowInvalidProject
                (leftExpandedValue != null && rightExpandedValue != null,
                state.ElementLocation,
                "IllFormedCondition",
                state.Condition);

            UpdateConditionedProperties(state);

            return(Compare(leftExpandedValue, rightExpandedValue));
        }
Exemplo n.º 18
0
        /// <summary>
        /// Returns true if this node evaluates to an empty string,
        /// otherwise false.
        /// It may be cheaper to determine whether an expression will evaluate
        /// to empty than to fully evaluate it.
        /// Implementations should cache the result so that calls after the first are free.
        /// </summary>
        internal override bool EvaluatesToEmpty(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (_cachedExpandedValue == null)
            {
                if (_expandable)
                {
                    switch (_value.Length)
                    {
                    case 0:
                        _cachedExpandedValue = String.Empty;
                        return(true);

                    // If the length is 1 or 2, it can't possibly be a property, item, or metadata, and it isn't empty.
                    case 1:
                    case 2:
                        _cachedExpandedValue = _value;
                        return(false);

                    default:
                        if (_value[1] != '(' || (_value[0] != '$' && _value[0] != '%' && _value[0] != '@') || _value[_value.Length - 1] != ')')
                        {
                            // This isn't just a property, item, or metadata value, and it isn't empty.
                            return(false);
                        }
                        break;
                    }

                    string expandBreakEarly = state.ExpandIntoStringBreakEarly(_value);

                    if (expandBreakEarly == null)
                    {
                        // It broke early: we can't store the value, we just
                        // know it's non empty
                        return(false);
                    }

                    // It didn't break early, the result is accurate,
                    // so store it so the work isn't done again.
                    _cachedExpandedValue = expandBreakEarly;
                }
                else
                {
                    _cachedExpandedValue = _value;
                }
            }

            return(_cachedExpandedValue.Length == 0);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Value after any item and property expressions are expanded
        /// </summary>
        /// <returns></returns>
        internal override string GetExpandedValue(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (_cachedExpandedValue == null)
            {
                if (_expandable)
                {
                    _cachedExpandedValue = state.ExpandIntoString(_value);
                }
                else
                {
                    _cachedExpandedValue = _value;
                }
            }

            return(_cachedExpandedValue);
        }
Exemplo n.º 20
0
        /// <summary>
        /// Evaluates as boolean and evaluates children as boolean, numeric, or string.
        /// Order in which comparisons are attempted is numeric, boolean, then string.
        /// Updates conditioned properties table.
        /// </summary>
        internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            ProjectErrorUtilities.VerifyThrowInvalidProject
                (LeftChild != null && RightChild != null,
                state.ElementLocation,
                "IllFormedCondition",
                state.Condition);

            // It's sometimes possible to bail out of expansion early if we just need to know whether
            // the result is empty string.
            // If at least one of the left or the right hand side will evaluate to empty,
            // and we know which do, then we already have enough information to evaluate this expression.
            // That means we don't have to fully expand a condition like " '@(X)' == '' "
            // which is a performance advantage if @(X) is a huge item list.
            if (LeftChild.EvaluatesToEmpty(state) || RightChild.EvaluatesToEmpty(state))
            {
                UpdateConditionedProperties(state);

                return(Compare(LeftChild.EvaluatesToEmpty(state), RightChild.EvaluatesToEmpty(state)));
            }

            if (LeftChild.CanNumericEvaluate(state) && RightChild.CanNumericEvaluate(state))
            {
                return(Compare(LeftChild.NumericEvaluate(state), RightChild.NumericEvaluate(state)));
            }
            else if (LeftChild.CanBoolEvaluate(state) && RightChild.CanBoolEvaluate(state))
            {
                return(Compare(LeftChild.BoolEvaluate(state), RightChild.BoolEvaluate(state)));
            }
            else // string comparison
            {
                string leftExpandedValue  = LeftChild.GetExpandedValue(state);
                string rightExpandedValue = RightChild.GetExpandedValue(state);

                ProjectErrorUtilities.VerifyThrowInvalidProject
                    (leftExpandedValue != null && rightExpandedValue != null,
                    state.ElementLocation,
                    "IllFormedCondition",
                    state.Condition);

                UpdateConditionedProperties(state);

                return(Compare(leftExpandedValue, rightExpandedValue));
            }
        }
Exemplo n.º 21
0
        /// <summary>
        /// Evaluate as boolean
        /// </summary>
        internal override bool BoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
        {
            bool isLeftNum         = LeftChild.CanNumericEvaluate(state);
            bool isLeftVersion     = LeftChild.CanVersionEvaluate(state);
            bool isRightNum        = RightChild.CanNumericEvaluate(state);
            bool isRightVersion    = RightChild.CanVersionEvaluate(state);
            bool isNumeric         = isLeftNum && isRightNum;
            bool isVersion         = isLeftVersion && isRightVersion;
            bool isValidComparison = isNumeric || isVersion || (isLeftNum && isRightVersion) || (isLeftVersion && isRightNum);

            ProjectErrorUtilities.VerifyThrowInvalidProject
                (isValidComparison,
                state.ElementLocation,
                "ComparisonOnNonNumericExpression",
                state.Condition,
                /* helpfully display unexpanded token and expanded result in error message */
                LeftChild.CanNumericEvaluate(state) ? RightChild.GetUnexpandedValue(state) : LeftChild.GetUnexpandedValue(state),
                LeftChild.CanNumericEvaluate(state) ? RightChild.GetExpandedValue(state) : LeftChild.GetExpandedValue(state));

            // If the values identify as numeric, make that comparison instead of the Version comparison since numeric has a stricter definition
            if (isNumeric)
            {
                return(Compare(LeftChild.NumericEvaluate(state), RightChild.NumericEvaluate(state)));
            }
            else if (isVersion)
            {
                return(Compare(LeftChild.VersionEvaluate(state), RightChild.VersionEvaluate(state)));
            }

            // If the numbers are of a mixed type, call that specific Compare method
            if (isLeftNum && isRightVersion)
            {
                return(Compare(LeftChild.NumericEvaluate(state), RightChild.VersionEvaluate(state)));
            }
            else if (isLeftVersion && isRightNum)
            {
                return(Compare(LeftChild.VersionEvaluate(state), RightChild.NumericEvaluate(state)));
            }

            // Throw error here as this code should be unreachable
            ErrorUtilities.ThrowInternalErrorUnreachable();
            return(false);
        }
Exemplo n.º 22
0
        /// <summary>
        /// Should this node be treated as an expansion of VisualStudioVersion, rather than
        /// its literal meaning?
        /// </summary>
        /// <remarks>
        /// Needed to provide a compat shim for numeric/version comparisons
        /// on MSBuildToolsVersion, which were fine when it was a number
        /// but now cause the project to throw InvalidProjectException when
        /// ToolsVersion is "Current". https://github.com/Microsoft/msbuild/issues/4150
        /// </remarks>
        private bool ShouldBeTreatedAsVisualStudioVersion(ConditionEvaluator.IConditionEvaluationState state)
        {
            if (!_shouldBeTreatedAsVisualStudioVersion.HasValue)
            {
                // Treat specially if the node would expand to "Current".

                // Do this check first, because if it's not (common) we can early-out and the next
                // expansion will be cheap because this will populate the cached expanded value.
                if (string.Equals(GetExpandedValue(state), MSBuildConstants.CurrentToolsVersion, StringComparison.Ordinal))
                {
                    // and it is just an expansion of MSBuildToolsVersion
                    _shouldBeTreatedAsVisualStudioVersion = string.Equals(_value, "$(MSBuildToolsVersion)", StringComparison.OrdinalIgnoreCase);
                }
                else
                {
                    _shouldBeTreatedAsVisualStudioVersion = false;
                }
            }

            return(_shouldBeTreatedAsVisualStudioVersion.Value);
        }
Exemplo n.º 23
0
 /// <summary>
 /// Whether it can be evaluated as a boolean: never allowed for numerics
 /// </summary>
 internal override bool CanBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state)
 {
     // Numeric expressions are never allowed to be treated as booleans.
     return(false);
 }
Exemplo n.º 24
0
 /// <summary>
 /// Evaluate as numeric
 /// </summary>
 internal override double NumericEvaluate(ConditionEvaluator.IConditionEvaluationState state)
 {
     return(ConversionUtilities.ConvertDecimalOrHexToDouble(_value));
 }
Exemplo n.º 25
0
 /// <summary>
 /// Evaluate as a Version
 /// </summary>
 internal override Version VersionEvaluate(ConditionEvaluator.IConditionEvaluationState state)
 {
     return(Version.Parse(_value));
 }
Exemplo n.º 26
0
 /// <summary>
 /// Whether it can be evaluated as numeric
 /// </summary>
 internal override bool CanNumericEvaluate(ConditionEvaluator.IConditionEvaluationState state)
 {
     // It is not always possible to numerically evaluate even a numerical expression -
     // for example, it may overflow a double. So check here.
     return(ConversionUtilities.ValidDecimalOrHexNumber(_value));
 }
Exemplo n.º 27
0
 internal override bool TryBoolEvaluate(ConditionEvaluator.IConditionEvaluationState state, out bool result)
 {
     return(ConversionUtilities.TryConvertStringToBool(GetExpandedValue(state), out result));
 }
Exemplo n.º 28
0
 /// <summary>
 /// Value before any item and property expressions are expanded
 /// </summary>
 /// <returns></returns>
 internal override string GetUnexpandedValue(ConditionEvaluator.IConditionEvaluationState state)
 {
     return(_value);
 }
Exemplo n.º 29
0
        private void AssertParseEvaluate(Parser p, string expression, Expander <ProjectPropertyInstance, ProjectItemInstance> expander, bool expected, ConditionEvaluator.IConditionEvaluationState state)
        {
            if (expander.Metadata == null)
            {
                expander.Metadata = new StringMetadataTable(null);
            }

            GenericExpressionNode tree = p.Parse(expression, ParserOptions.AllowAll, MockElementLocation.Instance);

            if (state == null)
            {
                state =
                    new ConditionEvaluator.ConditionEvaluationState <ProjectPropertyInstance, ProjectItemInstance>
                    (
                        String.Empty,
                        expander,
                        ExpanderOptions.ExpandAll,
                        null,
                        Directory.GetCurrentDirectory(),
                        ElementLocation.EmptyLocation,
                        FileSystems.Default
                    );
            }

            bool result = tree.Evaluate(state);

            Assert.Equal(expected, result);
        }
Exemplo n.º 30
0
        private void AssertParseEvaluateThrow(Parser p, string expression, Expander <ProjectPropertyInstance, ProjectItemInstance> expander, ConditionEvaluator.IConditionEvaluationState state)
        {
            bool fExceptionCaught;

            if (expander.Metadata == null)
            {
                expander.Metadata = new StringMetadataTable(null);
            }

            try
            {
                fExceptionCaught = false;
                GenericExpressionNode tree = p.Parse(expression, ParserOptions.AllowAll, MockElementLocation.Instance);
                if (state == null)
                {
                    state =
                        new ConditionEvaluator.ConditionEvaluationState <ProjectPropertyInstance, ProjectItemInstance>
                        (
                            String.Empty,
                            expander,
                            ExpanderOptions.ExpandAll,
                            null,
                            Directory.GetCurrentDirectory(),
                            ElementLocation.EmptyLocation,
                            FileSystems.Default
                        );
                }
                tree.Evaluate(state);
            }
            catch (InvalidProjectFileException e)
            {
                Console.WriteLine(e.BaseMessage);
                fExceptionCaught = true;
            }

            Assert.True(fExceptionCaught);
        }