提交 505bac3d 编写于 作者: C Charles Stoner

Support tuple element names in locals and expressions in the EE

Requires emitting tuple element names for locals to the PDB as custom debug information, similar to support for 'dynamic' in C# for the EE.
上级 330bca57
......@@ -2538,13 +2538,14 @@ internal static TypeSymbol MergeDynamic(TypeSymbol first, TypeSymbol second, Typ
/// </summary>
internal static TypeSymbol MergeTupleNames(TypeSymbol first, TypeSymbol second, TypeSymbol target, AssemblySymbol corLibrary)
{
if (!target.ContainsTuple() ||
first.Equals(second, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreDynamic) ||
if (first.Equals(second, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreDynamic) ||
!target.ContainsTupleNames())
{
return target;
}
Debug.Assert(target.ContainsTuple());
ImmutableArray<string> names1 = CSharpCompilation.TupleNamesEncoder.Encode(first);
ImmutableArray<string> names2 = CSharpCompilation.TupleNamesEncoder.Encode(second);
......
......@@ -158,8 +158,8 @@ private LocalDefinition LazyReturnTemp
id: new LocalDebugId(syntaxOffset, ordinal: 0),
pdbAttributes: localSymbol.SynthesizedKind.PdbAttributes(),
constraints: slotConstraints,
isDynamic: false,
dynamicTransformFlags: ImmutableArray<TypedConstant>.Empty,
tupleElementNames: ImmutableArray<TypedConstant>.Empty,
isSlotReusable: false);
}
else
......
......@@ -1389,20 +1389,23 @@ private LocalDefinition GetLocal(LocalSymbol symbol)
private LocalDefinition DefineLocal(LocalSymbol local, SyntaxNode syntaxNode)
{
var transformFlags = default(ImmutableArray<TypedConstant>);
bool hasDynamic = local.Type.ContainsDynamic();
var isDynamicSourceLocal = hasDynamic && !local.IsCompilerGenerated;
if (isDynamicSourceLocal)
{
NamedTypeSymbol booleanType = _module.Compilation.GetSpecialType(SpecialType.System_Boolean);
transformFlags = CSharpCompilation.DynamicTransformsEncoder.Encode(local.Type, booleanType, 0, RefKind.None);
}
var dynamicTransformFlags = !local.IsCompilerGenerated && local.Type.ContainsDynamic() ?
CSharpCompilation.DynamicTransformsEncoder.Encode(local.Type, _module.Compilation.GetSpecialType(SpecialType.System_Boolean), 0, RefKind.None) :
ImmutableArray<TypedConstant>.Empty;
var tupleElementNames = !local.IsCompilerGenerated && local.Type.ContainsTupleNames() ?
CSharpCompilation.TupleNamesEncoder.Encode(local.Type, _module.Compilation.GetSpecialType(SpecialType.System_String)) :
ImmutableArray<TypedConstant>.Empty;
if (local.IsConst)
{
Debug.Assert(local.HasConstantValue);
MetadataConstant compileTimeValue = _module.CreateConstant(local.Type, local.ConstantValue, syntaxNode, _diagnostics);
LocalConstantDefinition localConstantDef = new LocalConstantDefinition(local.Name, local.Locations.FirstOrDefault() ?? Location.None, compileTimeValue, isDynamicSourceLocal, transformFlags);
LocalConstantDefinition localConstantDef = new LocalConstantDefinition(
local.Name,
local.Locations.FirstOrDefault() ?? Location.None,
compileTimeValue,
dynamicTransformFlags: dynamicTransformFlags,
tupleElementNames: tupleElementNames);
_builder.AddLocalConstantToScope(localConstantDef);
return null;
}
......@@ -1452,8 +1455,8 @@ private LocalDefinition DefineLocal(LocalSymbol local, SyntaxNode syntaxNode)
id: localId,
pdbAttributes: local.SynthesizedKind.PdbAttributes(),
constraints: constraints,
isDynamic: isDynamicSourceLocal,
dynamicTransformFlags: transformFlags,
dynamicTransformFlags: dynamicTransformFlags,
tupleElementNames: tupleElementNames,
isSlotReusable: local.SynthesizedKind.IsSlotReusable(_ilEmitStyle != ILEmitStyle.Release));
// If named, add it to the local debug scope.
......
......@@ -546,10 +546,9 @@ public static ImmutableArray<TypedConstant> Encode(TypeSymbol type, TypeSymbol s
return names;
}
private static bool TryGetNames(TypeSymbol type, ArrayBuilder<string> namesBuilder)
internal static bool TryGetNames(TypeSymbol type, ArrayBuilder<string> namesBuilder)
{
type.VisitType((t, builder, _ignore) => AddNames(t, builder), namesBuilder);
Debug.Assert(namesBuilder.Any());
return namesBuilder.Any(name => name != null);
}
......@@ -585,7 +584,7 @@ internal static class DynamicTransformsEncoder
internal static ImmutableArray<TypedConstant> Encode(TypeSymbol type, TypeSymbol booleanType, int customModifiersCount, RefKind refKind)
{
var flagsBuilder = ArrayBuilder<bool>.GetInstance();
EncodeInternal(type, customModifiersCount, refKind, flagsBuilder, addCustomModifierFlags: true);
Encode(type, customModifiersCount, refKind, flagsBuilder, addCustomModifierFlags: true);
Debug.Assert(flagsBuilder.Any());
Debug.Assert(flagsBuilder.Contains(true));
......@@ -599,21 +598,14 @@ internal static ImmutableArray<TypedConstant> Encode(TypeSymbol type, TypeSymbol
return constantsBuilder.ToImmutableAndFree();
}
internal static ImmutableArray<bool> Encode(TypeSymbol type, int customModifiersCount, RefKind refKind)
{
var transformFlagsBuilder = ArrayBuilder<bool>.GetInstance();
EncodeInternal(type, customModifiersCount, refKind, transformFlagsBuilder, addCustomModifierFlags: true);
return transformFlagsBuilder.ToImmutableAndFree();
}
internal static ImmutableArray<bool> EncodeWithoutCustomModifierFlags(TypeSymbol type, RefKind refKind)
{
var transformFlagsBuilder = ArrayBuilder<bool>.GetInstance();
EncodeInternal(type, -1, refKind, transformFlagsBuilder, addCustomModifierFlags: false);
Encode(type, -1, refKind, transformFlagsBuilder, addCustomModifierFlags: false);
return transformFlagsBuilder.ToImmutableAndFree();
}
private static void EncodeInternal(TypeSymbol type, int customModifiersCount, RefKind refKind, ArrayBuilder<bool> transformFlagsBuilder, bool addCustomModifierFlags)
internal static void Encode(TypeSymbol type, int customModifiersCount, RefKind refKind, ArrayBuilder<bool> transformFlagsBuilder, bool addCustomModifierFlags)
{
Debug.Assert(!transformFlagsBuilder.Any());
......
......@@ -157,6 +157,7 @@
<Compile Include="PDB\PDBIteratorTests.cs" />
<Compile Include="PDB\PDBLambdaTests.cs" />
<Compile Include="PDB\PDBTests.cs" />
<Compile Include="PDB\PDBTupleTests.cs" />
<Compile Include="PDB\PDBUsingTests.cs" />
<Compile Include="PDB\PDBWinMdExpTests.cs" />
<Compile Include="PDB\PortablePdbTests.cs" />
......@@ -191,4 +192,4 @@
</ItemGroup>
<Import Project="..\..\..\..\..\build\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\..\..\build\Targets\Roslyn.Toolsets.Xunit.targets" />
</Project>
</Project>
\ No newline at end of file
......@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests
public class LocalSlotMappingTests : EditAndContinueTestBase
{
/// <summary>
/// If no changes were made we don't product a syntax map.
/// If no changes were made we don't produce a syntax map.
/// If we don't have syntax map and preserve variables is true we should still successfully map the locals to their previous slots.
/// </summary>
[Fact]
......@@ -1476,6 +1476,96 @@ static void M(object o)
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, method0, method1, GetEquivalentNodesMap(method1, method0), preserveLocalVariables: true)));
}
[Fact]
public void ForEachWithDynamicAndTuple()
{
var source =
@"class C
{
static void M((dynamic, int) t)
{
foreach (var o in t.Item1)
{
}
}
}";
var compilation0 = CreateCompilationWithMscorlib(
source,
options: TestOptions.DebugDll,
references: new[] { SystemCoreRef, CSharpRef, ValueTupleRef });
var compilation1 = compilation0.WithSource(source);
var testData0 = new CompilationTestData();
var bytes0 = compilation0.EmitToArray(testData: testData0);
var methodData0 = testData0.GetMethodData("C.M");
var method0 = compilation0.GetMember<MethodSymbol>("C.M");
var generation0 = EmitBaseline.CreateInitialBaseline(
ModuleMetadata.CreateFromImage(bytes0),
methodData0.EncDebugInfoProvider());
var method1 = compilation1.GetMember<MethodSymbol>("C.M");
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, method0, method1, GetEquivalentNodesMap(method1, method0), preserveLocalVariables: true)));
diff1.VerifyIL("C.M",
@"{
// Code size 119 (0x77)
.maxstack 3
.locals init (System.Collections.IEnumerator V_0,
object V_1, //o
[unchanged] V_2,
System.IDisposable V_3)
IL_0000: nop
IL_0001: nop
IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> C.<>o__0#1.<>p__0""
IL_0007: brfalse.s IL_000b
IL_0009: br.s IL_002f
IL_000b: ldc.i4.0
IL_000c: ldtoken ""System.Collections.IEnumerable""
IL_0011: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0016: ldtoken ""C""
IL_001b: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_0020: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)""
IL_0025: call ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_002a: stsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> C.<>o__0#1.<>p__0""
IL_002f: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> C.<>o__0#1.<>p__0""
IL_0034: ldfld ""System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>>.Target""
IL_0039: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> C.<>o__0#1.<>p__0""
IL_003e: ldarg.0
IL_003f: ldfld ""dynamic System.ValueTuple<dynamic, int>.Item1""
IL_0044: callvirt ""System.Collections.IEnumerable System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)""
IL_0049: callvirt ""System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()""
IL_004e: stloc.0
.try
{
IL_004f: br.s IL_005a
IL_0051: ldloc.0
IL_0052: callvirt ""object System.Collections.IEnumerator.Current.get""
IL_0057: stloc.1
IL_0058: nop
IL_0059: nop
IL_005a: ldloc.0
IL_005b: callvirt ""bool System.Collections.IEnumerator.MoveNext()""
IL_0060: brtrue.s IL_0051
IL_0062: leave.s IL_0076
}
finally
{
IL_0064: ldloc.0
IL_0065: isinst ""System.IDisposable""
IL_006a: stloc.3
IL_006b: ldloc.3
IL_006c: brfalse.s IL_0075
IL_006e: ldloc.3
IL_006f: callvirt ""void System.IDisposable.Dispose()""
IL_0074: nop
IL_0075: endfinally
}
IL_0076: ret
}");
}
[Fact]
public void AddAndDelete()
{
......
......@@ -313,9 +313,7 @@ public static void Main(string[] args)
<namespace usingCount=""0"" />
</using>
<dynamicLocals>
<bucket flagCount=""0"" flags="""" slotId=""0"" localName="""" />
<bucket flagCount=""1"" flags=""1"" slotId=""1"" localName=""d12345678901234567890123456789012345678901234567890123456789012"" />
<bucket flagCount=""0"" flags="""" slotId=""0"" localName="""" />
<bucket flagCount=""1"" flags=""1"" slotId=""0"" localName=""b12345678901234567890123456789012345678901234567890123456789012"" />
</dynamicLocals>
<encLocalSlotMap>
......@@ -1760,9 +1758,6 @@ class F<T,V>
<using>
<namespace usingCount=""2"" />
</using>
<dynamicLocals>
<bucket flagCount=""0"" flags="""" slotId=""0"" localName=""zzz"" />
</dynamicLocals>
<encLocalSlotMap>
<slot kind=""0"" offset=""372"" />
</encLocalSlotMap>
......@@ -1815,11 +1810,6 @@ class F<T,V>
<using>
<namespace usingCount=""2"" />
</using>
<dynamicLocals>
<bucket flagCount=""0"" flags="""" slotId=""0"" localName=""z1"" />
<bucket flagCount=""0"" flags="""" slotId=""2"" localName=""z2"" />
<bucket flagCount=""0"" flags="""" slotId=""3"" localName=""z3"" />
</dynamicLocals>
<encLocalSlotMap>
<slot kind=""0"" offset=""372"" />
<slot kind=""0"" offset=""389"" />
......@@ -1882,10 +1872,8 @@ class F<T,V>
<namespace usingCount=""2"" />
</using>
<dynamicLocals>
<bucket flagCount=""0"" flags="""" slotId=""0"" localName=""z3"" />
<bucket flagCount=""1"" flags=""1"" slotId=""1"" localName=""www"" />
<bucket flagCount=""1"" flags=""1"" slotId=""2"" localName=""length63length63length63length63length63length63length63length6"" />
<bucket flagCount=""0"" flags="""" slotId=""0"" localName="""" />
</dynamicLocals>
<encLocalSlotMap>
<slot kind=""0"" offset=""372"" />
......@@ -1944,7 +1932,6 @@ class F<T>
</using>
<dynamicLocals>
<bucket flagCount=""64"" flags=""0000000000000000000000000000000000000000000000000000000000000001"" slotId=""0"" localName=""yes"" />
<bucket flagCount=""0"" flags="""" slotId=""1"" localName=""no"" />
<bucket flagCount=""1"" flags=""1"" slotId=""2"" localName=""www"" />
</dynamicLocals>
<encLocalSlotMap>
......
......@@ -395,6 +395,58 @@ public static void F()
</symbols>");
}
[Fact, WorkItem(1067635, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1067635")]
public void SuppressTupleElementNamesCDIForWinRT()
{
var source =
@"class C
{
static void F()
{
(int A, int B) o = (1, 2);
}
}";
var debug = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugWinMD);
debug.VerifyPdb(
@"<symbols>
<methods>
<method containingType=""C"" name=""F"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""35"" />
<entry offset=""0x9"" startLine=""6"" startColumn=""5"" endLine=""6"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xa"">
<local name=""o"" il_index=""0"" il_start=""0x0"" il_end=""0xa"" attributes=""0"" />
</scope>
</method>
</methods>
</symbols>");
var release = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.ReleaseWinMD);
release.VerifyPdb(
@"<symbols>
<methods>
<method containingType=""C"" name=""F"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""6"" startColumn=""5"" endLine=""6"" endColumn=""6"" />
</sequencePoints>
</method>
</methods>
</symbols>");
}
[Fact]
public void DuplicateDocuments()
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.PDB
{
public class PDBTupleTests : CSharpPDBTestBase
{
[Fact]
public void Local()
{
var source =
@"class C
{
static void F()
{
(int A, int B, (int C, int), int, int, int G, int H, int I) t = (1, 2, (3, 4), 5, 6, 7, 8, 9);
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
comp.VerifyPdb(
@"<symbols>
<methods>
<method containingType=""C"" name=""F"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<tupleElementNames>
<local elementNames=""|A|B||||G|H|I|C||"" slotIndex=""0"" localName=""t"" scopeStart=""0x0"" scopeEnd=""0x0"" />
</tupleElementNames>
<encLocalSlotMap>
<slot kind=""0"" offset=""71"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""103"" />
<entry offset=""0x1b"" startLine=""6"" startColumn=""5"" endLine=""6"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x1c"">
<local name=""t"" il_index=""0"" il_start=""0x0"" il_end=""0x1c"" attributes=""0"" />
</scope>
</method>
</methods>
</symbols>");
}
[Fact]
public void Constant()
{
var source =
@"class C<T>
{
static (int, int) F;
}
class C
{
static void F()
{
const C<(int A, int B)> c = null;
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
comp.VerifyPdb(
@"<symbols>
<methods>
<method containingType=""C"" name=""F"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<tupleElementNames>
<local elementNames=""|A|B"" slotIndex=""-1"" localName=""c"" scopeStart=""0x0"" scopeEnd=""0x2"" />
</tupleElementNames>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""8"" startColumn=""5"" endLine=""8"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""10"" startColumn=""5"" endLine=""10"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x2"">
<constant name=""c"" value=""null"" signature=""C`1{System.ValueTuple`2{Int32, Int32}}"" />
</scope>
</method>
</methods>
</symbols>");
}
[Fact]
public void TuplesAndDynamic()
{
var source =
@"class C<T>
{
}
class C
{
static void F()
{
{
(dynamic A, object B, object)[] x;
const C<(object, dynamic, object C)> y = null;
}
{
const C<(object A, object)> x = null;
}
{
const C<(object, dynamic)> x = null;
const C<(object, dynamic B)> y = null;
}
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
comp.VerifyPdb(
@"<symbols>
<methods>
<method containingType=""C"" name=""F"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<dynamicLocals>
<bucket flagCount=""5"" flags=""00100"" slotId=""0"" localName=""x"" />
<bucket flagCount=""5"" flags=""00010"" slotId=""0"" localName=""y"" />
<bucket flagCount=""4"" flags=""0001"" slotId=""0"" localName=""x"" />
<bucket flagCount=""4"" flags=""0001"" slotId=""0"" localName=""y"" />
</dynamicLocals>
<tupleElementNames>
<local elementNames=""|A|B|"" slotIndex=""0"" localName=""x"" scopeStart=""0x0"" scopeEnd=""0x0"" />
<local elementNames=""|||C"" slotIndex=""-1"" localName=""y"" scopeStart=""0x1"" scopeEnd=""0x3"" />
<local elementNames=""|A|"" slotIndex=""-1"" localName=""x"" scopeStart=""0x3"" scopeEnd=""0x5"" />
<local elementNames=""||B"" slotIndex=""-1"" localName=""y"" scopeStart=""0x5"" scopeEnd=""0x7"" />
</tupleElementNames>
<encLocalSlotMap>
<slot kind=""0"" offset=""58"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""7"" startColumn=""5"" endLine=""7"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""8"" startColumn=""9"" endLine=""8"" endColumn=""10"" />
<entry offset=""0x2"" startLine=""11"" startColumn=""9"" endLine=""11"" endColumn=""10"" />
<entry offset=""0x3"" startLine=""12"" startColumn=""9"" endLine=""12"" endColumn=""10"" />
<entry offset=""0x4"" startLine=""14"" startColumn=""9"" endLine=""14"" endColumn=""10"" />
<entry offset=""0x5"" startLine=""15"" startColumn=""9"" endLine=""15"" endColumn=""10"" />
<entry offset=""0x6"" startLine=""18"" startColumn=""9"" endLine=""18"" endColumn=""10"" />
<entry offset=""0x7"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x8"">
<scope startOffset=""0x1"" endOffset=""0x3"">
<local name=""x"" il_index=""0"" il_start=""0x1"" il_end=""0x3"" attributes=""0"" />
<constant name=""y"" value=""null"" signature=""C`1{System.ValueTuple`3{Object, Object, Object}}"" />
</scope>
<scope startOffset=""0x3"" endOffset=""0x5"">
<constant name=""x"" value=""null"" signature=""C`1{System.ValueTuple`2{Object, Object}}"" />
</scope>
<scope startOffset=""0x5"" endOffset=""0x7"">
<constant name=""x"" value=""null"" signature=""C`1{System.ValueTuple`2{Object, Object}}"" />
<constant name=""y"" value=""null"" signature=""C`1{System.ValueTuple`2{Object, Object}}"" />
</scope>
</scope>
</method>
</methods>
</symbols>");
}
[Fact]
public void MultiByteCharacters()
{
var source =
@"class C
{
static void F()
{
(int \u1234, int, int \u005f\u1200\u005f) \u1200 = (1, 2, 3);
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
comp.VerifyPdb(
string.Format(@"<symbols>
<methods>
<method containingType=""C"" name=""F"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<tupleElementNames>
<local elementNames=""|{0}||{1}"" slotIndex=""0"" localName=""{2}"" scopeStart=""0x0"" scopeEnd=""0x0"" />
</tupleElementNames>
<encLocalSlotMap>
<slot kind=""0"" offset=""53"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""70"" />
<entry offset=""0xa"" startLine=""6"" startColumn=""5"" endLine=""6"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xb"">
<local name=""{2}"" il_index=""0"" il_start=""0x0"" il_end=""0xb"" attributes=""0"" />
</scope>
</method>
</methods>
</symbols>",
"\u1234",
"_\u1200_",
"\u1200"));
}
}
}
......@@ -1179,7 +1179,7 @@ internal void CloseStateMachineScope()
/// </summary>
internal void AddLocalToScope(LocalDefinition local)
{
HasDynamicLocal |= local.IsDynamic;
HasDynamicLocal |= !local.DynamicTransformFlags.IsEmpty;
_scopeManager.AddLocal(local);
}
......@@ -1188,7 +1188,7 @@ internal void AddLocalToScope(LocalDefinition local)
/// </summary>
internal void AddLocalConstantToScope(LocalConstantDefinition localConstant)
{
HasDynamicLocal |= localConstant.IsDynamic;
HasDynamicLocal |= !localConstant.DynamicTransformFlags.IsEmpty;
_scopeManager.AddLocalConstant(localConstant);
}
......
......@@ -4,7 +4,6 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Symbols;
namespace Microsoft.CodeAnalysis.CodeGen
{
......@@ -14,34 +13,30 @@ namespace Microsoft.CodeAnalysis.CodeGen
/// </summary>
internal sealed class LocalConstantDefinition : Cci.ILocalDefinition
{
private readonly string _name;
private readonly Location _location;
private readonly Cci.IMetadataConstant _compileTimeValue;
private readonly bool _isDynamic;
//Gives the synthesized dynamic attributes of the local definition
private readonly ImmutableArray<TypedConstant> _dynamicTransformFlags;
public LocalConstantDefinition(string name, Location location, Cci.IMetadataConstant compileTimeValue, bool isDynamic = false,
ImmutableArray<TypedConstant> dynamicTransformFlags = default(ImmutableArray<TypedConstant>))
public LocalConstantDefinition(
string name,
Location location,
Cci.IMetadataConstant compileTimeValue,
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames)
{
Debug.Assert(!string.IsNullOrEmpty(name));
Debug.Assert(compileTimeValue != null);
_name = name;
_location = location;
_compileTimeValue = compileTimeValue;
_isDynamic = isDynamic;
_dynamicTransformFlags = dynamicTransformFlags;
Name = name;
Location = location;
CompileTimeValue = compileTimeValue;
DynamicTransformFlags = dynamicTransformFlags.NullToEmpty();
TupleElementNames = tupleElementNames.NullToEmpty();
}
public string Name => _name;
public string Name { get; }
public Location Location => _location;
public Location Location { get; }
public Cci.IMetadataConstant CompileTimeValue => _compileTimeValue;
public Cci.IMetadataConstant CompileTimeValue { get; }
public Cci.ITypeReference Type => _compileTimeValue.Type;
public Cci.ITypeReference Type => CompileTimeValue.Type;
public bool IsConstant => true;
......@@ -56,11 +51,11 @@ public ImmutableArray<Cci.ICustomModifier> CustomModifiers
public LocalSlotConstraints Constraints => LocalSlotConstraints.None;
public bool IsDynamic => _isDynamic;
public LocalVariableAttributes PdbAttributes => LocalVariableAttributes.None;
public ImmutableArray<TypedConstant> DynamicTransformFlags => _dynamicTransformFlags;
public ImmutableArray<TypedConstant> DynamicTransformFlags { get; }
public ImmutableArray<TypedConstant> TupleElementNames { get; }
public int SlotIndex => -1;
......
......@@ -3,7 +3,6 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGen
......@@ -33,9 +32,6 @@ internal sealed class LocalDefinition : Cci.ILocalDefinition
//ordinal position of the slot in the local signature.
private readonly int _slot;
//Says if the local variable is Dynamic
private readonly bool _isDynamic;
private readonly LocalSlotDebugInfo _slotInfo;
/// <see cref="Cci.ILocalDefinition.PdbAttributes"/>.
......@@ -44,6 +40,8 @@ internal sealed class LocalDefinition : Cci.ILocalDefinition
//Gives the synthesized dynamic attributes of the local definition
private readonly ImmutableArray<TypedConstant> _dynamicTransformFlags;
private readonly ImmutableArray<TypedConstant> _tupleElementNames;
/// <summary>
/// Creates a new LocalDefinition.
/// </summary>
......@@ -51,12 +49,12 @@ internal sealed class LocalDefinition : Cci.ILocalDefinition
/// <param name="nameOpt">Name associated with the slot.</param>
/// <param name="type">Type associated with the slot.</param>
/// <param name="slot">Slot position in the signature.</param>
/// <param name="dynamicTransformFlags">Contains the synthesized dynamic attributes of the local</param>
/// <param name="synthesizedKind">Local kind.</param>
/// <param name="id">Local id.</param>
/// <param name="pdbAttributes">Value to emit in the attributes field in the PDB.</param>
/// <param name="constraints">Specifies whether slot type should have pinned modifier and whether slot should have byref constraint.</param>
/// <param name="isDynamic">Specifies if the type is Dynamic.</param>
/// <param name="dynamicTransformFlags">The synthesized dynamic attributes of the local.</param>
/// <param name="tupleElementNames">Tuple element names of the local.</param>
public LocalDefinition(
ILocalSymbol symbolOpt,
string nameOpt,
......@@ -66,8 +64,8 @@ internal sealed class LocalDefinition : Cci.ILocalDefinition
LocalDebugId id,
LocalVariableAttributes pdbAttributes,
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags)
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames)
{
_symbolOpt = symbolOpt;
_nameOpt = nameOpt;
......@@ -75,9 +73,9 @@ internal sealed class LocalDefinition : Cci.ILocalDefinition
_slot = slot;
_slotInfo = new LocalSlotDebugInfo(synthesizedKind, id);
_pdbAttributes = pdbAttributes;
_dynamicTransformFlags = dynamicTransformFlags;
_dynamicTransformFlags = dynamicTransformFlags.NullToEmpty();
_tupleElementNames = tupleElementNames.NullToEmpty();
_constraints = constraints;
_isDynamic = isDynamic;
}
internal string GetDebuggerDisplay()
......@@ -127,12 +125,12 @@ public bool IsPinned
public bool IsReference
=> (_constraints & LocalSlotConstraints.ByRef) != 0;
public bool IsDynamic => _isDynamic;
public LocalVariableAttributes PdbAttributes => _pdbAttributes;
public ImmutableArray<TypedConstant> DynamicTransformFlags => _dynamicTransformFlags;
public ImmutableArray<TypedConstant> TupleElementNames => _tupleElementNames;
public Cci.ITypeReference Type => _type;
public string Name => _nameOpt;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
using System.Reflection.Metadata;
namespace Microsoft.CodeAnalysis.CodeGen
{
......@@ -127,15 +125,15 @@ public LocalSlotManager(VariableSlotAllocator slotAllocatorOpt)
LocalDebugId id,
LocalVariableAttributes pdbAttributes,
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames,
bool isSlotReusable)
{
LocalDefinition local;
if (!isSlotReusable || !FreeSlots.TryPop(new LocalSignature(type, constraints), out local))
{
local = this.DeclareLocalImpl(type, symbol, name, kind, id, pdbAttributes, constraints, isDynamic, dynamicTransformFlags);
local = this.DeclareLocalImpl(type, symbol, name, kind, id, pdbAttributes, constraints, dynamicTransformFlags, tupleElementNames);
}
LocalMap.Add(symbol, local);
......@@ -167,7 +165,8 @@ internal void FreeLocal(ILocalSymbol symbol)
internal LocalDefinition AllocateSlot(
Cci.ITypeReference type,
LocalSlotConstraints constraints,
ImmutableArray<TypedConstant> dynamicTransformFlags = default(ImmutableArray<TypedConstant>))
ImmutableArray<TypedConstant> dynamicTransformFlags = default(ImmutableArray<TypedConstant>),
ImmutableArray<TypedConstant> tupleElementNames = default(ImmutableArray<TypedConstant>))
{
LocalDefinition local;
if (!FreeSlots.TryPop(new LocalSignature(type, constraints), out local))
......@@ -180,8 +179,8 @@ internal void FreeLocal(ILocalSymbol symbol)
id: LocalDebugId.None,
pdbAttributes: LocalVariableAttributes.DebuggerHidden,
constraints: constraints,
isDynamic: false,
dynamicTransformFlags: dynamicTransformFlags);
dynamicTransformFlags: dynamicTransformFlags,
tupleElementNames: tupleElementNames);
}
return local;
......@@ -195,8 +194,8 @@ internal void FreeLocal(ILocalSymbol symbol)
LocalDebugId id,
LocalVariableAttributes pdbAttributes,
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags)
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames)
{
if (_lazyAllLocals == null)
{
......@@ -207,7 +206,16 @@ internal void FreeLocal(ILocalSymbol symbol)
if (symbolOpt != null && _slotAllocatorOpt != null)
{
local = _slotAllocatorOpt.GetPreviousLocal(type, symbolOpt, nameOpt, kind, id, pdbAttributes, constraints, isDynamic, dynamicTransformFlags);
local = _slotAllocatorOpt.GetPreviousLocal(
type,
symbolOpt,
nameOpt,
kind,
id,
pdbAttributes,
constraints,
dynamicTransformFlags: dynamicTransformFlags,
tupleElementNames: tupleElementNames);
if (local != null)
{
int slot = local.SlotIndex;
......@@ -225,8 +233,8 @@ internal void FreeLocal(ILocalSymbol symbol)
id: id,
pdbAttributes: pdbAttributes,
constraints: constraints,
isDynamic: isDynamic,
dynamicTransformFlags: dynamicTransformFlags);
dynamicTransformFlags: dynamicTransformFlags,
tupleElementNames: tupleElementNames);
_lazyAllLocals.Add(local);
return local;
......
......@@ -3,7 +3,6 @@
using System;
using System.Collections.Immutable;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGen
......@@ -37,7 +36,12 @@ public ImmutableArray<Cci.ICustomModifier> CustomModifiers
public ImmutableArray<TypedConstant> DynamicTransformFlags
{
get { throw ExceptionUtilities.Unreachable; }
get { return ImmutableArray<TypedConstant>.Empty; }
}
public ImmutableArray<TypedConstant> TupleElementNames
{
get { return ImmutableArray<TypedConstant>.Empty; }
}
/// <remarks>
......@@ -46,8 +50,6 @@ public ImmutableArray<TypedConstant> DynamicTransformFlags
/// </remarks>
public LocalVariableAttributes PdbAttributes => LocalVariableAttributes.DebuggerHidden;
public bool IsDynamic => false;
public bool IsPinned
{
get { throw ExceptionUtilities.Unreachable; }
......
......@@ -2,7 +2,6 @@
using System.Collections.Immutable;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Symbols;
namespace Microsoft.CodeAnalysis.CodeGen
{
......@@ -18,8 +17,8 @@ internal abstract class VariableSlotAllocator
LocalDebugId id,
LocalVariableAttributes pdbAttributes,
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags);
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames);
public abstract string PreviousStateMachineTypeName { get; }
......
......@@ -136,8 +136,8 @@ private bool TryGetPreviousLocalId(SyntaxNode currentDeclarator, LocalDebugId cu
LocalDebugId id,
LocalVariableAttributes pdbAttributes,
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags)
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames)
{
if (id.IsNone)
{
......@@ -175,8 +175,8 @@ private bool TryGetPreviousLocalId(SyntaxNode currentDeclarator, LocalDebugId cu
id,
pdbAttributes,
constraints,
isDynamic,
dynamicTransformFlags);
dynamicTransformFlags,
tupleElementNames);
}
public override string PreviousStateMachineTypeName => _stateMachineTypeNameOpt;
......
......@@ -100,6 +100,7 @@ public byte[] SerializeMethodDebugInfo(EmitContext context, IMethodBody methodBo
if (!suppressNewCustomDebugInfo)
{
SerializeDynamicLocalInfo(methodBody, customDebugInfo);
SerializeTupleElementNames(methodBody, customDebugInfo);
// delta doesn't need this information - we use information recorded by previous generation emit
if (!isEncDelta)
......@@ -146,10 +147,10 @@ internal static void SerializeCustomDebugInformation(EditAndContinueMethodDebugI
}
}
private static PooledBlobBuilder SerializeRecord(
private static PooledBlobBuilder SerializeRecord<T>(
CustomDebugInfoKind kind,
EditAndContinueMethodDebugInformation debugInfo,
Action<EditAndContinueMethodDebugInformation, BlobBuilder> recordSerializer)
T debugInfo,
Action<T, BlobBuilder> recordSerializer)
{
var cmw = PooledBlobBuilder.GetInstance();
cmw.WriteByte(CustomDebugInfoConstants.Version);
......@@ -188,8 +189,7 @@ private static void SerializeReferenceToIteratorClass(string iteratorClassName,
uint length = 10 + (uint)iteratorClassName.Length * 2;
if ((length & 3) != 0) length += 4 - (length & 3);
cmw.WriteUInt32(length);
cmw.WriteUTF16(iteratorClassName);
cmw.WriteInt16(0);
WriteUtf16String(cmw, iteratorClassName);
cmw.Align(4);
Debug.Assert(cmw.Count == length);
customDebugInfo.Add(cmw);
......@@ -228,21 +228,23 @@ private static void SerializeStateMachineLocalScopes(IMethodBody methodBody, Arr
customDebugInfo.Add(cmw);
}
private static void SerializeDynamicLocalInfo(IMethodBody methodBody, ArrayBuilder<PooledBlobBuilder> customDebugInfo)
private static ArrayBuilder<T> GetLocalInfoToSerialize<T>(
IMethodBody methodBody,
Func<ILocalDefinition, bool> filter,
Func<LocalScope, ILocalDefinition, T> getInfo)
{
if (!methodBody.HasDynamicLocalVariables)
{
return; //There are no dynamic locals
}
ArrayBuilder<T> builder = null;
var dynamicLocals = ArrayBuilder<ILocalDefinition>.GetInstance();
foreach (ILocalDefinition local in methodBody.LocalVariables)
foreach (var local in methodBody.LocalVariables)
{
Debug.Assert(local.SlotIndex >= 0);
if (local.IsDynamic)
if (filter(local))
{
dynamicLocals.Add(local);
if (builder == null)
{
builder = ArrayBuilder<T>.GetInstance();
}
builder.Add(getInfo(default(LocalScope), local));
}
}
......@@ -251,17 +253,45 @@ private static void SerializeDynamicLocalInfo(IMethodBody methodBody, ArrayBuild
foreach (var localConstant in currentScope.Constants)
{
Debug.Assert(localConstant.SlotIndex < 0);
if (localConstant.IsDynamic)
if (filter(localConstant))
{
dynamicLocals.Add(localConstant);
if (builder == null)
{
builder = ArrayBuilder<T>.GetInstance();
}
builder.Add(getInfo(currentScope, localConstant));
}
}
}
Debug.Assert(dynamicLocals.Any()); // There must be at least one dynamic local if this point is reached
return builder;
}
private static void SerializeDynamicLocalInfo(IMethodBody methodBody, ArrayBuilder<PooledBlobBuilder> customDebugInfo)
{
if (!methodBody.HasDynamicLocalVariables)
{
return; //There are no dynamic locals
}
const int dynamicAttributeSize = 64;
const int identifierSize = 64;
var dynamicLocals = GetLocalInfoToSerialize(
methodBody,
local =>
{
var dynamicTransformFlags = local.DynamicTransformFlags;
return !dynamicTransformFlags.IsEmpty &&
dynamicTransformFlags.Length <= dynamicAttributeSize &&
local.Name.Length < identifierSize;
},
(scope, local) => local);
if (dynamicLocals == null)
{
return;
}
const int blobSize = dynamicAttributeSize + 4 + 4 + identifierSize * 2;//DynamicAttribute: 64, DynamicAttributeLength: 4, SlotIndex: 4, IdentifierName: 128
var cmw = PooledBlobBuilder.GetInstance();
cmw.WriteByte(CustomDebugInfoConstants.Version);
......@@ -273,30 +303,17 @@ private static void SerializeDynamicLocalInfo(IMethodBody methodBody, ArrayBuild
foreach (ILocalDefinition local in dynamicLocals)
{
if (local.Name.Length >= identifierSize)//Ignore and push empty information
{
cmw.WriteBytes(0, blobSize);
continue;
}
var dynamicTransformFlags = local.DynamicTransformFlags;
if (!dynamicTransformFlags.IsDefault && dynamicTransformFlags.Length <= dynamicAttributeSize)
byte[] flag = new byte[dynamicAttributeSize];
for (int k = 0; k < dynamicTransformFlags.Length; k++)
{
byte[] flag = new byte[dynamicAttributeSize];
for (int k = 0; k < dynamicTransformFlags.Length; k++)
if ((bool)dynamicTransformFlags[k].Value)
{
if ((bool)dynamicTransformFlags[k].Value)
{
flag[k] = 1;
}
flag[k] = 1;
}
cmw.WriteBytes(flag); //Written Flag
cmw.WriteUInt32((uint)dynamicTransformFlags.Length); //Written Length
}
else
{
cmw.WriteBytes(0, dynamicAttributeSize + 4); //Empty flag array and size.
}
cmw.WriteBytes(flag); //Written Flag
cmw.WriteUInt32((uint)dynamicTransformFlags.Length); //Written Length
var localIndex = local.SlotIndex;
cmw.WriteUInt32((localIndex < 0) ? 0u : (uint)localIndex);
......@@ -310,6 +327,57 @@ private static void SerializeDynamicLocalInfo(IMethodBody methodBody, ArrayBuild
customDebugInfo.Add(cmw);
}
private struct LocalAndScope
{
internal readonly ILocalDefinition Local;
internal readonly LocalScope Scope;
internal LocalAndScope(ILocalDefinition local, LocalScope scope)
{
Local = local;
Scope = scope;
}
}
private static void SerializeTupleElementNames(IMethodBody methodBody, ArrayBuilder<PooledBlobBuilder> customDebugInfo)
{
var builder = GetLocalInfoToSerialize(
methodBody,
local => !local.TupleElementNames.IsEmpty,
(scope, local) => new LocalAndScope(local, scope));
if (builder == null)
{
return;
}
customDebugInfo.Add(
SerializeRecord(
CustomDebugInfoKind.TupleElementNames,
builder,
SerializeTupleElementNames));
builder.Free();
}
private static void SerializeTupleElementNames(ArrayBuilder<LocalAndScope> locals, BlobBuilder cmw)
{
cmw.WriteInt32(locals.Count);
foreach (var localAndScope in locals)
{
var local = localAndScope.Local;
var scope = localAndScope.Scope;
var tupleElementNames = local.TupleElementNames;
cmw.WriteInt32(tupleElementNames.Length);
foreach (var tupleElementName in tupleElementNames)
{
WriteUtf8String(cmw, (string)tupleElementName.Value ?? string.Empty);
}
cmw.WriteInt32(local.SlotIndex);
cmw.WriteInt32(scope.StartOffset);
cmw.WriteInt32(scope.EndOffset);
WriteUtf8String(cmw, local.Name);
}
}
// internal for testing
internal static byte[] SerializeCustomDebugMetadata(ArrayBuilder<PooledBlobBuilder> recordWriters)
{
......@@ -457,5 +525,23 @@ private void SerializeReferenceToPreviousMethodWithUsingInfo(ArrayBuilder<Pooled
cmw.WriteUInt32((uint)_previousMethodTokenWithUsingInfo);
customDebugInfo.Add(cmw);
}
/// <summary>
/// Write string as UTF8 with null terminator.
/// </summary>
private static void WriteUtf8String(BlobBuilder cmw, string str)
{
cmw.WriteUTF8(str);
cmw.WriteByte(0);
}
/// <summary>
/// Write string as UTF16 with null terminator.
/// </summary>
private static void WriteUtf16String(BlobBuilder cmw, string str)
{
cmw.WriteUTF16(str);
cmw.WriteUInt16(0);
}
}
}
......@@ -256,11 +256,6 @@ ImmutableArray<ICustomModifier> CustomModifiers
LocalSlotConstraints Constraints { get; }
/// <summary>
/// True if the local variable is of type Dynamic.
/// </summary>
bool IsDynamic { get; }
/// <summary>
/// Each local has an attributes field in the PDB. To match the native compiler,
/// we emit <see cref="LocalVariableAttributes.DebuggerHidden"/> for locals that should
......@@ -276,10 +271,15 @@ ImmutableArray<ICustomModifier> CustomModifiers
LocalVariableAttributes PdbAttributes { get; }
/// <summary>
/// Should return the synthesized dynamic attributes of the local definition if any. Else null.
/// The synthesized dynamic attributes of the local definition if any, or empty.
/// </summary>
ImmutableArray<TypedConstant> DynamicTransformFlags { get; }
/// <summary>
/// The tuple element names of the local definition if any, or empty.
/// </summary>
ImmutableArray<TypedConstant> TupleElementNames { get; }
/// <summary>
/// The type of the local.
/// </summary>
......
......@@ -106,7 +106,7 @@ private void SerializeMethodDebugInfo(IMethodBody bodyOpt, int methodRid, Standa
index: local.SlotIndex,
name: _debugMetadataOpt.GetOrAddString(local.Name));
SerializeDynamicLocalInfo(local, lastLocalVariableHandle);
SerializeLocalInfo(local, lastLocalVariableHandle);
}
foreach (ILocalDefinition constant in scope.Constants)
......@@ -118,7 +118,7 @@ private void SerializeMethodDebugInfo(IMethodBody bodyOpt, int methodRid, Standa
name: _debugMetadataOpt.GetOrAddString(constant.Name),
signature: SerializeLocalConstantSignature(constant));
SerializeDynamicLocalInfo(constant, lastLocalConstantHandle);
SerializeLocalInfo(constant, lastLocalConstantHandle);
}
}
}
......@@ -465,20 +465,30 @@ private void SerializeModuleDefaultNamespace()
#region Locals
private void SerializeDynamicLocalInfo(ILocalDefinition local, EntityHandle parent)
private void SerializeLocalInfo(ILocalDefinition local, EntityHandle parent)
{
var dynamicFlags = local.DynamicTransformFlags;
if (dynamicFlags.IsDefault)
if (!dynamicFlags.IsEmpty)
{
return;
var value = SerializeBitVector(dynamicFlags);
_debugMetadataOpt.AddCustomDebugInformation(
parent: parent,
kind: _debugMetadataOpt.GetOrAddGuid(PortableCustomDebugInfoKinds.DynamicLocalVariables),
value: _debugMetadataOpt.GetOrAddBlob(value));
}
var value = SerializeBitVector(dynamicFlags);
var tupleElementNames = local.TupleElementNames;
if (!tupleElementNames.IsEmpty)
{
var builder = new BlobBuilder();
SerializeTupleElementNames(builder, tupleElementNames);
_debugMetadataOpt.AddCustomDebugInformation(
parent: parent,
kind: _debugMetadataOpt.GetOrAddGuid(PortableCustomDebugInfoKinds.DynamicLocalVariables),
value: _debugMetadataOpt.GetOrAddBlob(value));
_debugMetadataOpt.AddCustomDebugInformation(
parent: parent,
kind: _debugMetadataOpt.GetOrAddGuid(PortableCustomDebugInfoKinds.TupleElementNames),
value: _debugMetadataOpt.GetOrAddBlob(builder));
}
}
private static ImmutableArray<byte> SerializeBitVector(ImmutableArray<TypedConstant> vector)
......@@ -525,6 +535,23 @@ private static ImmutableArray<byte> SerializeBitVector(ImmutableArray<TypedConst
return builder.ToImmutableAndFree();
}
private static void SerializeTupleElementNames(BlobBuilder builder, ImmutableArray<TypedConstant> names)
{
foreach (var name in names)
{
WriteUtf8String(builder, (string)name.Value ?? string.Empty);
}
}
/// <summary>
/// Write string as UTF8 with null terminator.
/// </summary>
private static void WriteUtf8String(BlobBuilder builder, string str)
{
builder.WriteUTF8(str);
builder.WriteByte(0);
}
#endregion
#region State Machines
......
......@@ -268,11 +268,10 @@ public static SemanticInfoSummary GetSpeculativeSemanticInfoSummary(this Semanti
internal static ImmutableArray<SynthesizedAttributeData> GetSynthesizedAttributes(this ISymbol symbol, bool forReturnType = false)
{
var context = new ModuleCompilationState();
ArrayBuilder<SynthesizedAttributeData> attributes = null;
if (!forReturnType)
{
var context = new ModuleCompilationState();
((Symbol)symbol).AddSynthesizedAttributes(context, ref attributes);
}
else
......
......@@ -1200,10 +1200,15 @@ Friend Module CompilationUtils
End Sub
<Extension>
Friend Function GetSynthesizedAttributes(symbol As ISymbol) As ImmutableArray(Of SynthesizedAttributeData)
Friend Function GetSynthesizedAttributes(symbol As ISymbol, Optional forReturnType As Boolean = False) As ImmutableArray(Of SynthesizedAttributeData)
Dim attributes As ArrayBuilder(Of SynthesizedAttributeData) = Nothing
Dim context = New ModuleCompilationState()
DirectCast(symbol, Symbol).AddSynthesizedAttributes(context, attributes)
If Not forReturnType Then
Dim context = New ModuleCompilationState()
DirectCast(symbol, Symbol).AddSynthesizedAttributes(context, attributes)
Else
' Call AddSynthesizedReturnTypeAttributes() when available: https://github.com/dotnet/roslyn/issues/13948
' DirectCast(symbol, MethodSymbol).AddSynthesizedReturnTypeAttributes(attributes)
End If
Return If(attributes IsNot Nothing, attributes.ToImmutableAndFree(), ImmutableArray.Create(Of SynthesizedAttributeData)())
End Function
......
......@@ -1255,7 +1255,12 @@ OtherExpressions:
If local.HasConstantValue Then
Dim compileTimeValue As MetadataConstant = _module.CreateConstant(local.Type, local.ConstantValue, syntaxNode, _diagnostics)
Dim localConstantDef = New LocalConstantDefinition(local.Name, If(local.Locations.FirstOrDefault(), Location.None), compileTimeValue)
Dim localConstantDef = New LocalConstantDefinition(
local.Name,
If(local.Locations.FirstOrDefault(), Location.None),
compileTimeValue,
dynamicTransformFlags:=Nothing,
tupleElementNames:=Nothing)
' Reference in the scope for debugging purpose
_builder.AddLocalConstantToScope(localConstantDef)
Return Nothing
......@@ -1285,8 +1290,8 @@ OtherExpressions:
id:=localId,
pdbAttributes:=synthesizedKind.PdbAttributes(),
constraints:=constraints,
isDynamic:=False,
dynamicTransformFlags:=Nothing,
tupleElementNames:=Nothing,
isSlotReusable:=synthesizedKind.IsSlotReusable(_ilEmitStyle <> ILEmitStyle.Release))
' If named, add it to the local debug scope.
......@@ -1438,16 +1443,16 @@ OtherExpressions:
Private Sub DefineUserDefinedStateMachineHoistedLocal(field As StateMachineFieldSymbol)
Debug.Assert(field.SlotIndex >= 0)
Dim fakePdbOnlyLocal = New LocalDefinition(
symbolOpt:=Nothing,
nameOpt:=field.Name,
type:=Nothing,
slot:=field.SlotIndex,
synthesizedKind:=SynthesizedLocalKind.EmitterTemp,
id:=Nothing,
pdbAttributes:=LocalVariableAttributes.None,
constraints:=LocalSlotConstraints.None,
isDynamic:=False,
dynamicTransformFlags:=Nothing)
symbolOpt:=Nothing,
nameOpt:=field.Name,
type:=Nothing,
slot:=field.SlotIndex,
synthesizedKind:=SynthesizedLocalKind.EmitterTemp,
id:=Nothing,
pdbAttributes:=LocalVariableAttributes.None,
constraints:=LocalSlotConstraints.None,
dynamicTransformFlags:=Nothing,
tupleElementNames:=Nothing)
_builder.AddLocalToScope(fakePdbOnlyLocal)
End Sub
......
......@@ -15,5 +15,6 @@ internal enum CustomDebugInfoKind : byte
DynamicLocals = 5,
EditAndContinueLocalSlotMap = 6,
EditAndContinueLambdaMap = 7,
TupleElementNames = 8,
}
}
......@@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Microsoft.CodeAnalysis.Collections;
#pragma warning disable RS0010 // Avoid using cref tags with a prefix
......@@ -94,11 +95,16 @@ public static IEnumerable<CustomDebugInfoRecord> GetCustomDebugInfoRecords(byte[
throw new InvalidOperationException("Invalid header.");
}
if (kind != CustomDebugInfoKind.EditAndContinueLambdaMap &&
kind != CustomDebugInfoKind.EditAndContinueLocalSlotMap)
switch (kind)
{
// ignore alignment for CDIs that don't support it
alignmentSize = 0;
case CustomDebugInfoKind.EditAndContinueLambdaMap:
case CustomDebugInfoKind.EditAndContinueLocalSlotMap:
case CustomDebugInfoKind.TupleElementNames:
break;
default:
// ignore alignment for CDIs that don't support it
alignmentSize = 0;
break;
}
int bodySize = size - CustomDebugInfoConstants.RecordHeaderSize;
......@@ -271,6 +277,37 @@ public static ImmutableArray<DynamicLocalInfo> DecodeDynamicLocalsRecord(Immutab
return builder.ToImmutableAndFree();
}
/// <summary>
/// Tuple element names for locals.
/// </summary>
public static ImmutableArray<TupleElementNamesInfo> DecodeTupleElementNamesRecord(ImmutableArray<byte> bytes)
{
int offset = 0;
int n = ReadInt32(bytes, ref offset);
var builder = ArrayBuilder<TupleElementNamesInfo>.GetInstance(n);
for (int i = 0; i < n; i++)
{
builder.Add(DecodeTupleElementNamesInfo(bytes, ref offset));
}
return builder.ToImmutableAndFree();
}
private static TupleElementNamesInfo DecodeTupleElementNamesInfo(ImmutableArray<byte> bytes, ref int offset)
{
int n = ReadInt32(bytes, ref offset);
var builder = ArrayBuilder<string>.GetInstance(n);
for (int i = 0; i < n; i++)
{
var value = ReadUtf8String(bytes, ref offset);
builder.Add(string.IsNullOrEmpty(value) ? null : value);
}
int slotIndex = ReadInt32(bytes, ref offset);
int scopeStart = ReadInt32(bytes, ref offset);
int scopeEnd = ReadInt32(bytes, ref offset);
var localName = ReadUtf8String(bytes, ref offset);
return new TupleElementNamesInfo(builder.ToImmutableAndFree(), slotIndex, localName, scopeStart, scopeEnd);
}
/// <summary>
/// Returns the raw bytes of a record.
/// </summary>
......@@ -815,5 +852,24 @@ private static string FormatMethodToken(int methodToken)
{
return string.Format("0x{0:x8}", methodToken);
}
/// <summary>
/// Read UTF8 string with null terminator.
/// </summary>
private static string ReadUtf8String(ImmutableArray<byte> bytes, ref int offset)
{
var builder = ArrayBuilder<byte>.GetInstance();
while (true)
{
var b = ReadByte(bytes, ref offset);
if (b == 0)
{
break;
}
builder.Add(b);
}
var block = builder.ToArrayAndFree();
return Encoding.UTF8.GetString(block, 0, block.Length);
}
}
}
......@@ -7,14 +7,14 @@ internal struct DynamicLocalInfo
public readonly int FlagCount;
public readonly ulong Flags;
public readonly int SlotId;
public readonly string Name;
public readonly string LocalName;
public DynamicLocalInfo(int flagCount, ulong flags, int slotId, string name)
public DynamicLocalInfo(int flagCount, ulong flags, int slotId, string localName)
{
FlagCount = flagCount;
Flags = flags;
SlotId = slotId;
Name = name;
LocalName = localName;
}
}
}
......@@ -17,6 +17,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ImportTargetKind.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PortableCustomDebugInfoKinds.cs" />
<Compile Include="$(MSBuildThisFileDirectory)StateMachineHoistedLocalScope.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TupleElementNamesInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)VBImportScopeKind.cs" />
</ItemGroup>
</Project>
\ No newline at end of file
......@@ -9,6 +9,7 @@ internal static class PortableCustomDebugInfoKinds
public static readonly Guid AsyncMethodSteppingInformationBlob = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8");
public static readonly Guid StateMachineHoistedLocalScopes = new Guid("6DA9A61E-F8C7-4874-BE62-68BC5630DF71");
public static readonly Guid DynamicLocalVariables = new Guid("83C563C4-B4F3-47D5-B824-BA5441477EA8");
public static readonly Guid TupleElementNames = new Guid("ED9FDF71-8879-4747-8ED3-FE5EDE3CE710");
public static readonly Guid DefaultNamespace = new Guid("58b2eab6-209f-4e4e-a22c-b2d0f910c782");
public static readonly Guid EncLocalSlotMap = new Guid("755F52A8-91C5-45BE-B4B8-209571E552BD");
public static readonly Guid EncLambdaAndClosureMap = new Guid("A643004C-0240-496F-A783-30D64F4979DE");
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.Debugging
{
internal struct TupleElementNamesInfo
{
internal readonly ImmutableArray<string> ElementNames;
internal readonly int SlotIndex; // Locals only
internal readonly string LocalName;
internal readonly int ScopeStart; // Constants only
internal readonly int ScopeEnd; // Constants only
internal TupleElementNamesInfo(ImmutableArray<string> elementNames, int slotIndex, string localName, int scopeStart, int scopeEnd)
{
Debug.Assert(!elementNames.IsDefault);
ElementNames = elementNames;
SlotIndex = slotIndex;
LocalName = localName;
ScopeStart = scopeStart;
ScopeEnd = scopeEnd;
}
}
}
......@@ -2,7 +2,6 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
......@@ -28,7 +27,17 @@ private TypeSymbol GetDynamicType(TypeSymbol type, RefKind refKind, ImmutableArr
return DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(type, _sourceAssembly, refKind, dynamicFlags, checkLength: false);
}
public override LocalSymbol GetLocalVariable(string name, int slotIndex, LocalInfo<TypeSymbol> info, ImmutableArray<bool> dynamicFlagsOpt)
private TypeSymbol IncludeTupleElementNames(TypeSymbol type, ImmutableArray<string> tupleElementNames)
{
return TupleTypeDecoder.DecodeTupleTypesIfApplicable(type, _sourceAssembly, tupleElementNames);
}
public override LocalSymbol GetLocalVariable(
string name,
int slotIndex,
LocalInfo<TypeSymbol> info,
ImmutableArray<bool> dynamicFlagsOpt,
ImmutableArray<string> tupleElementNamesOpt)
{
var isPinned = info.IsPinned;
......@@ -53,19 +62,34 @@ public override LocalSymbol GetLocalVariable(string name, int slotIndex, LocalIn
type = GetDynamicType(type, refKind, dynamicFlagsOpt);
}
if (!tupleElementNamesOpt.IsDefault)
{
type = IncludeTupleElementNames(type, tupleElementNamesOpt);
}
// Custom modifiers can be dropped since binding ignores custom
// modifiers from locals and since we only need to preserve
// the type of the original local in the generated method.
return new EELocalSymbol(_method, EELocalSymbol.NoLocations, name, slotIndex, kind, type, refKind, isPinned, isCompilerGenerated: false, canScheduleToStack: false);
}
public override LocalSymbol GetLocalConstant(string name, TypeSymbol type, ConstantValue value, ImmutableArray<bool> dynamicFlagsOpt)
public override LocalSymbol GetLocalConstant(
string name,
TypeSymbol type,
ConstantValue value,
ImmutableArray<bool> dynamicFlagsOpt,
ImmutableArray<string> tupleElementNamesOpt)
{
if (!dynamicFlagsOpt.IsDefault)
{
type = GetDynamicType(type, RefKind.None, dynamicFlagsOpt);
}
if (!tupleElementNamesOpt.IsDefault)
{
type = IncludeTupleElementNames(type, tupleElementNamesOpt);
}
return new EELocalConstantSymbol(_method, name, type, value);
}
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.CSharp.Symbols;
......@@ -103,6 +104,44 @@ internal static CSharpCompilation ToCompilation(this ImmutableArray<MetadataRefe
options: s_compilationOptions);
}
internal static ReadOnlyCollection<byte> GetCustomTypeInfoPayload(
this CSharpCompilation compilation,
TypeSymbol type,
int customModifiersCount,
RefKind refKind)
{
return CustomTypeInfo.Encode(
GetDynamicTransforms(compilation, type, customModifiersCount, refKind),
GetTupleElementNames(compilation, type));
}
private static ReadOnlyCollection<byte> GetDynamicTransforms(
this CSharpCompilation compilation,
TypeSymbol type,
int customModifiersCount,
RefKind refKind)
{
var builder = ArrayBuilder<bool>.GetInstance();
CSharpCompilation.DynamicTransformsEncoder.Encode(type, customModifiersCount, refKind, builder, addCustomModifierFlags: true);
var bytes = builder.Count > 0 && compilation.HasDynamicEmitAttributes() ?
DynamicFlagsCustomTypeInfo.ToBytes(builder) :
null;
builder.Free();
return bytes;
}
private static ReadOnlyCollection<string> GetTupleElementNames(
this CSharpCompilation compilation,
TypeSymbol type)
{
var builder = ArrayBuilder<string>.GetInstance();
var names = CSharpCompilation.TupleNamesEncoder.TryGetNames(type, builder) && compilation.HasTupleNamesAttributes ?
new ReadOnlyCollection<string>(builder.ToArray()) :
null;
builder.Free();
return names;
}
internal static readonly AssemblyIdentityComparer IdentityComparer = DesktopAssemblyIdentityComparer.Default;
// XML file references, #r directives not supported:
......
......@@ -125,8 +125,8 @@ private static LocalDefinition ToLocalDefinition(LocalSymbol local, int index)
id: LocalDebugId.None,
pdbAttributes: LocalVariableAttributes.None,
constraints: constraints,
isDynamic: false,
dynamicTransformFlags: ImmutableArray<TypedConstant>.Empty);
dynamicTransformFlags: ImmutableArray<TypedConstant>.Empty,
tupleElementNames: ImmutableArray<TypedConstant>.Empty);
}
private sealed class SlotAllocator : VariableSlotAllocator
......@@ -151,8 +151,8 @@ public override void AddPreviousLocals(ArrayBuilder<Cci.ILocalDefinition> builde
LocalDebugId id,
LocalVariableAttributes pdbAttributes,
LocalSlotConstraints constraints,
bool isDynamic,
ImmutableArray<TypedConstant> dynamicTransformFlags)
ImmutableArray<TypedConstant> dynamicTransformFlags,
ImmutableArray<TypedConstant> tupleElementNames)
{
var local = symbol as EELocalSymbol;
if ((object)local == null)
......
......@@ -195,7 +195,13 @@ internal sealed class EvaluationContext : EvaluationContextBase
var reuseSpan = debugInfo.ReuseSpan;
var localsBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
MethodDebugInfo<TypeSymbol, LocalSymbol>.GetLocals(localsBuilder, symbolProvider, debugInfo.LocalVariableNames, localInfo, debugInfo.DynamicLocalMap);
MethodDebugInfo<TypeSymbol, LocalSymbol>.GetLocals(
localsBuilder,
symbolProvider,
debugInfo.LocalVariableNames,
localInfo,
debugInfo.DynamicLocalMap,
debugInfo.TupleLocalMap);
if (!debugInfo.HoistedLocalScopeRecords.IsDefaultOrEmpty)
{
inScopeHoistedLocals = new CSharpInScopeHoistedLocals(debugInfo.GetInScopeHoistedLocalIndices(ilOffset, ref reuseSpan));
......
......@@ -118,7 +118,7 @@ private static BoundExpression GetCustomTypeInfoPayload(LocalSymbol local, Synta
compilation.Assembly,
compilation.GetSpecialType(SpecialType.System_Byte));
var bytes = CSharpCompilation.DynamicTransformsEncoder.Encode(local.Type, customModifiersCount: 0, refKind: RefKind.None).ToBytes();
var bytes = compilation.GetCustomTypeInfoPayload(local.Type, customModifiersCount: 0, refKind: RefKind.None);
hasCustomTypeInfoPayload = bytes != null;
if (!hasCustomTypeInfoPayload)
{
......
......@@ -2,9 +2,7 @@
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
......@@ -20,19 +18,7 @@ internal static ImmutableArray<TypeParameterSymbol> GetAllTypeParameters(this Me
internal static ReadOnlyCollection<byte> GetCustomTypeInfoPayload(this MethodSymbol method)
{
return CSharpCompilation.DynamicTransformsEncoder.Encode(method.ReturnType, method.ReturnTypeCustomModifiers.Length, RefKind.None).ToBytes();
return method.DeclaringCompilation.GetCustomTypeInfoPayload(method.ReturnType, method.ReturnTypeCustomModifiers.Length, RefKind.None);
}
internal static ReadOnlyCollection<byte> ToBytes(this ImmutableArray<bool> dynamicFlags)
{
Debug.Assert(!dynamicFlags.IsDefaultOrEmpty);
var builder = ArrayBuilder<bool>.GetInstance(dynamicFlags.Length);
builder.AddRange(dynamicFlags);
var bytes = DynamicFlagsCustomTypeInfo.ToBytes(builder);
builder.Free();
return CustomTypeInfo.Encode(bytes, null);
}
}
}
......@@ -651,13 +651,17 @@ internal override void AddSynthesizedReturnTypeAttributes(ref ArrayBuilder<Synth
{
base.AddSynthesizedReturnTypeAttributes(ref attributes);
if (this.ReturnType.ContainsDynamic())
var compilation = this.DeclaringCompilation;
var returnType = this.ReturnType;
if (returnType.ContainsDynamic() && compilation.HasDynamicEmitAttributes())
{
var compilation = this.DeclaringCompilation;
if ((object)compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_DynamicAttribute__ctor) != null)
{
AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(this.ReturnType, this.ReturnTypeCustomModifiers.Length));
}
AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(returnType, ReturnTypeCustomModifiers.Length));
}
if (returnType.ContainsTupleNames() && compilation.HasTupleNamesAttributes)
{
AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(returnType));
}
}
......
......@@ -93,6 +93,7 @@
<Compile Include="NoPIATests.cs" />
<Compile Include="PseudoVariableTests.cs" />
<Compile Include="MethodContextReuseConstraintsTests.cs" />
<Compile Include="TupleTests.cs" />
<Compile Include="UsingDebugInfoTests.cs" />
<Compile Include="WinMdTests.cs" />
</ItemGroup>
......@@ -106,4 +107,4 @@
</ItemGroup>
<Import Project="..\..\..\..\..\build\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\..\..\build\Targets\Roslyn.Toolsets.Xunit.targets" />
</Project>
</Project>
\ No newline at end of file
......@@ -684,8 +684,12 @@ public void Dynamic()
static void M()
{
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var compilation0 = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var compilation0 = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, TestOptions.DebugDll);
WithRuntimeInstance(compilation0, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
......
......@@ -48,11 +48,11 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, method.ReturnType.TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x01);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
.locals init (dynamic V_0) //d
......@@ -89,11 +89,11 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, ((ArrayTypeSymbol)method.ReturnType).ElementType.TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x02);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
.locals init (dynamic[] V_0) //d
......@@ -130,11 +130,11 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, ((NamedTypeSymbol)method.ReturnType).TypeArguments.Single().TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x02);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
.locals init (System.Collections.Generic.List<dynamic> V_0) //d
......@@ -171,11 +171,11 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, method.ReturnType.TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x01);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedFlags: DkmClrCompilationResultFlags.ReadOnlyResult, expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldnull
......@@ -211,7 +211,7 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, ((ArrayTypeSymbol)method.ReturnType).ElementType.TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x02);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedFlags: DkmClrCompilationResultFlags.ReadOnlyResult, expectedILOpt: @"
......@@ -256,7 +256,7 @@ class Generic<T>
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, ((NamedTypeSymbol)method.ReturnType).TypeArguments.Single().TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x02);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedFlags: DkmClrCompilationResultFlags.ReadOnlyResult, expectedILOpt: @"
......@@ -290,8 +290,13 @@ static void M()
dynamic[] b = null;
}
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var comp = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, methodName: "C.M", atLineNumber: 799);
......@@ -354,8 +359,13 @@ static void M()
object[] b = null;
}
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var comp = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, methodName: "C.M", atLineNumber: 799);
......@@ -416,8 +426,13 @@ static void M()
const dynamic c = null;
}
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var comp = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, methodName: "C.M", atLineNumber: 799);
......@@ -510,8 +525,13 @@ static void M()
const dynamic[] c = null;
}
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var comp = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, methodName: "C.M", atLineNumber: 799);
......@@ -573,8 +593,13 @@ static void M()
dynamic c123456789012345678901234567890123456789012345678901234567890123 = null; // 64 chars
dynamic d = null;
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var comp = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, methodName: "C.M");
......@@ -626,7 +651,7 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, method.ReturnType.TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x01);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
......@@ -665,11 +690,11 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, ((ArrayTypeSymbol)method.ReturnType).ElementType.TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x02);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
......@@ -704,11 +729,11 @@ static dynamic ForceDynamicAttribute()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
Assert.Equal(TypeKind.Dynamic, ((NamedTypeSymbol)method.ReturnType).TypeArguments.Single().TypeKind);
VerifyCustomTypeInfo(locals[0], "d", 0x02);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
......@@ -752,10 +777,10 @@ public class Inner<V, W>
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasDynamicAttribute(method);
Assert.NotNull(GetDynamicAttributeIfAny(method));
VerifyCustomTypeInfo(locals[0], "d", 0x04, 0x03);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
@"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
......@@ -790,7 +815,7 @@ .maxstack 1
VerifyCustomTypeInfo(result, null);
Assert.Equal(resultProperties.Flags, DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
@"{
// Code size 60 (0x3c)
.maxstack 6
IL_0000: ldtoken ""Outer<dynamic[], object[]>.Inner<Outer<object, dynamic>[], dynamic>""
......@@ -823,8 +848,13 @@ public void DynamicAliases()
static void M()
{
}
static dynamic ForceDynamicAttribute()
{
return null;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
var comp = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(
......@@ -860,7 +890,7 @@ static void M()
VerifyCustomTypeInfo(locals[0], "d1", 0x01);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d1", expectedILOpt:
@"{
@"{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldstr ""d1""
......@@ -870,7 +900,7 @@ .maxstack 1
VerifyCustomTypeInfo(locals[1], "d2", 0x84, 0x00); // Note: read flags right-to-left in each byte: 0010 0001 0(000 0000)
VerifyLocal(testData, typeName, locals[1], "<>m1", "d2", expectedILOpt:
@"{
@"{
// Code size 16 (0x10)
.maxstack 1
IL_0000: ldstr ""d2""
......@@ -913,25 +943,20 @@ static void M()
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
var method = testData.Methods.Single().Value.Method;
AssertHasNoDynamicAttribute(method);
Assert.Null(GetDynamicAttributeIfAny(method));
VerifyCustomTypeInfo(locals[0], "d", null);
VerifyLocal(testData, typeName, locals[0], "<>m0", "d", expectedILOpt:
@"{
// Code size 2 (0x2)
.maxstack 1
.locals init (dynamic V_0) //d
IL_0000: ldloc.0
IL_0001: ret
}");
locals.Free();
});
}
private static void AssertHasDynamicAttribute(IMethodSymbol method)
{
Assert.Contains(
"System.Runtime.CompilerServices.DynamicAttribute",
method.GetSynthesizedAttributes(forReturnType: true).Select(a => a.AttributeClass.ToTestDisplayString()));
}
private static void AssertHasNoDynamicAttribute(IMethodSymbol method)
{
Assert.DoesNotContain(
"System.Runtime.CompilerServices.DynamicAttribute",
method.GetSynthesizedAttributes(forReturnType: true).Select(a => a.AttributeClass.ToTestDisplayString()));
}
[Fact]
public void DynamicCall()
{
......
......@@ -357,5 +357,22 @@ internal static Alias Alias(DkmClrAliasKind kind, string name, string fullName,
return MethodDebugInfo<TypeSymbol, LocalSymbol>.ReadMethodDebugInfo((ISymUnmanagedReader3)symReader, symbolProvider, MetadataTokens.GetToken(peMethod.Handle), methodVersion: 1, ilOffset: ilOffset, isVisualBasicMethod: false);
}
internal static SynthesizedAttributeData GetDynamicAttributeIfAny(IMethodSymbol method)
{
return GetAttributeIfAny(method, "System.Runtime.CompilerServices.DynamicAttribute");
}
internal static SynthesizedAttributeData GetTupleElementNamesAttributeIfAny(IMethodSymbol method)
{
return GetAttributeIfAny(method, "System.Runtime.CompilerServices.TupleElementNamesAttribute");
}
internal static SynthesizedAttributeData GetAttributeIfAny(IMethodSymbol method, string typeName)
{
return method.GetSynthesizedAttributes(forReturnType: true).
Where(a => a.AttributeClass.ToTestDisplayString() == typeName).
SingleOrDefault();
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests;
using Microsoft.VisualStudio.Debugger.Evaluation;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using Roslyn.Test.Utilities;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.UnitTests
{
public class TupleTests : ExpressionCompilerTestBase
{
[Fact]
public void Literal()
{
var source =
@"class C
{
static void M()
{
(int, int) o;
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
var result = context.CompileExpression("(A: 1, B: 2)", out error, testData);
Assert.Null(error);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = result.GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(new[] { "A", "B" }, tupleElementNames);
var methodData = testData.GetMethodData("<>x.<>m0");
var method = methodData.Method;
Assert.True(method.ReturnType.IsTupleType);
Assert.NotNull(GetTupleElementNamesAttributeIfAny(method));
methodData.VerifyIL(
@"{
// Code size 8 (0x8)
.maxstack 2
.locals init (System.ValueTuple<int, int> V_0) //o
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj ""System.ValueTuple<int, int>..ctor(int, int)""
IL_0007: ret
}");
});
}
[Fact]
public void TupleElementNamesAttribute_NotAvailable()
{
var source =
@"namespace System
{
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 _1, T2 _2)
{
Item1 = _1;
Item2 = _2;
}
}
}
class C
{
static void M()
{
(int, int) o;
}
}";
var comp = CreateCompilationWithMscorlib(source, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
var result = context.CompileExpression("(A: 1, B: 2)", out error, testData);
Assert.Null(error);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = result.GetCustomTypeInfo(out customTypeInfo);
Assert.Null(customTypeInfo);
var methodData = testData.GetMethodData("<>x.<>m0");
var method = methodData.Method;
Assert.True(method.ReturnType.IsTupleType);
Assert.Null(GetTupleElementNamesAttributeIfAny(method));
methodData.VerifyIL(
@"{
// Code size 8 (0x8)
.maxstack 2
.locals init (System.ValueTuple<int, int> V_0) //o
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj ""System.ValueTuple<int, int>..ctor(int, int)""
IL_0007: ret
}");
});
}
[Fact]
public void Local()
{
var source =
@"class C
{
static void M()
{
(int A\u1234, int \u1234B) o = (1, 2);
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = locals[0].GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(new[] { "A\u1234", "\u1234B" }, tupleElementNames);
var method = testData.Methods.Single().Value.Method;
Assert.NotNull(GetTupleElementNamesAttributeIfAny(method));
Assert.True(method.ReturnType.IsTupleType);
VerifyLocal(testData, typeName, locals[0], "<>m0", "o", expectedILOpt:
string.Format(@"{{
// Code size 2 (0x2)
.maxstack 1
.locals init ((int A{0}, int {0}B) V_0) //o
IL_0000: ldloc.0
IL_0001: ret
}}", '\u1234'));
locals.Free();
});
}
[Fact]
public void Constant()
{
var source =
@"class A<T>
{
internal class B<U>
{
}
}
class C
{
static (object, object) F;
static void M()
{
const A<(int, int A)>.B<(object B, object)>[] c = null;
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = locals[0].GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(new[] { null, "A", "B", null }, tupleElementNames);
var method = (MethodSymbol)testData.Methods.Single().Value.Method;
Assert.NotNull(GetTupleElementNamesAttributeIfAny(method));
var returnType = method.ReturnType;
Assert.False(returnType.IsTupleType);
Assert.True(returnType.ContainsTuple());
VerifyLocal(testData, typeName, locals[0], "<>m0", "c", expectedFlags: DkmClrCompilationResultFlags.ReadOnlyResult, expectedILOpt:
@"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldnull
IL_0001: ret
}");
locals.Free();
});
}
[Fact]
public void DeclareLocal()
{
var source =
@"class C
{
static void M()
{
var x = (1, 2);
}
}";
var comp = CreateCompilationWithMscorlib(source, new[] { ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
ResultProperties resultProperties;
ImmutableArray<AssemblyIdentity> missingAssemblyIdentities;
var result = context.CompileExpression(
"(int A, int B) y = x;",
DkmEvaluationFlags.None,
NoAliases,
DebuggerDiagnosticFormatter.Instance,
out resultProperties,
out error,
out missingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData);
Assert.Null(error);
Assert.Equal(resultProperties.Flags, DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = result.GetCustomTypeInfo(out customTypeInfo);
Assert.Null(customTypeInfo);
var methodData = testData.GetMethodData("<>x.<>m0");
var method = methodData.Method;
Assert.Null(GetTupleElementNamesAttributeIfAny(method));
methodData.VerifyIL(
@"{
// Code size 60 (0x3c)
.maxstack 6
.locals init (System.ValueTuple<int, int> V_0) //x
IL_0000: ldtoken ""System.ValueTuple<int, int>""
IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_000a: ldstr ""y""
IL_000f: ldstr ""108766ce-df68-46ee-b761-0dcb7ac805f1""
IL_0014: newobj ""System.Guid..ctor(string)""
IL_0019: ldc.i4.5
IL_001a: newarr ""byte""
IL_001f: dup
IL_0020: ldtoken ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=5 <PrivateImplementationDetails>.362A905A18EA2A18A9EB2574618C490DE8A1F5C3""
IL_0025: call ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
IL_002a: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])""
IL_002f: ldstr ""y""
IL_0034: call ""(int A, int B) Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<(int A, int B)>(string)""
IL_0039: ldloc.0
IL_003a: stind.ref
IL_003b: ret
}");
});
}
}
}
......@@ -26,8 +26,19 @@ internal abstract class EESymbolProvider<TTypeSymbol, TLocalSymbol>
public abstract TTypeSymbol GetTypeSymbolForSerializedType(string typeName);
public abstract TLocalSymbol GetLocalVariable(string name, int slotIndex, LocalInfo<TTypeSymbol> info, ImmutableArray<bool> dynamicFlagsOpt);
public abstract TLocalSymbol GetLocalConstant(string name, TTypeSymbol type, ConstantValue value, ImmutableArray<bool> dynamicFlagsOpt);
public abstract TLocalSymbol GetLocalVariable(
string name,
int slotIndex,
LocalInfo<TTypeSymbol> info,
ImmutableArray<bool> dynamicFlagsOpt,
ImmutableArray<string> tupleElementNamesOpt);
public abstract TLocalSymbol GetLocalConstant(
string name,
TTypeSymbol type,
ConstantValue value,
ImmutableArray<bool> dynamicFlagsOpt,
ImmutableArray<string> tupleElementNamesOpt);
/// <exception cref="BadImageFormatException"></exception>
public abstract IAssemblySymbol GetReferencedAssembly(AssemblyReferenceHandle handle);
......
......@@ -15,6 +15,39 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
{
private struct LocalNameAndScope : IEquatable<LocalNameAndScope>
{
internal readonly string LocalName;
internal readonly int ScopeStart;
internal readonly int ScopeEnd;
internal LocalNameAndScope(string localName, int scopeStart, int scopeEnd)
{
LocalName = localName;
ScopeStart = scopeStart;
ScopeEnd = scopeEnd;
}
public bool Equals(LocalNameAndScope other)
{
return ScopeStart == other.ScopeStart &&
ScopeEnd == other.ScopeEnd &&
string.Equals(LocalName, other.LocalName, StringComparison.Ordinal);
}
public override bool Equals(object obj)
{
throw new NotImplementedException();
}
public override int GetHashCode()
{
return Hash.Combine(
Hash.Combine(ScopeStart, ScopeEnd),
LocalName.GetHashCode());
}
}
public unsafe static MethodDebugInfo<TTypeSymbol, TLocalSymbol> ReadMethodDebugInfo(
ISymUnmanagedReader3 symReader,
EESymbolProvider<TTypeSymbol, TLocalSymbol> symbolProviderOpt, // TODO: only null in DTEE case where we looking for default namesapace
......@@ -64,6 +97,8 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
ImmutableArray<ExternAliasRecord> externAliasRecords;
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap;
ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMap;
ImmutableDictionary<int, ImmutableArray<string>> tupleLocalMap;
ImmutableDictionary<LocalNameAndScope, ImmutableArray<string>> tupleLocalConstantMap;
string defaultNamespaceName;
var symMethod = symReader.GetMethodByVersion(methodToken, methodVersion);
......@@ -85,6 +120,8 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
externAliasRecords = ImmutableArray<ExternAliasRecord>.Empty;
dynamicLocalMap = null;
dynamicLocalConstantMap = null;
tupleLocalMap = null;
tupleLocalConstantMap = null;
}
else
{
......@@ -105,7 +142,9 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
allScopes,
out hoistedLocalScopeRecords,
out dynamicLocalMap,
out dynamicLocalConstantMap);
out dynamicLocalConstantMap,
out tupleLocalMap,
out tupleLocalConstantMap);
defaultNamespaceName = "";
}
......@@ -113,7 +152,7 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
var constantsBuilder = ArrayBuilder<TLocalSymbol>.GetInstance();
if (symbolProviderOpt != null) // TODO
{
GetConstants(constantsBuilder, symbolProviderOpt, containingScopes, dynamicLocalConstantMap);
GetConstants(constantsBuilder, symbolProviderOpt, containingScopes, dynamicLocalConstantMap, tupleLocalConstantMap);
}
var reuseSpan = GetReuseSpan(allScopes, ilOffset, isVisualBasicMethod);
......@@ -123,6 +162,7 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
importRecordGroups,
externAliasRecords,
dynamicLocalMap,
tupleLocalMap,
defaultNamespaceName,
containingScopes.GetLocalNames(),
constantsBuilder.ToImmutableAndFree(),
......@@ -308,11 +348,15 @@ private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider
IEnumerable<ISymUnmanagedScope> scopes,
out ImmutableArray<HoistedLocalScopeRecord> hoistedLocalScopeRecords,
out ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap,
out ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMap)
out ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMap,
out ImmutableDictionary<int, ImmutableArray<string>> tupleLocalMap,
out ImmutableDictionary<LocalNameAndScope, ImmutableArray<string>> tupleLocalConstantMap)
{
hoistedLocalScopeRecords = ImmutableArray<HoistedLocalScopeRecord>.Empty;
dynamicLocalMap = ImmutableDictionary<int, ImmutableArray<bool>>.Empty;
dynamicLocalConstantMap = ImmutableDictionary<string, ImmutableArray<bool>>.Empty;
dynamicLocalMap = null;
dynamicLocalConstantMap = null;
tupleLocalMap = null;
tupleLocalConstantMap = null;
byte[] customDebugInfoBytes = reader.GetCustomDebugInfoBytes(methodToken, methodVersion);
if (customDebugInfoBytes == null)
......@@ -329,24 +373,25 @@ private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider
GetCSharpDynamicLocalInfo(
customDebugInfoBytes,
methodToken,
methodVersion,
scopes,
out dynamicLocalMap,
out dynamicLocalConstantMap);
GetTupleElementNamesLocalInfo(
customDebugInfoBytes,
out tupleLocalMap,
out tupleLocalConstantMap);
}
/// <exception cref="InvalidOperationException">Bad data.</exception>
private static void GetCSharpDynamicLocalInfo(
byte[] customDebugInfo,
int methodToken,
int methodVersion,
IEnumerable<ISymUnmanagedScope> scopes,
out ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap,
out ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMap)
{
dynamicLocalMap = ImmutableDictionary<int, ImmutableArray<bool>>.Empty;
dynamicLocalConstantMap = ImmutableDictionary<string, ImmutableArray<bool>>.Empty;
dynamicLocalMap = null;
dynamicLocalConstantMap = null;
var record = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.DynamicLocals);
if (record.IsDefault)
......@@ -354,26 +399,38 @@ private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider
return;
}
var localKindsByName = PooledDictionary<string, LocalKind>.GetInstance();
GetLocalKindByName(localKindsByName, scopes);
ImmutableDictionary<int, ImmutableArray<bool>>.Builder localBuilder = null;
ImmutableDictionary<string, ImmutableArray<bool>>.Builder constantBuilder = null;
var dynamicLocals = RemoveAmbiguousLocals(CustomDebugInfoReader.DecodeDynamicLocalsRecord(record), scopes);
var dynamicLocals = CustomDebugInfoReader.DecodeDynamicLocalsRecord(record);
foreach (var dynamicLocal in dynamicLocals)
{
int slot = dynamicLocal.SlotId;
var flags = GetFlags(dynamicLocal);
if (slot < 0)
{
constantBuilder = constantBuilder ?? ImmutableDictionary.CreateBuilder<string, ImmutableArray<bool>>();
constantBuilder[dynamicLocal.Name] = flags;
}
else
if (slot == 0)
{
localBuilder = localBuilder ?? ImmutableDictionary.CreateBuilder<int, ImmutableArray<bool>>();
localBuilder[slot] = flags;
LocalKind kind;
var name = dynamicLocal.LocalName;
localKindsByName.TryGetValue(name, out kind);
switch (kind)
{
case LocalKind.DuplicateName:
// Drop locals with ambiguous names.
continue;
case LocalKind.ConstantName:
constantBuilder = constantBuilder ?? ImmutableDictionary.CreateBuilder<string, ImmutableArray<bool>>();
constantBuilder[name] = flags;
continue;
}
}
localBuilder = localBuilder ?? ImmutableDictionary.CreateBuilder<int, ImmutableArray<bool>>();
localBuilder[slot] = flags;
}
if (localBuilder != null)
{
dynamicLocalMap = localBuilder.ToImmutable();
......@@ -383,6 +440,8 @@ private static bool TryCreateImportRecordFromCSharpImportString(EESymbolProvider
{
dynamicLocalConstantMap = constantBuilder.ToImmutable();
}
localKindsByName.Free();
}
private static ImmutableArray<bool> GetFlags(DynamicLocalInfo bucket)
......@@ -397,28 +456,24 @@ private static ImmutableArray<bool> GetFlags(DynamicLocalInfo bucket)
return builder.ToImmutableAndFree();
}
private enum LocalKind { DuplicateName, VariableName, ConstantName }
/// <summary>
/// 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.
/// This method returns the local kind (variable, constant, or duplicate) based on name.
/// </summary>
private static ImmutableArray<DynamicLocalInfo> RemoveAmbiguousLocals(
ImmutableArray<DynamicLocalInfo> dynamicLocals,
IEnumerable<ISymUnmanagedScope> scopes)
private static void GetLocalKindByName(Dictionary<string, LocalKind> localNames, IEnumerable<ISymUnmanagedScope> scopes)
{
const byte DuplicateName = 0;
const byte VariableName = 1;
const byte ConstantName = 2;
var localNames = PooledDictionary<string, byte>.GetInstance();
Debug.Assert(localNames.Count == 0);
var firstLocal = scopes.SelectMany(scope => scope.GetLocals()).FirstOrDefault(variable => variable.GetSlot() == 0);
if (firstLocal != null)
var localSlot0 = scopes.SelectMany(scope => scope.GetLocals()).FirstOrDefault(variable => variable.GetSlot() == 0);
if (localSlot0 != null)
{
localNames.Add(firstLocal.GetName(), VariableName);
localNames.Add(localSlot0.GetName(), LocalKind.VariableName);
}
foreach (var scope in scopes)
......@@ -426,36 +481,55 @@ private static ImmutableArray<bool> GetFlags(DynamicLocalInfo bucket)
foreach (var constant in scope.GetConstants())
{
string name = constant.GetName();
localNames[name] = localNames.ContainsKey(name) ? DuplicateName : ConstantName;
localNames[name] = localNames.ContainsKey(name) ? LocalKind.DuplicateName : LocalKind.ConstantName;
}
}
}
var builder = ArrayBuilder<DynamicLocalInfo>.GetInstance();
foreach (var dynamicLocal in dynamicLocals)
private static void GetTupleElementNamesLocalInfo(
byte[] customDebugInfo,
out ImmutableDictionary<int, ImmutableArray<string>> tupleLocalMap,
out ImmutableDictionary<LocalNameAndScope, ImmutableArray<string>> tupleLocalConstantMap)
{
tupleLocalMap = null;
tupleLocalConstantMap = null;
var record = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.TupleElementNames);
if (record.IsDefault)
{
int slot = dynamicLocal.SlotId;
var name = dynamicLocal.Name;
if (slot == 0)
{
byte localOrConstant;
localNames.TryGetValue(name, out localOrConstant);
if (localOrConstant == DuplicateName)
{
continue;
}
return;
}
if (localOrConstant == ConstantName)
{
slot = -1;
}
ImmutableDictionary<int, ImmutableArray<string>>.Builder localBuilder = null;
ImmutableDictionary<LocalNameAndScope, ImmutableArray<string>>.Builder constantBuilder = null;
var tuples = CustomDebugInfoReader.DecodeTupleElementNamesRecord(record);
foreach (var tuple in tuples)
{
var slotIndex = tuple.SlotIndex;
var elementNames = tuple.ElementNames;
if (slotIndex < 0)
{
constantBuilder = constantBuilder ?? ImmutableDictionary.CreateBuilder<LocalNameAndScope, ImmutableArray<string>>();
var localAndScope = new LocalNameAndScope(tuple.LocalName, tuple.ScopeStart, tuple.ScopeEnd);
constantBuilder[localAndScope] = elementNames;
}
else
{
localBuilder = localBuilder ?? ImmutableDictionary.CreateBuilder<int, ImmutableArray<string>>();
localBuilder[slotIndex] = elementNames;
}
}
builder.Add(new DynamicLocalInfo(dynamicLocal.FlagCount, dynamicLocal.Flags, slot, name));
if (localBuilder != null)
{
tupleLocalMap = localBuilder.ToImmutable();
}
var result = builder.ToImmutableAndFree();
localNames.Free();
return result;
if (constantBuilder != null)
{
tupleLocalConstantMap = constantBuilder.ToImmutable();
}
}
private static void ReadVisualBasicImportsDebugInfo(
......@@ -574,11 +648,12 @@ private static ILSpan GetReuseSpan(ArrayBuilder<ISymUnmanagedScope> scopes, int
scopes.Select(scope => new ILSpan((uint)scope.GetStartOffset(), (uint)(scope.GetEndOffset() + (isEndInclusive ? 1 : 0)))));
}
public static void GetConstants(
private static void GetConstants(
ArrayBuilder<TLocalSymbol> builder,
EESymbolProvider<TTypeSymbol, TLocalSymbol> symbolProvider,
ArrayBuilder<ISymUnmanagedScope> scopes,
ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMapOpt)
ImmutableDictionary<string, ImmutableArray<bool>> dynamicLocalConstantMapOpt,
ImmutableDictionary<LocalNameAndScope, ImmutableArray<string>> tupleLocalConstantMapOpt)
{
foreach (var scope in scopes)
{
......@@ -613,9 +688,20 @@ private static ILSpan GetReuseSpan(ArrayBuilder<ISymUnmanagedScope> scopes, int
}
var dynamicFlags = default(ImmutableArray<bool>);
dynamicLocalConstantMapOpt?.TryGetValue(name, out dynamicFlags);
if (dynamicLocalConstantMapOpt != null)
{
dynamicLocalConstantMapOpt.TryGetValue(name, out dynamicFlags);
}
builder.Add(symbolProvider.GetLocalConstant(name, type, constantValue, dynamicFlags));
var tupleElementNames = default(ImmutableArray<string>);
if (tupleLocalConstantMapOpt != null)
{
int scopeStart = scope.GetStartOffset();
int scopeEnd = scope.GetEndOffset();
tupleLocalConstantMapOpt.TryGetValue(new LocalNameAndScope(name, scopeStart, scopeEnd), out tupleElementNames);
}
builder.Add(symbolProvider.GetLocalConstant(name, type, constantValue, dynamicFlags, tupleElementNames));
}
}
}
......@@ -631,7 +717,8 @@ private static ILSpan GetReuseSpan(ArrayBuilder<ISymUnmanagedScope> scopes, int
EESymbolProvider<TTypeSymbol, TLocalSymbol> symbolProvider,
ImmutableArray<string> names,
ImmutableArray<LocalInfo<TTypeSymbol>> localInfo,
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMapOpt)
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMapOpt,
ImmutableDictionary<int, ImmutableArray<string>> tupleLocalConstantMapOpt)
{
if (localInfo.Length == 0)
{
......@@ -653,7 +740,10 @@ private static ILSpan GetReuseSpan(ArrayBuilder<ISymUnmanagedScope> scopes, int
var dynamicFlags = default(ImmutableArray<bool>);
dynamicLocalMapOpt?.TryGetValue(i, out dynamicFlags);
builder.Add(symbolProvider.GetLocalVariable(name, i, localInfo[i], dynamicFlags));
var tupleElementNames = default(ImmutableArray<string>);
tupleLocalConstantMapOpt?.TryGetValue(i, out tupleElementNames);
builder.Add(symbolProvider.GetLocalVariable(name, i, localInfo[i], dynamicFlags, tupleElementNames));
}
}
}
......
......@@ -6,6 +6,7 @@
using System.Reflection.Metadata.Ecma335;
using System.Text;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.Collections;
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
......@@ -16,7 +17,8 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
{
string defaultNamespace;
ImmutableArray<HoistedLocalScopeRecord> hoistedLocalScopes;
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocals;
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap;
ImmutableDictionary<int, ImmutableArray<string>> tupleLocalMap;
ImmutableArray<ImmutableArray<ImportRecord>> importGroups;
ImmutableArray<ExternAliasRecord> externAliases;
ImmutableArray<string> localVariableNames;
......@@ -25,14 +27,27 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
var methodHandle = (MethodDefinitionHandle)MetadataTokens.EntityHandle(methodToken);
ReadLocalScopeInformation(reader, methodHandle, ilOffset, symbolProvider, isVisualBasicMethod, out importGroups, out externAliases, out localVariableNames, out dynamicLocals, out localConstants, out reuseSpan);
ReadLocalScopeInformation(
reader,
methodHandle,
ilOffset,
symbolProvider,
isVisualBasicMethod,
out importGroups,
out externAliases,
out localVariableNames,
out dynamicLocalMap,
out tupleLocalMap,
out localConstants,
out reuseSpan);
ReadMethodCustomDebugInformation(reader, methodHandle, out hoistedLocalScopes, out defaultNamespace);
return new MethodDebugInfo<TTypeSymbol, TLocalSymbol>(
hoistedLocalScopes,
importGroups,
externAliases,
dynamicLocals,
dynamicLocalMap,
tupleLocalMap,
defaultNamespace,
localVariableNames,
localConstants,
......@@ -48,7 +63,8 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
out ImmutableArray<ImmutableArray<ImportRecord>> importGroups,
out ImmutableArray<ExternAliasRecord> externAliases,
out ImmutableArray<string> localVariableNames,
out ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocals,
out ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap,
out ImmutableDictionary<int, ImmutableArray<string>> tupleLocalMap,
out ImmutableArray<TLocalSymbol> localConstants,
out ILSpan reuseSpan)
{
......@@ -56,6 +72,7 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
var localConstantsBuilder = ArrayBuilder<TLocalSymbol>.GetInstance();
ImmutableDictionary<int, ImmutableArray<bool>>.Builder lazyDynamicLocalsBuilder = null;
ImmutableDictionary<int, ImmutableArray<string>>.Builder lazyTupleLocalsBuilder = null;
var innerMostImportScope = default(ImportScopeHandle);
uint reuseSpanStart = 0;
......@@ -105,6 +122,13 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
lazyDynamicLocalsBuilder = lazyDynamicLocalsBuilder ?? ImmutableDictionary.CreateBuilder<int, ImmutableArray<bool>>();
lazyDynamicLocalsBuilder[variable.Index] = dynamicFlags;
}
var tupleElementNames = ReadTupleCustomDebugInformation(reader, variableHandle);
if (!tupleElementNames.IsDefault)
{
lazyTupleLocalsBuilder = lazyTupleLocalsBuilder ?? ImmutableDictionary.CreateBuilder<int, ImmutableArray<string>>();
lazyTupleLocalsBuilder[variable.Index] = tupleElementNames;
}
}
// constants (from all contained scopes):
......@@ -117,8 +141,10 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
var sigReader = reader.GetBlobReader(constant.Signature);
symbolProvider.DecodeLocalConstant(ref sigReader, out typeSymbol, out value);
var name = reader.GetString(constant.Name);
var dynamicFlags = ReadDynamicCustomDebugInformation(reader, constantHandle);
localConstantsBuilder.Add(symbolProvider.GetLocalConstant(reader.GetString(constant.Name), typeSymbol, value, dynamicFlags));
var tupleElementNames = ReadTupleCustomDebugInformation(reader, constantHandle);
localConstantsBuilder.Add(symbolProvider.GetLocalConstant(name, typeSymbol, value, dynamicFlags, tupleElementNames));
}
}
catch (Exception e) when (e is UnsupportedSignatureContent || e is BadImageFormatException)
......@@ -131,7 +157,8 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
{
localVariableNames = localVariableNamesBuilder.ToImmutableAndFree();
localConstants = localConstantsBuilder.ToImmutableAndFree();
dynamicLocals = lazyDynamicLocalsBuilder?.ToImmutable();
dynamicLocalMap = lazyDynamicLocalsBuilder?.ToImmutable();
tupleLocalMap = lazyTupleLocalsBuilder?.ToImmutable();
reuseSpan = new ILSpan(reuseSpanStart, reuseSpanEnd);
}
......@@ -160,6 +187,25 @@ private static string ReadUtf8String(MetadataReader reader, BlobHandle handle)
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
/// <summary>
/// Read UTF8 string with null terminator.
/// </summary>
private static string ReadUtf8String(ref BlobReader reader)
{
var builder = ArrayBuilder<byte>.GetInstance();
while (true)
{
var b = reader.ReadByte();
if (b == 0)
{
break;
}
builder.Add(b);
}
var bytes = builder.ToArrayAndFree();
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
private static void PopulateImports(
MetadataReader reader,
......@@ -262,65 +308,65 @@ private static string ReadUtf8String(MetadataReader reader, BlobHandle handle)
out ImmutableArray<HoistedLocalScopeRecord> hoistedLocalScopes,
out string defaultNamespace)
{
hoistedLocalScopes = ImmutableArray<HoistedLocalScopeRecord>.Empty;
CustomDebugInformation info;
foreach (var infoHandle in reader.GetCustomDebugInformation(methodHandle))
{
var info = reader.GetCustomDebugInformation(infoHandle);
var id = reader.GetGuid(info.Kind);
if (id == PortableCustomDebugInfoKinds.StateMachineHoistedLocalScopes)
{
// only single CDIof this kind is allowed on a method:
if (!hoistedLocalScopes.IsEmpty)
{
throw new BadImageFormatException();
}
hoistedLocalScopes = DecodeHoistedLocalScopes(reader.GetBlobReader(info.Value));
}
}
hoistedLocalScopes = TryGetCustomDebugInformation(reader, methodHandle, PortableCustomDebugInfoKinds.StateMachineHoistedLocalScopes, out info) ?
DecodeHoistedLocalScopes(reader.GetBlobReader(info.Value)) :
ImmutableArray<HoistedLocalScopeRecord>.Empty;
// TODO: consider looking this up once per module (not for every method)
defaultNamespace = null;
foreach (var infoHandle in reader.GetCustomDebugInformation(EntityHandle.ModuleDefinition))
{
var info = reader.GetCustomDebugInformation(infoHandle);
var id = reader.GetGuid(info.Kind);
if (id == PortableCustomDebugInfoKinds.DefaultNamespace)
{
// only single CDI of this kind is allowed on the module:
if (defaultNamespace != null)
{
throw new BadImageFormatException();
}
defaultNamespace = TryGetCustomDebugInformation(reader, EntityHandle.ModuleDefinition, PortableCustomDebugInfoKinds.DefaultNamespace, out info) ?
DecodeDefaultNamespace(reader.GetBlobReader(info.Value)) :
null;
defaultNamespace = defaultNamespace ?? "";
}
var valueReader = reader.GetBlobReader(info.Value);
defaultNamespace = valueReader.ReadUTF8(valueReader.Length);
}
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
private static ImmutableArray<bool> ReadDynamicCustomDebugInformation(MetadataReader reader, EntityHandle variableOrConstantHandle)
{
CustomDebugInformation info;
if (TryGetCustomDebugInformation(reader, variableOrConstantHandle, PortableCustomDebugInfoKinds.DynamicLocalVariables, out info))
{
return DecodeDynamicFlags(reader.GetBlobReader(info.Value));
}
return default(ImmutableArray<bool>);
}
defaultNamespace = defaultNamespace ?? "";
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
private static ImmutableArray<string> ReadTupleCustomDebugInformation(MetadataReader reader, EntityHandle variableOrConstantHandle)
{
CustomDebugInformation info;
if (TryGetCustomDebugInformation(reader, variableOrConstantHandle, PortableCustomDebugInfoKinds.TupleElementNames, out info))
{
return DecodeTupleElementNames(reader.GetBlobReader(info.Value));
}
return default(ImmutableArray<string>);
}
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
private static ImmutableArray<bool> ReadDynamicCustomDebugInformation(MetadataReader reader, EntityHandle variableOrConstantHandle)
private static bool TryGetCustomDebugInformation(MetadataReader reader, EntityHandle handle, Guid kind, out CustomDebugInformation customDebugInfo)
{
foreach (var infoHandle in reader.GetCustomDebugInformation(variableOrConstantHandle))
bool foundAny = false;
customDebugInfo = default(CustomDebugInformation);
foreach (var infoHandle in reader.GetCustomDebugInformation(handle))
{
var info = reader.GetCustomDebugInformation(infoHandle);
var id = reader.GetGuid(info.Kind);
if (id == PortableCustomDebugInfoKinds.DynamicLocalVariables)
if (id == kind)
{
return DecodeDynamicFlags(reader.GetBlobReader(info.Value));
if (foundAny)
{
throw new BadImageFormatException();
}
customDebugInfo = info;
foundAny = true;
}
}
return default(ImmutableArray<bool>);
return foundAny;
}
// internal for testing
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
internal static ImmutableArray<bool> DecodeDynamicFlags(BlobReader reader)
private static ImmutableArray<bool> DecodeDynamicFlags(BlobReader reader)
{
var builder = ImmutableArray.CreateBuilder<bool>(reader.Length * 8);
......@@ -336,6 +382,18 @@ internal static ImmutableArray<bool> DecodeDynamicFlags(BlobReader reader)
return builder.MoveToImmutable();
}
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
private static ImmutableArray<string> DecodeTupleElementNames(BlobReader reader)
{
var builder = ArrayBuilder<string>.GetInstance();
while (reader.RemainingBytes > 0)
{
var value = ReadUtf8String(ref reader);
builder.Add(value.Length == 0 ? null : value);
}
return builder.ToImmutableAndFree();
}
/// <exception cref="BadImageFormatException">Invalid data format.</exception>
private static ImmutableArray<HoistedLocalScopeRecord> DecodeHoistedLocalScopes(BlobReader reader)
{
......@@ -352,5 +410,10 @@ private static ImmutableArray<HoistedLocalScopeRecord> DecodeHoistedLocalScopes(
return result.ToImmutableAndFree();
}
private static string DecodeDefaultNamespace(BlobReader reader)
{
return reader.ReadUTF8(reader.Length);
}
}
}
......@@ -10,38 +10,33 @@ internal partial class MethodDebugInfo<TTypeSymbol, TLocalSymbol>
where TTypeSymbol : class, ITypeSymbol
where TLocalSymbol : class
{
public static readonly MethodDebugInfo<TTypeSymbol, TLocalSymbol> None = new MethodDebugInfo<TTypeSymbol, TLocalSymbol>();
public static readonly MethodDebugInfo<TTypeSymbol, TLocalSymbol> None = new MethodDebugInfo<TTypeSymbol, TLocalSymbol>(
ImmutableArray<HoistedLocalScopeRecord>.Empty,
ImmutableArray<ImmutableArray<ImportRecord>>.Empty,
ImmutableArray<ExternAliasRecord>.Empty,
null,
null,
"",
ImmutableArray<string>.Empty,
ImmutableArray<TLocalSymbol>.Empty,
ILSpan.MaxValue);
public readonly ImmutableArray<HoistedLocalScopeRecord> HoistedLocalScopeRecords;
public readonly ImmutableArray<ImmutableArray<ImportRecord>> ImportRecordGroups;
public readonly ImmutableArray<ExternAliasRecord> ExternAliasRecords; // C# only.
public readonly ImmutableDictionary<int, ImmutableArray<bool>> DynamicLocalMap; // C# only.
public readonly ImmutableDictionary<int, ImmutableArray<string>> TupleLocalMap;
public readonly string DefaultNamespaceName; // VB only.
// TODO: readonly
public ImmutableArray<string> LocalVariableNames { get; private set; }
public ImmutableArray<TLocalSymbol> LocalConstants { get; private set; }
public ILSpan ReuseSpan;
private MethodDebugInfo()
: this(ImmutableArray<HoistedLocalScopeRecord>.Empty,
ImmutableArray<ImmutableArray<ImportRecord>>.Empty,
ImmutableArray<ExternAliasRecord>.Empty,
null,
"",
ImmutableArray<string>.Empty,
ImmutableArray<TLocalSymbol>.Empty,
ILSpan.MaxValue)
{
}
public readonly ImmutableArray<string> LocalVariableNames;
public readonly ImmutableArray<TLocalSymbol> LocalConstants;
public readonly ILSpan ReuseSpan;
public MethodDebugInfo(
ImmutableArray<HoistedLocalScopeRecord> hoistedLocalScopeRecords,
ImmutableArray<ImmutableArray<ImportRecord>> importRecordGroups,
ImmutableArray<ExternAliasRecord> externAliasRecords,
ImmutableDictionary<int, ImmutableArray<bool>> dynamicLocalMap,
ImmutableDictionary<int, ImmutableArray<string>> tupleLocalMap,
string defaultNamespaceName,
ImmutableArray<string> localVariableNames,
ImmutableArray<TLocalSymbol> localConstants,
......@@ -57,6 +52,7 @@ private MethodDebugInfo()
ExternAliasRecords = externAliasRecords;
DynamicLocalMap = dynamicLocalMap;
TupleLocalMap = tupleLocalMap;
DefaultNamespaceName = defaultNamespaceName;
......@@ -67,7 +63,7 @@ private MethodDebugInfo()
public ImmutableSortedSet<int> GetInScopeHoistedLocalIndices(int ilOffset, ref ILSpan methodContextReuseSpan)
{
if (this.HoistedLocalScopeRecords.IsDefault)
if (this.HoistedLocalScopeRecords.IsEmpty)
{
return ImmutableSortedSet<int>.Empty;
}
......
......@@ -2,7 +2,6 @@
extern alias PDB;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
......@@ -16,7 +15,6 @@
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit;
......
......@@ -9,9 +9,9 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports Roslyn.Utilities
Imports System.Runtime.InteropServices
Imports System.Reflection.Metadata
Imports System.Runtime.InteropServices
Imports Roslyn.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
......@@ -110,8 +110,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
id:=Nothing,
pdbAttributes:=LocalVariableAttributes.None,
constraints:=constraints,
isDynamic:=False,
dynamicTransformFlags:=ImmutableArray(Of TypedConstant).Empty)
dynamicTransformFlags:=ImmutableArray(Of TypedConstant).Empty,
tupleElementNames:=ImmutableArray(Of TypedConstant).Empty)
End Function
Friend Overrides ReadOnly Property AllowOmissionOfConditionalCalls As Boolean
......@@ -141,8 +141,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
id As LocalDebugId,
pdbAttributes As LocalVariableAttributes,
constraints As LocalSlotConstraints,
isDynamic As Boolean,
dynamicTransformFlags As ImmutableArray(Of TypedConstant)) As LocalDefinition
dynamicTransformFlags As ImmutableArray(Of TypedConstant),
tupleElementNames As ImmutableArray(Of TypedConstant)) As LocalDefinition
Dim local = TryCast(symbol, EELocalSymbol)
If local Is Nothing Then
......
......@@ -204,7 +204,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Dim localsBuilder = ArrayBuilder(Of LocalSymbol).GetInstance()
Dim inScopeHoistedLocalNames As ImmutableHashSet(Of String) = Nothing
Dim localNames = GetActualLocalNames(debugInfo.LocalVariableNames, inScopeHoistedLocalNames)
MethodDebugInfo(Of TypeSymbol, LocalSymbol).GetLocals(localsBuilder, symbolProvider, localNames, localInfo, Nothing)
MethodDebugInfo(Of TypeSymbol, LocalSymbol).GetLocals(localsBuilder, symbolProvider, localNames, localInfo, Nothing, debugInfo.TupleLocalMap)
Dim inScopeHoistedLocals = New VisualBasicInScopeHoistedLocalsByName(inScopeHoistedLocalNames)
GetStaticLocals(localsBuilder, currentFrame, methodHandle, metadataDecoder)
......@@ -344,7 +344,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
importRecordGroups:=importRecordGroups,
defaultNamespaceName:="",
externAliasRecords:=ImmutableArray(Of ExternAliasRecord).Empty,
dynamicLocalMap:=ImmutableDictionary(Of Integer, ImmutableArray(Of Boolean)).Empty,
dynamicLocalMap:=Nothing,
tupleLocalMap:=Nothing,
localVariableNames:=ImmutableArray(Of String).Empty,
localConstants:=ImmutableArray(Of LocalSymbol).Empty,
reuseSpan:=Nothing)
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System
Imports System.Collections.Immutable
Imports System.Diagnostics
......@@ -6,7 +7,6 @@ Imports System.Reflection.Metadata
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Roslyn.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
......@@ -21,7 +21,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
_method = method
End Sub
Public Overrides Function GetLocalVariable(name As String, slotIndex As Integer, info As LocalInfo(Of TypeSymbol), dynamicFlagsOpt As ImmutableArray(Of Boolean)) As LocalSymbol
Public Overrides Function GetLocalVariable(
name As String,
slotIndex As Integer,
info As LocalInfo(Of TypeSymbol),
dynamicFlagsOpt As ImmutableArray(Of Boolean),
tupleElementNamesOpt As ImmutableArray(Of String)) As LocalSymbol
' ignore dynamic flags (in theory the CDI might be present in bad PDB)
' Custom modifiers can be dropped since binding ignores custom
......@@ -31,7 +37,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return New EELocalSymbol(_method, EELocalSymbol.NoLocations, name, slotIndex, kind, info.Type, info.IsByRef, info.IsPinned, canScheduleToStack:=False)
End Function
Public Overrides Function GetLocalConstant(name As String, type As TypeSymbol, value As ConstantValue, dynamicFlagsOpt As ImmutableArray(Of Boolean)) As LocalSymbol
Public Overrides Function GetLocalConstant(
name As String,
type As TypeSymbol,
value As ConstantValue,
dynamicFlagsOpt As ImmutableArray(Of Boolean),
tupleElementNamesOpt As ImmutableArray(Of String)) As LocalSymbol
' ignore dynamic flags (in theory the CDI might be present in bad PDB)
Return New EELocalConstantSymbol(_method, name, type, value)
End Function
......
......@@ -27,6 +27,10 @@
<Project>{2523D0E6-DF32-4A3E-8AE0-A19BFFAE2EF6}</Project>
<Name>BasicCodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Compilers\CSharp\Portable\CSharpCodeAnalysis.csproj">
<Project>{b501a547-c911-4a05-ac6e-274a50dff30e}</Project>
<Name>CSharpCodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Core\Source\ExpressionCompiler\ExpressionCompiler.csproj">
<Project>{b8da3a90-a60c-42e3-9d8e-6c67b800c395}</Project>
<Name>ExpressionCompiler</Name>
......@@ -95,6 +99,7 @@
<Compile Include="ImportDebugInfoTests.vb" />
<Compile Include="StatementTests.vb" />
<Compile Include="StaticLocalsTests.vb" />
<Compile Include="TupleTests.vb" />
<Compile Include="WinMdTests.vb" />
</ItemGroup>
<ItemGroup>
......@@ -105,4 +110,4 @@
</ItemGroup>
<Import Project="..\..\..\..\..\build\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\..\..\build\Targets\Roslyn.Toolsets.Xunit.targets" />
</Project>
</Project>
\ No newline at end of file
......@@ -358,5 +358,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.UnitTests
Dim symbolProvider = New VisualBasicEESymbolProvider(peModule, peMethod)
Return MethodDebugInfo(Of TypeSymbol, LocalSymbol).ReadMethodDebugInfo(DirectCast(symReader, ISymUnmanagedReader3), symbolProvider, MetadataTokens.GetToken(peMethod.Handle), methodVersion:=1, ilOffset:=ilOffset, isVisualBasicMethod:=True)
End Function
Friend Shared Function GetTupleElementNamesAttributeIfAny(method As IMethodSymbol) As SynthesizedAttributeData
Return GetAttributeIfAny(method, "System.Runtime.CompilerServices.TupleElementNamesAttribute")
End Function
Friend Shared Function GetAttributeIfAny(method As IMethodSymbol, typeName As String) As SynthesizedAttributeData
Return method.GetSynthesizedAttributes(forReturnType:=True).
Where(Function(a) a.AttributeClass.ToTestDisplayString() = typeName).
SingleOrDefault()
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.ObjectModel
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation
Imports Roslyn.Test.Utilities
Imports Xunit
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.UnitTests
Public Class TupleTests
Inherits ExpressionCompilerTestBase
<WorkItem(13948, "https://github.com/dotnet/roslyn/issues/13948")>
<Fact(Skip:="13948")>
Public Sub Local()
Const source =
"class C
{
static void M()
{
(int A, int B) o = (1, 2);
}
}"
Dim comp = CreateCSharpCompilation(source, referencedAssemblies:={MscorlibRef, ValueTupleRef})
WithRuntimeInstance(comp,
Sub(runtime)
Dim context = CreateMethodContext(runtime, "C.M")
Dim testData = New CompilationTestData()
Dim locals = ArrayBuilder(Of LocalAndMethod).GetInstance()
Dim typeName As String = Nothing
Dim assembly = context.CompileGetLocals(locals, argumentsOnly:=False, typeName:=typeName, testData:=testData)
Assert.Equal(1, locals.Count)
Dim typeInfo As ReadOnlyCollection(Of Byte) = Nothing
Dim typeInfoId = locals(0).GetCustomTypeInfo(typeInfo)
Dim dynamicFlags As ReadOnlyCollection(Of Byte) = Nothing
Dim tupleElementNames As ReadOnlyCollection(Of String) = Nothing
CustomTypeInfo.Decode(typeInfoId, typeInfo, dynamicFlags, tupleElementNames)
Assert.Equal({"A", "B"}, tupleElementNames)
Dim method = testData.Methods.Single().Value.Method
Assert.NotNull(GetTupleElementNamesAttributeIfAny(method))
Assert.True(method.ReturnType.IsTupleType)
VerifyLocal(testData, typeName, locals(0), "<>m0", "o", expectedILOpt:=
"{
// Code size 2 (0x2)
.maxstack 1
.locals init ((int A, int B) V_0) //o
IL_0000: ldloc.0
IL_0001: ret
}")
locals.Free()
End Sub)
End Sub
<WorkItem(13948, "https://github.com/dotnet/roslyn/issues/13948")>
<Fact(Skip:="13948")>
Public Sub Constant()
Const source =
"class A<T>
{
internal class B<U>
{
}
}
class C
{
static (object, object) F;
static void M()
{
const A<(int, int A)>.B<(object B, object)>[] c = null;
}
}"
Dim comp = CreateCSharpCompilation(source, referencedAssemblies:={MscorlibRef, ValueTupleRef})
WithRuntimeInstance(comp,
Sub(runtime)
Dim context = CreateMethodContext(runtime, "C.M")
Dim testData = New CompilationTestData()
Dim locals = ArrayBuilder(Of LocalAndMethod).GetInstance()
Dim typeName As String = Nothing
Dim assembly = context.CompileGetLocals(locals, argumentsOnly:=False, typeName:=typeName, testData:=testData)
Assert.Equal(1, locals.Count)
Dim typeInfo As ReadOnlyCollection(Of Byte) = Nothing
Dim typeInfoId = locals(0).GetCustomTypeInfo(typeInfo)
Dim dynamicFlags As ReadOnlyCollection(Of Byte) = Nothing
Dim tupleElementNames As ReadOnlyCollection(Of String) = Nothing
CustomTypeInfo.Decode(typeInfoId, typeInfo, dynamicFlags, tupleElementNames)
Assert.Equal({Nothing, "A", "B", Nothing}, tupleElementNames)
Dim method = DirectCast(testData.Methods.Single().Value.Method, MethodSymbol)
Assert.NotNull(GetTupleElementNamesAttributeIfAny(method))
Dim returnType = method.ReturnType
Assert.False(returnType.IsTupleType)
Assert.True(returnType.ContainsTuple())
VerifyLocal(testData, typeName, locals(0), "<>m0", "c", expectedFlags:=DkmClrCompilationResultFlags.ReadOnlyResult, expectedILOpt:=
"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldnull
IL_0001: ret
}")
locals.Free()
End Sub)
End Sub
End Class
End Namespace
......@@ -295,6 +295,9 @@ private void WriteCustomDebugInfo(byte[] bytes)
case CustomDebugInfoKind.EditAndContinueLambdaMap:
WriteEditAndContinueLambdaMap(record);
break;
case CustomDebugInfoKind.TupleElementNames:
WriteTupleElementNamesCustomDebugInfo(record);
break;
default:
WriteUnknownCustomDebugInfo(record);
break;
......@@ -470,13 +473,50 @@ private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
_writer.WriteAttributeString("flagCount", CultureInvariantToString(flagCount));
_writer.WriteAttributeString("flags", pooled.ToStringAndFree());
_writer.WriteAttributeString("slotId", CultureInvariantToString(dynamicLocal.SlotId));
_writer.WriteAttributeString("localName", dynamicLocal.Name);
_writer.WriteAttributeString("localName", dynamicLocal.LocalName);
_writer.WriteEndElement(); //bucket
}
_writer.WriteEndElement(); //dynamicLocals
}
private void WriteTupleElementNamesCustomDebugInfo(CustomDebugInfoRecord record)
{
Debug.Assert(record.Kind == CustomDebugInfoKind.TupleElementNames);
_writer.WriteStartElement("tupleElementNames");
var tuples = CustomDebugInfoReader.DecodeTupleElementNamesRecord(record.Data);
foreach (var tuple in tuples)
{
_writer.WriteStartElement("local");
_writer.WriteAttributeString("elementNames", JoinNames(tuple.ElementNames));
_writer.WriteAttributeString("slotIndex", CultureInvariantToString(tuple.SlotIndex));
_writer.WriteAttributeString("localName", tuple.LocalName);
_writer.WriteAttributeString("scopeStart", AsILOffset(tuple.ScopeStart));
_writer.WriteAttributeString("scopeEnd", AsILOffset(tuple.ScopeEnd));
_writer.WriteEndElement();
}
_writer.WriteEndElement();
}
private static string JoinNames(ImmutableArray<string> names)
{
var pooledBuilder = PooledStringBuilder.GetInstance();
var builder = pooledBuilder.Builder;
foreach (var name in names)
{
builder.Append('|');
if (name != null)
{
builder.Append(name);
}
}
return pooledBuilder.ToStringAndFree();
}
private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record)
{
Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册