private static void GetAllScopes(ISymUnmanagedScope scope, ArrayBuilder<ISymUnmanagedScope> builder, int offset, bool isScopeEndInclusive) { builder.Add(scope); foreach (var nested in scope.GetScopes()) { if ((offset < 0) || nested.IsInScope(offset, isScopeEndInclusive)) { GetAllScopes(nested, builder, offset, isScopeEndInclusive); if (offset >= 0) { return; } } } }
private static void GetAllScopes(ISymUnmanagedScope scope, ArrayBuilder <ISymUnmanagedScope> builder, int offset, bool isScopeEndInclusive) { builder.Add(scope); foreach (var nested in scope.GetScopes()) { if ((offset < 0) || nested.IsInScope(offset, isScopeEndInclusive)) { GetAllScopes(nested, builder, offset, isScopeEndInclusive); if (offset >= 0) { return; } } } }
/// <summary> /// Get the (unprocessed) import strings for a given method. /// </summary> /// <remarks> /// Doesn't consider forwarding. /// /// CONSIDER: Dev12 doesn't just check the root scope - it digs around to find the best /// match based on the IL offset and then walks up to the root scope (see PdbUtil::GetScopeFromOffset). /// However, it's not clear that this matters, since imports can't be scoped in VB. This is probably /// just based on the way they were extracting locals and constants based on a specific scope. /// </remarks> internal static ImmutableArray <string> GetImportStrings(this ISymUnmanagedMethod method) { if (method == null) { // In rare circumstances (only bad PDBs?) GetMethodByVersion can return null. // If there's no debug info for the method, then no import strings are available. return(ImmutableArray <string> .Empty); } ISymUnmanagedScope rootScope = method.GetRootScope(); if (rootScope == null) { Debug.Assert(false, "Expected a root scope."); return(ImmutableArray <string> .Empty); } ImmutableArray <ISymUnmanagedScope> childScopes = rootScope.GetScopes(); if (childScopes.Length == 0) { // It seems like there should always be at least one child scope, but we've // seen PDBs where that is not the case. return(ImmutableArray <string> .Empty); } // As in NamespaceListWrapper::Init, we only consider namespaces in the first // child of the root scope. ISymUnmanagedScope firstChildScope = childScopes[0]; ImmutableArray <ISymUnmanagedNamespace> namespaces = firstChildScope.GetNamespaces(); if (namespaces.Length == 0) { // It seems like there should always be at least one namespace (i.e. the global // namespace), but we've seen PDBs where that is not the case. return(ImmutableArray <string> .Empty); } return(ImmutableArray.CreateRange(namespaces, n => n.GetName())); }
private void WriteScopes(ISymUnmanagedScope scope) { writer.WriteStartElement("scope"); { writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset())); writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset())); { foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces()) { WriteNamespace(@namespace); } WriteLocalsHelper(scope, slotNames: null, includeChildScopes: false); } foreach (ISymUnmanagedScope child in scope.GetScopes()) { WriteScopes(child); } } writer.WriteEndElement(); // </scope> }
/// <summary> /// Get the (unprocessed) import strings for a given method. /// </summary> /// <remarks> /// Doesn't consider forwarding. /// </remarks> private static ImmutableArray <string> GetImportStrings(this ISymUnmanagedMethod method) { ISymUnmanagedScope rootScope = method.GetRootScope(); if (rootScope == null) { Debug.Assert(false, "Expected a root scope."); return(ImmutableArray <string> .Empty); } ImmutableArray <ISymUnmanagedScope> childScopes = rootScope.GetScopes(); if (childScopes.Length == 0) { //Debug.Assert(false, "Expected at least one child scope."); // TODO (acasey): Why can't we assume this? return(ImmutableArray <string> .Empty); } // As in NamespaceListWrapper::Init, we only consider namespaces in the first // child of the root scope. ISymUnmanagedScope firstChildScope = childScopes[0]; ImmutableArray <ISymUnmanagedNamespace> namespaces = firstChildScope.GetNamespaces(); if (namespaces.Length == 0) { //Debug.Assert(false, "Expected at least one namespace (i.e. the global namespace)."); // TODO (acasey): Why can't we assume this? return(ImmutableArray <string> .Empty); } ArrayBuilder <string> importsBuilder = ArrayBuilder <string> .GetInstance(namespaces.Length); foreach (ISymUnmanagedNamespace @namespace in namespaces) { importsBuilder.Add(@namespace.GetName()); } return(importsBuilder.ToImmutableAndFree()); }
private void WriteLocalsHelper(ISymUnmanagedScope scope, Dictionary <int, ImmutableArray <string> > slotNames, bool includeChildScopes) { foreach (ISymUnmanagedVariable l in scope.GetLocals()) { writer.WriteStartElement("local"); { writer.WriteAttributeString("name", l.GetName()); // Each local maps to a "IL Index" or "slot" number. // The index is not necessarily unique. Several locals may refer to the same slot. // It just means that the same local is known under different names inside the same or different scopes. // This index is what you pass to ICorDebugILFrame::GetLocalVariable() to get // a specific local variable. // NOTE: VB emits "fake" locals for resumable locals which are actually backed by fields. // These locals always map to the slot #0 which is just a valid number that is // not used. Only scoping information is used by EE in this case. int slot = l.GetSlot(); writer.WriteAttributeString("il_index", CultureInvariantToString(slot)); bool reusingSlot = false; // collect slot names so that we can verify ISymUnmanagedReader APIs if (slotNames != null) { ImmutableArray <string> existingNames; if (slotNames.TryGetValue(slot, out existingNames)) { slotNames[slot] = existingNames.Add(l.GetName()); reusingSlot = true; } else { slotNames.Add(slot, ImmutableArray.Create(l.GetName())); } } // Provide scope range writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset())); writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset())); writer.WriteAttributeString("attributes", l.GetAttributes().ToString()); if (reusingSlot) { writer.WriteAttributeString("reusingslot", reusingSlot.ToString(CultureInfo.InvariantCulture)); } } writer.WriteEndElement(); // </local> } foreach (ISymUnmanagedConstant c in scope.GetConstants()) { // Note: We can retrieve constant tokens by saving it into signature blob // in our implementation of IMetadataImport.GetSigFromToken. writer.WriteStartElement("constant"); { writer.WriteAttributeString("name", c.GetName()); object value = c.GetValue(); string typeName = value.GetType().Name; // certain Unicode characters will give Xml writers fits...in order to avoid this, we'll replace // problematic characters/sequences with their hexadecimal equivalents, like U+0000, etc... var chars = value as string; if (chars != null) { PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); var valueWithPlaceholders = pooled.Builder; foreach (var ch in chars) { // if we end up with more, we can add them here if (0 == (int)ch) { valueWithPlaceholders.AppendFormat("U+{0:X4}", (int)ch); } else { valueWithPlaceholders.Append(ch); } } if (valueWithPlaceholders.Length > chars.Length) { value = valueWithPlaceholders.ToString(); } pooled.Free(); } writer.WriteAttributeString("value", value.ToString()); writer.WriteAttributeString("type", typeName); } writer.WriteEndElement(); // </constant> } if (includeChildScopes) { foreach (ISymUnmanagedScope childScope in scope.GetScopes()) { WriteLocalsHelper(childScope, slotNames, includeChildScopes); } } }
private void WriteScope(ISymUnmanagedScope scope, bool isRoot) { _writer.WriteStartElement(isRoot ? "rootScope" : "scope"); _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset())); _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset())); foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces()) { WriteNamespace(@namespace); } WriteLocals(scope); foreach (ISymUnmanagedScope child in scope.GetScopes()) { WriteScope(child, isRoot: false); } _writer.WriteEndElement(); }
private void WriteScopes(ISymUnmanagedScope rootScope) { // The root scope is always empty. The first scope opened by SymWriter is the child of the root scope. if (rootScope.GetNamespaces().IsEmpty && rootScope.GetLocals().IsEmpty && rootScope.GetConstants().IsEmpty) { foreach (ISymUnmanagedScope child in rootScope.GetScopes()) { WriteScope(child, isRoot: false); } } else { // This shouldn't be executed for PDBs generated via SymWriter. WriteScope(rootScope, isRoot: true); } }
private void WriteLocalsHelper(ISymUnmanagedScope scope, Dictionary<int, ImmutableArray<string>> slotNames, bool includeChildScopes) { foreach (ISymUnmanagedVariable l in scope.GetLocals()) { writer.WriteStartElement("local"); { writer.WriteAttributeString("name", l.GetName()); // Each local maps to a "IL Index" or "slot" number. // The index is not necessarily unique. Several locals may refer to the same slot. // It just means that the same local is known under different names inside the same or different scopes. // This index is what you pass to ICorDebugILFrame::GetLocalVariable() to get // a specific local variable. // NOTE: VB emits "fake" locals for resumable locals which are actually backed by fields. // These locals always map to the slot #0 which is just a valid number that is // not used. Only scoping information is used by EE in this case. int slot = l.GetSlot(); writer.WriteAttributeString("il_index", CultureInvariantToString(slot)); bool reusingSlot = false; // collect slot names so that we can verify ISymUnmanagedReader APIs if (slotNames != null) { ImmutableArray<string> existingNames; if (slotNames.TryGetValue(slot, out existingNames)) { slotNames[slot] = existingNames.Add(l.GetName()); reusingSlot = true; } else { slotNames.Add(slot, ImmutableArray.Create(l.GetName())); } } // Provide scope range writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset())); writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset())); writer.WriteAttributeString("attributes", l.GetAttributes().ToString()); if (reusingSlot) { writer.WriteAttributeString("reusingslot", reusingSlot.ToString(CultureInfo.InvariantCulture)); } } writer.WriteEndElement(); // </local> } foreach (ISymUnmanagedConstant c in scope.GetConstants()) { // Note: We can retrieve constant tokens by saving it into signature blob // in our implementation of IMetadataImport.GetSigFromToken. writer.WriteStartElement("constant"); { writer.WriteAttributeString("name", c.GetName()); object value = c.GetValue(); string typeName = value.GetType().Name; // certain Unicode characters will give Xml writers fits...in order to avoid this, we'll replace // problematic characters/sequences with their hexadecimal equivalents, like U+0000, etc... var chars = value as string; if (chars != null) { PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); var valueWithPlaceholders = pooled.Builder; foreach (var ch in chars) { // if we end up with more, we can add them here if (0 == (int)ch) { valueWithPlaceholders.AppendFormat("U+{0:X4}", (int)ch); } else { valueWithPlaceholders.Append(ch); } } if (valueWithPlaceholders.Length > chars.Length) { value = valueWithPlaceholders.ToString(); } pooled.Free(); } writer.WriteAttributeString("value", value.ToString()); writer.WriteAttributeString("type", typeName); } writer.WriteEndElement(); // </constant> } if (includeChildScopes) { foreach (ISymUnmanagedScope childScope in scope.GetScopes()) { WriteLocalsHelper(childScope, slotNames, includeChildScopes); } } }