コード例 #1
0
ファイル: ConditionEvaluator.cs プロジェクト: yazici/msbuild
 /// <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));
 }
コード例 #2
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
 /// <summary>
 /// Special cased constructor. Where we are only going to expand properties and metadata,
 /// it's a waste of memory to use a lookup. Just use the property group.
 /// PERF: This improves the EvaluateAllItemDefinitions codepath.
 /// </summary>
 internal Expander(BuildPropertyGroup properties, string implicitMetadataItemType, Dictionary<string, string> unqualifiedItemMetadata)
 {
     this.options = ExpanderOptions.ExpandPropertiesAndMetadata;
     this.properties = properties;
     this.itemMetadata = unqualifiedItemMetadata;
     this.implicitMetadataItemType = implicitMetadataItemType;
 }
コード例 #3
0
        private static bool EvaluateCondition(
            string condition,
            ProjectElement element,
            ExpanderOptions expanderOptions,
            ParserOptions parserOptions,
            Expander <P, I> expander,
            LazyItemEvaluator <P, I, M, D> lazyEvaluator
            )
        {
            if (condition?.Length == 0)
            {
                return(true);
            }
            MSBuildEventSource.Log.EvaluateConditionStart(condition);

            using (lazyEvaluator._evaluationProfiler.TrackCondition(element.ConditionLocation, condition))
            {
                bool result = ConditionEvaluator.EvaluateCondition
                              (
                    condition,
                    parserOptions,
                    expander,
                    expanderOptions,
                    GetCurrentDirectoryForConditionEvaluation(element, lazyEvaluator),
                    element.ConditionLocation,
                    lazyEvaluator._loggingContext.LoggingService,
                    lazyEvaluator._loggingContext.BuildEventContext,
                    lazyEvaluator.FileSystem
                              );
                MSBuildEventSource.Log.EvaluateConditionStop(condition, result);

                return(result);
            }
        }
コード例 #4
0
ファイル: ConditionEvaluator.cs プロジェクト: yazici/msbuild
            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, "condition");
                ErrorUtilities.VerifyThrowArgumentNull(expander, "expander");
                ErrorUtilities.VerifyThrowArgumentNull(evaluationDirectory, "evaluationDirectory");
                ErrorUtilities.VerifyThrowArgumentNull(elementLocation, "elementLocation");

                Condition        = condition;
                _expander        = expander;
                _expanderOptions = expanderOptions;
                ConditionedPropertiesInProject = conditionedPropertiesInProject; // May be null
                EvaluationDirectory            = evaluationDirectory;
                ElementLocation     = elementLocation;
                LoadedProjectsCache = projectRootElementCache;
                FileSystem          = fileSystem;
            }
コード例 #5
0
 private static bool EvaluateCondition(
     ProjectElement element,
     ExpanderOptions expanderOptions,
     ParserOptions parserOptions,
     Expander <P, I> expander,
     LazyItemEvaluator <P, I, M, D> lazyEvaluator
     )
 {
     return(EvaluateCondition(element.Condition, element, expanderOptions, parserOptions, expander, lazyEvaluator));
 }
コード例 #6
0
        private void ProcessMetadataElements(ProjectItemElement itemElement, OperationBuilderWithMetadata operationBuilder)
        {
            if (itemElement.HasMetadata)
            {
                operationBuilder.Metadata.AddRange(itemElement.Metadata);

                var values = new List <string>(itemElement.Metadata.Count * 2);

                // Expand properties here, because a property may have a value which is an item reference (ie "@(Bar)"), and
                // if so we need to add the right item reference.
                foreach (var metadatumElement in itemElement.Metadata)
                {
                    // Since we're just attempting to expand properties in order to find referenced items and not expanding metadata,
                    // unexpected errors may occur when evaluating property functions on unexpanded metadata. Just ignore them if that happens.
                    // See: https://github.com/Microsoft/msbuild/issues/3460
                    const ExpanderOptions expanderOptions = ExpanderOptions.ExpandProperties | ExpanderOptions.LeavePropertiesUnexpandedOnError;

                    var valueWithPropertiesExpanded = _expander.ExpandIntoStringLeaveEscaped(
                        metadatumElement.Value,
                        expanderOptions,
                        metadatumElement.Location);

                    var conditionWithPropertiesExpanded = _expander.ExpandIntoStringLeaveEscaped(
                        metadatumElement.Condition,
                        expanderOptions,
                        metadatumElement.ConditionLocation);

                    values.Add(valueWithPropertiesExpanded);
                    values.Add(conditionWithPropertiesExpanded);
                }

                var itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values);
                if (itemsAndMetadataFound.Items != null)
                {
                    foreach (var itemType in itemsAndMetadataFound.Items)
                    {
                        AddReferencedItemList(itemType, operationBuilder.ReferencedItemLists);
                    }
                }
            }
        }
コード例 #7
0
        static bool EvaluateCondition(ProjectElement element, ExpanderOptions expanderOptions, ParserOptions parserOptions, Expander <P, I> expander, LazyItemEvaluator <P, I, M, D> lazyEvaluator)
        {
            if (element.Condition.Length == 0)
            {
                return(true);
            }

            bool result = ConditionEvaluator.EvaluateCondition
                          (
                element.Condition,
                parserOptions,
                expander,
                expanderOptions,
                GetCurrentDirectoryForConditionEvaluation(element, lazyEvaluator),
                element.ConditionLocation,
                lazyEvaluator._loggingService,
                lazyEvaluator._buildEventContext
                          );

            return(result);
        }
コード例 #8
0
        private static IEnumerable <string> GetExpandedMetadataValuesAndConditions(ICollection <ProjectMetadataElement> metadata, Expander <P, I> expander)
        {
            // Since we're just attempting to expand properties in order to find referenced items and not expanding metadata,
            // unexpected errors may occur when evaluating property functions on unexpanded metadata. Just ignore them if that happens.
            // See: https://github.com/dotnet/msbuild/issues/3460
            const ExpanderOptions expanderOptions = ExpanderOptions.ExpandProperties | ExpanderOptions.LeavePropertiesUnexpandedOnError;

            // Expand properties here, because a property may have a value which is an item reference (ie "@(Bar)"), and
            // if so we need to add the right item reference.
            foreach (var metadatumElement in metadata)
            {
                yield return(expander.ExpandIntoStringLeaveEscaped(
                                 metadatumElement.Value,
                                 expanderOptions,
                                 metadatumElement.Location));

                yield return(expander.ExpandIntoStringLeaveEscaped(
                                 metadatumElement.Condition,
                                 expanderOptions,
                                 metadatumElement.ConditionLocation));
            }
        }
コード例 #9
0
 public bool EvaluateConditionWithCurrentState(ProjectElement element, ExpanderOptions expanderOptions, ParserOptions parserOptions)
 {
     return(EvaluateCondition(element, expanderOptions, parserOptions, _expander, this));
 }
コード例 #10
0
            protected void DecorateItemsWithMetadata(ImmutableList <I> items, ImmutableList <ProjectMetadataElement> metadata)
            {
                if (metadata.Count > 0)
                {
                    ////////////////////////////////////////////////////
                    // UNDONE: Implement batching here.
                    //
                    // We want to allow built-in metadata in metadata values here.
                    // For example, so that an Idl file can specify that its Tlb output should be named %(Filename).tlb.
                    //
                    // In other words, we want batching. However, we won't need to go to the trouble of using the regular batching code!
                    // That's because that code is all about grouping into buckets of similar items. In this context, we're not
                    // invoking a task, and it's fine to process each item individually, which will always give the correct results.
                    //
                    // For the CTP, to make the minimal change, we will not do this quite correctly.
                    //
                    // We will do this:
                    // -- check whether any metadata values or their conditions contain any bare built-in metadata expressions,
                    //    or whether they contain any custom metadata && the Include involved an @(itemlist) expression.
                    // -- if either case is found, we go ahead and evaluate all the metadata separately for each item.
                    // -- otherwise we can do the old thing (evaluating all metadata once then applying to all items)
                    //
                    // This algorithm gives the correct results except when:
                    // -- batchable expressions exist on the include, exclude, or condition on the item element itself
                    //
                    // It means that 99% of cases still go through the old code, which is best for the CTP.
                    // When we ultimately implement this correctly, we should make sure we optimize for the case of very many items
                    // and little metadata, none of which varies between items.

                    // Do not expand properties as they have been already expanded by the lazy evaluator upon item operation construction.
                    // Prior to lazy evaluation ExpanderOptions.ExpandAll was used.
                    const ExpanderOptions metadataExpansionOptions = ExpanderOptions.ExpandAll;

                    List <string> values = new List <string>(metadata.Count * 2);

                    foreach (var metadataElement in metadata)
                    {
                        values.Add(metadataElement.Value);
                        values.Add(metadataElement.Condition);
                    }

                    ItemsAndMetadataPair itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values);

                    bool needToProcessItemsIndividually = false;

                    if (itemsAndMetadataFound.Metadata != null && itemsAndMetadataFound.Metadata.Values.Count > 0)
                    {
                        // If there is bare metadata of any kind, and the Include involved an item list, we should
                        // run items individually, as even non-built-in metadata might differ between items

                        if (_referencedItemLists.Count >= 0)
                        {
                            needToProcessItemsIndividually = true;
                        }
                        else
                        {
                            // If there is bare built-in metadata, we must always run items individually, as that almost
                            // always differs between items.

                            // UNDONE: When batching is implemented for real, we need to make sure that
                            // item definition metadata is included in all metadata operations during evaluation
                            if (itemsAndMetadataFound.Metadata.Values.Count > 0)
                            {
                                needToProcessItemsIndividually = true;
                            }
                        }
                    }

                    if (needToProcessItemsIndividually)
                    {
                        foreach (I item in items)
                        {
                            _expander.Metadata = item;

                            foreach (var metadataElement in metadata)
                            {
#if FEATURE_MSBUILD_DEBUGGER
                                //if (DebuggerManager.DebuggingEnabled)
                                //{
                                //    DebuggerManager.PulseState(metadataElementElement.Location, _itemPassLocals);
                                //}
#endif

                                if (!EvaluateCondition(metadataElement.Condition, metadataElement, metadataExpansionOptions, ParserOptions.AllowAll, _expander, _lazyEvaluator))
                                {
                                    continue;
                                }

                                string evaluatedValue = _expander.ExpandIntoStringLeaveEscaped(metadataElement.Value, metadataExpansionOptions, metadataElement.Location);

                                item.SetMetadata(metadataElement, FileUtilities.MaybeAdjustFilePath(evaluatedValue, metadataElement.ContainingProject.DirectoryPath));
                            }
                        }

                        // End of legal area for metadata expressions.
                        _expander.Metadata = null;
                    }

                    // End of pseudo batching
                    ////////////////////////////////////////////////////
                    // Start of old code
                    else
                    {
                        // Metadata expressions are allowed here.
                        // Temporarily gather and expand these in a table so they can reference other metadata elements above.
                        EvaluatorMetadataTable metadataTable = new EvaluatorMetadataTable(_itemType);
                        _expander.Metadata = metadataTable;

                        // Also keep a list of everything so we can get the predecessor objects correct.
                        List <Pair <ProjectMetadataElement, string> > metadataList = new List <Pair <ProjectMetadataElement, string> >(metadata.Count);

                        foreach (var metadataElement in metadata)
                        {
                            // Because of the checking above, it should be safe to expand metadata in conditions; the condition
                            // will be true for either all the items or none
                            if (!EvaluateCondition(metadataElement.Condition, metadataElement, metadataExpansionOptions, ParserOptions.AllowAll, _expander, _lazyEvaluator))
                            {
                                continue;
                            }

#if FEATURE_MSBUILD_DEBUGGER
                            //if (DebuggerManager.DebuggingEnabled)
                            //{
                            //    DebuggerManager.PulseState(metadataElementElement.Location, _itemPassLocals);
                            //}
#endif

                            string evaluatedValue = _expander.ExpandIntoStringLeaveEscaped(metadataElement.Value, metadataExpansionOptions, metadataElement.Location);
                            evaluatedValue = FileUtilities.MaybeAdjustFilePath(evaluatedValue, metadataElement.ContainingProject.DirectoryPath);

                            metadataTable.SetValue(metadataElement, evaluatedValue);
                            metadataList.Add(new Pair <ProjectMetadataElement, string>(metadataElement, evaluatedValue));
                        }

                        // Apply those metadata to each item
                        // Note that several items could share the same metadata objects

                        // Set all the items at once to make a potential copy-on-write optimization possible.
                        // This is valuable in the case where one item element evaluates to
                        // many items (either by semicolon or wildcards)
                        // and that item also has the same piece/s of metadata for each item.
                        _itemFactory.SetMetadata(metadataList, items);

                        // End of legal area for metadata expressions.
                        _expander.Metadata = null;
                    }
                }
            }
コード例 #11
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
            /// <summary>
            /// Execute the function on the given instance
            /// </summary>
            public object Execute(Expander expander, object objectInstance, BuildPropertyGroup properties, ExpanderOptions options)
            {
                object functionResult = String.Empty;

                object[] args = null;

                try
                {
                    // If there is no object instance, then the method invocation will be a static
                    if (objectInstance == null)
                    {
                        // Check that the function that we're going to call is valid to call
                        if (!IsStaticMethodAvailable(ObjectType, name))
                        {
                            ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionMethodUnavailable", name, ObjectType.FullName);
                        }

                        bindingFlags |= BindingFlags.Static;

                        // For our intrinsic function we need to support calling of internal methods
                        // since we don't want them to be public
                        if (objectType == typeof(Microsoft.Build.BuildEngine.IntrinsicFunctions))
                        {
                            bindingFlags |= BindingFlags.NonPublic;
                        }
                    }
                    else
                    {
                        bindingFlags |= BindingFlags.Instance;
                    }

                    // We have a methodinfo match, need to plug in the arguments

                    args = new object[arguments.Length];

                    // Assemble our arguments ready for passing to our method
                    for (int n = 0; n < arguments.Length; n++)
                    {
                        object argument = expander.ExpandPropertiesLeaveTypedAndEscaped(this.arguments[n], null);
                        string argumentValue = argument as string;

                        if (argumentValue != null)
                        {
                            // remove our 'quotes' from the escaped string, leaving escaped quotes intact
                            args[n] = EscapingUtilities.UnescapeAll(argumentValue.Trim('`', '"', '\''));
                        }
                        else
                        {
                            args[n] = argument;
                        }
                    }

                    // Handle special cases where the object type needs to affect the choice of method
                    // The default binder and method invoke, often chooses the incorrect Equals and CompareTo and 
                    // fails the comparison, because what we have on the right is generally a string.
                    // This special casing is to realize that its a comparison that is taking place and handle the
                    // argument type coercion accordingly; effectively pre-preparing the argument type so 
                    // that it matches the left hand side ready for the default binder�s method invoke.
                    if (objectInstance != null && args.Length == 1 && (String.Equals("Equals", this.name, StringComparison.OrdinalIgnoreCase) || String.Equals("CompareTo", this.name, StringComparison.OrdinalIgnoreCase)))
                    {
                        // change the type of the final unescaped string into the destination
                        args[0] = Convert.ChangeType(args[0], objectInstance.GetType(), CultureInfo.InvariantCulture);
                    }
                    
                    // If we've been asked for and instance to be constructed, then we
                    // need to locate an appropriate constructor and invoke it
                    if (String.Equals("new", this.name, StringComparison.OrdinalIgnoreCase))
                    {
                        functionResult = LateBindExecute(null /* no previous exception */, BindingFlags.Public | BindingFlags.Instance, null /* no instance for a constructor */, args, true /* is constructor */);
                    }
                    else
                    {
                        // Execute the function given converted arguments
                        // The only exception that we should catch to try a late bind here is missing method
                        // otherwise there is the potential of running a function twice!
                        try
                        {
                            // First use InvokeMember using the standard binder - this will match and coerce as needed
                            functionResult = objectType.InvokeMember(this.name, bindingFlags, Type.DefaultBinder, objectInstance, args, CultureInfo.InvariantCulture);
                        }
                        catch (MissingMethodException ex) // Don't catch and retry on any other exception
                        {
                            // If we're invoking a method, then there are deeper attempts that
                            // can be made to invoke the method
                            if ((bindingFlags & BindingFlags.InvokeMethod) == BindingFlags.InvokeMethod)
                            {
                                // The standard binder failed, so do our best to coerce types into the arguments for the function
                                // This may happen if the types need coercion, but it may also happen if the object represents a type that contains open type parameters, that is, ContainsGenericParameters returns true. 
                                functionResult = LateBindExecute(ex, bindingFlags, objectInstance, args, false /* is not constructor */);
                            }
                            else
                            {
                                // We were asked to get a property or field, and we found that we cannot
                                // locate it. Since there is no further argument coersion possible
                                // we'll throw right now.
                                throw;
                            }
                        }
                    }

                    // If the result of the function call is a string, then we need to escape the result
                    // so that we maintain the "engine contains escaped data" state.
                    // The exception is that the user is explicitly calling MSBuild::Unescape or MSBuild::Escape
                    if (functionResult is string && !String.Equals("Unescape", name, StringComparison.OrdinalIgnoreCase) && !String.Equals("Escape", name, StringComparison.OrdinalIgnoreCase))
                    {
                        functionResult = EscapingUtilities.Escape((string)functionResult);
                    }
                    
                    // There's nothing left to deal within the function expression, return the result from the execution
                    if (String.IsNullOrEmpty(remainder))
                    {
                        return functionResult;
                    }

                    // Recursively expand the remaining property body after execution
                    return expander.ExpandPropertyBody(remainder, functionResult, properties, options);
                }
                // Exceptions coming from the actual function called are wrapped in a TargetInvocationException
                catch (TargetInvocationException ex)
                {
                    // We ended up with something other than a function expression
                    string partiallyEvaluated = GenerateStringOfMethodExecuted(expression, objectInstance, name, args);
                    ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionPropertyExpression", partiallyEvaluated, ex.InnerException.Message.Replace("\r\n", " "));
                    return null;
                }
                // Any other exception was thrown by trying to call it
                catch (Exception ex)
                {
                    if (ExceptionHandling.NotExpectedFunctionException(ex))
                    {
                        throw;
                    }

                    // We ended up with something other than a function expression
                    string partiallyEvaluated = GenerateStringOfMethodExecuted(expression, objectInstance, name, args);
                    ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionPropertyExpression", partiallyEvaluated, ex.Message);
                    return null;
                }
            }
コード例 #12
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
 // Used by BuildItemGroup.Evaluate
 internal Expander(BuildPropertyGroup properties, Hashtable items, ExpanderOptions options)
     : this(new ReadOnlyLookup(items, properties), null, options)
 {
 }
コード例 #13
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
        /// <summary>
        /// Expand the body of the property, including any functions that it may contain
        /// </summary>
        private object ExpandPropertyBody(string propertyBody, object propertyValue, BuildPropertyGroup properties, ExpanderOptions options)
        {
            Function function = null;
            string propertyName = propertyBody;

            // Trim the body for comatibility reasons:
            // Spaces are not valid property name chars, but $( Foo ) is allowed, and should always expand to BLANK.
            // Do a very fast check for leading and trailing whitespace, and trim them from the property body if we have any.
            // But we will do a property name lookup on the propertyName that we held onto.
            if (Char.IsWhiteSpace(propertyBody[0]) || Char.IsWhiteSpace(propertyBody[propertyBody.Length - 1]))
            {
                propertyBody = propertyBody.Trim();
            }

            // If we don't have a clean propertybody then we'll do deeper checks to see
            // if what we have is a function
            if (!IsValidPropertyName(propertyBody))
            {
                if (propertyBody.Contains(".") || propertyBody[0] == '[')
                {
                    // This is a function
                    function = Function.ExtractPropertyFunction(propertyBody, propertyValue);

                    // We may not have been able to parse out a function
                    if (function != null)
                    {
                        // We will have either extracted the actual property name
                        // or realised that there is none (static function), and have recorded a null
                        propertyName = function.ExpressionRootName;
                    }
                    else
                    {
                        // In the event that we have been handed an unrecognized property body, throw
                        // an invalid function property exception.
                        ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionPropertyExpression", propertyBody, String.Empty);
                        return null;
                    }
                }
                else
                {
                    // In the event that we have been handed an unrecognized property body, throw
                    // an invalid function property exception.
                    ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionPropertyExpression", propertyBody, String.Empty);
                    return null;
                }
            }

            // Find the property value in our property collection.  This 
            // will automatically return "" (empty string) if the property
            // doesn't exist in the collection, and we're not executing a static function
            if (!String.IsNullOrEmpty(propertyName))
            {
                BuildProperty property;
                if (lookup != null)
                {
                    // We're using a lookup
                    property = lookup.GetProperty(propertyName);
                }
                else
                {
                    // We're only using a property group
                    property = properties[propertyName];
                }

                if (property == null)
                {
                    propertyValue = String.Empty;
                }
                else
                {
                    propertyValue = property.FinalValueEscaped;
                }
            }

            if (function != null)
            {
                // Because of the rich expansion capabilities of MSBuild, we need to keep things
                // as strings, since property expansion & string embedding can happen anywhere
                // propertyValue can be null here, when we're invoking a static function
                propertyValue = function.Execute(this, propertyValue, properties, options);
            }

            return propertyValue;
        }
コード例 #14
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
 /// <summary>
 /// Special cased constructor. Where we are only going to expand properties,
 /// it's a waste of memory to use a lookup. Just use the property group.
 /// PERF: This improves the EvaluateAllPropertyGroups codepath.
 /// </summary>
 internal Expander(BuildPropertyGroup properties)
 {
     this.options = ExpanderOptions.ExpandProperties;
     this.properties = properties;
 }
コード例 #15
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
 /// <summary>
 /// Create an expander from another expander, but with different
 /// options
 /// </summary>
 internal Expander(Expander expander, ExpanderOptions options)
     : this(expander.lookup, expander.itemMetadata, options)
 {
 }
コード例 #16
0
ファイル: ConditionEvaluator.cs プロジェクト: yazici/msbuild
        /// <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);
        }
コード例 #17
0
        /// <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);
        }
コード例 #18
0
ファイル: Expander.cs プロジェクト: nikson/msbuild
        // Used by unit tests
        internal Expander(ReadOnlyLookup lookup, Dictionary<string, string> itemMetadata, ExpanderOptions options)
        {
            ErrorUtilities.VerifyThrow(options != ExpanderOptions.Invalid, "Must specify options");

            this.lookup = lookup;
            this.itemMetadata = itemMetadata;
            this.options = options;
        }