private List <DebugLocalVariableInfo> GetLocalVariablesInScope(ISymUnmanagedScope symScope) { List <DebugLocalVariableInfo> vars = new List <DebugLocalVariableInfo>(); foreach (ISymUnmanagedVariable symVar in symScope.GetLocals()) { ISymUnmanagedVariable symVarCopy = symVar; int start; SignatureReader sigReader = new SignatureReader(symVar.GetSignature()); LocalVarSig.LocalVariable locVarSig = sigReader.ReadLocalVariable(sigReader.Blob, 0, out start); DebugType locVarType = DebugType.CreateFromSignature(this.DebugModule, locVarSig.Type, declaringType); // Compiler generated? // NB: Display class does not have the compiler-generated flag if ((symVar.GetAttributes() & 1) == 1 || symVar.GetName().StartsWith("CS$")) { // Get display class from local variable if (locVarType.IsDisplayClass) { AddCapturedLocalVariables( vars, (int)symScope.GetStartOffset(), (int)symScope.GetEndOffset(), delegate(StackFrame context) { return(GetLocalVariableValue(context, symVarCopy)); }, locVarType ); } } else { DebugLocalVariableInfo locVar = new DebugLocalVariableInfo( symVar.GetName(), (int)symVar.GetAddressField1(), // symVar also has Get*Offset methods, but the are not implemented (int)symScope.GetStartOffset(), (int)symScope.GetEndOffset(), locVarType, delegate(StackFrame context) { return(GetLocalVariableValue(context, symVarCopy)); } ); vars.Add(locVar); } } foreach (ISymUnmanagedScope childScope in symScope.GetChildren()) { vars.AddRange(GetLocalVariablesInScope(childScope)); } return(vars); }
public static int GetStartOffset(this ISymUnmanagedScope scope) { int startOffset; scope.GetStartOffset(out startOffset); return(startOffset); }
public static int GetStartOffset(this ISymUnmanagedScope scope) { int startOffset; int hr = scope.GetStartOffset(out startOffset); ThrowExceptionForHR(hr); return(startOffset); }
public static void ValidateRange(ISymUnmanagedScope scope, int expectedStartOffset, int expectedLength) { int actualOffset; Assert.Equal(HResult.S_OK, scope.GetStartOffset(out actualOffset)); Assert.Equal(expectedStartOffset, actualOffset); Assert.Equal(HResult.S_OK, scope.GetEndOffset(out actualOffset)); Assert.Equal(expectedStartOffset + expectedLength, 2); }
public static void ValidateRange(ISymUnmanagedScope scope, int expectedStartOffset, int expectedLength) { int actualOffset; Assert.Equal(HResult.S_OK, scope.GetStartOffset(out actualOffset)); Assert.Equal(expectedStartOffset, actualOffset); Assert.Equal(HResult.S_OK, scope.GetEndOffset(out actualOffset)); Assert.Equal(expectedStartOffset + expectedLength, 2); }
public static int GetStartOffset(this ISymUnmanagedScope scope) { if (scope == null) { throw new ArgumentNullException(nameof(scope)); } int startOffset; ThrowExceptionForHR(scope.GetStartOffset(out startOffset)); return(startOffset); }
private static bool IsInScope(ISymUnmanagedScope scope, int offset, bool isEndInclusive) { int startOffset = scope.GetStartOffset(); if (offset < startOffset) { return false; } int endOffset = scope.GetEndOffset(); // In PDBs emitted by VB the end offset is inclusive, // in PDBs emitted by C# the end offset is exclusive. return isEndInclusive ? offset <= endOffset : offset < endOffset; }
private static bool IsInScope(this ISymUnmanagedScope scope, int offset) { int startOffset; scope.GetStartOffset(out startOffset); if (offset < startOffset) { return(false); } int endOffset; scope.GetEndOffset(out endOffset); return(offset <= endOffset); }
/// <summary> /// Get definitions of local variables in scope. /// </summary> /// <param name="scope">Interface refering to desired scope.</param> /// <param name="index">Index pointing to the last read variable.</param> /// <param name="definitions">List of definitions, where found variable definition are placed.</param> private void getDefinitionsForScope(ISymUnmanagedScope scope, ref int index, List <LocalVarDefinition> definitions) { ISymUnmanagedVariable[] variables = scope.GetLocals(); foreach (ISymUnmanagedVariable var in variables) { LocalVarDefinition def = new LocalVarDefinition(); def.ilStart = scope.GetStartOffset(); def.ilEnd = scope.GetEndOffset(); def.name = var.GetName(); def.signature = var.GetSignature(); definitions.Add(def); ++index; } foreach (ISymUnmanagedScope cScope in scope.GetChildren()) { getDefinitionsForScope(cScope, ref index, definitions); } }
internal static bool IsInScope(this ISymUnmanagedScope scope, int offset, bool isEndInclusive) { int startOffset; scope.GetStartOffset(out startOffset); if (offset < startOffset) { return(false); } int endOffset; scope.GetEndOffset(out endOffset); // In PDBs emitted by VB the end offset is inclusive, // in PDBs emitted by C# the end offset is exclusive. return(isEndInclusive ? offset <= endOffset : offset < endOffset); }
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> }
public IEnumerable <ILLocalVariable> GetLocalVariables(IMethod method) { List <ILLocalVariable> vars = new List <ILLocalVariable>(); var symMethod = method.GetSymMethod(); if (symMethod == null) { return(vars); } Stack <ISymUnmanagedScope> scopes = new Stack <ISymUnmanagedScope>(); scopes.Push(symMethod.GetRootScope()); while (scopes.Count > 0) { ISymUnmanagedScope scope = scopes.Pop(); foreach (ISymUnmanagedVariable symVar in scope.GetLocals()) { int index = (int)symVar.GetAddressField1(); vars.Add(new ILLocalVariable() { Index = index, Type = method.GetLocalVariableType(index), Name = symVar.GetName(), IsCompilerGenerated = (symVar.GetAttributes() & 1) == 1, // symVar also has Get*Offset methods, but the are not implemented ILRanges = new [] { new ILRange() { From = (int)scope.GetStartOffset(), To = (int)scope.GetEndOffset() } } }); } foreach (ISymUnmanagedScope childScope in scope.GetChildren()) { scopes.Push(childScope); } } return(vars); }
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> }
List<DebugLocalVariableInfo> GetLocalVariablesInScope(ISymUnmanagedScope symScope) { List<DebugLocalVariableInfo> vars = new List<DebugLocalVariableInfo>(); foreach (ISymUnmanagedVariable symVar in symScope.GetLocals()) { ISymUnmanagedVariable symVarCopy = symVar; var locVarSig = new DebugSignatureReader().ReadTypeSignature(symVar.GetSignature()); DebugType locVarType = DebugType.CreateFromSignature(this.DebugModule, locVarSig, declaringType); // Compiler generated? // NB: Display class does not have the compiler-generated flag if ((symVar.GetAttributes() & 1) == 1 || symVar.GetName().StartsWith("CS$")) { // Get display class from local variable if (locVarType.IsDisplayClass) { AddCapturedLocalVariables( vars, (int)symScope.GetStartOffset(), (int)symScope.GetEndOffset(), delegate(StackFrame context) { return GetLocalVariableValue(context, symVarCopy); }, locVarType ); } } else { DebugLocalVariableInfo locVar = new DebugLocalVariableInfo( symVar.GetName(), (int)symVar.GetAddressField1(), // symVar also has Get*Offset methods, but the are not implemented (int)symScope.GetStartOffset(), (int)symScope.GetEndOffset(), locVarType, delegate(StackFrame context) { return GetLocalVariableValue(context, symVarCopy); } ); vars.Add(locVar); } } foreach(ISymUnmanagedScope childScope in symScope.GetChildren()) { vars.AddRange(GetLocalVariablesInScope(childScope)); } return vars; }
private void WriteLocals(ISymUnmanagedScope scope) { foreach (ISymUnmanagedVariable l in scope.GetLocals()) { _writer.WriteStartElement("local"); _writer.WriteAttributeString("name", l.GetName()); // 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. _writer.WriteAttributeString("il_index", CultureInvariantToString(l.GetSlot())); _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset())); _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset())); _writer.WriteAttributeString("attributes", CultureInvariantToString(l.GetAttributes())); _writer.WriteEndElement(); } foreach (ISymUnmanagedConstant constant in scope.GetConstants()) { string name = constant.GetName(); var signature = constant.GetSignature(); object value = constant.GetValue(); _writer.WriteStartElement("constant"); _writer.WriteAttributeString("name", name); if (value is int && (int)value == 0 && (signature[0] == (byte)ConstantTypeCode.NullReference || signature[0] == (int)SignatureTypeCode.Object || signature[0] == (int)SignatureTypeCode.String || signature[0] == (int)SignatureTypeCode.GenericTypeInstance)) { // TODO: 0 for enums nested in a generic class, null for reference type // We need to decode the signature and see if the target type is enum. _writer.WriteAttributeString("value", "null"); if (signature[0] == (int)SignatureTypeCode.String) { _writer.WriteAttributeString("type", "String"); } else if (signature[0] == (int)SignatureTypeCode.Object) { _writer.WriteAttributeString("type", "Object"); } else { // TODO: // A null reference, the type is encoded in the signature. // Ideally we would parse the signature and display the target type name. // That requires MetadataReader vNext though. _writer.WriteAttributeString("signature", BitConverter.ToString(signature.ToArray())); } } else if (value == null) { // empty string if (signature[0] == (byte)SignatureTypeCode.String) { _writer.WriteAttributeString("value", ""); _writer.WriteAttributeString("type", "String"); } else { _writer.WriteAttributeString("value", "null"); _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray())); } } else if (value is decimal) { // TODO: check that the signature is a TypeRef _writer.WriteAttributeString("value", ((decimal)value).ToString(CultureInfo.InvariantCulture)); _writer.WriteAttributeString("type", value.GetType().Name); } else if (value is double && signature[0] != (byte)SignatureTypeCode.Double) { // TODO: check that the signature is a TypeRef _writer.WriteAttributeString("value", DateTimeUtilities.ToDateTime((double)value).ToString(CultureInfo.InvariantCulture)); _writer.WriteAttributeString("type", "DateTime"); } else { _writer.WriteAttributeString("value", (value as string)?.Replace("\0", "U+0000") ?? string.Format(CultureInfo.InvariantCulture, "{0}", value)); var runtimeType = GetConstantRuntimeType(signature); if (runtimeType == null && (value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is ulong)) { // TODO: // Enum. // Ideally we would parse the signature and display the target type name. // That requires MetadataReader vNext though. _writer.WriteAttributeString("signature", BitConverter.ToString(signature.ToArray())); } else if (runtimeType == value.GetType()) { _writer.WriteAttributeString("type", ((SignatureTypeCode)signature[0]).ToString()); } else { _writer.WriteAttributeString("runtime-type", value.GetType().Name); _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray())); } } _writer.WriteEndElement(); } }
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(); }
/// <summary> /// Get definitions of local variables in scope. /// </summary> /// <param name="scope">Interface refering to desired scope.</param> /// <param name="index">Index pointing to the last read variable.</param> /// <param name="definitions">List of definitions, where found variable definition are placed.</param> private void getDefinitionsForScope(ISymUnmanagedScope scope, ref int index, List<LocalVarDefinition> definitions) { ISymUnmanagedVariable[] variables = scope.GetLocals(); foreach (ISymUnmanagedVariable var in variables) { LocalVarDefinition def = new LocalVarDefinition(); def.ilStart = scope.GetStartOffset(); def.ilEnd = scope.GetEndOffset(); def.name = var.GetName(); def.signature = var.GetSignature(); definitions.Add(def); ++index; } foreach (ISymUnmanagedScope cScope in scope.GetChildren()) { getDefinitionsForScope(cScope, ref index, definitions); } }
private void WriteLocals(ISymUnmanagedScope scope) { foreach (ISymUnmanagedVariable l in scope.GetLocals()) { _writer.WriteStartElement("local"); _writer.WriteAttributeString("name", l.GetName()); // 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. _writer.WriteAttributeString("il_index", CultureInvariantToString(l.GetSlot())); _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset())); _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset())); _writer.WriteAttributeString("attributes", CultureInvariantToString(l.GetAttributes())); _writer.WriteEndElement(); } foreach (ISymUnmanagedConstant constant in scope.GetConstants()) { string name = constant.GetName(); var signature = constant.GetSignature(); object value = constant.GetValue(); _writer.WriteStartElement("constant"); _writer.WriteAttributeString("name", name); if (value is int && (int)value == 0 && (signature[0] == (byte)ConstantTypeCode.NullReference || signature[0] == (int)SignatureTypeCode.Object || signature[0] == (int)SignatureTypeCode.String || (signature.Length > 2 && signature[0] == (int)SignatureTypeCode.GenericTypeInstance && signature[1] == (byte)ConstantTypeCode.NullReference))) { _writer.WriteAttributeString("value", "null"); if (signature[0] == (int)SignatureTypeCode.String) { _writer.WriteAttributeString("type", "String"); } else if (signature[0] == (int)SignatureTypeCode.Object) { _writer.WriteAttributeString("type", "Object"); } else { _writer.WriteAttributeString("signature", FormatSignature(signature)); } } else if (value == null) { // empty string if (signature[0] == (byte)SignatureTypeCode.String) { _writer.WriteAttributeString("value", ""); _writer.WriteAttributeString("type", "String"); } else { _writer.WriteAttributeString("value", "null"); _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray())); } } else if (value is decimal) { // TODO: check that the signature is a TypeRef _writer.WriteAttributeString("value", ((decimal)value).ToString(CultureInfo.InvariantCulture)); _writer.WriteAttributeString("type", value.GetType().Name); } else if (value is double && signature[0] != (byte)SignatureTypeCode.Double) { // TODO: check that the signature is a TypeRef _writer.WriteAttributeString("value", DateTimeUtilities.ToDateTime((double)value).ToString(CultureInfo.InvariantCulture)); _writer.WriteAttributeString("type", "DateTime"); } else { string str = value as string; if (str != null) { _writer.WriteAttributeString("value", StringUtilities.EscapeNonPrintableCharacters(str)); } else { _writer.WriteAttributeString("value", string.Format(CultureInfo.InvariantCulture, "{0}", value)); } var runtimeType = GetConstantRuntimeType(signature); if (runtimeType == null && (value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is ulong)) { _writer.WriteAttributeString("signature", FormatSignature(signature)); } else if (runtimeType == value.GetType()) { _writer.WriteAttributeString("type", ((SignatureTypeCode)signature[0]).ToString()); } else { _writer.WriteAttributeString("runtime-type", value.GetType().Name); _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray())); } } _writer.WriteEndElement(); } }
private static void SerializeScope( MetadataBuilder metadataBuilder, MetadataModel metadataModel, MethodDefinitionHandle methodHandle, ImportScopeHandle importScopeHandle, ISymUnmanagedScope symScope, Dictionary <int, DynamicLocalInfo> dynamicSlots, Dictionary <string, DynamicLocalInfo> dynamicNames, bool vbSemantics, ref LocalVariableHandle lastLocalVariableHandle, ref LocalConstantHandle lastLocalConstantHandle) { // VB Windows PDB encode the range as end-inclusive, // all Portable PDBs use end-exclusive encoding. int start = symScope.GetStartOffset(); int end = symScope.GetEndOffset() + (vbSemantics ? 1 : 0); metadataBuilder.AddLocalScope( method: methodHandle, importScope: importScopeHandle, variableList: NextHandle(lastLocalVariableHandle), constantList: NextHandle(lastLocalConstantHandle), startOffset: start, length: end - start); foreach (var symLocal in symScope.GetLocals()) { int slot = symLocal.GetSlot(); string name = symLocal.GetName(); lastLocalVariableHandle = metadataBuilder.AddLocalVariable( attributes: (LocalVariableAttributes)symLocal.GetAttributes(), index: slot, name: metadataBuilder.GetOrAddString(name)); DynamicLocalInfo dynamicInfo; if (slot > 0 && dynamicSlots.TryGetValue(slot, out dynamicInfo) || slot == 0 && dynamicNames.TryGetValue(name, out dynamicInfo)) { metadataBuilder.AddCustomDebugInformation( parent: lastLocalVariableHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.DynamicLocalVariables), value: SerializeDynamicLocalBlob(metadataBuilder, dynamicInfo)); } } foreach (var symConstant in symScope.GetConstants()) { string name = symConstant.GetName(); object value = symConstant.GetValue(); lastLocalConstantHandle = metadataBuilder.AddLocalConstant( name: metadataBuilder.GetOrAddString(name), signature: SerializeConstantSignature(metadataBuilder, metadataModel, symConstant.GetSignature(), value)); DynamicLocalInfo dynamicInfo; if (dynamicNames.TryGetValue(name, out dynamicInfo)) { metadataBuilder.AddCustomDebugInformation( parent: lastLocalConstantHandle, kind: metadataBuilder.GetOrAddGuid(PortableCustomDebugInfoKinds.DynamicLocalVariables), value: SerializeDynamicLocalBlob(metadataBuilder, dynamicInfo)); } } int previousChildScopeEnd = start; foreach (ISymUnmanagedScope child in symScope.GetChildren()) { int childScopeStart = child.GetStartOffset(); int childScopeEnd = child.GetEndOffset(); // scopes are properly nested: if (childScopeStart < previousChildScopeEnd || childScopeEnd > end) { // TODO: loc/warning throw new BadImageFormatException($"Invalid scope IL offset range: [{childScopeStart}, {childScopeEnd}), method 0x{MetadataTokens.GetToken(methodHandle):x}."); } previousChildScopeEnd = childScopeEnd; SerializeScope(metadataBuilder, metadataModel, methodHandle, importScopeHandle, child, dynamicSlots, dynamicNames, vbSemantics, ref lastLocalVariableHandle, ref lastLocalConstantHandle); } }
public int GetStartOffset(out int pRetVal) { return(_scope.GetStartOffset(out pRetVal)); }
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 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); } } }