public OpportunisticInternLegacySimpleConcurrecy_Tests(ITestOutputHelper testOutput) : base(testOutput) { _env.SetEnvironmentVariable("MSBuildUseLegacyStringInterner", "1"); _env.SetEnvironmentVariable("MSBuildUseSimpleInternConcurrency", "1"); OpportunisticIntern.ResetForTests(); }
/// <summary> /// Execute the task. /// </summary> /// <returns></returns> public override bool Execute() { ArrayList inPathList = new ArrayList(); ArrayList outOfPathList = new ArrayList(); string conePath; try { conePath = OpportunisticIntern.InternStringIfPossible(System.IO.Path.GetFullPath(_path.ItemSpec)); conePath = FileUtilities.EnsureTrailingSlash(conePath); } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { Log.LogErrorWithCodeFromResources(null, "", 0, 0, 0, 0, "FindUnderPath.InvalidParameter", "Path", _path.ItemSpec, e.Message); return(false); } int conePathLength = conePath.Length; Log.LogMessageFromResources(MessageImportance.Low, "FindUnderPath.ComparisonPath", Path.ItemSpec); foreach (ITaskItem item in Files) { string fullPath; try { fullPath = OpportunisticIntern.InternStringIfPossible(System.IO.Path.GetFullPath(item.ItemSpec)); } catch (Exception e) when(ExceptionHandling.IsIoRelatedException(e)) { Log.LogErrorWithCodeFromResources(null, "", 0, 0, 0, 0, "FindUnderPath.InvalidParameter", "Files", item.ItemSpec, e.Message); return(false); } // Compare the left side of both strings to see if they're equal. if (String.Compare(conePath, 0, fullPath, 0, conePathLength, StringComparison.OrdinalIgnoreCase) == 0) { // If we should use the absolute path, update the item contents // Since ItemSpec, which fullPath comes from, is unescaped, re-escape when setting // item.ItemSpec, since the setter for ItemSpec expects an escaped value. if (_updateToAbsolutePaths) { item.ItemSpec = EscapingUtilities.Escape(fullPath); } inPathList.Add(item); } else { outOfPathList.Add(item); } } InPath = (ITaskItem[])inPathList.ToArray(typeof(ITaskItem)); OutOfPath = (ITaskItem[])outOfPathList.ToArray(typeof(ITaskItem)); return(true); }
private static bool IsInternable(OpportunisticIntern.IInternable internable) { string i1 = OpportunisticIntern.InternableToString(internable); string i2 = OpportunisticIntern.InternableToString(internable); Assert.Equal(i1, i2); // No matter what, the same string value should return. return(Object.ReferenceEquals(i1, i2)); }
/// <summary> /// Parse the given <paramref name="fileSpec" /> into a <see cref="MSBuildGlob" /> using a given /// <paramref name="globRoot" />. /// </summary> /// <param name="globRoot"> /// The root of the glob. /// The fixed directory part of the glob and the match arguments (<see cref="IsMatch" /> and <see cref="MatchInfo" />) /// will get normalized against this root. /// If empty, the current working directory is used. /// Cannot be null, and cannot contain invalid path arguments. /// </param> /// <param name="fileSpec">The string to parse</param> /// <returns></returns> public static MSBuildGlob Parse(string globRoot, string fileSpec) { ErrorUtilities.VerifyThrowArgumentNull(globRoot, nameof(globRoot)); ErrorUtilities.VerifyThrowArgumentNull(fileSpec, nameof(fileSpec)); ErrorUtilities.VerifyThrowArgumentInvalidPath(globRoot, nameof(globRoot)); if (globRoot == string.Empty) { globRoot = Directory.GetCurrentDirectory(); } globRoot = OpportunisticIntern.InternStringIfPossible(FileUtilities.NormalizePath(globRoot).WithTrailingSlash()); var lazyState = new Lazy <GlobState>(() => { string fixedDirectoryPart = null; string wildcardDirectoryPart = null; string filenamePart = null; string matchFileExpression; bool needsRecursion; bool isLegalFileSpec; FileMatcher.Default.GetFileSpecInfo( fileSpec, out fixedDirectoryPart, out wildcardDirectoryPart, out filenamePart, out matchFileExpression, out needsRecursion, out isLegalFileSpec, (fixedDirPart, wildcardDirPart, filePart) => { var normalizedFixedPart = NormalizeTheFixedDirectoryPartAgainstTheGlobRoot(fixedDirPart, globRoot); return(Tuple.Create(normalizedFixedPart, wildcardDirPart, filePart)); }); // compile the regex since it's expected to be used multiple times var regex = isLegalFileSpec ? new Regex(matchFileExpression, FileMatcher.DefaultRegexOptions | RegexOptions.Compiled) : null; return(new GlobState(globRoot, fileSpec, isLegalFileSpec, fixedDirectoryPart, wildcardDirectoryPart, filenamePart, matchFileExpression, needsRecursion, regex)); }, true); return(new MSBuildGlob(lazyState)); }
/// <summary> /// Adds instances of %XX in the input string where the char to be escaped appears /// XX is the hex value of the ASCII code for the char. Caches if requested. /// </summary> /// <param name="unescapedString">The string to escape.</param> /// <param name="cache"> /// True if the cache should be checked, and if the resultant string /// should be cached. /// </param> private static string EscapeWithOptionalCaching(string unescapedString, bool cache) { // If there are no special chars, just return the original string immediately. // Don't even instantiate the StringBuilder. if (String.IsNullOrEmpty(unescapedString) || !ContainsReservedCharacters(unescapedString)) { return(unescapedString); } // next, if we're caching, check to see if it's already there. if (cache) { string cachedEscapedString = null; lock (s_unescapedToEscapedStrings) { if (s_unescapedToEscapedStrings.TryGetValue(unescapedString, out cachedEscapedString)) { return(cachedEscapedString); } } } // This is where we're going to build up the final string to return to the caller. StringBuilder escapedStringBuilder = StringBuilderCache.Acquire(unescapedString.Length * 2); AppendEscapedString(escapedStringBuilder, unescapedString); if (!cache) { return(StringBuilderCache.GetStringAndRelease(escapedStringBuilder)); } string escapedString = OpportunisticIntern.StringBuilderToString(escapedStringBuilder); StringBuilderCache.Release(escapedStringBuilder); lock (s_unescapedToEscapedStrings) { s_unescapedToEscapedStrings[unescapedString] = escapedString; } return(escapedString); }
/// <summary> /// Returns a whitespace-trimmed and possibly interned substring of the expression. /// </summary> /// <param name="startIndex">Start index of the substring.</param> /// <param name="length">Length of the substring.</param> /// <returns>Equivalent to _expression.Substring(startIndex, length).Trim() or null if the trimmed substring is empty.</returns> private string GetExpressionSubstring(int startIndex, int length) { int endIndex = startIndex + length; while (startIndex < endIndex && char.IsWhiteSpace(_expression[startIndex])) { startIndex++; } while (startIndex < endIndex && char.IsWhiteSpace(_expression[endIndex - 1])) { endIndex--; } if (startIndex < endIndex) { var target = new SubstringInternTarget(_expression, startIndex, endIndex - startIndex); return(OpportunisticIntern.InternableToString(target)); } return(null); }
public OpportunisticIntern_Tests(ITestOutputHelper testOutput) : base(testOutput) { OpportunisticIntern.ResetForTests(); }
/// <summary> /// Given a string of semi-colon delimited name=value pairs, this method parses it and creates /// a hash table containing the property names as keys and the property values as values. /// This method escapes any special characters found in the property values, in case they /// are going to be passed to a method (such as that expects the appropriate escaping to have happened /// already. /// </summary> /// <returns>true on success, false on failure.</returns> internal static bool GetTableWithEscaping(TaskLoggingHelper log, string parameterName, string syntaxName, string[] propertyNameValueStrings, out Dictionary <string, string> finalPropertiesTable) { finalPropertiesTable = null; if (propertyNameValueStrings != null) { finalPropertiesTable = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var finalPropertiesList = new List <PropertyNameValuePair>(); // Loop through the array. Each string in the array should be of the form: // MyPropName=MyPropValue foreach (string propertyNameValueString in propertyNameValueStrings) { // Find the first '=' sign in the string. int indexOfEqualsSign = propertyNameValueString.IndexOf('='); if (indexOfEqualsSign != -1) { // If we found one, then grab the stuff before it and put it into "propertyName", // and grab the stuff after it and put it into "propertyValue". But trim the // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) string propertyName = propertyNameValueString.Substring(0, indexOfEqualsSign).Trim(); string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Substring(indexOfEqualsSign + 1).Trim()); // Make sure we have a property name and property value (though the value is allowed to be blank). if (propertyName.Length == 0) { // No property name? That's no good to us. log?.LogErrorWithCodeFromResources("General.InvalidPropertyError", syntaxName, propertyNameValueString); return(false); } // Store the property in our list. finalPropertiesList.Add(new PropertyNameValuePair(propertyName, propertyValue)); } else { // There's no '=' sign in the string. When this happens, we treat this string as basically // an appendage on the value of the previous property. For example, if the project file contains // // <PropertyGroup> // <WarningsAsErrors>1234;5678;9999</WarningsAsErrors> // </PropertyGroup> // <Target Name="Build"> // <MSBuild Projects="ConsoleApplication1.csproj" // Properties="WarningsAsErrors=$(WarningsAsErrors)"/> // </Target> // // , then this method (GetTableWithEscaping) will see this: // // propertyNameValueStrings[0] = "WarningsAsErrors=1234" // propertyNameValueStrings[1] = "5678" // propertyNameValueStrings[2] = "9999" // // And what we actually want to end up with in our final hashtable is this: // // NAME VALUE // =================== ================================ // WarningsAsErrors 1234;5678;9999 // if (finalPropertiesList.Count > 0) { // There was a property definition previous to this one. Append the current string // to that previous value, using semicolon as a separator. string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Trim()); finalPropertiesList[finalPropertiesList.Count - 1].Value.Append(';'); finalPropertiesList[finalPropertiesList.Count - 1].Value.Append(propertyValue); } else { // No equals sign in the very first property? That's a problem. log?.LogErrorWithCodeFromResources("General.InvalidPropertyError", syntaxName, propertyNameValueString); return(false); } } } // Convert the data in the List to a Hashtable, because that's what the MSBuild task eventually // needs to pass onto the engine. log?.LogMessageFromText(parameterName, MessageImportance.Low); foreach (PropertyNameValuePair propertyNameValuePair in finalPropertiesList) { string propertyValue = OpportunisticIntern.StringBuilderToString(propertyNameValuePair.Value); finalPropertiesTable[propertyNameValuePair.Name] = propertyValue; log?.LogMessageFromText( $" {propertyNameValuePair.Name}={propertyValue}", MessageImportance.Low); } } return(true); }