diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj
index 44f46341125a1..bb88c53d90628 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpExpressionCompiler.csproj
@@ -79,6 +79,7 @@
+
@@ -113,4 +114,4 @@
-
+
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs
index 11fa7caa89349..63ffdb55f5eb3 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpFrameDecoder.cs
@@ -1,12 +1,15 @@
// 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.Diagnostics;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
[DkmReportNonFatalWatsonException(ExcludeExceptionType = typeof(NotImplementedException)), DkmContinueCorruptingException]
- internal sealed class CSharpFrameDecoder : FrameDecoder
+ internal sealed class CSharpFrameDecoder : FrameDecoder
{
public CSharpFrameDecoder()
: base(CSharpInstructionDecoder.Instance)
diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs
index 0c155cc3954c9..772ecc5898a15 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs
@@ -1,5 +1,7 @@
// 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.Diagnostics;
+using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
@@ -9,7 +11,7 @@
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
- internal sealed class CSharpInstructionDecoder : InstructionDecoder
+ internal sealed class CSharpInstructionDecoder : InstructionDecoder
{
// This string was not localized in the old EE. We'll keep it that way
// so as not to break consumers who may have been parsing frame names...
@@ -28,7 +30,7 @@ private CSharpInstructionDecoder()
AddMemberOptions(SymbolDisplayMemberOptions.IncludeParameters).
WithParameterOptions(SymbolDisplayParameterOptions.IncludeType);
- internal override void AppendFullName(StringBuilder builder, PEMethodSymbol method)
+ internal override void AppendFullName(StringBuilder builder, MethodSymbol method)
{
var displayFormat =
((method.MethodKind == MethodKind.PropertyGet) || (method.MethodKind == MethodKind.PropertySet)) ?
@@ -86,7 +88,28 @@ internal override void AppendFullName(StringBuilder builder, PEMethodSymbol meth
}
}
- internal override PEMethodSymbol GetMethod(DkmClrInstructionAddress instructionAddress)
+ internal override MethodSymbol ConstructMethod(MethodSymbol method, ImmutableArray typeParameters, ImmutableArray typeArguments)
+ {
+ var methodArity = method.Arity;
+ var methodArgumentStartIndex = typeParameters.Length - methodArity;
+ var typeMap = new TypeMap(
+ ImmutableArray.Create(typeParameters, 0, methodArgumentStartIndex),
+ ImmutableArray.Create(typeArguments, 0, methodArgumentStartIndex));
+ var substitutedType = typeMap.SubstituteNamedType(method.ContainingType);
+ method = method.AsMember(substitutedType);
+ if (methodArity > 0)
+ {
+ method = method.Construct(ImmutableArray.Create(typeArguments, methodArgumentStartIndex, methodArity));
+ }
+ return method;
+ }
+
+ internal override ImmutableArray GetAllTypeParameters(MethodSymbol method)
+ {
+ return method.GetAllTypeParameters();
+ }
+
+ internal override CSharpCompilation GetCompilation(DkmClrInstructionAddress instructionAddress)
{
var moduleInstance = instructionAddress.ModuleInstance;
var appDomain = moduleInstance.AppDomain;
@@ -105,7 +128,18 @@ internal override PEMethodSymbol GetMethod(DkmClrInstructionAddress instructionA
compilation = dataItem.Compilation;
}
- return compilation.GetSourceMethod(moduleInstance.Mvid, instructionAddress.MethodId.Token);
+ return compilation;
+ }
+
+ internal override MethodSymbol GetMethod(CSharpCompilation compilation, DkmClrInstructionAddress instructionAddress)
+ {
+ return compilation.GetSourceMethod(instructionAddress.ModuleInstance.Mvid, instructionAddress.MethodId.Token);
+ }
+
+ internal override TypeNameDecoder GetTypeNameDecoder(CSharpCompilation compilation, MethodSymbol method)
+ {
+ Debug.Assert(method is PEMethodSymbol);
+ return new EETypeNameDecoder(compilation, (PEModuleSymbol)method.ContainingModule);
}
}
}
diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs
index f068b0364d1cf..5787513d95ebc 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpLanguageInstructionDecoder.cs
@@ -2,16 +2,17 @@
using System;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
[DkmReportNonFatalWatsonException(ExcludeExceptionType = typeof(NotImplementedException)), DkmContinueCorruptingException]
- internal sealed class CSharpLanguageInstructionDecoder : LanguageInstructionDecoder
+ internal sealed class CSharpLanguageInstructionDecoder : LanguageInstructionDecoder
{
public CSharpLanguageInstructionDecoder()
: base(CSharpInstructionDecoder.Instance)
{
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs
index 4fd8220a4b1c4..26345a6da8a8a 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs
@@ -245,14 +245,6 @@ internal CommonPEModuleBuilder CompileAssignment(
return module;
}
- private static ImmutableArray GetAllTypeParameters(MethodSymbol method)
- {
- var builder = ArrayBuilder.GetInstance();
- method.ContainingType.GetAllTypeParameters(builder);
- builder.AddRange(method.TypeParameters);
- return builder.ToImmutableAndFree();
- }
-
private static string GetNextMethodName(ArrayBuilder builder)
{
return string.Format("<>m{0}", builder.Count);
@@ -270,7 +262,7 @@ internal CommonPEModuleBuilder CompileGetLocals(
DiagnosticBag diagnostics)
{
var objectType = this.Compilation.GetSpecialType(SpecialType.System_Object);
- var allTypeParameters = GetAllTypeParameters(_currentFrame);
+ var allTypeParameters = _currentFrame.GetAllTypeParameters();
var additionalTypes = ArrayBuilder.GetInstance();
EENamedTypeSymbol typeVariablesType = null;
diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymbolExtensions.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymbolExtensions.cs
new file mode 100644
index 0000000000000..d498e56290b11
--- /dev/null
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SymbolExtensions.cs
@@ -0,0 +1,18 @@
+// 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 Microsoft.CodeAnalysis.CSharp.Symbols;
+
+namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
+{
+ internal static class SymbolExtensions
+ {
+ internal static ImmutableArray GetAllTypeParameters(this MethodSymbol method)
+ {
+ var builder = ArrayBuilder.GetInstance();
+ method.ContainingType.GetAllTypeParameters(builder);
+ builder.AddRange(method.TypeParameters);
+ return builder.ToImmutableAndFree();
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs
index 7e33eba502981..26f3a00c48a8b 100644
--- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs
+++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs
@@ -1,7 +1,10 @@
// 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.Diagnostics;
+using System.Linq;
using System.Reflection.Metadata.Ecma335;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
@@ -14,8 +17,90 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
public class InstructionDecoderTests : ExpressionCompilerTestBase
{
+ [Fact]
+ void GetNameGenerics()
+ {
+ var source = @"
+using System;
+class Class1
+{
+ void M1(Action a)
+ {
+ }
+ void M2(Action a)
+ {
+ }
+ void M3(Action a)
+ {
+ }
+}";
+
+ Assert.Equal(
+ "Class1.M1(System.Action a)",
+ GetName(source, "Class1.M1", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types));
+
+ Assert.Equal(
+ "Class1.M2(System.Action a)",
+ GetName(source, "Class1.M2", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types));
+
+ Assert.Equal(
+ "Class1.M3(System.Action a)",
+ GetName(source, "Class1.M3", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types));
+
+ Assert.Equal(
+ "Class1.M1(System.Action a)",
+ GetName(source, "Class1.M1", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(string), typeof(decimal) }));
+
+ Assert.Equal(
+ "Class1.M2(System.Action a)",
+ GetName(source, "Class1.M2", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(string), typeof(decimal) }));
+
+ Assert.Equal(
+ "Class1.M3(System.Action a)",
+ GetName(source, "Class1.M3", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(string), typeof(decimal) }));
+ }
+
+ [Fact]
+ void GetNameNullTypeArguments()
+ {
+ var source = @"
+using System;
+class Class1
+{
+ void M(Action a)
+ {
+ }
+}";
+
+ Assert.Equal(
+ "Class1.M(System.Action a)",
+ GetName(source, "Class1.M", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new Type[] { null, null }));
+
+ Assert.Equal(
+ "Class1.M(System.Action a)",
+ GetName(source, "Class1.M", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new[] { typeof(string), null }));
+
+ Assert.Equal(
+ "Class1.M(System.Action a)",
+ GetName(source, "Class1.M", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new[] { null, typeof(decimal) }));
+ }
+
+ [Fact]
+ void GetNameGenericArgumentTypeNotInReferences()
+ {
+ var source = @"
+class Class1
+{
+}";
+
+ var serializedTypeArgumentName = "Class1, " + nameof(InstructionDecoderTests) + ", Culture=neutral, PublicKeyToken=null";
+ Assert.Equal(
+ "System.Collections.Generic.Comparer.Create(System.Comparison comparison)",
+ GetName(source, "System.Collections.Generic.Comparer.Create", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, typeArguments: new[] { serializedTypeArgumentName }));
+ }
+
[Fact, WorkItem(1107977)]
- public void GetNameGenericAsync()
+ void GetNameGenericAsync()
{
var source = @"
using System.Threading.Tasks;
@@ -29,12 +114,12 @@ static async Task M(T x)
}";
Assert.Equal(
- "C.M(T x)",
- GetName(source, "C.d__0.MoveNext", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types));
+ "C.M(System.Exception x)",
+ GetName(source, "C.d__0.MoveNext", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(Exception) }));
}
[Fact]
- public void GetNameLambda()
+ void GetNameLambda()
{
var source = @"
using System;
@@ -52,7 +137,7 @@ void M()
}
[Fact]
- public void GetNameGenericLambda()
+ void GetNameGenericLambda()
{
var source = @"
using System;
@@ -65,12 +150,12 @@ void M() where U : T
}";
Assert.Equal(
- "C.M.AnonymousMethod__0_0(U u)",
- GetName(source, "C.<>c__0.b__0_0", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types));
+ "C.M.AnonymousMethod__0_0(System.ArgumentException u)",
+ GetName(source, "C.<>c__0.b__0_0", DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types, new[] { typeof(Exception), typeof(ArgumentException) }));
}
[Fact]
- public void GetNameProperties()
+ void GetNameProperties()
{
var source = @"
class C
@@ -101,7 +186,7 @@ int this[object x]
}
[Fact]
- public void GetNameExplicitInterfaceImplementation()
+ void GetNameExplicitInterfaceImplementation()
{
var source = @"
using System;
@@ -116,7 +201,7 @@ void IDisposable.Dispose() { }
}
[Fact]
- public void GetNameExtensionMethod()
+ void GetNameExtensionMethod()
{
var source = @"
static class Extensions
@@ -130,7 +215,7 @@ static void M(this string @this) { }
}
[Fact]
- public void GetNameArgumentFlagsNone()
+ void GetNameArgumentFlagsNone()
{
var source = @"
static class C
@@ -148,41 +233,154 @@ static void M2(int x, int y) { }
GetName(source, "C.M2", DkmVariableInfoFlags.None));
}
- private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, params string[] argumentValues)
+ [Fact]
+ void GetReturnTypeNamePrimitive()
+ {
+ var source = @"
+static class C
+{
+ static uint M1() { return 42; }
+}";
+
+ Assert.Equal("uint", GetReturnTypeName(source, "C.M1"));
+ }
+
+ [Fact]
+ void GetReturnTypeNameNested()
+ {
+ var source = @"
+static class C
+{
+ static N.D.E M1() { return default(N.D.E); }
+}
+namespace N
+{
+ class D
+ {
+ internal struct E
+ {
+ }
+ }
+}";
+
+ Assert.Equal("N.D.E", GetReturnTypeName(source, "C.M1"));
+ }
+
+ [Fact]
+ void GetReturnTypeNameGenericOfPrimitive()
+ {
+ var source = @"
+using System;
+class C
+{
+ Action M1() { return null; }
+}";
+
+ Assert.Equal("System.Action", GetReturnTypeName(source, "C.M1"));
+ }
+
+ [Fact]
+ void GetReturnTypeNameGenericOfNested()
+ {
+ var source = @"
+using System;
+class C
+{
+ Action M1() { return null; }
+ class D
+ {
+ }
+}";
+
+ Assert.Equal("System.Action", GetReturnTypeName(source, "C.M1"));
+ }
+
+ [Fact]
+ void GetReturnTypeNameGenericOfGeneric()
+ {
+ var source = @"
+using System;
+class C
+{
+ Action> M1() { return null; }
+}";
+
+ Assert.Equal("System.Action>", GetReturnTypeName(source, "C.M1", new[] { typeof(object) }));
+ }
+
+ private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, Type[] typeArguments = null, string[] argumentValues = null)
+ {
+ var serializedTypeArgumentNames = typeArguments?.Select(t => t?.AssemblyQualifiedName).ToArray();
+ return GetName(source, methodName, argumentFlags, serializedTypeArgumentNames, argumentValues);
+ }
+
+ private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, string[] typeArguments, string[] argumentValues = null)
{
Debug.Assert((argumentFlags & (DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)) == argumentFlags,
"Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags);
- var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugDll);
- var runtime = CreateRuntimeInstance(compilation);
- var moduleInstances = runtime.Modules;
- var blocks = moduleInstances.SelectAsArray(m => m.MetadataBlock);
- compilation = blocks.ToCompilation();
- var frame = (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName);
+ var instructionDecoder = CSharpInstructionDecoder.Instance;
+ var method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder);
- // Once we have the method token, we want to look up the method (again)
- // using the same helper as the product code. This helper will also map
- // async/iterator "MoveNext" methods to the original source method.
- var method = compilation.GetSourceMethod(
- ((PEModuleSymbol)frame.ContainingModule).Module.GetModuleVersionIdOrThrow(),
- MetadataTokens.GetToken(frame.Handle));
var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types);
var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names);
ArrayBuilder builder = null;
- if (argumentValues.Length > 0)
+ if (argumentValues != null)
{
+ Assert.InRange(argumentValues.Length, 1, int.MaxValue);
builder = ArrayBuilder.GetInstance();
builder.AddRange(argumentValues);
}
- var frameDecoder = CSharpInstructionDecoder.Instance;
- var frameName = frameDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder);
+ var name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder);
if (builder != null)
{
builder.Free();
}
- return frameName;
+ return name;
+ }
+
+ private string GetReturnTypeName(string source, string methodName, Type[] typeArguments = null)
+ {
+ var instructionDecoder = CSharpInstructionDecoder.Instance;
+ var serializedTypeArgumentNames = typeArguments?.Select(t => (t != null) ? t.AssemblyQualifiedName : null).ToArray();
+ var method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder);
+
+ return instructionDecoder.GetReturnTypeName(method);
+ }
+
+ private MethodSymbol GetConstructedMethod(string source, string methodName, string[] serializedTypeArgumentNames, CSharpInstructionDecoder instructionDecoder)
+ {
+ var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugDll, assemblyName: nameof(InstructionDecoderTests));
+ var runtime = CreateRuntimeInstance(compilation);
+ var moduleInstances = runtime.Modules;
+ var blocks = moduleInstances.SelectAsArray(m => m.MetadataBlock);
+ compilation = blocks.ToCompilation();
+ var frame = (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName);
+
+ // Once we have the method token, we want to look up the method (again)
+ // using the same helper as the product code. This helper will also map
+ // async/iterator "MoveNext" methods to the original source method.
+ MethodSymbol method = compilation.GetSourceMethod(
+ ((PEModuleSymbol)frame.ContainingModule).Module.GetModuleVersionIdOrThrow(),
+ MetadataTokens.GetToken(frame.Handle));
+ if (serializedTypeArgumentNames != null)
+ {
+ Assert.NotEmpty(serializedTypeArgumentNames);
+ var typeParameters = instructionDecoder.GetAllTypeParameters(method);
+ Assert.NotEmpty(typeParameters);
+ var typeNameDecoder = new EETypeNameDecoder(compilation, (PEModuleSymbol)method.ContainingModule);
+ // Use the same helper method as the FrameDecoder to get the TypeSymbols for the
+ // generic type arguments (rather than using EETypeNameDecoder directly).
+ var typeArguments = instructionDecoder.GetTypeSymbols(compilation, method, serializedTypeArgumentNames);
+ if (!typeArguments.IsEmpty)
+ {
+ method = instructionDecoder.ConstructMethod(method, typeParameters, typeArguments);
+ }
+ }
+
+ return method;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs
index 30601e88a1587..77d27323a45b7 100644
--- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs
+++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/ExpressionEvaluatorFatalError.cs
@@ -4,11 +4,12 @@
using System.Diagnostics;
using System.Reflection;
using Microsoft.VisualStudio.Debugger;
+using Roslyn.Utilities;
#if !EXPRESSIONCOMPILER
using Microsoft.CodeAnalysis.ErrorReporting;
-
#endif
+
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
internal static class ExpressionEvaluatorFatalError
@@ -80,5 +81,20 @@ internal static bool CrashIfFailFastEnabled(Exception exception)
return FatalError.Report(exception);
}
+
+ internal delegate bool NonFatalExceptionHandler(Exception exception, string implementationName);
+
+ internal static bool ReportNonFatalException(Exception exception, NonFatalExceptionHandler handler)
+ {
+ if (CrashIfFailFastEnabled(exception))
+ {
+ throw ExceptionUtilities.Unreachable;
+ }
+
+ // Ignore the return value, because we always want to continue after reporting the Exception.
+ handler(exception, nameof(ExpressionEvaluatorFatalError));
+
+ return true;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs
index 0ab210df78e5f..f3e1b24a68b84 100644
--- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs
+++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/FrameDecoder.cs
@@ -1,6 +1,7 @@
// 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.Collections.Immutable;
using System.Diagnostics;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.CallStack;
@@ -19,86 +20,35 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator
/// always used C# syntax (but with language-specific "special names"). Since these names are exposed through public
/// APIs, we will remain consistent with the old behavior (for consumers who may be parsing the frame names).
///
- internal abstract class FrameDecoder : IDkmLanguageFrameDecoder
+ internal abstract class FrameDecoder : IDkmLanguageFrameDecoder
+ where TCompilation : Compilation
+ where TMethodSymbol : class, IMethodSymbol
+ where TModuleSymbol : class, IModuleSymbol
+ where TTypeSymbol : class, ITypeSymbol
+ where TTypeParameterSymbol : class, ITypeParameterSymbol
{
- private readonly InstructionDecoder _instructionDecoder;
+ private readonly InstructionDecoder _instructionDecoder;
- internal FrameDecoder(InstructionDecoder instructionDecoder)
+ internal FrameDecoder(InstructionDecoder instructionDecoder)
{
_instructionDecoder = instructionDecoder;
}
- void IDkmLanguageFrameDecoder.GetFrameName(DkmInspectionContext inspectionContext, DkmWorkList workList, DkmStackWalkFrame frame, DkmVariableInfoFlags argumentFlags, DkmCompletionRoutine completionRoutine)
+ void IDkmLanguageFrameDecoder.GetFrameName(
+ DkmInspectionContext inspectionContext,
+ DkmWorkList workList,
+ DkmStackWalkFrame frame,
+ DkmVariableInfoFlags argumentFlags,
+ DkmCompletionRoutine completionRoutine)
{
try
{
Debug.Assert((argumentFlags & (DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types | DkmVariableInfoFlags.Values)) == argumentFlags,
"Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags);
- var instructionAddress = (DkmClrInstructionAddress)frame.InstructionAddress;
- var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types);
- var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names);
-
- if (argumentFlags.Includes(DkmVariableInfoFlags.Values))
- {
- // No need to compute the Expandable bit on
- // argument values since that can be expensive.
- inspectionContext = DkmInspectionContext.Create(
- inspectionContext.InspectionSession,
- inspectionContext.RuntimeInstance,
- inspectionContext.Thread,
- inspectionContext.Timeout,
- inspectionContext.EvaluationFlags | DkmEvaluationFlags.NoExpansion,
- inspectionContext.FuncEvalFlags,
- inspectionContext.Radix,
- inspectionContext.Language,
- inspectionContext.ReturnValue,
- inspectionContext.AdditionalVisualizationData,
- inspectionContext.AdditionalVisualizationDataPriority,
- inspectionContext.ReturnValues);
-
- // GetFrameArguments returns an array of formatted argument values. We'll pass
- // ourselves (GetFrameName) as the continuation of the GetFrameArguments call.
- inspectionContext.GetFrameArguments(
- workList,
- frame,
- result =>
- {
- try
- {
- var builder = ArrayBuilder.GetInstance();
- foreach (var argument in result.Arguments)
- {
- var evaluatedArgument = argument as DkmSuccessEvaluationResult;
- // Not expecting Expandable bit, at least not from this EE.
- Debug.Assert((evaluatedArgument == null) || (evaluatedArgument.Flags & DkmEvaluationResultFlags.Expandable) == 0);
- builder.Add((evaluatedArgument != null) ? evaluatedArgument.Value : null);
- }
-
- var frameName = _instructionDecoder.GetName(instructionAddress, includeParameterTypes, includeParameterNames, builder);
- builder.Free();
- completionRoutine(new DkmGetFrameNameAsyncResult(frameName));
- }
- // TODO: Consider calling DkmComponentManager.ReportCurrentNonFatalException() to
- // trigger a non-fatal Watson when this occurs.
- catch (Exception e) when (!ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e))
- {
- completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e));
- }
- finally
- {
- foreach (var argument in result.Arguments)
- {
- argument.Close();
- }
- }
- });
- }
- else
- {
- var frameName = _instructionDecoder.GetName(instructionAddress, includeParameterTypes, includeParameterNames, null);
- completionRoutine(new DkmGetFrameNameAsyncResult(frameName));
- }
+ GetNameWithGenericTypeArguments(inspectionContext, workList, frame,
+ onSuccess: method => GetFrameName(inspectionContext, workList, frame, argumentFlags, completionRoutine, method),
+ onFailure: e => completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e)));
}
catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e))
{
@@ -106,18 +56,135 @@ void IDkmLanguageFrameDecoder.GetFrameName(DkmInspectionContext inspectionContex
}
}
- void IDkmLanguageFrameDecoder.GetFrameReturnType(DkmInspectionContext inspectionContext, DkmWorkList workList, DkmStackWalkFrame frame, DkmCompletionRoutine completionRoutine)
+ void IDkmLanguageFrameDecoder.GetFrameReturnType(
+ DkmInspectionContext inspectionContext,
+ DkmWorkList workList,
+ DkmStackWalkFrame frame,
+ DkmCompletionRoutine completionRoutine)
{
try
{
- var returnType = _instructionDecoder.GetReturnType((DkmClrInstructionAddress)frame.InstructionAddress);
- var result = new DkmGetFrameReturnTypeAsyncResult(returnType);
- completionRoutine(result);
+ GetNameWithGenericTypeArguments(inspectionContext, workList, frame,
+ onSuccess: method => completionRoutine(new DkmGetFrameReturnTypeAsyncResult(_instructionDecoder.GetReturnTypeName(method))),
+ onFailure: e => completionRoutine(DkmGetFrameReturnTypeAsyncResult.CreateErrorResult(e)));
}
catch (Exception e) when (ExpressionEvaluatorFatalError.CrashIfFailFastEnabled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
+
+ private void GetNameWithGenericTypeArguments(
+ DkmInspectionContext inspectionContext,
+ DkmWorkList workList,
+ DkmStackWalkFrame frame,
+ Action onSuccess,
+ Action onFailure)
+ {
+ // NOTE: We could always call GetClrGenericParameters, pass them to GetMethod and have that
+ // return a constructed method symbol, but it seems unwise to call GetClrGenericParameters
+ // for all frames (as this call requires a round-trip to the debuggee process).
+ var instructionAddress = (DkmClrInstructionAddress)frame.InstructionAddress;
+ var compilation = _instructionDecoder.GetCompilation(instructionAddress);
+ var method = _instructionDecoder.GetMethod(compilation, instructionAddress);
+ var typeParameters = _instructionDecoder.GetAllTypeParameters(method);
+ if (!typeParameters.IsEmpty)
+ {
+ frame.GetClrGenericParameters(
+ workList,
+ result =>
+ {
+ try
+ {
+ var typeArguments = _instructionDecoder.GetTypeSymbols(compilation, method, result.ParameterTypeNames);
+ if (!typeArguments.IsEmpty)
+ {
+ method = _instructionDecoder.ConstructMethod(method, typeParameters, typeArguments);
+ }
+ onSuccess(method);
+ }
+ catch (Exception e) when (ExpressionEvaluatorFatalError.ReportNonFatalException(e, DkmComponentManager.ReportCurrentNonFatalException))
+ {
+ onFailure(e);
+ }
+ });
+ }
+ else
+ {
+ onSuccess(method);
+ }
+ }
+
+ private void GetFrameName(
+ DkmInspectionContext inspectionContext,
+ DkmWorkList workList,
+ DkmStackWalkFrame frame,
+ DkmVariableInfoFlags argumentFlags,
+ DkmCompletionRoutine completionRoutine,
+ TMethodSymbol method)
+ {
+ var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types);
+ var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names);
+
+ if (argumentFlags.Includes(DkmVariableInfoFlags.Values))
+ {
+ // No need to compute the Expandable bit on
+ // argument values since that can be expensive.
+ inspectionContext = DkmInspectionContext.Create(
+ inspectionContext.InspectionSession,
+ inspectionContext.RuntimeInstance,
+ inspectionContext.Thread,
+ inspectionContext.Timeout,
+ inspectionContext.EvaluationFlags | DkmEvaluationFlags.NoExpansion,
+ inspectionContext.FuncEvalFlags,
+ inspectionContext.Radix,
+ inspectionContext.Language,
+ inspectionContext.ReturnValue,
+ inspectionContext.AdditionalVisualizationData,
+ inspectionContext.AdditionalVisualizationDataPriority,
+ inspectionContext.ReturnValues);
+
+ // GetFrameArguments returns an array of formatted argument values. We'll pass
+ // ourselves (GetFrameName) as the continuation of the GetFrameArguments call.
+ inspectionContext.GetFrameArguments(
+ workList,
+ frame,
+ result =>
+ {
+ var argumentValues = result.Arguments;
+ try
+ {
+ var builder = ArrayBuilder.GetInstance();
+ foreach (var argument in argumentValues)
+ {
+ var formattedArgument = argument as DkmSuccessEvaluationResult;
+ // Not expecting Expandable bit, at least not from this EE.
+ Debug.Assert((formattedArgument == null) || (formattedArgument.Flags & DkmEvaluationResultFlags.Expandable) == 0);
+ builder.Add(formattedArgument?.Value);
+ }
+
+ var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder);
+ builder.Free();
+ completionRoutine(new DkmGetFrameNameAsyncResult(frameName));
+ }
+ catch (Exception e) when (ExpressionEvaluatorFatalError.ReportNonFatalException(e, DkmComponentManager.ReportCurrentNonFatalException))
+ {
+ completionRoutine(DkmGetFrameNameAsyncResult.CreateErrorResult(e));
+ }
+ finally
+ {
+ foreach (var argument in argumentValues)
+ {
+ argument.Close();
+ }
+ }
+ });
+ }
+ else
+ {
+ var frameName = _instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, null);
+ completionRoutine(new DkmGetFrameNameAsyncResult(frameName));
+ }
+ }
}
}
diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs
index be947e272e0a1..80126b5f5e6bd 100644
--- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs
+++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs
@@ -1,5 +1,6 @@
// 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;
using System.Text;
using Microsoft.CodeAnalysis.Collections;
@@ -7,13 +8,12 @@
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
- internal abstract class InstructionDecoder
- {
- internal abstract string GetName(DkmClrInstructionAddress instructionAddress, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder argumentValues);
- internal abstract string GetReturnType(DkmClrInstructionAddress instructionAddress);
- }
-
- internal abstract class InstructionDecoder : InstructionDecoder where TMethodSymbol : class, IMethodSymbol
+ internal abstract class InstructionDecoder
+ where TCompilation : Compilation
+ where TMethodSymbol : class, IMethodSymbol
+ where TModuleSymbol : class, IModuleSymbol
+ where TTypeSymbol : class, ITypeSymbol
+ where TTypeParameterSymbol : class, ITypeParameterSymbol
{
internal static readonly SymbolDisplayFormat DisplayFormat = new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
@@ -21,21 +21,18 @@ internal abstract class InstructionDecoder : InstructionDecoder w
memberOptions: SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeExplicitInterface,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
- internal override string GetName(DkmClrInstructionAddress instructionAddress, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder argumentValues)
- {
- var method = this.GetMethod(instructionAddress);
- return this.GetName(method, includeParameterTypes, includeParameterNames, argumentValues);
- }
+ internal abstract void AppendFullName(StringBuilder builder, TMethodSymbol method);
- internal override string GetReturnType(DkmClrInstructionAddress instructionAddress)
- {
- var method = this.GetMethod(instructionAddress);
- return method.ReturnType.ToDisplayString(DisplayFormat);
- }
+ ///
+ /// Constructs a method and any of its generic containing types using the specified .
+ ///
+ internal abstract TMethodSymbol ConstructMethod(TMethodSymbol method, ImmutableArray typeParameters, ImmutableArray typeArguments);
- internal abstract void AppendFullName(StringBuilder builder, TMethodSymbol method);
+ internal abstract ImmutableArray GetAllTypeParameters(TMethodSymbol method);
+
+ internal abstract TCompilation GetCompilation(DkmClrInstructionAddress instructionAddress);
- internal abstract TMethodSymbol GetMethod(DkmClrInstructionAddress instructionAddress);
+ internal abstract TMethodSymbol GetMethod(TCompilation compilation, DkmClrInstructionAddress instructionAddress);
internal string GetName(TMethodSymbol method, bool includeParameterTypes, bool includeParameterNames, ArrayBuilder argumentValues = null)
{
@@ -96,5 +93,33 @@ internal string GetName(TMethodSymbol method, bool includeParameterTypes, bool i
return pooled.ToStringAndFree();
}
+
+ internal string GetReturnTypeName(TMethodSymbol method)
+ {
+ return method.ReturnType.ToDisplayString(DisplayFormat);
+ }
+
+ internal abstract TypeNameDecoder GetTypeNameDecoder(TCompilation compilation, TMethodSymbol method);
+
+ internal ImmutableArray GetTypeSymbols(TCompilation compilation, TMethodSymbol method, string[] serializedTypeNames)
+ {
+ var builder = ArrayBuilder.GetInstance();
+ foreach (var name in serializedTypeNames)
+ {
+ // The list of type names will include null values if type arguments are not available.
+ // It seems unlikely that only some type arguments will be missing (and it also seems
+ // like very little incremental value to include only some of the arguments), so we'll
+ // keep things simple and omit all type arguments if any are missing.
+ if (name == null)
+ {
+ builder.Free();
+ return ImmutableArray.Empty;
+ }
+
+ var typeNameDecoder = GetTypeNameDecoder(compilation, method);
+ builder.Add(typeNameDecoder.GetTypeSymbolForSerializedType(name));
+ }
+ return builder.ToImmutableAndFree();
+ }
}
}
diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs
index d5a7b0a357593..bbed5e6088945 100644
--- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs
+++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/LanguageInstructionDecoder.cs
@@ -17,11 +17,16 @@ namespace Microsoft.CodeAnalysis.ExpressionEvaluator
///
/// This class provides function name information for the Breakpoints window.
///
- internal abstract class LanguageInstructionDecoder : IDkmLanguageInstructionDecoder where TMethodSymbol : class, IMethodSymbol
+ internal abstract class LanguageInstructionDecoder : IDkmLanguageInstructionDecoder
+ where TCompilation : Compilation
+ where TMethodSymbol : class, IMethodSymbol
+ where TModuleSymbol : class, IModuleSymbol
+ where TTypeSymbol : class, ITypeSymbol
+ where TTypeParameterSymbol : class, ITypeParameterSymbol
{
- private readonly InstructionDecoder _instructionDecoder;
+ private readonly InstructionDecoder _instructionDecoder;
- internal LanguageInstructionDecoder(InstructionDecoder instructionDecoder)
+ internal LanguageInstructionDecoder(InstructionDecoder instructionDecoder)
{
_instructionDecoder = instructionDecoder;
}
@@ -37,7 +42,9 @@ string IDkmLanguageInstructionDecoder.GetMethodName(DkmLanguageInstructionAddres
Debug.Assert((argumentFlags & (DkmVariableInfoFlags.FullNames | DkmVariableInfoFlags.Names | DkmVariableInfoFlags.Types)) == argumentFlags,
"Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags);
- var method = _instructionDecoder.GetMethod((DkmClrInstructionAddress)languageInstructionAddress.Address);
+ var instructionAddress = (DkmClrInstructionAddress)languageInstructionAddress.Address;
+ var compilation = _instructionDecoder.GetCompilation(instructionAddress);
+ var method = _instructionDecoder.GetMethod(compilation, instructionAddress);
var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types);
var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names);
diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb
index 1b42c478cc57d..1538af4c4605d 100644
--- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/CompilationContext.vb
@@ -1,4 +1,6 @@
-Imports System
+' 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.Runtime.InteropServices
Imports System.Threading
@@ -173,13 +175,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return moduleBuilder
End Function
- Private Shared Function GetAllTypeParameters(method As MethodSymbol) As ImmutableArray(Of TypeParameterSymbol)
- Dim builder = ArrayBuilder(Of TypeParameterSymbol).GetInstance()
- method.ContainingType.GetAllTypeParameters(builder)
- builder.AddRange(method.TypeParameters)
- Return builder.ToImmutableAndFree()
- End Function
-
Private Shared Function GetNextMethodName(builder As ArrayBuilder(Of MethodSymbol)) As String
' NOTE: These names are consumed by Concord, so there's no native precedent.
Return String.Format("<>m{0}", builder.Count)
diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb
index 4c6b96d59311e..b2f418a0b8278 100644
--- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SymbolExtensions.vb
@@ -1,4 +1,7 @@
-Imports System.Runtime.CompilerServices
+' 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.Immutable
+Imports System.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
@@ -59,5 +62,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Friend Function IsStateMachineType(type As TypeSymbol) As Boolean
Return type.Name.StartsWith(StringConstants.StateMachineTypeNamePrefix, StringComparison.Ordinal)
End Function
+
+
+ Friend Function GetAllTypeParameters(method As MethodSymbol) As ImmutableArray(Of TypeParameterSymbol)
+ Dim builder = ArrayBuilder(Of TypeParameterSymbol).GetInstance()
+ method.ContainingType.GetAllTypeParameters(builder)
+ builder.AddRange(method.TypeParameters)
+ Return builder.ToImmutableAndFree()
+ End Function
End Module
End Namespace
diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb
index fc1aa18494b38..dc71a62a05c4a 100644
--- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicFrameDecoder.vb
@@ -1,9 +1,13 @@
-Imports Microsoft.CodeAnalysis.ExpressionEvaluator
+' 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 Microsoft.CodeAnalysis.ExpressionEvaluator
+Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
+Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
- Friend NotInheritable Class VisualBasicFrameDecoder : Inherits FrameDecoder
+ Friend NotInheritable Class VisualBasicFrameDecoder : Inherits FrameDecoder(Of VisualBasicCompilation, MethodSymbol, PEModuleSymbol, TypeSymbol, TypeParameterSymbol)
Public Sub New()
MyBase.New(VisualBasicInstructionDecoder.Instance)
diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb
index ba82c0aac338b..c4fac085d08f5 100644
--- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicInstructionDecoder.vb
@@ -1,6 +1,8 @@
-Imports System.Runtime.InteropServices
+' 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.Immutable
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
+Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.VisualStudio.Debugger
Imports Microsoft.VisualStudio.Debugger.Clr
@@ -8,7 +10,7 @@ Imports System.Text
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
- Friend NotInheritable Class VisualBasicInstructionDecoder : Inherits InstructionDecoder(Of PEMethodSymbol)
+ Friend NotInheritable Class VisualBasicInstructionDecoder : Inherits InstructionDecoder(Of VisualBasicCompilation, MethodSymbol, PEModuleSymbol, TypeSymbol, TypeParameterSymbol)
' These strings were not localized in the old EE. We'll keep them that way
' so as not to break consumers who may have been parsing frame names...
@@ -18,12 +20,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
'''
''' Singleton instance of (created using default constructor).
'''
- Friend Shared ReadOnly Instance as VisualBasicInstructionDecoder = New VisualBasicInstructionDecoder()
+ Friend Shared ReadOnly Instance As VisualBasicInstructionDecoder = New VisualBasicInstructionDecoder()
Private Sub New()
End Sub
- Friend Overrides Sub AppendFullName(builder As StringBuilder, method As PEMethodSymbol)
+ Friend Overrides Sub AppendFullName(builder As StringBuilder, method As MethodSymbol)
Dim parts = method.ToDisplayParts(DisplayFormat)
Dim numParts = parts.Length
For i = 0 To numParts - 1
@@ -59,9 +61,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Next
End Sub
- Friend Overrides Function GetMethod(instructionAddress As DkmClrInstructionAddress) As PEMethodSymbol
- Dim moduleInstance = instructionAddress.ModuleInstance
- Dim appDomain = moduleInstance.AppDomain
+ Friend Overrides Function ConstructMethod(method As MethodSymbol, typeParameters As ImmutableArray(Of TypeParameterSymbol), typeArguments As ImmutableArray(Of TypeSymbol)) As MethodSymbol
+ Dim methodArity = method.Arity
+ Dim methodArgumentStartIndex = typeParameters.Length - methodArity
+ Dim typeMap = TypeSubstitution.Create(
+ method,
+ ImmutableArray.Create(typeParameters, 0, methodArgumentStartIndex),
+ ImmutableArray.Create(typeArguments, 0, methodArgumentStartIndex))
+ Dim substitutedType = typeMap.SubstituteNamedType(method.ContainingType)
+ method = method.AsMember(substitutedType)
+ If methodArity > 0 Then
+ method = method.Construct(ImmutableArray.Create(typeArguments, methodArgumentStartIndex, methodArity))
+ End If
+ Return method
+ End Function
+
+ Friend Overrides Function GetAllTypeParameters(method As MethodSymbol) As ImmutableArray(Of TypeParameterSymbol)
+ Return method.GetAllTypeParameters()
+ End Function
+
+ Friend Overrides Function GetCompilation(instructionAddress As DkmClrInstructionAddress) As VisualBasicCompilation
+ Dim appDomain = instructionAddress.ModuleInstance.AppDomain
Dim previous = appDomain.GetDataItem(Of VisualBasicMetadataContext)()
Dim metadataBlocks = instructionAddress.Process.GetMetadataBlocks(appDomain)
@@ -73,7 +93,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
appDomain.SetDataItem(DkmDataCreationDisposition.CreateAlways, New VisualBasicMetadataContext(metadataBlocks))
End If
- Return compilation.GetSourceMethod(moduleInstance.Mvid, instructionAddress.MethodId.Token)
+ Return compilation
+ End Function
+
+ Friend Overrides Function GetMethod(compilation As VisualBasicCompilation, instructionAddress As DkmClrInstructionAddress) As MethodSymbol
+ Return compilation.GetSourceMethod(instructionAddress.ModuleInstance.Mvid, instructionAddress.MethodId.Token)
+ End Function
+
+ Friend Overrides Function GetTypeNameDecoder(compilation As VisualBasicCompilation, method As MethodSymbol) As TypeNameDecoder(Of PEModuleSymbol, TypeSymbol)
+ Debug.Assert(TypeOf method Is PEMethodSymbol)
+ Return New EETypeNameDecoder(compilation, DirectCast(method.ContainingModule, PEModuleSymbol))
End Function
End Class
diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb
index cd4ba00673d41..c8fad9add4aa5 100644
--- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/VisualBasicLanguageInstructionDecoder.vb
@@ -1,10 +1,13 @@
-Imports Microsoft.CodeAnalysis.ExpressionEvaluator
+' 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 Microsoft.CodeAnalysis.ExpressionEvaluator
+Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
- Friend NotInheritable Class VisualBasicLanguageInstructionDecoder : Inherits LanguageInstructionDecoder(Of PEMethodSymbol)
+ Friend NotInheritable Class VisualBasicLanguageInstructionDecoder : Inherits LanguageInstructionDecoder(Of VisualBasicCompilation, MethodSymbol, PEModuleSymbol, TypeSymbol, TypeParameterSymbol)
Public Sub New()
MyBase.New(VisualBasicInstructionDecoder.Instance)
diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb
index 5b0f03b1db133..ccc81e7fa618e 100644
--- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/ExpressionCompilerTests.vb
@@ -1,4 +1,6 @@
-Imports System.Collections.Immutable
+' 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.Immutable
Imports System.Globalization
Imports System.Reflection.Metadata
Imports System.Threading
diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb
index 1b33fa3b22a4f..93796e17f3f75 100644
--- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/InstructionDecoderTests.vb
@@ -1,4 +1,7 @@
-Imports System.Reflection.Metadata.Ecma335
+' 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.Reflection.Metadata.Ecma335
+Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests
@@ -20,11 +23,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
'// TODO: string argument values requiring quotes
'// TODO: argument flags == names only, types only, values only
'// TODO: params Argument values
- '// TODO: GetFrameReturnType primitive types
- '// TODO: GetFrameReturnType non-primitive types (nested namespace/class)
- '// TODO: GetFrameReturnType generic(Of non-primitive, nested)
- '// TODO: GetFrameReturnType generic(Of generic)
- '// TODO: GetFrameReturnType generic(Of primitive)
+ '// TODO: generic class/method with 2 or more type parameters
+ '// TODO: generic argument type that is not from a referenced assembly
Public Class InstructionDecoderTests : Inherits ExpressionCompilerTestBase
@@ -85,7 +85,6 @@ Class Class1(Of T)
Sub M3(Of U)(a As Action(Of U))
End Sub
End Class"
- ' TODO: Type parameters should be substituted with type arguments once we have an API to retrieve them.
Assert.Equal(
"Class1(Of T).M1(Of U)(System.Action(Of Integer) a)",
@@ -98,6 +97,52 @@ End Class"
Assert.Equal(
"Class1(Of T).M3(Of U)(System.Action(Of U) a)",
GetName(source, "Class1.M3", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
+
+ Assert.Equal(
+ "Class1(Of String).M1(Of Decimal)(System.Action(Of Integer) a)",
+ GetName(source, "Class1.M1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)}))
+
+ Assert.Equal(
+ "Class1(Of String).M2(Of Decimal)(System.Action(Of String) a)",
+ GetName(source, "Class1.M2", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)}))
+
+ Assert.Equal(
+ "Class1(Of String).M3(Of Decimal)(System.Action(Of Decimal) a)",
+ GetName(source, "Class1.M3", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)}))
+ End Sub
+
+
+ Sub GetNameNullTypeArguments()
+ Dim source = "
+Imports System
+Class Class1(Of T)
+ Sub M(Of U)(a As Action(Of U))
+ End Sub
+End Class"
+
+ Assert.Equal(
+ "Class1(Of T).M(Of U)(System.Action(Of U) a)",
+ GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:=New Type() {Nothing, Nothing}))
+
+ Assert.Equal(
+ "Class1(Of T).M(Of U)(System.Action(Of U) a)",
+ GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), Nothing}))
+
+ Assert.Equal(
+ "Class1(Of T).M(Of U)(System.Action(Of U) a)",
+ GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={Nothing, GetType(Decimal)}))
+ End Sub
+
+
+ Sub GetNameGenericArgumentTypeNotInReferences()
+ Dim source = "
+Class Class1
+End Class"
+
+ Dim serializedTypeArgumentName = "Class1, " & NameOf(InstructionDecoderTests) & ", Culture=neutral, PublicKeyToken=null"
+ Assert.Equal(
+ "System.Collections.Generic.Comparer(Of Class1).Create(System.Comparison(Of Class1) comparison)",
+ GetName(source, "System.Collections.Generic.Comparer.Create", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={serializedTypeArgumentName}))
End Sub
@@ -130,8 +175,8 @@ Class C
End Class"
Assert.Equal(
- "C.M(Of T)(T x)",
- GetName(source, "C.VB$StateMachine_1_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
+ "C.M(Of Long)(Long x)",
+ GetName(source, "C.VB$StateMachine_1_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(Long)}))
End Sub
@@ -175,10 +220,10 @@ Class Class1(Of T)
Dim f As Func(Of U, T) = Function(u2 As U) u2
End Sub
End Class"
- ' TODO: Type parameter $CLS0 should be substituted with a type argument once we have an API to retrieve it.
+
Assert.Equal(
- "Class1(Of T)..($CLS0 u2)",
- GetName(source, "Class1._Closure$__1._Lambda$__1-1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
+ "Class1(Of System.Exception)..(System.ArgumentException u2)",
+ GetName(source, "Class1._Closure$__1._Lambda$__1-1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(Exception), GetType(ArgumentException)}))
End Sub
@@ -196,7 +241,7 @@ End Module"
Assert.Equal(
"Module1.M(Date d = #6/23/1912#)",
- GetName(source, "Module1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, "#6/23/1912#"))
+ GetName(source, "Module1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, argumentValues:={"#6/23/1912#"}))
End Sub
@@ -284,14 +329,120 @@ End Module"
GetName(source, "Module1.M2", DkmVariableInfoFlags.None))
End Sub
- Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, ParamArray argumentValues() As String) As String
+
+ Sub GetReturnTypeNamePrimitive()
+ Dim source = "
+Class C
+ Function M1() As UInteger
+ Return 42
+ End Function
+End Class"
+
+ Assert.Equal("UInteger", GetReturnTypeName(source, "C.M1"))
+ End Sub
+
+
+ Sub GetReturnTypeNameNested()
+ Dim source = "
+Class C
+ Function M1() As N.D.E
+ Return Nothing
+ End Function
+End Class
+Namespace N
+ Class D
+ Friend Structure E
+ End Structure
+ End Class
+End Namespace"
+
+ Assert.Equal("N.D.E", GetReturnTypeName(source, "C.M1"))
+ End Sub
+
+
+ Sub GetReturnTypeNameGenericOfPrimitive()
+ Dim source = "
+Imports System
+Class C
+ Function M1() As Action(Of Int32)
+ Return Nothing
+ End Function
+End Class"
+
+ Assert.Equal("System.Action(Of Integer)", GetReturnTypeName(source, "C.M1"))
+ End Sub
+
+
+ Sub GetReturnTypeNameGenericOfNested()
+ Dim source = "
+Imports System
+Class C
+ Function M1() As Action(Of D)
+ Return Nothing
+ End Function
+ Class D
+ End Class
+End Class"
+
+ Assert.Equal("System.Action(Of C.D)", GetReturnTypeName(source, "C.M1"))
+ End Sub
+
+
+ Sub GetReturnTypeNameGenericOfGeneric()
+ Dim source = "
+Imports System
+Class C
+ Function M1(Of T)() As Action(Of Func(Of T))
+ Return Nothing
+ End Function
+End Class"
+
+ Assert.Equal("System.Action(Of System.Func(Of Object))", GetReturnTypeName(source, "C.M1", typeArguments:={GetType(Object)}))
+ End Sub
+
+ Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, Optional typeArguments() As Type = Nothing, Optional argumentValues() As String = Nothing) As String
+ Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray()
+ Return GetName(source, methodName, argumentFlags, serializedTypeArgumentNames, argumentValues)
+ End Function
+
+ Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, typeArguments() As String, Optional argumentValues() As String = Nothing) As String
Debug.Assert((argumentFlags And (DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types)) = argumentFlags,
"Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags)
+ Dim instructionDecoder = VisualBasicInstructionDecoder.Instance
+ Dim method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder)
+
+ Dim includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types)
+ Dim includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names)
+ Dim builder As ArrayBuilder(Of String) = Nothing
+ If argumentValues IsNot Nothing Then
+ Assert.InRange(argumentValues.Length, 1, Integer.MaxValue)
+ builder = ArrayBuilder(Of String).GetInstance()
+ builder.AddRange(argumentValues)
+ End If
+
+ Dim name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder)
+ If builder IsNot Nothing Then
+ builder.Free()
+ End If
+
+ Return name
+ End Function
+
+ Private Function GetReturnTypeName(source As String, methodName As String, Optional typeArguments() As Type = Nothing) As String
+ Dim instructionDecoder = VisualBasicInstructionDecoder.Instance
+ Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray()
+ Dim method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder)
+
+ Return instructionDecoder.GetReturnTypeName(method)
+ End Function
+
+ Private Function GetConstructedMethod(source As String, methodName As String, serializedTypeArgumentNames() As String, instructionDecoder As VisualBasicInstructionDecoder) As MethodSymbol
Dim compilation = CreateCompilationWithReferences(
{VisualBasicSyntaxTree.ParseText(source)},
references:={MscorlibRef_v4_0_30316_17626, MsvbRef_v4_0_30319_17929},
- options:=TestOptions.DebugDll)
+ options:=TestOptions.DebugDll,
+ assemblyName:=NameOf(InstructionDecoderTests))
Dim runtime = CreateRuntimeInstance(compilation)
Dim moduleInstances = runtime.Modules
Dim blocks = moduleInstances.SelectAsArray(Function(m) m.MetadataBlock)
@@ -301,26 +452,24 @@ End Module"
' Once we have the method token, we want to look up the method (again)
' using the same helper as the product code. This helper will also map
' async/ iterator "MoveNext" methods to the original source method.
- Dim method = compilation.GetSourceMethod(
+ Dim method As MethodSymbol = compilation.GetSourceMethod(
DirectCast(frame.ContainingModule, PEModuleSymbol).Module.GetModuleVersionIdOrThrow(),
MetadataTokens.GetToken(frame.Handle))
- Dim includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types)
- Dim includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names)
- Dim builder As ArrayBuilder(Of String) = Nothing
- If argumentValues.Length > 0 Then
- builder = ArrayBuilder(Of String).GetInstance()
- builder.AddRange(argumentValues)
- End If
-
- Dim frameDecoder = VisualBasicInstructionDecoder.Instance
- Dim frameName = frameDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder)
- If builder IsNot Nothing Then
- builder.Free()
+ If serializedTypeArgumentNames IsNot Nothing Then
+ Assert.NotEmpty(serializedTypeArgumentNames)
+ Dim typeParameters = instructionDecoder.GetAllTypeParameters(method)
+ Assert.NotEmpty(typeParameters)
+ Dim typeNameDecoder = New EETypeNameDecoder(compilation, DirectCast(method.ContainingModule, PEModuleSymbol))
+ ' Use the same helper method as the FrameDecoder to get the TypeSymbols for the
+ ' generic type arguments (rather than using EETypeNameDecoder directly).
+ Dim typeArgumentSymbols = instructionDecoder.GetTypeSymbols(compilation, method, serializedTypeArgumentNames)
+ If Not typeArgumentSymbols.IsEmpty Then
+ method = instructionDecoder.ConstructMethod(method, typeParameters, typeArgumentSymbols)
+ End If
End If
- Return frameName
+ Return method
End Function
-
End Class
End Namespace