} // end _PrepareAdd() private SortedList <TItem> _PrepareRemove(DbgTemplateNode template) { Util.Assert(!String.IsNullOrEmpty(m_matchedTemplateName)); if (0 != Util.Strcmp_OI(m_matchedTemplateName, template.TemplateName)) { Util.Fail("should not happen"); throw new ArgumentException(Util.Sprintf("The supplied item is for template {0}, but this list is for template {1}.", template.TemplateName, m_matchedTemplateName), "item"); } SortedList <int, SortedList <TItem> > targetCategoryList; if (template.HasMultiTypeWildcard) { targetCategoryList = m_itemLists[NoMultiMatchIdx]; } else { targetCategoryList = m_itemLists[HasMultiMatchIdx]; } SortedList <TItem> itemList; if (!targetCategoryList.TryGetValue(template.SingleTypeWildcardCount, out itemList)) { return(null); } return(itemList); } // end _PrepareRemove()
} // end constructor private static string _VerifyParamsAndBuildFullTypeName(string templateName, IReadOnlyList <DbgTemplateNode> parameters, DbgTemplateNode nestedType) { if (String.IsNullOrEmpty(templateName)) { throw new ArgumentException("You must supply a template name.", "templateName"); } if (null == parameters) { throw new ArgumentNullException("parameters"); } for (int i = 0; i < parameters.Count; i++) { if (null == parameters[i]) { throw new ArgumentException("Null parameters not allowed.", Util.Sprintf("parameters[ {0} ]", i)); } if ((parameters[i].FullName == c_MultiTypeWildcard) && (i != (parameters.Count - 1))) { throw new ArgumentException(Util.Sprintf("The '{0}' wildcard must be the last parameter of a template.", c_MultiTypeWildcard), "parameters"); } } // end for( each param ) StringBuilder sb = new StringBuilder(templateName.Length + (parameters.Count * (templateName.Length * 3))); sb.Append(templateName); if (parameters.Count > 0) { sb.Append('<'); bool first = true; foreach (var pn in parameters) { if (first) { first = false; } else { sb.Append(','); } sb.Append(pn); } sb.Append('>'); } if (null != nestedType) { sb.Append("::"); sb.Append(nestedType.FullName); } return(sb.ToString()); } // end _VerifyParamsAndBuildFullTypeName()
} // end TryFindMatchingItem() /// <summary> /// Finds all items with a matching template. /// </summary> public IEnumerable <TItem> FindMatchingItems(DbgTemplateNode typeTemplate) { if (null == typeTemplate) { throw new ArgumentNullException("typeTemplate"); } if (0 != Util.Strcmp_OI(m_matchedTemplateName, typeTemplate.TemplateName)) { Util.Fail("should not happen"); throw new ArgumentException(Util.Sprintf("The supplied item is for template {0}, but this list is for template {1}.", typeTemplate.TemplateName, m_matchedTemplateName), "item"); } foreach (var categoryList in m_itemLists) { foreach (var kvp in categoryList) { foreach (var item in kvp.Value) { if (item.TypeName.Matches(typeTemplate)) { yield return(item); } } } } } // end FindMatchingItems()
/// <summary> /// Creates a new DbgValueConverterInfo object. /// </summary> /// <param name="typeName"> /// This can be module-qualified (like "dfsrs!_FILETIME"), or not (like /// "!_FILETIME"). /// </param> public DbgValueConverterInfo(string typeName, IDbgValueConverter converter, string sourceScript) { if (String.IsNullOrEmpty(typeName)) { throw new ArgumentException("You must supply a type name.", "typeName"); } if (null == converter) { throw new ArgumentNullException("converter"); } int bangIdx = typeName.IndexOf('!'); if (bangIdx < 0) { m_typeName = DbgTemplateNode.CrackTemplate(typeName); } else { ScopingModule = typeName.Substring(0, bangIdx); if (bangIdx == (typeName.Length - 1)) { throw new ArgumentException("No type name after the '!'.", typeName); } var tn = typeName.Substring(bangIdx + 1); m_typeName = DbgTemplateNode.CrackTemplate(tn); } Converter = converter; SourceScript = sourceScript; } // end constructor
_GetMatchingEntriesForModule(Dictionary <string, TypeNameMatchList <DbgValueConverterInfo> > entryDict, string templateFilter, bool exactMatchOnly, bool throwIfNoResult) { if (String.IsNullOrEmpty(templateFilter)) { if (exactMatchOnly) { throw new ArgumentException("You asked for exact matches only, but gave no templateFilter."); } foreach (var tml in entryDict.Values) { foreach (var converter in tml) { yield return(converter); } } } else { TypeNameMatchList <DbgValueConverterInfo> tml; DbgTemplateNode crackedName = DbgTemplate.CrackTemplate(templateFilter); if (!entryDict.TryGetValue(crackedName.TemplateName, out tml)) { if (throwIfNoResult) { throw new DbgProviderException(Util.Sprintf("Could not find converter info matching '{0}'.", templateFilter), "NoConverterForTemplateFilter", ErrorCategory.ObjectNotFound, templateFilter); } } else { if (exactMatchOnly) { int count = 0; foreach (var converter in tml.FindMatchingItemsExact(crackedName)) { count++; yield return(converter); } Util.Assert(count <= 1); // shouldn't be able to have more than one exact match } else { foreach (var converter in tml.FindMatchingItems(crackedName)) { yield return(converter); } } } } } // end _GetMatchingEntriesForModule()
/// <summary> /// Indicates if two templates "match" exactly (if they are identical). The /// comparison is case-insensitive. /// </summary> /// <remarks> /// <para> /// For an exact match, the templates must be identical, including wildcards. /// In other words, the FullName of each template must be the same (except for /// case). /// </para> /// <para> /// Normally when using template names, you should use the <see cref="Match"/> /// method, so that wildcards are taken into account. This method is useful, /// for instance, when you need to find something stored by template name in /// order to update it. /// </para> /// <para> /// N.B. The "const-ness" (HasConst) of a type name does not affect matching. /// </para> /// </remarks> public bool MatchesExact(DbgTemplateNode other) { if (null == other) { throw new ArgumentNullException("other"); } return(0 == Util.Strcmp_OI(FullName, other.FullName)); } // end MatchesExact()
} // end FindMatchingItems() /// <summary> /// Enumerates all items that have the exact same template. /// </summary> /// <remarks> /// Items with templates that match but are not exact matches will not be /// returned. /// </remarks> public IEnumerable <TItem> FindMatchingItemsExact(string typeName) { if (String.IsNullOrEmpty(typeName)) { throw new ArgumentException("You must supply a type name.", "typeName"); } DbgTemplateNode matchMe = DbgTemplateNode.CrackTemplate(typeName); return(FindMatchingItemsExact(matchMe)); } // end FindMatchingItemsExact()
} // end class ExactMatchEqualityComparer /// <summary> /// Attempts to find an item with a matching template. /// </summary> /// <remarks> /// There may be multiple matches in the list; this just returns the first /// match, where matches are made according to priority order. /// </remarks> public TItem TryFindMatchingItem(string typeName) { if (String.IsNullOrEmpty(typeName)) { throw new ArgumentException("You must supply a type name.", "typeName"); } DbgTemplateNode matchMe = DbgTemplateNode.CrackTemplate(typeName); return(TryFindMatchingItem(matchMe)); } // end TryFindMatchingItem()
} // end constructor public override bool Matches(DbgTemplateNode other) { // Special typename wildcard: if (IsEitherNameSingleTypeWildcard(FullName, other.FullName)) { return(true); } if (other.IsTemplate) { return(false); } // TODO: PS Wildcard? Regex? return(0 == Util.Strcmp_OI(other.FullName, FullName)); } // end Matches()
} // end Remove() // public void AddRange( IEnumerable< TItem > items ) // { // if( null == items ) // throw new ArgumentNullException( "items" ); // DbgTemplateNode template = null; // int count = 0; // foreach( var item in items ) // { // count++; // if( null == template ) // template = item.TypeName; // // Technically they only need to have wildcard type and count match, but // // this should be fine. // if( !item.TypeName.MatchesExact( template ) ) // throw new InvalidOperationException( "All items used with AddRange must have exactly-matching templates." ); // } // if( 0 == count ) // { // Util.Fail( "Why AddRange with nothing?" ); // Not a program invariant; just weird. // return; // } // List< TItem > itemList = _PrepareAdd( template, count ); // itemList.AddRange( items ); // } // end AddRange() private SortedList <TItem> _PrepareAdd(DbgTemplateNode template, int sizeHint) { m_version++; if (0 >= sizeHint) { sizeHint = 4; } if (null == m_matchedTemplateName) { m_matchedTemplateName = template.TemplateName; Util.Assert(!String.IsNullOrEmpty(m_matchedTemplateName)); } else { if (0 != Util.Strcmp_OI(m_matchedTemplateName, template.TemplateName)) { Util.Fail("should not happen"); throw new ArgumentException(Util.Sprintf("The supplied item is for template {0}, but this list is for template {1}.", template.TemplateName, m_matchedTemplateName), "item"); } } SortedList <int, SortedList <TItem> > targetCategoryList; if (template.HasMultiTypeWildcard) { targetCategoryList = m_itemLists[NoMultiMatchIdx]; } else { targetCategoryList = m_itemLists[HasMultiMatchIdx]; } SortedList <TItem> itemList; if (!targetCategoryList.TryGetValue(template.SingleTypeWildcardCount, out itemList)) { itemList = new SortedList <TItem>(sizeHint, NonWildcardParamComparer.Instance, ExactMatchEqualityComparer.Instance); targetCategoryList.Add(template.SingleTypeWildcardCount, itemList); } return(itemList); } // end _PrepareAdd()
// TODO: perhaps this should be private as well, to prevent ill-formed templateName. internal DbgTemplate(string templateName, IReadOnlyList <DbgTemplateNode> parameters, DbgTemplateNode nestedType, bool hasConst) : base(_VerifyParamsAndBuildFullTypeName(templateName, parameters, nestedType), hasConst) { if (null == parameters) { throw new ArgumentNullException("parameters"); } m_templateName = templateName; Parameters = parameters; NestedNode = nestedType; } // end constructor
} // end FindMatchingItemsExact() /// <summary> /// Enumerates all items that have the exact same template. /// </summary> /// <remarks> /// Items with templates that match but are not exact matches will not be /// returned. /// </remarks> public IEnumerable <TItem> FindMatchingItemsExact(DbgTemplateNode typeTemplate) { if (null == typeTemplate) { throw new ArgumentNullException("typeTemplate"); } if (0 != Util.Strcmp_OI(m_matchedTemplateName, typeTemplate.TemplateName)) { Util.Fail("should not happen"); throw new ArgumentException(Util.Sprintf("The supplied item is for template {0}, but this list is for template {1}.", typeTemplate.TemplateName, m_matchedTemplateName), "item"); } SortedList <int, SortedList <TItem> > targetCategoryList; if (typeTemplate.HasMultiTypeWildcard) { targetCategoryList = m_itemLists[NoMultiMatchIdx]; } else { targetCategoryList = m_itemLists[HasMultiMatchIdx]; } SortedList <TItem> itemList; if (!targetCategoryList.TryGetValue(typeTemplate.SingleTypeWildcardCount, out itemList)) { yield break; } foreach (var item in itemList) { // TODO: But couldn't I go faster by using NonWildcardParameterCount? if (item.TypeName.MatchesExact(typeTemplate)) { yield return(item); } } } // end FindMatchingItemsExact()
} // end _AddBaseClassNodesToList() private void _AddBaseClassPointerNodesToList(List <DbgTemplateNode> list, DbgPointerTypeInfo pti) { DbgNamedTypeInfo dnti = pti; int numStars = 0; while (dnti is DbgPointerTypeInfo) { dnti = ((DbgPointerTypeInfo)dnti).PointeeType; numStars++; } if (!typeof(DbgUdtTypeInfo).IsAssignableFrom(dnti.GetType())) { return; // It doesn't point to a UDT. } string stars = new String('*', numStars); var q = new Queue <DbgUdtTypeInfo>(); var uti = (DbgUdtTypeInfo)dnti; uti.VisitAllBaseClasses((bc) => list.Add(DbgTemplateNode.CrackTemplate(bc.TemplateNode.FullName + stars))); } // end _AddBaseClassPointerNodesToList()
public IReadOnlyList <DbgTemplateNode> GetTemplateNodes() { if (null == m_templateNodes) { var templateTypeNames = new List <DbgTemplateNode>(); DbgArrayTypeInfo ati = this as DbgArrayTypeInfo; if (null != ati) { templateTypeNames.Add(DbgTemplateNode.CrackTemplate(ati.ArrayElementType.Name + "[]")); // we don't want the dimensions // TODO: And maybe for array types, we'd like to include the array type /with/ dimensions, // so that, for instance, all Foo[8] could be treated specially or something. But maybe that's // stretching it. // TODO: How about co-(or is it contra-)variance? (should we get base types of the element type, like we do for pointers?) } else if (typeof(DbgUdtTypeInfo).IsAssignableFrom(GetType())) { DbgUdtTypeInfo uti = (DbgUdtTypeInfo)this; templateTypeNames.Add(uti.TemplateNode); _AddBaseClassNodesToList(templateTypeNames, uti); } else if (typeof(DbgPointerTypeInfo).IsAssignableFrom(GetType())) { DbgPointerTypeInfo pti = (DbgPointerTypeInfo)this; templateTypeNames.Add(pti.TemplateNode); _AddBaseClassPointerNodesToList(templateTypeNames, pti); } else { templateTypeNames.Add(DbgTemplateNode.CrackTemplate(Name)); } m_templateNodes = templateTypeNames.AsReadOnly(); } return(m_templateNodes); } // end GetTemplateNodes()
} // end constructor /// <summary> /// Indicates if two templates "match", taking special template-matching /// wildcards into account. /// </summary> /// <remarks> /// <para> /// Two special wildcards can be used to match template parameters: /// <list type="unordered"> /// <item>? /// <description>Matches any single template parameter.</description> /// </item> /// <item>?* /// <description>Matches one or more template parameters. Must be the last /// parameter in a list.</description> /// </item> /// </list> /// For example "std::foo<WCHAR,?>" matches "std::foo<WCHAR,unsigned char>". /// </para> /// <para> /// Note that for DbgTemplate objects, these wildcards are only applicable in /// the template parameter list; and DbgTemplateLeaf objects can consist of the /// single-type wildcard (?), but not the multi-type wildcard /// (?*). /// </para> /// <para> /// The SingleTypeWildcardCount and HasMultiTypeWildcard properties can be /// used to establish precedence between multiple matching templates. /// </para> /// <para> /// N.B. The "const-ness" (HasConst) of a type name does not affect matching. /// </para> /// </remarks> public abstract bool Matches(DbgTemplateNode other);
} // end _HasTwoColonsPreceding() private static bool _TryCrackTemplate(string name, int startIdx, out DbgTemplateNode templatePart, out string problem) { templatePart = null; problem = null; name = name.Trim(); bool hasConst = false; if (name.StartsWith("const ", StringComparison.OrdinalIgnoreCase)) { // I haven't actually observed any type names with "const" at the // beginning, but it seems like it could be possible. hasConst = true; name = name.Substring(6).Trim(); } else if (name.EndsWith(" const", StringComparison.OrdinalIgnoreCase)) { hasConst = true; name = name.Substring(0, name.Length - 6).Trim(); } int idx; if (!_LooksLikeATemplateName(name, startIdx, /* angleBracketIdx */ out idx)) { templatePart = new DbgTemplateLeaf(name.Substring(startIdx), hasConst); return(true); } var templateName = name.Substring(startIdx, idx - startIdx); StringBuilder sb = new StringBuilder(); DbgTemplateNode nestedType = null; int depth = 1; var templateParams = new List <DbgTemplateNode>(); for (idx = idx + 1; idx < name.Length; idx++) // start after the first '<' { char c = name[idx]; if ('<' == c) { depth++; } else if ('>' == c) { depth--; if (depth < 0) { problem = Util.Sprintf("Unbalanced closing angle bracket at position {0}.", idx); return(false); } if (0 == depth) { if (sb.Length > 0) { templateParams.Add(CrackTemplate(sb.ToString().Trim())); } if (idx != (name.Length - 1)) { // TODO: '+' for nested types in managed generic types? if ((name.Length < (idx + 4)) || // there has to be at least "::X" (name[idx + 1] != ':') || (name[idx + 2] != ':')) { problem = Util.Sprintf("Unexpected characters at position {0}.", idx); return(false); } idx += 3; // skip the "::" if (!_TryCrackTemplate(name, idx, out nestedType, out problem)) { Util.Assert(!String.IsNullOrEmpty(problem)); return(false); } } break; } } if (depth > 1) { sb.Append(c); } else { Util.Assert(1 == depth); if (',' == c) { // TODO: Hmm... I wonder if it's possible to get a symbol with ",," (which // would lead to an empty part name, which will throw). templateParams.Add(CrackTemplate(sb.ToString().Trim())); sb.Clear(); } else { sb.Append(c); } } } // end for( each character ) templatePart = new DbgTemplate(templateName, templateParams.AsReadOnly(), nestedType, hasConst); return(true); } // end _TryCrackTemplate()
protected static bool IsMultiMatchWildcard(DbgTemplateNode dtp) { return(dtp.FullName == c_MultiTypeWildcard); }
} // end _VerifyParamsAndBuildFullTypeName() public override bool Matches(DbgTemplateNode other) { // Special typename wildcard: if (IsEitherNameSingleTypeWildcard(other.FullName, FullName)) { return(true); } if (!other.IsTemplate) { return(false); } DbgTemplate dtOther = (DbgTemplate)other; // TODO: PS Wildcard? Regex? if (0 != Util.Strcmp_OI(dtOther.TemplateName, TemplateName)) { return(false); } for (int i = 0; i < dtOther.Parameters.Count; i++) { if (i >= Parameters.Count) { return(false); } // Special typename wildcard: if (IsMultiMatchWildcard(dtOther.Parameters[i])) { if (i != (dtOther.Parameters.Count - 1)) { Util.Fail("Not reachable."); // construction of such now disallowed. throw new ArgumentException(Util.Sprintf("The '{0}' placeholder can only come last in a template parameter list.", c_MultiTypeWildcard), "other"); } break; } // Special typename wildcard: if (IsMultiMatchWildcard(Parameters[i])) { if (i != (Parameters.Count - 1)) { throw new ArgumentException(Util.Sprintf("The '{0}' placeholder can only come last in a template parameter list.", c_MultiTypeWildcard), "this"); } break; } if (!Parameters[i].Matches(dtOther.Parameters[i])) { return(false); } } if ((null == NestedNode) != (null == dtOther.NestedNode)) { // One has a nested node, and the other doesn't. return(false); } if (null != NestedNode) { return(NestedNode.Matches(dtOther.NestedNode)); } else { return(true); } } // end Matches()