diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.Native.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.Native.cs
index 800c6d401b9da648fd880dd59f28251ca37c2132..be94d1fd731da59310e85dfe0daeabf93fb4336d 100644
--- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.Native.cs
+++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/PDB/MethodDebugInfo.Native.cs
@@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
+using Microsoft.CodeAnalysis.Collections;
using Microsoft.DiaSymReader;
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
@@ -264,7 +265,7 @@ private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider
.SelectAsArray(s => new HoistedLocalScopeRecord(s.StartOffset, s.EndOffset - s.StartOffset + 1));
}
- CustomDebugInfoReader.GetCSharpDynamicLocalInfo(
+ GetCSharpDynamicLocalInfo(
customDebugInfoBytes,
methodToken,
methodVersion,
@@ -273,6 +274,128 @@ private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider
out dynamicLocalConstantMap);
}
+ /// Bad data.
+ private static void GetCSharpDynamicLocalInfo(
+ byte[] customDebugInfo,
+ int methodToken,
+ int methodVersion,
+ IEnumerable scopes,
+ out ImmutableDictionary> dynamicLocalMap,
+ out ImmutableDictionary> dynamicLocalConstantMap)
+ {
+ dynamicLocalMap = ImmutableDictionary>.Empty;
+ dynamicLocalConstantMap = ImmutableDictionary>.Empty;
+
+ var record = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.DynamicLocals);
+ if (record.IsDefault)
+ {
+ return;
+ }
+
+ ImmutableDictionary>.Builder localBuilder = null;
+ ImmutableDictionary>.Builder constantBuilder = null;
+
+ var dynamicLocals = RemoveAmbiguousLocals(CustomDebugInfoReader.DecodeDynamicLocalsRecord(record), scopes);
+ foreach (var dynamicLocal in dynamicLocals)
+ {
+ int slot = dynamicLocal.SlotId;
+ var flags = GetFlags(dynamicLocal);
+ if (slot < 0)
+ {
+ constantBuilder = constantBuilder ?? ImmutableDictionary.CreateBuilder>();
+ constantBuilder[dynamicLocal.Name] = flags;
+ }
+ else
+ {
+ localBuilder = localBuilder ?? ImmutableDictionary.CreateBuilder>();
+ localBuilder[slot] = flags;
+ }
+ }
+
+ if (localBuilder != null)
+ {
+ dynamicLocalMap = localBuilder.ToImmutable();
+ }
+
+ if (constantBuilder != null)
+ {
+ dynamicLocalConstantMap = constantBuilder.ToImmutable();
+ }
+ }
+
+ private static ImmutableArray GetFlags(DynamicLocalInfo bucket)
+ {
+ int flagCount = bucket.FlagCount;
+ ulong flags = bucket.Flags;
+ var builder = ArrayBuilder.GetInstance(flagCount);
+ for (int i = 0; i < flagCount; i++)
+ {
+ builder.Add((flags & (1u << i)) != 0);
+ }
+ return builder.ToImmutableAndFree();
+ }
+
+ ///
+ /// Dynamic CDI encodes slot id and name for each dynamic local variable, but only name for a constant.
+ /// Constants have slot id set to 0. As a result there is a potential for ambiguity. If a variable in a slot 0
+ /// and a constant defined anywhere in the method body have the same name we can't say which one
+ /// the dynamic flags belong to (if there is a dynamic record for at least one of them).
+ ///
+ /// This method removes ambiguous dynamic records.
+ ///
+ private static ImmutableArray RemoveAmbiguousLocals(
+ ImmutableArray dynamicLocals,
+ IEnumerable scopes)
+ {
+ const byte DuplicateName = 0;
+ const byte VariableName = 1;
+ const byte ConstantName = 2;
+
+ var localNames = PooledDictionary.GetInstance();
+
+ var firstLocal = scopes.SelectMany(scope => scope.GetLocals()).FirstOrDefault(variable => variable.GetSlot() == 0);
+ if (firstLocal != null)
+ {
+ localNames.Add(firstLocal.GetName(), VariableName);
+ }
+
+ foreach (var scope in scopes)
+ {
+ foreach (var constant in scope.GetConstants())
+ {
+ string name = constant.GetName();
+ localNames[name] = localNames.ContainsKey(name) ? DuplicateName : ConstantName;
+ }
+ }
+
+ var builder = ArrayBuilder.GetInstance();
+ foreach (var dynamicLocal in dynamicLocals)
+ {
+ int slot = dynamicLocal.SlotId;
+ var name = dynamicLocal.Name;
+ if (slot == 0)
+ {
+ byte localOrConstant;
+ localNames.TryGetValue(name, out localOrConstant);
+ if (localOrConstant == DuplicateName)
+ {
+ continue;
+ }
+
+ if (localOrConstant == ConstantName)
+ {
+ slot = -1;
+ }
+ }
+
+ builder.Add(new DynamicLocalInfo(dynamicLocal.FlagCount, dynamicLocal.Flags, slot, name));
+ }
+
+ var result = builder.ToImmutableAndFree();
+ localNames.Free();
+ return result;
+ }
+
private static void ReadVisualBasicImportsDebugInfo(
ISymUnmanagedReader reader,
int methodToken,
diff --git a/src/Test/PdbUtilities/Pdb/PdbToXml.cs b/src/Test/PdbUtilities/Pdb/PdbToXml.cs
index 43c63d760436a97f90d1edc24fc44bc4174325ef..f4c6692e4a254e39178a21d9d8c5bdef5c400bbc 100644
--- a/src/Test/PdbUtilities/Pdb/PdbToXml.cs
+++ b/src/Test/PdbUtilities/Pdb/PdbToXml.cs
@@ -453,12 +453,12 @@ private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
_writer.WriteStartElement("dynamicLocals");
- var buckets = CDI.DecodeDynamicLocalsRecord(record.Data);
+ var dynamicLocals = CDI.DecodeDynamicLocalsRecord(record.Data);
- foreach (DynamicLocalBucket bucket in buckets)
+ foreach (DynamicLocalInfo dynamicLocal in dynamicLocals)
{
- ulong flags = bucket.Flags;
- int flagCount = bucket.FlagCount;
+ ulong flags = dynamicLocal.Flags;
+ int flagCount = dynamicLocal.FlagCount;
PooledStringBuilder pooled = PooledStringBuilder.GetInstance();
StringBuilder flagsBuilder = pooled.Builder;
@@ -470,8 +470,8 @@ private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
_writer.WriteStartElement("bucket");
_writer.WriteAttributeString("flagCount", CultureInvariantToString(flagCount));
_writer.WriteAttributeString("flags", pooled.ToStringAndFree());
- _writer.WriteAttributeString("slotId", CultureInvariantToString(bucket.SlotId));
- _writer.WriteAttributeString("localName", bucket.Name);
+ _writer.WriteAttributeString("slotId", CultureInvariantToString(dynamicLocal.SlotId));
+ _writer.WriteAttributeString("localName", dynamicLocal.Name);
_writer.WriteEndElement(); //bucket
}
diff --git a/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs b/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs
index 69bbaf9a3e3e8dadf8dc76844c54eae95eacea7d..bcc402d147304e3eb9f89d9fb1ff5ee824a7f451 100644
--- a/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs
+++ b/src/Test/PdbUtilities/Shared/CustomDebugInfoReader.cs
@@ -226,12 +226,12 @@ public static string DecodeForwardIteratorRecord(ImmutableArray bytes)
/// Exposed for .
///
/// Bad data.
- public static ImmutableArray DecodeDynamicLocalsRecord(ImmutableArray bytes)
+ public static ImmutableArray DecodeDynamicLocalsRecord(ImmutableArray bytes)
{
int offset = 0;
int bucketCount = ReadInt32(bytes, ref offset);
- var builder = ArrayBuilder.GetInstance(bucketCount);
+ var builder = ArrayBuilder.GetInstance(bucketCount);
for (int i = 0; i < bucketCount; i++)
{
const int FlagBytesCount = 64;
@@ -269,8 +269,7 @@ public static ImmutableArray DecodeDynamicLocalsRecord(Immut
var name = pooled.ToStringAndFree();
- var bucket = new DynamicLocalBucket(flagCount, flags, slotId, name);
- builder.Add(bucket);
+ builder.Add(new DynamicLocalInfo(flagCount, flags, slotId, name));
}
return builder.ToImmutableAndFree();
@@ -475,140 +474,6 @@ public static ImmutableArray GetVisualBasicImportStrings(this ISymUnmana
return importStrings;
}
- /// Bad data.
- public static void GetCSharpDynamicLocalInfo(
- byte[] customDebugInfo,
- int methodToken,
- int methodVersion,
- IEnumerable scopes,
- out ImmutableDictionary> dynamicLocalMap,
- out ImmutableDictionary> dynamicLocalConstantMap)
- {
- dynamicLocalMap = ImmutableDictionary>.Empty;
- dynamicLocalConstantMap = ImmutableDictionary>.Empty;
-
- var record = TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.DynamicLocals);
- if (record.IsDefault)
- {
- return;
- }
-
- ImmutableDictionary>.Builder localBuilder = null;
- ImmutableDictionary>.Builder constantBuilder = null;
-
- var buckets = RemoveAmbiguousLocals(DecodeDynamicLocalsRecord(record), scopes);
- foreach (var bucket in buckets)
- {
- var slot = bucket.SlotId;
- var flags = GetFlags(bucket);
- if (slot < 0)
- {
- constantBuilder = constantBuilder ?? ImmutableDictionary.CreateBuilder>();
- constantBuilder[bucket.Name] = flags;
- }
- else
- {
- localBuilder = localBuilder ?? ImmutableDictionary.CreateBuilder>();
- localBuilder[slot] = flags;
- }
- }
-
- if (localBuilder != null)
- {
- dynamicLocalMap = localBuilder.ToImmutable();
- }
-
- if (constantBuilder != null)
- {
- dynamicLocalConstantMap = constantBuilder.ToImmutable();
- }
- }
-
- ///
- /// If there are dynamic locals or constants associated with SlotId == 0, check all locals and
- /// constants with SlotId == 0 for duplicate names and discard duplicates since we
- /// cannot determine which local or constant the dynamic info is associated with.
- ///
- private static ImmutableArray RemoveAmbiguousLocals(
- ImmutableArray locals,
- IEnumerable scopes)
- {
- const byte DuplicateName = 0;
- const byte VariableName = 1;
- const byte ConstantName = 2;
-
- var localNames = PooledDictionary.GetInstance();
- var firstLocal = GetFirstLocal(scopes);
- if (firstLocal != null)
- {
- localNames.Add(firstLocal.GetName(), VariableName);
- }
-
- foreach (var scope in scopes)
- {
- foreach (var constant in scope.GetConstants())
- {
- var name = constant.GetName();
- localNames[name] = localNames.ContainsKey(name) ? DuplicateName : ConstantName;
- }
- }
-
- var builder = ArrayBuilder.GetInstance();
- foreach (var local in locals)
- {
- int slot = local.SlotId;
- var name = local.Name;
- if (slot == 0)
- {
- byte localOrConstant;
- localNames.TryGetValue(name, out localOrConstant);
- if (localOrConstant == DuplicateName)
- {
- continue;
- }
-
- if (localOrConstant == ConstantName)
- {
- slot = -1;
- }
- }
-
- builder.Add(new DynamicLocalBucket(local.FlagCount, local.Flags, slot, name));
- }
-
- var result = builder.ToImmutableAndFree();
- localNames.Free();
- return result;
- }
-
- private static ISymUnmanagedVariable GetFirstLocal(IEnumerable scopes)
- {
- foreach (var scope in scopes)
- {
- foreach (var local in scope.GetLocals())
- {
- if (local.GetSlot() == 0)
- {
- return local;
- }
- }
- }
-
- return null;
- }
-
- private static ImmutableArray GetFlags(DynamicLocalBucket bucket)
- {
- int flagCount = bucket.FlagCount;
- ulong flags = bucket.Flags;
- var builder = ArrayBuilder.GetInstance(flagCount);
- for (int i = 0; i < flagCount; i++)
- {
- builder.Add((flags & (1u << i)) != 0);
- }
- return builder.ToImmutableAndFree();
- }
-
private static void CheckVersion(byte globalVersion, int methodToken)
{
if (globalVersion != CDI.CdiVersion)
@@ -1032,14 +897,14 @@ public StateMachineHoistedLocalScope(int startoffset, int endOffset)
}
}
- internal struct DynamicLocalBucket
+ internal struct DynamicLocalInfo
{
public readonly int FlagCount;
public readonly ulong Flags;
public readonly int SlotId;
public readonly string Name;
- public DynamicLocalBucket(int flagCount, ulong flags, int slotId, string name)
+ public DynamicLocalInfo(int flagCount, ulong flags, int slotId, string name)
{
this.FlagCount = flagCount;
this.Flags = flags;