diff --git a/Roslyn.sln b/Roslyn.sln
index 3a0c0c701d85ab72ce30fe49025d256f8bdc48ce..47bd7afd5e313cdb659b05e9f043804cb63c22b9 100644
--- a/Roslyn.sln
+++ b/Roslyn.sln
@@ -365,6 +365,10 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "ServicesTestUtilities2", "s
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "VisualStudioTestUtilities2", "src\VisualStudio\TestUtilities2\VisualStudioTestUtilities2.vbproj", "{69F853E5-BD04-4842-984F-FC68CC51F402}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionResolver", "src\ExpressionEvaluator\Core\Source\FunctionResolver\FunctionResolver.csproj", "{6FC8E6F5-659C-424D-AEB5-331B95883E29}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunctionResolverTest", "src\ExpressionEvaluator\Core\Test\FunctionResolver\FunctionResolverTest.csproj", "{DD317BE1-42A1-4795-B1D4-F370C40D649A}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Compilers\Core\CommandLine\CommandLine.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4
@@ -3212,6 +3216,46 @@ Global
{69F853E5-BD04-4842-984F-FC68CC51F402}.Release|x64.Build.0 = Release|Any CPU
{69F853E5-BD04-4842-984F-FC68CC51F402}.Release|x86.ActiveCfg = Release|Any CPU
{69F853E5-BD04-4842-984F-FC68CC51F402}.Release|x86.Build.0 = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|ARM.Build.0 = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|x64.Build.0 = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Debug|x86.Build.0 = Debug|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|ARM.ActiveCfg = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|ARM.Build.0 = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|x64.ActiveCfg = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|x64.Build.0 = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|x86.ActiveCfg = Release|Any CPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}.Release|x86.Build.0 = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|ARM.Build.0 = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|x64.Build.0 = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Debug|x86.Build.0 = Debug|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|ARM.ActiveCfg = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|ARM.Build.0 = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|x64.ActiveCfg = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|x64.Build.0 = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|x86.ActiveCfg = Release|Any CPU
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3383,5 +3427,7 @@ Global
{57557F32-0635-421A-BCF8-549AED50F764} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{3DFB4701-E3D6-4435-9F70-A6E35822C4F2} = {EE97CB90-33BB-4F3A-9B3D-69375DEC6AC6}
{69F853E5-BD04-4842-984F-FC68CC51F402} = {8DBA5174-B0AA-4561-82B1-A46607697753}
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6}
+ {DD317BE1-42A1-4795-B1D4-F370C40D649A} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6}
EndGlobalSection
EndGlobal
diff --git a/build/config/SignToolData.json b/build/config/SignToolData.json
index 811c5f856f8c765024a78ef4e4706ba5229b38a9..5d78c3ef163f92b6201bd0b38d3f4e28a3789a5c 100644
--- a/build/config/SignToolData.json
+++ b/build/config/SignToolData.json
@@ -66,6 +66,7 @@
"strongName": "MsSharedLib72",
"values": [
"Dlls\\ExpressionCompiler\\Microsoft.CodeAnalysis.ExpressionEvaluator.ExpressionCompiler.dll",
+ "Dlls\\FunctionResolver\\Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver.dll",
"Dlls\\ResultProvider.Portable\\Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider.dll",
"Dlls\\CSharpExpressionCompiler\\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler.dll",
"Dlls\\CSharpResultProvider.Portable\\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider.dll",
diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
index 6acf1f9fcd76701f932893f9be914e18f281e098..3d2721f1334130e20105e369ea02a6b7e9a38518 100644
--- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
+++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
@@ -787,6 +787,7 @@
+
diff --git a/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs
index 83c0be3bc7d59769a4862d3ddf8270f334383216..31b8783d4f3414f5497325c0beac18090cd62c44 100644
--- a/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs
@@ -1,8 +1,5 @@
// 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.Symbols;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
@@ -36,47 +33,5 @@ public static RefKind GetRefKind(this SyntaxKind syntaxKind)
throw ExceptionUtilities.UnexpectedValue(syntaxKind);
}
}
-
- internal static SpecialType GetSpecialType(this SyntaxKind kind)
- {
- switch (kind)
- {
- case SyntaxKind.VoidKeyword:
- return SpecialType.System_Void;
- case SyntaxKind.BoolKeyword:
- return SpecialType.System_Boolean;
- case SyntaxKind.ByteKeyword:
- return SpecialType.System_Byte;
- case SyntaxKind.SByteKeyword:
- return SpecialType.System_SByte;
- case SyntaxKind.ShortKeyword:
- return SpecialType.System_Int16;
- case SyntaxKind.UShortKeyword:
- return SpecialType.System_UInt16;
- case SyntaxKind.IntKeyword:
- return SpecialType.System_Int32;
- case SyntaxKind.UIntKeyword:
- return SpecialType.System_UInt32;
- case SyntaxKind.LongKeyword:
- return SpecialType.System_Int64;
- case SyntaxKind.ULongKeyword:
- return SpecialType.System_UInt64;
- case SyntaxKind.DoubleKeyword:
- return SpecialType.System_Double;
- case SyntaxKind.FloatKeyword:
- return SpecialType.System_Single;
- case SyntaxKind.DecimalKeyword:
- return SpecialType.System_Decimal;
- case SyntaxKind.StringKeyword:
- return SpecialType.System_String;
- case SyntaxKind.CharKeyword:
- return SpecialType.System_Char;
- case SyntaxKind.ObjectKeyword:
- return SpecialType.System_Object;
- default:
- // Note that "dynamic" is a contextual keyword, so it should never show up here.
- throw ExceptionUtilities.UnexpectedValue(kind);
- }
- }
}
}
diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8c8febb382b69353095da7c037e1fea9dc7bc1c8
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs
@@ -0,0 +1,51 @@
+// 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 Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp
+{
+ internal static partial class SyntaxKindExtensions
+ {
+ internal static SpecialType GetSpecialType(this SyntaxKind kind)
+ {
+ switch (kind)
+ {
+ case SyntaxKind.VoidKeyword:
+ return SpecialType.System_Void;
+ case SyntaxKind.BoolKeyword:
+ return SpecialType.System_Boolean;
+ case SyntaxKind.ByteKeyword:
+ return SpecialType.System_Byte;
+ case SyntaxKind.SByteKeyword:
+ return SpecialType.System_SByte;
+ case SyntaxKind.ShortKeyword:
+ return SpecialType.System_Int16;
+ case SyntaxKind.UShortKeyword:
+ return SpecialType.System_UInt16;
+ case SyntaxKind.IntKeyword:
+ return SpecialType.System_Int32;
+ case SyntaxKind.UIntKeyword:
+ return SpecialType.System_UInt32;
+ case SyntaxKind.LongKeyword:
+ return SpecialType.System_Int64;
+ case SyntaxKind.ULongKeyword:
+ return SpecialType.System_UInt64;
+ case SyntaxKind.DoubleKeyword:
+ return SpecialType.System_Double;
+ case SyntaxKind.FloatKeyword:
+ return SpecialType.System_Single;
+ case SyntaxKind.DecimalKeyword:
+ return SpecialType.System_Decimal;
+ case SyntaxKind.StringKeyword:
+ return SpecialType.System_String;
+ case SyntaxKind.CharKeyword:
+ return SpecialType.System_Char;
+ case SyntaxKind.ObjectKeyword:
+ return SpecialType.System_Object;
+ default:
+ // Note that "dynamic" is a contextual keyword, so it should never show up here.
+ throw ExceptionUtilities.UnexpectedValue(kind);
+ }
+ }
+ }
+}
diff --git a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj
index f1dd7086cd3c745a64a1271c6b2cce3609b3c161..846ad16cf384f8b73749c35decff87621296bb60 100644
--- a/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj
+++ b/src/Compilers/Test/Utilities/CSharp.Desktop/CSharpCompilerTestUtilities.Desktop.csproj
@@ -107,6 +107,7 @@
+
diff --git a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/CSharpResultProvider.projitems b/src/ExpressionEvaluator/CSharp/Source/ResultProvider/CSharpResultProvider.projitems
index f8d5968d31c22716e51d2a810abe21de7d321160..c8910885947080730336709f8531a33c0cfd4d7b 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/CSharpResultProvider.projitems
+++ b/src/ExpressionEvaluator/CSharp/Source/ResultProvider/CSharpResultProvider.projitems
@@ -21,4 +21,4 @@
Designer
-
+
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/CSharpFunctionResolver.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/CSharpFunctionResolver.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bae45692d498f451fc3d45d1c49e1639d9875067
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/CSharpFunctionResolver.cs
@@ -0,0 +1,21 @@
+// 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 Microsoft.CodeAnalysis.ExpressionEvaluator;
+using Microsoft.VisualStudio.Debugger.FunctionResolution;
+
+namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
+{
+ [DkmReportNonFatalWatsonException(ExcludeExceptionType = typeof(NotImplementedException)), DkmContinueCorruptingException]
+ internal sealed class CSharpFunctionResolver : FunctionResolver
+ {
+ public CSharpFunctionResolver()
+ {
+ }
+
+ internal override RequestSignature GetParsedSignature(DkmRuntimeFunctionResolutionRequest request)
+ {
+ return MemberSignatureParser.Parse(request.FunctionName);
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/GreenNode.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/GreenNode.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e0a2e171c05bdc8040d13f406822c6ced6c5a110
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/GreenNode.cs
@@ -0,0 +1,9 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.CodeAnalysis
+{
+ internal static class GreenNode
+ {
+ internal const int ListKind = 1; // See SyntaxKind.
+ }
+}
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/MemberSignatureParser.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/MemberSignatureParser.cs
new file mode 100644
index 0000000000000000000000000000000000000000..aa1289a132f9b2e3778baaa3cab68ec1325e2d42
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/MemberSignatureParser.cs
@@ -0,0 +1,340 @@
+// 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.ExpressionEvaluator;
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+
+namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
+{
+ internal sealed partial class MemberSignatureParser
+ {
+ internal static RequestSignature Parse(string signature)
+ {
+ var scanner = new Scanner(signature);
+ var builder = ImmutableArray.CreateBuilder();
+ Token token;
+ do
+ {
+ scanner.MoveNext();
+ token = scanner.CurrentToken;
+ builder.Add(token);
+ } while (token.Kind != TokenKind.End);
+ var parser = new MemberSignatureParser(builder.ToImmutable());
+ try
+ {
+ return parser.Parse();
+ }
+ catch (InvalidSignatureException)
+ {
+ return null;
+ }
+ }
+
+ private readonly ImmutableArray _tokens;
+ private int _tokenIndex;
+
+ private MemberSignatureParser(ImmutableArray tokens)
+ {
+ _tokens = tokens;
+ _tokenIndex = 0;
+ }
+
+ private Token CurrentToken => _tokens[_tokenIndex];
+
+ private Token PeekToken(int offset)
+ {
+ return _tokens[_tokenIndex + offset];
+ }
+
+ private Token EatToken()
+ {
+ var token = CurrentToken;
+ Debug.Assert(token.Kind != TokenKind.End);
+ _tokenIndex++;
+ return token;
+ }
+
+ private RequestSignature Parse()
+ {
+ var methodName = ParseName();
+ var parameters = default(ImmutableArray);
+ if (CurrentToken.Kind == TokenKind.OpenParen)
+ {
+ parameters = ParseParameters();
+ }
+ if (CurrentToken.Kind != TokenKind.End)
+ {
+ throw InvalidSignature();
+ }
+ return new RequestSignature(methodName, parameters);
+ }
+
+ private Name ParseName()
+ {
+ Name signature = null;
+ while (true)
+ {
+ if (CurrentToken.Kind != TokenKind.Identifier)
+ {
+ throw InvalidSignature();
+ }
+ var name = EatToken().Text;
+ signature = new QualifiedName(signature, name);
+ if (CurrentToken.Kind == TokenKind.LessThan)
+ {
+ var typeParameters = ParseTypeParameters();
+ signature = new GenericName((QualifiedName)signature, typeParameters);
+ }
+ if (CurrentToken.Kind != TokenKind.Dot)
+ {
+ return signature;
+ }
+ EatToken();
+ }
+ }
+
+ private ImmutableArray ParseTypeParameters()
+ {
+ Debug.Assert(CurrentToken.Kind == TokenKind.LessThan);
+ EatToken();
+ var builder = ImmutableArray.CreateBuilder();
+ while (true)
+ {
+ if (CurrentToken.Kind != TokenKind.Identifier)
+ {
+ throw InvalidSignature();
+ }
+ var name = EatToken().Text;
+ builder.Add(name);
+ switch (CurrentToken.Kind)
+ {
+ case TokenKind.GreaterThan:
+ EatToken();
+ return builder.ToImmutable();
+ case TokenKind.Comma:
+ EatToken();
+ break;
+ default:
+ throw InvalidSignature();
+ }
+ }
+ }
+
+ private TypeSignature ParseTypeName()
+ {
+ TypeSignature signature = null;
+ while (true)
+ {
+ switch (CurrentToken.Kind)
+ {
+ case TokenKind.Identifier:
+ {
+ var token = EatToken();
+ var name = token.Text;
+ signature = new QualifiedTypeSignature(signature, name);
+ }
+ break;
+ case TokenKind.Keyword:
+ if (signature == null)
+ {
+ // Expand special type keywords (object, int, etc.) to qualified names.
+ // This is only done for the first identifier in a qualified name.
+ var specialType = GetSpecialType(CurrentToken.KeywordKind);
+ if (specialType != SpecialType.None)
+ {
+ EatToken();
+ signature = specialType.GetTypeSignature();
+ Debug.Assert(signature != null);
+ }
+ if (signature != null)
+ {
+ break;
+ }
+ }
+ throw InvalidSignature();
+ default:
+ throw InvalidSignature();
+ }
+ if (CurrentToken.Kind == TokenKind.LessThan)
+ {
+ var typeArguments = ParseTypeArguments();
+ signature = new GenericTypeSignature((QualifiedTypeSignature)signature, typeArguments);
+ }
+ if (CurrentToken.Kind != TokenKind.Dot)
+ {
+ return signature;
+ }
+ EatToken();
+ }
+ }
+
+ private ImmutableArray ParseTypeArguments()
+ {
+ Debug.Assert(CurrentToken.Kind == TokenKind.LessThan);
+ EatToken();
+ var builder = ImmutableArray.CreateBuilder();
+ while (true)
+ {
+ var name = ParseType();
+ builder.Add(name);
+ switch (CurrentToken.Kind)
+ {
+ case TokenKind.GreaterThan:
+ EatToken();
+ return builder.ToImmutable();
+ case TokenKind.Comma:
+ EatToken();
+ break;
+ default:
+ throw InvalidSignature();
+ }
+ }
+ }
+
+ private TypeSignature ParseType()
+ {
+ TypeSignature type = ParseTypeName();
+ while (true)
+ {
+ switch (CurrentToken.Kind)
+ {
+ case TokenKind.OpenBracket:
+ EatToken();
+ int rank = 1;
+ while (CurrentToken.Kind == TokenKind.Comma)
+ {
+ EatToken();
+ rank++;
+ }
+ if (CurrentToken.Kind != TokenKind.CloseBracket)
+ {
+ throw InvalidSignature();
+ }
+ EatToken();
+ type = new ArrayTypeSignature(type, rank);
+ break;
+ case TokenKind.Asterisk:
+ EatToken();
+ type = new PointerTypeSignature(type);
+ break;
+ case TokenKind.QuestionMark:
+ EatToken();
+ type = new GenericTypeSignature(
+ SpecialType.System_Nullable_T.GetTypeSignature(),
+ ImmutableArray.Create(type));
+ break;
+ default:
+ return type;
+ }
+ }
+ }
+
+ // Returns true if the parameter is by reference.
+ private bool ParseParameterModifiers()
+ {
+ bool isByRef = false;
+ while (true)
+ {
+ var kind = CurrentToken.KeywordKind;
+ if (kind != SyntaxKind.RefKeyword && kind != SyntaxKind.OutKeyword)
+ {
+ break;
+ }
+ if (isByRef)
+ {
+ // Duplicate modifiers.
+ throw InvalidSignature();
+ }
+ isByRef = true;
+ EatToken();
+ }
+ return isByRef;
+ }
+
+ private ImmutableArray ParseParameters()
+ {
+ Debug.Assert(CurrentToken.Kind == TokenKind.OpenParen);
+ EatToken();
+ if (CurrentToken.Kind == TokenKind.CloseParen)
+ {
+ EatToken();
+ return ImmutableArray.Empty;
+ }
+ var builder = ImmutableArray.CreateBuilder();
+ while (true)
+ {
+ bool isByRef = ParseParameterModifiers();
+ var parameterType = ParseType();
+ builder.Add(new ParameterSignature(parameterType, isByRef));
+ switch (CurrentToken.Kind)
+ {
+ case TokenKind.CloseParen:
+ EatToken();
+ return builder.ToImmutable();
+ case TokenKind.Comma:
+ EatToken();
+ break;
+ default:
+ throw InvalidSignature();
+ }
+ }
+ }
+
+ private static bool IsKeyword(string text)
+ {
+ return SyntaxFacts.GetKeywordKind(text) != SyntaxKind.None;
+ }
+
+ private static SpecialType GetSpecialType(SyntaxKind kind)
+ {
+ switch (kind)
+ {
+ case SyntaxKind.VoidKeyword:
+ return SpecialType.System_Void;
+ case SyntaxKind.BoolKeyword:
+ return SpecialType.System_Boolean;
+ case SyntaxKind.CharKeyword:
+ return SpecialType.System_Char;
+ case SyntaxKind.SByteKeyword:
+ return SpecialType.System_SByte;
+ case SyntaxKind.ByteKeyword:
+ return SpecialType.System_Byte;
+ case SyntaxKind.ShortKeyword:
+ return SpecialType.System_Int16;
+ case SyntaxKind.UShortKeyword:
+ return SpecialType.System_UInt16;
+ case SyntaxKind.IntKeyword:
+ return SpecialType.System_Int32;
+ case SyntaxKind.UIntKeyword:
+ return SpecialType.System_UInt32;
+ case SyntaxKind.LongKeyword:
+ return SpecialType.System_Int64;
+ case SyntaxKind.ULongKeyword:
+ return SpecialType.System_UInt64;
+ case SyntaxKind.FloatKeyword:
+ return SpecialType.System_Single;
+ case SyntaxKind.DoubleKeyword:
+ return SpecialType.System_Double;
+ case SyntaxKind.StringKeyword:
+ return SpecialType.System_String;
+ case SyntaxKind.ObjectKeyword:
+ return SpecialType.System_Object;
+ case SyntaxKind.DecimalKeyword:
+ return SpecialType.System_Decimal;
+ default:
+ return SpecialType.None;
+ }
+ }
+
+
+ private static Exception InvalidSignature()
+ {
+ return new InvalidSignatureException();
+ }
+
+ private sealed class InvalidSignatureException : Exception
+ {
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/Scanner.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/Scanner.cs
new file mode 100644
index 0000000000000000000000000000000000000000..02d2d492fa9ffa74edabf4bba588cdce6e985fb9
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/CSharp/Scanner.cs
@@ -0,0 +1,135 @@
+// 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 Roslyn.Utilities;
+using System;
+using System.Diagnostics;
+
+namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
+{
+ internal sealed partial class MemberSignatureParser
+ {
+ private enum TokenKind
+ {
+ OpenParen = '(',
+ CloseParen = ')',
+ OpenBracket = '[',
+ CloseBracket = ']',
+ Dot = '.',
+ Comma = ',',
+ Asterisk = '*',
+ QuestionMark = '?',
+ LessThan = '<',
+ GreaterThan = '>',
+
+ Start = char.MaxValue + 1,
+ End,
+ Identifier,
+ Keyword,
+ ColonColon,
+ }
+
+ [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
+ private struct Token
+ {
+ internal readonly TokenKind Kind;
+ internal readonly string Text;
+ internal readonly SyntaxKind KeywordKind;
+
+ internal Token(TokenKind kind, string text = null, SyntaxKind keywordKind = SyntaxKind.None)
+ {
+ Kind = kind;
+ Text = text;
+ KeywordKind = keywordKind;
+ }
+
+ private string GetDebuggerDisplay()
+ {
+ return (Text == null) ?
+ Kind.ToString() :
+ $"{Kind}: \"{Text}\"";
+ }
+ }
+
+ private sealed class Scanner
+ {
+ private readonly string _text;
+ private int _offset;
+ private Token _currentToken;
+
+ internal Scanner(string text)
+ {
+ _text = text;
+ _offset = 0;
+ _currentToken = default(Token);
+ }
+
+ internal Token CurrentToken
+ {
+ get
+ {
+ if (_currentToken.Kind == TokenKind.Start)
+ {
+ throw new InvalidOperationException();
+ }
+ return _currentToken;
+ }
+ }
+
+ internal void MoveNext()
+ {
+ _currentToken = Scan();
+ }
+
+ private Token Scan()
+ {
+ int length = _text.Length;
+ while (_offset < length && char.IsWhiteSpace(_text[_offset]))
+ {
+ _offset++;
+ }
+
+ if (_offset == length)
+ {
+ return new Token(TokenKind.End);
+ }
+
+ var c = _text[_offset++];
+ if (UnicodeCharacterUtilities.IsIdentifierStartCharacter(c))
+ {
+ return ScanIdentifierAfterStartCharacter(verbatim: false);
+ }
+ else if (c == '@' && _offset < length && UnicodeCharacterUtilities.IsIdentifierStartCharacter(_text[_offset]))
+ {
+ _offset++;
+ return ScanIdentifierAfterStartCharacter(verbatim: true);
+ }
+
+ return new Token((TokenKind)c);
+ }
+
+ private Token ScanIdentifierAfterStartCharacter(bool verbatim)
+ {
+ // Assert the offset is immediately following the start character.
+ Debug.Assert(_offset > 0);
+ Debug.Assert(UnicodeCharacterUtilities.IsIdentifierStartCharacter(_text[_offset - 1]));
+ Debug.Assert(_offset == 1 || !UnicodeCharacterUtilities.IsIdentifierPartCharacter(_text[_offset - 2]));
+
+ int length = _text.Length;
+ int start = _offset - 1;
+ while ((_offset < length) && UnicodeCharacterUtilities.IsIdentifierPartCharacter(_text[_offset]))
+ {
+ _offset++;
+ }
+ var text = _text.Substring(start, _offset - start);
+ var keywordKind = verbatim ?
+ SyntaxKind.None :
+ SyntaxFacts.GetKeywordKind(text);
+ if (keywordKind == SyntaxKind.None)
+ {
+ return new Token(TokenKind.Identifier, text);
+ }
+ return new Token(TokenKind.Keyword, text, keywordKind);
+ }
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f07a04ebe94ac0ace28e7cbb5878a6709f543724
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.cs
@@ -0,0 +1,206 @@
+// 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.VisualStudio.Debugger;
+using Microsoft.VisualStudio.Debugger.Clr;
+using Microsoft.VisualStudio.Debugger.ComponentInterfaces;
+using Microsoft.VisualStudio.Debugger.Evaluation;
+using Microsoft.VisualStudio.Debugger.FunctionResolution;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal abstract class FunctionResolver :
+ FunctionResolverBase,
+ IDkmRuntimeFunctionResolver,
+ IDkmModuleInstanceLoadNotification,
+ IDkmModuleInstanceUnloadNotification,
+ IDkmModuleModifiedNotification
+ {
+ void IDkmRuntimeFunctionResolver.EnableResolution(DkmRuntimeFunctionResolutionRequest request, DkmWorkList workList)
+ {
+ if (request.LineOffset > 0)
+ {
+ return;
+ }
+
+ var languageId = request.CompilerId.LanguageId;
+ if (languageId == DkmLanguageId.MethodId)
+ {
+ return;
+ }
+ else if (languageId != default(Guid))
+ {
+ // Verify module matches language before binding
+ // (see https://github.com/dotnet/roslyn/issues/15119).
+ }
+
+ EnableResolution(request.Process, request);
+ }
+
+ void IDkmModuleInstanceLoadNotification.OnModuleInstanceLoad(DkmModuleInstance moduleInstance, DkmWorkList workList, DkmEventDescriptorS eventDescriptor)
+ {
+ var module = moduleInstance as DkmClrModuleInstance;
+ Debug.Assert(module != null); // should ensure this.
+ if (module == null)
+ {
+ // Only interested in managed modules.
+ return;
+ }
+
+ OnModuleLoad(module.Process, module);
+ }
+
+ void IDkmModuleInstanceUnloadNotification.OnModuleInstanceUnload(DkmModuleInstance moduleInstance, DkmWorkList workList, DkmEventDescriptor eventDescriptor)
+ {
+ // Implementing IDkmModuleInstanceUnloadNotification
+ // (with Synchronized="true" in .vsdconfigxml) prevents
+ // caller from unloading modules while binding.
+ }
+
+ void IDkmModuleModifiedNotification.OnModuleModified(DkmModuleInstance moduleInstance)
+ {
+ // Implementing IDkmModuleModifiedNotification
+ // (with Synchronized="true" in .vsdconfigxml) prevents
+ // caller from modifying modules while binding.
+ }
+
+ internal override bool ShouldEnableFunctionResolver(DkmProcess process)
+ {
+ var dataItem = process.GetDataItem();
+ if (dataItem == null)
+ {
+ var enable = ShouldEnable(process);
+ dataItem = new FunctionResolverDataItem(enable);
+ process.SetDataItem(DkmDataCreationDisposition.CreateNew, dataItem);
+ }
+ return dataItem.Enabled;
+ }
+
+ internal override IEnumerable GetAllModules(DkmProcess process)
+ {
+ foreach (var runtimeInstance in process.GetRuntimeInstances())
+ {
+ var runtime = runtimeInstance as DkmClrRuntimeInstance;
+ if (runtime == null)
+ {
+ continue;
+ }
+ foreach (var moduleInstance in runtime.GetModuleInstances())
+ {
+ var module = moduleInstance as DkmClrModuleInstance;
+ // Only interested in managed modules.
+ if (module != null)
+ {
+ yield return module;
+ }
+ }
+ }
+ }
+
+ internal override string GetModuleName(DkmClrModuleInstance module)
+ {
+ return module.Name;
+ }
+
+ internal override MetadataReader GetModuleMetadata(DkmClrModuleInstance module)
+ {
+ uint length;
+ IntPtr ptr;
+ try
+ {
+ ptr = module.GetMetaDataBytesPtr(out length);
+ }
+ catch (Exception e) when (IsBadOrMissingMetadataException(e))
+ {
+ return null;
+ }
+ Debug.Assert(length > 0);
+ unsafe
+ {
+ return new MetadataReader((byte*)ptr, (int)length);
+ }
+ }
+
+ internal override DkmRuntimeFunctionResolutionRequest[] GetRequests(DkmProcess process)
+ {
+ return process.GetRuntimeFunctionResolutionRequests();
+ }
+
+ internal override string GetRequestModuleName(DkmRuntimeFunctionResolutionRequest request)
+ {
+ return request.ModuleName;
+ }
+
+ internal override void OnFunctionResolved(
+ DkmClrModuleInstance module,
+ DkmRuntimeFunctionResolutionRequest request,
+ int token,
+ int version,
+ int ilOffset)
+ {
+ var address = DkmClrInstructionAddress.Create(
+ module.RuntimeInstance,
+ module,
+ new DkmClrMethodId(Token: token, Version: (uint)version),
+ NativeOffset: uint.MaxValue,
+ ILOffset: (uint)ilOffset,
+ CPUInstruction: null);
+ request.OnFunctionResolved(address);
+ }
+
+ private static readonly Guid s_messageSourceId = new Guid("ac353c9b-c599-427b-9424-cbe1ad19f81e");
+
+ private static bool ShouldEnable(DkmProcess process)
+ {
+ var message = DkmCustomMessage.Create(
+ process.Connection,
+ process,
+ s_messageSourceId,
+ MessageCode: 1, // Is legacy EE enabled?
+ Parameter1: null,
+ Parameter2: null);
+ try
+ {
+ var reply = message.SendLower();
+ var result = (int)reply.Parameter1;
+ // Possible values are 0 = false, 1 = true, 2 = not ready.
+ // At this point, we should only get 0 or 1, but to be
+ // safe, treat values other than 0 or 1 as false.
+ Debug.Assert(result == 0 || result == 1);
+ return result == 0;
+ }
+ catch (NotImplementedException)
+ {
+ return false;
+ }
+ }
+
+ private const uint COR_E_BADIMAGEFORMAT = 0x8007000b;
+ private const uint CORDBG_E_MISSING_METADATA = 0x80131c35;
+
+ private static bool IsBadOrMissingMetadataException(Exception e)
+ {
+ switch (unchecked((uint)e.HResult))
+ {
+ case COR_E_BADIMAGEFORMAT:
+ case CORDBG_E_MISSING_METADATA:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private sealed class FunctionResolverDataItem : DkmDataItem
+ {
+ internal FunctionResolverDataItem(bool enabled)
+ {
+ Enabled = enabled;
+ }
+
+ internal readonly bool Enabled;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.csproj b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..21ed9b209aaa820be4dfc248a64992c7bbe7e2e8
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.csproj
@@ -0,0 +1,70 @@
+
+
+
+
+ AnyCPU
+ {6FC8E6F5-659C-424D-AEB5-331B95883E29}
+ Library
+ Microsoft.CodeAnalysis.ExpressionEvaluator
+ Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver
+ true
+ .NETPortable
+ v5.0
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ false
+
+
+
+ Compiler\ExceptionUtilities.cs
+
+
+ Compiler\MetadataTypeCodeExtensions.cs
+
+
+ Compiler\SpecialType.cs
+
+
+ Compiler\WellKnownMemberNames.cs
+
+
+ Compiler\UnicodeCharacterUtilities.cs
+
+
+ CSharp\Compiler\SyntaxKind.cs
+
+
+ CSharp\Compiler\SyntaxKindFacts.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {5002636a-fe8d-40bf-8818-ab513a2194fa}
+ Concord
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.vsdconfigxml b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.vsdconfigxml
new file mode 100644
index 0000000000000000000000000000000000000000..25272422350d74f59f83be6934ef68f57fdd3e66
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolver.vsdconfigxml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolverBase.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolverBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..711a6e1c8f2605bdc2776562665d470fa58a7f02
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/FunctionResolverBase.cs
@@ -0,0 +1,115 @@
+// 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.Generic;
+using System.Reflection.Metadata;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal abstract class FunctionResolverBase
+ where TProcess : class
+ where TModule : class
+ where TRequest : class
+ {
+ internal void EnableResolution(TProcess process, TRequest request)
+ {
+ ResolveRequest(process, request);
+ }
+
+ internal void OnModuleLoad(TProcess process, TModule module)
+ {
+ var requests = GetRequests(process);
+ ResolveRequests(process, module, requests);
+ }
+
+ internal abstract bool ShouldEnableFunctionResolver(TProcess process);
+ internal abstract IEnumerable GetAllModules(TProcess process);
+ internal abstract string GetModuleName(TModule module);
+ internal abstract MetadataReader GetModuleMetadata(TModule module);
+ internal abstract TRequest[] GetRequests(TProcess process);
+ internal abstract string GetRequestModuleName(TRequest request);
+ internal abstract RequestSignature GetParsedSignature(TRequest request);
+ internal abstract void OnFunctionResolved(TModule module, TRequest request, int token, int version, int ilOffset);
+
+ private void ResolveRequest(TProcess process, TRequest request)
+ {
+ var moduleName = GetRequestModuleName(request);
+ var signature = GetParsedSignature(request);
+ if (signature == null)
+ {
+ return;
+ }
+
+ bool checkEnabled = true;
+ foreach (var module in GetAllModules(process))
+ {
+ if (checkEnabled)
+ {
+ if (!ShouldEnableFunctionResolver(process))
+ {
+ return;
+ }
+ checkEnabled = false;
+ }
+ if (!ShouldModuleHandleRequest(module, moduleName))
+ {
+ continue;
+ }
+ var reader = GetModuleMetadata(module);
+ if (reader == null)
+ {
+ continue;
+ }
+ var resolver = new MetadataResolver(this, process, module, reader);
+ resolver.Resolve(request, signature);
+ }
+ }
+
+ private void ResolveRequests(TProcess process, TModule module, TRequest[] requests)
+ {
+ if (!ShouldEnableFunctionResolver(process))
+ {
+ return;
+ }
+
+ MetadataResolver resolver = null;
+
+ foreach (var request in requests)
+ {
+ var moduleName = GetRequestModuleName(request);
+ if (!ShouldModuleHandleRequest(module, moduleName))
+ {
+ continue;
+ }
+
+ var signature = GetParsedSignature(request);
+ if (signature == null)
+ {
+ continue;
+ }
+
+ if (resolver == null)
+ {
+ var reader = GetModuleMetadata(module);
+ if (reader == null)
+ {
+ return;
+ }
+ resolver = new MetadataResolver(this, process, module, reader);
+ }
+
+ resolver.Resolve(request, signature);
+ }
+ }
+
+ private bool ShouldModuleHandleRequest(TModule module, string moduleName)
+ {
+ if (string.IsNullOrEmpty(moduleName))
+ {
+ return true;
+ }
+ var name = GetModuleName(module);
+ return moduleName.Equals(name, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/MetadataDecoder.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/MetadataDecoder.cs
new file mode 100644
index 0000000000000000000000000000000000000000..651bdae0f71f12074f43b4e459a9fbe8a9f134e6
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/MetadataDecoder.cs
@@ -0,0 +1,306 @@
+// 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.Reflection.Metadata;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal struct MetadataDecoder
+ {
+ private readonly MetadataReader _reader;
+ private readonly ImmutableArray _allTypeParameters;
+ private readonly int _containingArity;
+ private readonly ImmutableArray _methodTypeParameters;
+
+ internal MetadataDecoder(
+ MetadataReader reader,
+ ImmutableArray allTypeParameters,
+ int containingArity,
+ ImmutableArray methodTypeParameters)
+ {
+ _reader = reader;
+ _allTypeParameters = allTypeParameters;
+ _containingArity = containingArity;
+ _methodTypeParameters = methodTypeParameters;
+ }
+
+ // cf. MetadataDecoder<>.GetSignatureForMethod.
+ internal ImmutableArray DecodeParameters(MethodDefinition methodDef)
+ {
+ var signatureReader = _reader.GetBlobReader(methodDef.Signature);
+ var signatureHeader = signatureReader.ReadSignatureHeader();
+ var typeParameterCount = signatureHeader.IsGeneric ? signatureReader.ReadCompressedInteger() : 0;
+ var parameterCount = signatureReader.ReadCompressedInteger();
+ var builder = ImmutableArray.CreateBuilder(parameterCount);
+ var returnType = DecodeParameter(ref signatureReader);
+ for (int i = 0; i < parameterCount; i++)
+ {
+ builder.Add(DecodeParameter(ref signatureReader));
+ }
+ return builder.ToImmutable();
+ }
+
+ // cf. MetadataDecoder<>.DecodeParameterOrThrow.
+ private ParameterSignature DecodeParameter(ref BlobReader signatureReader)
+ {
+ bool isByRef = false;
+ while (true)
+ {
+ var typeCode = signatureReader.ReadSignatureTypeCode();
+ switch (typeCode)
+ {
+ case SignatureTypeCode.RequiredModifier:
+ case SignatureTypeCode.OptionalModifier:
+ // Skip modifiers.
+ break;
+ case SignatureTypeCode.ByReference:
+ isByRef = true;
+ break;
+ default:
+ var type = DecodeType(ref signatureReader, typeCode);
+ return new ParameterSignature(type, isByRef);
+ }
+ }
+ }
+
+ // cf. MetadataDecoder<>.DecodeTypeOrThrow.
+ private TypeSignature DecodeType(ref BlobReader signatureReader, SignatureTypeCode typeCode)
+ {
+ switch (typeCode)
+ {
+ case SignatureTypeCode.TypeHandle:
+ {
+ int typeArgumentOffset = 0;
+ return DecodeType(signatureReader.ReadTypeHandle(), ImmutableArray.Empty, ref typeArgumentOffset);
+ }
+ case SignatureTypeCode.Array:
+ {
+ var elementType = DecodeModifiersAndType(ref signatureReader);
+ int rank;
+ int sizes;
+ signatureReader.TryReadCompressedInteger(out rank);
+ signatureReader.TryReadCompressedInteger(out sizes);
+ if (sizes != 0)
+ {
+ throw UnhandledMetadata();
+ }
+ return new ArrayTypeSignature(elementType, rank);
+ }
+ case SignatureTypeCode.SZArray:
+ {
+ var elementType = DecodeModifiersAndType(ref signatureReader);
+ return new ArrayTypeSignature(elementType, 1);
+ }
+ case SignatureTypeCode.GenericTypeInstance:
+ return DecodeGenericTypeInstance(ref signatureReader);
+ case SignatureTypeCode.Pointer:
+ {
+ var pointedAtType = DecodeModifiersAndType(ref signatureReader);
+ return new PointerTypeSignature(pointedAtType);
+ }
+ case SignatureTypeCode.GenericTypeParameter:
+ return DecodeGenericTypeParameter(ref signatureReader, _allTypeParameters, _containingArity);
+ case SignatureTypeCode.GenericMethodParameter:
+ return DecodeGenericTypeParameter(ref signatureReader, _methodTypeParameters, 0);
+ default:
+ {
+ var signature = typeCode.ToSpecialType().GetTypeSignature();
+ if (signature == null)
+ {
+ throw UnhandledMetadata();
+ }
+ return signature;
+ }
+ }
+ }
+
+ // Ignore modifiers and decode type.
+ private TypeSignature DecodeModifiersAndType(ref BlobReader signatureReader)
+ {
+ while (true)
+ {
+ var typeCode = signatureReader.ReadSignatureTypeCode();
+ switch (typeCode)
+ {
+ case SignatureTypeCode.RequiredModifier:
+ case SignatureTypeCode.OptionalModifier:
+ // Skip modifiers.
+ break;
+ default:
+ return DecodeType(ref signatureReader, typeCode);
+ }
+ }
+ }
+
+ private TypeSignature DecodeGenericTypeParameter(
+ ref BlobReader signatureReader,
+ ImmutableArray typeParameters,
+ int containingArity)
+ {
+ int index = signatureReader.ReadCompressedInteger();
+ if (index < containingArity)
+ {
+ // Unspecified type parameter.
+ throw UnhandledMetadata();
+ }
+ var name = typeParameters[index - containingArity];
+ return new QualifiedTypeSignature(null, name);
+ }
+
+ // cf. MetadataDecoder<>.DecodeGenericTypeInstanceOrThrow.
+ private TypeSignature DecodeGenericTypeInstance(ref BlobReader signatureReader)
+ {
+ var typeCode = signatureReader.ReadSignatureTypeCode();
+ var typeHandle = signatureReader.ReadTypeHandle();
+ var typeArguments = DecodeGenericTypeArguments(ref signatureReader);
+ int typeArgumentOffset = 0;
+ var type = DecodeType(typeHandle, typeArguments, ref typeArgumentOffset);
+ if (typeArgumentOffset != typeArguments.Length)
+ {
+ // Generic type reference names must include arity
+ // to avoid loading referenced assemblies.
+ throw UnhandledMetadata();
+ }
+ return type;
+ }
+
+ private ImmutableArray DecodeGenericTypeArguments(ref BlobReader signatureReader)
+ {
+ int typeArgCount;
+ signatureReader.TryReadCompressedInteger(out typeArgCount);
+ var builder = ImmutableArray.CreateBuilder(typeArgCount);
+ for (int i = 0; i < typeArgCount; i++)
+ {
+ var typeArg = DecodeModifiersAndType(ref signatureReader);
+ builder.Add(typeArg);
+ }
+ return builder.ToImmutable();
+ }
+
+ // cf. MetadataDecoder<>.GetSymbolForTypeHandleOrThrow.
+ private TypeSignature DecodeType(
+ EntityHandle handle,
+ ImmutableArray typeArguments,
+ ref int typeArgumentOffset)
+ {
+ switch (handle.Kind)
+ {
+ case HandleKind.TypeDefinition:
+ return DecodeTypeDefinition((TypeDefinitionHandle)handle, typeArguments, ref typeArgumentOffset);
+ case HandleKind.TypeReference:
+ return DecodeTypeReference((TypeReferenceHandle)handle, typeArguments, ref typeArgumentOffset);
+ default:
+ throw new BadImageFormatException();
+ }
+ }
+
+ // cf. MetadataDecoder<>.GetTypeOfTypeDef.
+ private TypeSignature DecodeTypeDefinition(
+ TypeDefinitionHandle handle,
+ ImmutableArray typeArguments,
+ ref int typeArgumentOffset)
+ {
+ var typeDef = _reader.GetTypeDefinition(handle);
+ TypeSignature qualifier;
+ var declaringTypeHandle = typeDef.GetDeclaringType();
+ if (declaringTypeHandle.IsNil)
+ {
+ // Include namespace.
+ qualifier = GetNamespace(typeDef.Namespace);
+ }
+ else
+ {
+ // Include declaring type.
+ qualifier = DecodeTypeDefinition(declaringTypeHandle, typeArguments, ref typeArgumentOffset);
+ }
+ return CreateTypeSignature(qualifier, _reader.GetString(typeDef.Name), typeArguments, ref typeArgumentOffset);
+ }
+
+ // cf. MetadataDecoder<>.GetTypeOfTypeRef.
+ private TypeSignature DecodeTypeReference(
+ TypeReferenceHandle handle,
+ ImmutableArray typeArguments,
+ ref int typeArgumentOffset)
+ {
+ var typeRef = _reader.GetTypeReference(handle);
+ TypeSignature qualifier;
+ var scope = typeRef.ResolutionScope;
+ switch (scope.Kind)
+ {
+ case HandleKind.AssemblyReference:
+ case HandleKind.ModuleReference:
+ // Include namespace.
+ qualifier = GetNamespace(typeRef.Namespace);
+ break;
+ case HandleKind.TypeReference:
+ // Include declaring type.
+ qualifier = DecodeTypeReference((TypeReferenceHandle)scope, typeArguments, ref typeArgumentOffset);
+ break;
+ default:
+ throw new BadImageFormatException();
+ }
+ return CreateTypeSignature(qualifier, _reader.GetString(typeRef.Name), typeArguments, ref typeArgumentOffset);
+ }
+
+ private string GetTypeName(StringHandle nameHandle, out int arity)
+ {
+ return RemoveAritySeparatorIfAny(_reader.GetString(nameHandle), out arity);
+ }
+
+ private QualifiedTypeSignature GetNamespace(StringHandle namespaceHandle)
+ {
+ var namespaceName = _reader.GetString(namespaceHandle);
+ if (string.IsNullOrEmpty(namespaceName))
+ {
+ return null;
+ }
+ QualifiedTypeSignature signature = null;
+ var parts = namespaceName.Split('.');
+ foreach (var part in parts)
+ {
+ signature = new QualifiedTypeSignature(signature, part);
+ }
+ return signature;
+ }
+
+ private static TypeSignature CreateTypeSignature(
+ TypeSignature qualifier,
+ string typeName,
+ ImmutableArray typeArguments,
+ ref int typeArgumentOffset)
+ {
+ int arity;
+ typeName = RemoveAritySeparatorIfAny(typeName, out arity);
+ var qualifiedName = new QualifiedTypeSignature(qualifier, typeName);
+ if (arity == 0)
+ {
+ return qualifiedName;
+ }
+ typeArguments = ImmutableArray.Create(typeArguments, typeArgumentOffset, arity);
+ typeArgumentOffset += arity;
+ return new GenericTypeSignature(qualifiedName, typeArguments);
+ }
+
+ private static string RemoveAritySeparatorIfAny(string typeName, out int arity)
+ {
+ arity = 0;
+ int index = typeName.LastIndexOf('`');
+ if (index < 0)
+ {
+ return typeName;
+ }
+ int n;
+ if (int.TryParse(typeName.Substring(index + 1), out n))
+ {
+ arity = n;
+ }
+ return typeName.Substring(0, index);
+ }
+
+ private static Exception UnhandledMetadata()
+ {
+ return new NotSupportedException();
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/MetadataResolver.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/MetadataResolver.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b2f920dbf139cfa7174e7f82fe4d9ee6c5292853
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/MetadataResolver.cs
@@ -0,0 +1,407 @@
+// 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 Roslyn.Utilities;
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal sealed class MetadataResolver
+ where TProcess : class
+ where TModule : class
+ where TRequest : class
+ {
+ private static readonly StringComparer s_stringComparer = StringComparer.Ordinal;
+
+ private readonly FunctionResolverBase _resolver;
+ private readonly TProcess _process;
+ private readonly TModule _module;
+ private readonly MetadataReader _reader;
+
+ internal MetadataResolver(
+ FunctionResolverBase resolver,
+ TProcess process,
+ TModule module,
+ MetadataReader reader)
+ {
+ _resolver = resolver;
+ _process = process;
+ _module = module;
+ _reader = reader;
+ }
+
+ internal void Resolve(TRequest request, RequestSignature signature)
+ {
+ QualifiedName qualifiedTypeName;
+ ImmutableArray memberTypeParameters;
+ GetNameAndTypeParameters(signature.MemberName, out qualifiedTypeName, out memberTypeParameters);
+
+ var typeName = qualifiedTypeName.Qualifier;
+ var memberName = qualifiedTypeName.Name;
+ var memberParameters = signature.Parameters;
+
+ var allTypeParameters = GetAllGenericTypeParameters(typeName);
+
+ foreach (var typeHandle in _reader.TypeDefinitions)
+ {
+ var typeDef = _reader.GetTypeDefinition(typeHandle);
+ int containingArity = CompareToTypeDefinition(typeDef, typeName);
+ if (containingArity < 0)
+ {
+ continue;
+ }
+ // Visit methods.
+ foreach (var methodHandle in typeDef.GetMethods())
+ {
+ var methodDef = _reader.GetMethodDefinition(methodHandle);
+ if (!IsResolvableMethod(methodDef))
+ {
+ continue;
+ }
+ if (MatchesMethod(typeDef, methodDef, typeName, memberName, allTypeParameters, containingArity, memberTypeParameters, memberParameters))
+ {
+ OnFunctionResolved(request, methodHandle);
+ }
+ }
+ if (memberTypeParameters.IsEmpty)
+ {
+ // Visit properties.
+ foreach (var propertyHandle in typeDef.GetProperties())
+ {
+ var propertyDef = _reader.GetPropertyDefinition(propertyHandle);
+ if (MatchesProperty(typeDef, propertyDef, typeName, memberName, allTypeParameters, containingArity, memberParameters))
+ {
+ var accessors = propertyDef.GetAccessors();
+ OnAccessorResolved(request, accessors.Getter);
+ OnAccessorResolved(request, accessors.Setter);
+ }
+ }
+ }
+ }
+ }
+
+ // If the signature matches the TypeDefinition, including some or all containing
+ // types and namespaces, returns a non-negative value indicating the arity of the
+ // containing types that were not specified in the signature; otherwise returns -1.
+ // For instance, "C.D" will return 1 matching against metadata type N.M.A.B.C.D,
+ // where N.M is a namespace and A, B, C, D are nested types. The value 1
+ // is the arity of the containing types A.B that were missing from the signature.
+ // "C.D" will not match C or C.D.E since the match must include the entire
+ // signature, from nested TypeDefinition, out.
+ private int CompareToTypeDefinition(TypeDefinition typeDef, Name signature)
+ {
+ if (signature == null)
+ {
+ return typeDef.GetGenericParameters().Count;
+ }
+
+ QualifiedName qualifiedName;
+ ImmutableArray typeParameters;
+ GetNameAndTypeParameters(signature, out qualifiedName, out typeParameters);
+
+ if (!MatchesTypeName(typeDef, qualifiedName.Name))
+ {
+ return -1;
+ }
+
+ var declaringTypeHandle = typeDef.GetDeclaringType();
+ var declaringType = declaringTypeHandle.IsNil ? default(TypeDefinition) : _reader.GetTypeDefinition(declaringTypeHandle);
+ int declaringTypeParameterCount = declaringTypeHandle.IsNil ? 0 : declaringType.GetGenericParameters().Count;
+ if (!MatchesTypeParameterCount(typeParameters, typeDef.GetGenericParameters(), declaringTypeParameterCount))
+ {
+ return -1;
+ }
+
+ var qualifier = qualifiedName.Qualifier;
+ if (declaringTypeHandle.IsNil)
+ {
+ // Compare namespace.
+ return MatchesNamespace(typeDef, qualifier) ? 0 : -1;
+ }
+ else
+ {
+ // Compare declaring type.
+ return CompareToTypeDefinition(declaringType, qualifier);
+ }
+ }
+
+ private bool MatchesNamespace(TypeDefinition typeDef, Name signature)
+ {
+ if (signature == null)
+ {
+ return true;
+ }
+
+ var namespaceName = _reader.GetString(typeDef.Namespace);
+ if (string.IsNullOrEmpty(namespaceName))
+ {
+ return false;
+ }
+
+ var parts = namespaceName.Split('.');
+ for (int index = parts.Length - 1; index >= 0; index--)
+ {
+ if (signature == null)
+ {
+ return true;
+ }
+ var qualifiedName = signature as QualifiedName;
+ if (qualifiedName == null)
+ {
+ return false;
+ }
+ var part = parts[index];
+ if (!s_stringComparer.Equals(qualifiedName.Name, part))
+ {
+ return false;
+ }
+ signature = qualifiedName.Qualifier;
+ }
+
+ return signature == null;
+ }
+
+ private bool MatchesTypeName(TypeDefinition typeDef, string name)
+ {
+ var typeName = RemoveAritySeparatorIfAny(_reader.GetString(typeDef.Name));
+ return s_stringComparer.Equals(typeName, name);
+ }
+
+ private bool MatchesMethod(
+ TypeDefinition typeDef,
+ MethodDefinition methodDef,
+ Name typeName,
+ string methodName,
+ ImmutableArray allTypeParameters,
+ int containingArity,
+ ImmutableArray methodTypeParameters,
+ ImmutableArray methodParameters)
+ {
+ if ((methodDef.Attributes & MethodAttributes.RTSpecialName) != 0)
+ {
+ if (!_reader.StringComparer.Equals(methodDef.Name, ".ctor"))
+ {
+ // Unhandled special name.
+ return false;
+ }
+ if (!MatchesTypeName(typeDef, methodName))
+ {
+ return false;
+ }
+ }
+ else if (!_reader.StringComparer.Equals(methodDef.Name, methodName))
+ {
+ return false;
+ }
+ if (!MatchesTypeParameterCount(methodTypeParameters, methodDef.GetGenericParameters(), offset: 0))
+ {
+ return false;
+ }
+ if (methodParameters.IsDefault)
+ {
+ return true;
+ }
+ return MatchesParameters(methodDef, allTypeParameters, containingArity, methodTypeParameters, methodParameters);
+ }
+
+ private bool MatchesProperty(
+ TypeDefinition typeDef,
+ PropertyDefinition propertyDef,
+ Name typeName,
+ string propertyName,
+ ImmutableArray allTypeParameters,
+ int containingArity,
+ ImmutableArray propertyParameters)
+ {
+ if (!_reader.StringComparer.Equals(propertyDef.Name, propertyName))
+ {
+ return false;
+ }
+ if (propertyParameters.IsDefault)
+ {
+ return true;
+ }
+ if (propertyParameters.Length == 0)
+ {
+ // Parameter-less properties should be specified
+ // with no parameter list.
+ return false;
+ }
+ // Match parameters against getter. Not supporting
+ // matching against setter for write-only properties.
+ var methodHandle = propertyDef.GetAccessors().Getter;
+ if (methodHandle.IsNil)
+ {
+ return false;
+ }
+ var methodDef = _reader.GetMethodDefinition(methodHandle);
+ return MatchesParameters(methodDef, allTypeParameters, containingArity, ImmutableArray.Empty, propertyParameters);
+ }
+
+ private ImmutableArray GetAllGenericTypeParameters(Name typeName)
+ {
+ var builder = ImmutableArray.CreateBuilder();
+ GetAllGenericTypeParameters(typeName, builder);
+ return builder.ToImmutable();
+ }
+
+ private void GetAllGenericTypeParameters(Name typeName, ImmutableArray.Builder builder)
+ {
+ if (typeName == null)
+ {
+ return;
+ }
+
+ QualifiedName qualifiedName;
+ ImmutableArray typeParameters;
+ GetNameAndTypeParameters(typeName, out qualifiedName, out typeParameters);
+ GetAllGenericTypeParameters(qualifiedName.Qualifier, builder);
+ builder.AddRange(typeParameters);
+ }
+
+ private bool MatchesParameters(
+ MethodDefinition methodDef,
+ ImmutableArray allTypeParameters,
+ int containingArity,
+ ImmutableArray methodTypeParameters,
+ ImmutableArray methodParameters)
+ {
+ ImmutableArray parameters;
+ try
+ {
+ var decoder = new MetadataDecoder(_reader, allTypeParameters, containingArity, methodTypeParameters);
+ parameters = decoder.DecodeParameters(methodDef);
+ }
+ catch (NotSupportedException)
+ {
+ return false;
+ }
+ catch (BadImageFormatException)
+ {
+ return false;
+ }
+ return methodParameters.SequenceEqual(parameters, MatchesParameter);
+ }
+
+ private void OnFunctionResolved(
+ TRequest request,
+ MethodDefinitionHandle handle)
+ {
+ Debug.Assert(!handle.IsNil);
+ _resolver.OnFunctionResolved(_module, request, token: MetadataTokens.GetToken(handle), version: 1, ilOffset: 0);
+ }
+
+ private void OnAccessorResolved(
+ TRequest request,
+ MethodDefinitionHandle handle)
+ {
+ if (handle.IsNil)
+ {
+ return;
+ }
+ var methodDef = _reader.GetMethodDefinition(handle);
+ if (IsResolvableMethod(methodDef))
+ {
+ OnFunctionResolved(request, handle);
+ }
+ }
+
+ private static bool MatchesTypeParameterCount(ImmutableArray typeArguments, GenericParameterHandleCollection typeParameters, int offset)
+ {
+ return typeArguments.Length == typeParameters.Count - offset;
+ }
+
+ // parameterA from string signature, parameterB from metadata.
+ private static bool MatchesParameter(ParameterSignature parameterA, ParameterSignature parameterB)
+ {
+ return MatchesType(parameterA.Type, parameterB.Type) &&
+ parameterA.IsByRef == parameterB.IsByRef;
+ }
+
+ // typeA from string signature, typeB from metadata.
+ private static bool MatchesType(TypeSignature typeA, TypeSignature typeB)
+ {
+ if (typeA.Kind != typeB.Kind)
+ {
+ return false;
+ }
+
+ switch (typeA.Kind)
+ {
+ case TypeSignatureKind.GenericType:
+ {
+ var genericA = (GenericTypeSignature)typeA;
+ var genericB = (GenericTypeSignature)typeB;
+ return MatchesType(genericA.QualifiedName, genericB.QualifiedName) &&
+ genericA.TypeArguments.SequenceEqual(genericB.TypeArguments, MatchesType);
+ }
+ case TypeSignatureKind.QualifiedType:
+ {
+ var qualifiedA = (QualifiedTypeSignature)typeA;
+ var qualifiedB = (QualifiedTypeSignature)typeB;
+ // Metadata signature may be more qualified than the
+ // string signature but still considered a match
+ // (e.g.: "B.C" should match N.A.B.C).
+ return (qualifiedA.Qualifier == null || (qualifiedB.Qualifier != null && MatchesType(qualifiedA.Qualifier, qualifiedB.Qualifier))) &&
+ s_stringComparer.Equals(qualifiedA.Name, qualifiedB.Name);
+ }
+ case TypeSignatureKind.ArrayType:
+ {
+ var arrayA = (ArrayTypeSignature)typeA;
+ var arrayB = (ArrayTypeSignature)typeB;
+ return MatchesType(arrayA.ElementType, arrayB.ElementType) &&
+ arrayA.Rank == arrayB.Rank;
+ }
+ case TypeSignatureKind.PointerType:
+ {
+ var pointerA = (PointerTypeSignature)typeA;
+ var pointerB = (PointerTypeSignature)typeB;
+ return MatchesType(pointerA.PointedAtType, pointerB.PointedAtType);
+ }
+ default:
+ throw ExceptionUtilities.UnexpectedValue(typeA.Kind);
+ }
+ }
+
+ private static void GetNameAndTypeParameters(
+ Name name,
+ out QualifiedName qualifiedName,
+ out ImmutableArray typeParameters)
+ {
+ switch (name.Kind)
+ {
+ case NameKind.GenericName:
+ {
+ var genericName = (GenericName)name;
+ qualifiedName = genericName.QualifiedName;
+ typeParameters = genericName.TypeParameters;
+ }
+ break;
+ case NameKind.QualifiedName:
+ {
+ qualifiedName = (QualifiedName)name;
+ typeParameters = ImmutableArray.Empty;
+ }
+ break;
+ default:
+ throw ExceptionUtilities.UnexpectedValue(name.Kind);
+ }
+ }
+
+ private static bool IsResolvableMethod(MethodDefinition methodDef)
+ {
+ return (methodDef.Attributes & (MethodAttributes.Abstract | MethodAttributes.PinvokeImpl)) == 0;
+ }
+
+ private static string RemoveAritySeparatorIfAny(string typeName)
+ {
+ int index = typeName.LastIndexOf('`');
+ return (index < 0) ? typeName : typeName.Substring(0, index);
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Name.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Name.cs
new file mode 100644
index 0000000000000000000000000000000000000000..23ff97762ed347c4e9bdc5f0440281a4c068b658
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Name.cs
@@ -0,0 +1,75 @@
+// 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;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal enum NameKind
+ {
+ GenericName,
+ QualifiedName,
+ }
+
+ [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
+ internal abstract class Name
+ {
+ internal abstract NameKind Kind { get; }
+
+ internal abstract string GetDebuggerDisplay();
+ }
+
+ internal sealed class GenericName : Name
+ {
+ internal GenericName(QualifiedName qualifiedName, ImmutableArray typeParameters)
+ {
+ Debug.Assert(qualifiedName != null);
+ Debug.Assert(!typeParameters.IsDefault);
+ QualifiedName = qualifiedName;
+ TypeParameters = typeParameters;
+ }
+
+ internal override NameKind Kind => NameKind.GenericName;
+ internal QualifiedName QualifiedName { get; }
+ internal ImmutableArray TypeParameters { get; }
+
+ internal override string GetDebuggerDisplay()
+ {
+ var builder = new StringBuilder();
+ builder.Append(QualifiedName.GetDebuggerDisplay());
+ builder.Append('<');
+ bool any = false;
+ foreach (var typeParam in TypeParameters)
+ {
+ if (any)
+ {
+ builder.Append(", ");
+ }
+ builder.Append(typeParam);
+ any = true;
+ }
+ builder.Append('>');
+ return builder.ToString();
+ }
+ }
+
+ internal sealed class QualifiedName : Name
+ {
+ internal QualifiedName(Name qualifier, string name)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(name));
+ Qualifier = qualifier;
+ Name = name;
+ }
+
+ internal override NameKind Kind => NameKind.QualifiedName;
+ internal Name Qualifier { get; }
+ internal string Name { get; }
+
+ internal override string GetDebuggerDisplay()
+ {
+ return (Qualifier == null) ? Name : $"{Qualifier.GetDebuggerDisplay()}.{Name}";
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/ParameterSignature.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/ParameterSignature.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8e2bbbbed795fc1fdbcb7f2b8b6db320b081f8b1
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/ParameterSignature.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal sealed class ParameterSignature
+ {
+ internal ParameterSignature(TypeSignature type, bool isByRef)
+ {
+ Type = type;
+ IsByRef = isByRef;
+ }
+
+ internal readonly TypeSignature Type;
+ internal readonly bool IsByRef;
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/RequestSignature.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/RequestSignature.cs
new file mode 100644
index 0000000000000000000000000000000000000000..80dc5c0484bef22682ffe8dcc0dca5db42c2c7d9
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/RequestSignature.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;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal sealed class RequestSignature
+ {
+ internal RequestSignature(Name memberName, ImmutableArray parameters)
+ {
+ MemberName = memberName;
+ Parameters = parameters;
+ }
+
+ internal readonly Name MemberName;
+ internal readonly ImmutableArray Parameters; // default if not specified
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/SpecialTypeExtensions.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/SpecialTypeExtensions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..dc19963866a8b6ca09733d00273699cc9b17946c
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/SpecialTypeExtensions.cs
@@ -0,0 +1,45 @@
+// 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;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal static class SpecialTypeExtensions
+ {
+ internal static QualifiedTypeSignature GetTypeSignature(this SpecialType type)
+ {
+ QualifiedTypeSignature signature;
+ s_typeSignatures.TryGetValue(type, out signature);
+ return signature;
+ }
+
+ private static readonly ImmutableDictionary s_typeSignatures = GetTypeSignatures();
+
+ private static ImmutableDictionary GetTypeSignatures()
+ {
+ var systemNamespace = new QualifiedTypeSignature(null, "System");
+ var builder = ImmutableDictionary.CreateBuilder();
+ builder.Add(SpecialType.System_Void, new QualifiedTypeSignature(systemNamespace, "Void"));
+ builder.Add(SpecialType.System_Boolean, new QualifiedTypeSignature(systemNamespace, "Boolean"));
+ builder.Add(SpecialType.System_Char, new QualifiedTypeSignature(systemNamespace, "Char"));
+ builder.Add(SpecialType.System_SByte, new QualifiedTypeSignature(systemNamespace, "SByte"));
+ builder.Add(SpecialType.System_Byte, new QualifiedTypeSignature(systemNamespace, "Byte"));
+ builder.Add(SpecialType.System_Int16, new QualifiedTypeSignature(systemNamespace, "Int16"));
+ builder.Add(SpecialType.System_UInt16, new QualifiedTypeSignature(systemNamespace, "UInt16"));
+ builder.Add(SpecialType.System_Int32, new QualifiedTypeSignature(systemNamespace, "Int32"));
+ builder.Add(SpecialType.System_UInt32, new QualifiedTypeSignature(systemNamespace, "UInt32"));
+ builder.Add(SpecialType.System_Int64, new QualifiedTypeSignature(systemNamespace, "Int64"));
+ builder.Add(SpecialType.System_UInt64, new QualifiedTypeSignature(systemNamespace, "UInt64"));
+ builder.Add(SpecialType.System_Single, new QualifiedTypeSignature(systemNamespace, "Single"));
+ builder.Add(SpecialType.System_Double, new QualifiedTypeSignature(systemNamespace, "Double"));
+ builder.Add(SpecialType.System_String, new QualifiedTypeSignature(systemNamespace, "String"));
+ builder.Add(SpecialType.System_Object, new QualifiedTypeSignature(systemNamespace, "Object"));
+ builder.Add(SpecialType.System_Decimal, new QualifiedTypeSignature(systemNamespace, "Decimal"));
+ builder.Add(SpecialType.System_IntPtr, new QualifiedTypeSignature(systemNamespace, "IntPtr"));
+ builder.Add(SpecialType.System_UIntPtr, new QualifiedTypeSignature(systemNamespace, "UIntPtr"));
+ builder.Add(SpecialType.System_TypedReference, new QualifiedTypeSignature(systemNamespace, "TypedReference"));
+ builder.Add(SpecialType.System_Nullable_T, new QualifiedTypeSignature(systemNamespace, "Nullable"));
+ return builder.ToImmutable();
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/TypeSignature.cs b/src/ExpressionEvaluator/Core/Source/FunctionResolver/TypeSignature.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b8816bd0a5aafc4d32a36bc524df4d6dffd18c97
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/TypeSignature.cs
@@ -0,0 +1,118 @@
+// 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;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator
+{
+ internal enum TypeSignatureKind
+ {
+ GenericType,
+ QualifiedType,
+ ArrayType,
+ PointerType,
+ }
+
+ [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
+ internal abstract class TypeSignature
+ {
+ internal abstract TypeSignatureKind Kind { get; }
+
+ internal abstract string GetDebuggerDisplay();
+ }
+
+ internal sealed class GenericTypeSignature : TypeSignature
+ {
+ internal GenericTypeSignature(QualifiedTypeSignature qualifiedName, ImmutableArray typeArguments)
+ {
+ Debug.Assert(qualifiedName != null);
+ Debug.Assert(!typeArguments.IsDefault);
+ QualifiedName = qualifiedName;
+ TypeArguments = typeArguments;
+ }
+
+ internal override TypeSignatureKind Kind => TypeSignatureKind.GenericType;
+ internal QualifiedTypeSignature QualifiedName { get; }
+ internal ImmutableArray TypeArguments { get; }
+
+ internal override string GetDebuggerDisplay()
+ {
+ var builder = new StringBuilder();
+ builder.Append(QualifiedName.GetDebuggerDisplay());
+ builder.Append('<');
+ bool any = false;
+ foreach (var typeArg in TypeArguments)
+ {
+ if (any)
+ {
+ builder.Append(", ");
+ }
+ builder.Append(typeArg.GetDebuggerDisplay());
+ any = true;
+ }
+ builder.Append('>');
+ return builder.ToString();
+ }
+ }
+
+ internal sealed class QualifiedTypeSignature : TypeSignature
+ {
+ internal QualifiedTypeSignature(TypeSignature qualifier, string name)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(name));
+ Qualifier = qualifier;
+ Name = name;
+ }
+
+ internal override TypeSignatureKind Kind => TypeSignatureKind.QualifiedType;
+ internal TypeSignature Qualifier { get; }
+ internal string Name { get; }
+
+ internal override string GetDebuggerDisplay()
+ {
+ return (Qualifier == null) ? Name : $"{Qualifier.GetDebuggerDisplay()}.{Name}";
+ }
+ }
+
+ internal sealed class ArrayTypeSignature : TypeSignature
+ {
+ internal ArrayTypeSignature(TypeSignature elementType, int rank)
+ {
+ Debug.Assert(elementType != null);
+ ElementType = elementType;
+ Rank = rank;
+ }
+
+ internal override TypeSignatureKind Kind => TypeSignatureKind.ArrayType;
+ internal TypeSignature ElementType { get; }
+ internal int Rank { get; }
+
+ internal override string GetDebuggerDisplay()
+ {
+ var builder = new StringBuilder();
+ builder.Append(ElementType.GetDebuggerDisplay());
+ builder.Append('[');
+ builder.Append(',', Rank - 1);
+ builder.Append(']');
+ return builder.ToString();
+ }
+ }
+
+ internal sealed class PointerTypeSignature : TypeSignature
+ {
+ internal PointerTypeSignature(TypeSignature pointedAtType)
+ {
+ Debug.Assert(pointedAtType != null);
+ PointedAtType = pointedAtType;
+ }
+
+ internal override TypeSignatureKind Kind => TypeSignatureKind.PointerType;
+ internal TypeSignature PointedAtType { get; }
+
+ internal override string GetDebuggerDisplay()
+ {
+ return $"{PointedAtType.GetDebuggerDisplay()}*";
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/project.json b/src/ExpressionEvaluator/Core/Source/FunctionResolver/project.json
new file mode 100644
index 0000000000000000000000000000000000000000..c6975658a974a195c8d9dd1bd18f4ec8d968e3bb
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/project.json
@@ -0,0 +1,12 @@
+{
+ "dependencies": {
+ "System.Collections.Immutable": "1.2.0",
+ "System.Reflection.Metadata": "1.4.1-beta-24430-01",
+ "Microsoft.VisualStudio.Debugger.Engine": "14.3.25422",
+ },
+ "frameworks": {
+ "netstandard1.3": {
+ "imports": "portable-net45+win8"
+ }
+ }
+}
diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/CSharpFunctionResolverTests.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/CSharpFunctionResolverTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..922f62fbdb78b5dcfac991486f8f496838751272
--- /dev/null
+++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/CSharpFunctionResolverTests.cs
@@ -0,0 +1,1400 @@
+// 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.ExpressionEvaluator;
+using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Microsoft.CodeAnalysis.Test.Utilities;
+using Roslyn.Test.Utilities;
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Text;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests
+{
+ public class CSharpFunctionResolverTests : CSharpTestBase
+ {
+ [Fact]
+ public void OnLoad()
+ {
+ var source =
+@"class C
+{
+ static void F(object o) { }
+ object F() => null;
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ var module = new Module(compilation.EmitToArray());
+ using (var process = new Process())
+ {
+ var resolver = new Resolver();
+ var request = new Request(null, MemberSignatureParser.Parse("C.F"));
+ resolver.EnableResolution(process, request);
+ VerifySignatures(request);
+ process.AddModule(module);
+ resolver.OnModuleLoad(process, module);
+ VerifySignatures(request, "C.F(System.Object)", "C.F()");
+ }
+ }
+
+ ///
+ /// ShouldEnableFunctionResolver should not be called
+ /// until the first module is available for binding.
+ ///
+ [Fact]
+ public void ShouldEnableFunctionResolver()
+ {
+ var sourceA =
+@"class A
+{
+ static void F() { }
+ static void G() { }
+}";
+ var sourceB =
+@"class B
+{
+ static void F() { }
+ static void G() { }
+}";
+ var bytesA = CreateCompilationWithMscorlib(sourceA).EmitToArray();
+ var bytesB = CreateCompilationWithMscorlib(sourceB).EmitToArray();
+ var resolver = new Resolver();
+
+ // Two modules loaded before two global requests,
+ // ... resolver enabled.
+ var moduleA = new Module(bytesA, name: "A.dll");
+ var moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: true, modules: new[] { moduleA, moduleB }))
+ {
+ var requestF = new Request(null, MemberSignatureParser.Parse("F"));
+ var requestG = new Request(null, MemberSignatureParser.Parse("G"));
+ Assert.Equal(0, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestF);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF, "A.F()", "B.F()");
+ VerifySignatures(requestG, "A.G()", "B.G()");
+ }
+
+ // ... resolver disabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: false, modules: new[] { moduleA, moduleB }))
+ {
+ var requestF = new Request(null, MemberSignatureParser.Parse("F"));
+ var requestG = new Request(null, MemberSignatureParser.Parse("G"));
+ Assert.Equal(0, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestF);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF);
+ VerifySignatures(requestG);
+ }
+
+ // Two modules loaded before two requests for same module,
+ // ... resolver enabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: true, modules: new[] { moduleA, moduleB }))
+ {
+ var requestF = new Request("B.dll", MemberSignatureParser.Parse("F"));
+ var requestG = new Request("B.dll", MemberSignatureParser.Parse("G"));
+ Assert.Equal(0, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestF);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF, "B.F()");
+ VerifySignatures(requestG, "B.G()");
+ }
+
+ // ... resolver disabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: false, modules: new[] { moduleA, moduleB }))
+ {
+ var requestF = new Request("B.dll", MemberSignatureParser.Parse("F"));
+ var requestG = new Request("B.dll", MemberSignatureParser.Parse("G"));
+ Assert.Equal(0, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestF);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF);
+ VerifySignatures(requestG);
+ }
+
+ // Two modules loaded after two global requests,
+ // ... resolver enabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: true))
+ {
+ var requestF = new Request(null, MemberSignatureParser.Parse("F"));
+ var requestG = new Request(null, MemberSignatureParser.Parse("G"));
+ resolver.EnableResolution(process, requestF);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(0, process.ShouldEnableRequests);
+ process.AddModule(moduleA);
+ resolver.OnModuleLoad(process, moduleA);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ VerifySignatures(requestF, "A.F()");
+ VerifySignatures(requestG, "A.G()");
+ process.AddModule(moduleB);
+ resolver.OnModuleLoad(process, moduleB);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF, "A.F()", "B.F()");
+ VerifySignatures(requestG, "A.G()", "B.G()");
+ }
+
+ // ... resolver enabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: false))
+ {
+ var requestF = new Request(null, MemberSignatureParser.Parse("F"));
+ var requestG = new Request(null, MemberSignatureParser.Parse("G"));
+ resolver.EnableResolution(process, requestF);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(0, process.ShouldEnableRequests);
+ process.AddModule(moduleA);
+ resolver.OnModuleLoad(process, moduleA);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ VerifySignatures(requestF);
+ VerifySignatures(requestG);
+ process.AddModule(moduleB);
+ resolver.OnModuleLoad(process, moduleB);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF);
+ VerifySignatures(requestG);
+ }
+
+ // Two modules after after two requests for same module,
+ // ... resolver enabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: true))
+ {
+ var requestF = new Request("A.dll", MemberSignatureParser.Parse("F"));
+ var requestG = new Request("A.dll", MemberSignatureParser.Parse("G"));
+ resolver.EnableResolution(process, requestF);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(0, process.ShouldEnableRequests);
+ process.AddModule(moduleA);
+ resolver.OnModuleLoad(process, moduleA);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ VerifySignatures(requestF, "A.F()");
+ VerifySignatures(requestG, "A.G()");
+ process.AddModule(moduleB);
+ resolver.OnModuleLoad(process, moduleB);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF, "A.F()");
+ VerifySignatures(requestG, "A.G()");
+ }
+
+ // ... resolver enabled.
+ moduleA = new Module(bytesA, name: "A.dll");
+ moduleB = new Module(bytesB, name: "B.dll");
+ using (var process = new Process(shouldEnable: false))
+ {
+ var requestF = new Request("A.dll", MemberSignatureParser.Parse("F"));
+ var requestG = new Request("A.dll", MemberSignatureParser.Parse("G"));
+ resolver.EnableResolution(process, requestF);
+ resolver.EnableResolution(process, requestG);
+ Assert.Equal(0, process.ShouldEnableRequests);
+ process.AddModule(moduleA);
+ resolver.OnModuleLoad(process, moduleA);
+ Assert.Equal(1, process.ShouldEnableRequests);
+ VerifySignatures(requestF);
+ VerifySignatures(requestG);
+ process.AddModule(moduleB);
+ resolver.OnModuleLoad(process, moduleB);
+ Assert.Equal(2, process.ShouldEnableRequests);
+ VerifySignatures(requestF);
+ VerifySignatures(requestG);
+ }
+ }
+
+ [Fact]
+ public void MissingMetadata()
+ {
+ var sourceA =
+@"class A
+{
+ static void F1() { }
+ static void F2() { }
+ static void F3() { }
+ static void F4() { }
+}";
+ var sourceC =
+@"class C
+{
+ static void F1() { }
+ static void F2() { }
+ static void F3() { }
+ static void F4() { }
+}";
+ var moduleA = new Module(CreateCompilationWithMscorlib(sourceA).EmitToArray(), name: "A.dll");
+ var moduleB = new Module(default(ImmutableArray), name: "B.dll");
+ var moduleC = new Module(CreateCompilationWithMscorlib(sourceC).EmitToArray(), name: "C.dll");
+
+ using (var process = new Process())
+ {
+ var resolver = new Resolver();
+ var requestAll = new Request(null, MemberSignatureParser.Parse("F1"));
+ var requestA = new Request("A.dll", MemberSignatureParser.Parse("F2"));
+ var requestB = new Request("B.dll", MemberSignatureParser.Parse("F3"));
+ var requestC = new Request("C.dll", MemberSignatureParser.Parse("F4"));
+
+ // Request to all modules.
+ resolver.EnableResolution(process, requestAll);
+ Assert.Equal(0, moduleA.GetMetadataCount);
+ Assert.Equal(0, moduleB.GetMetadataCount);
+ Assert.Equal(0, moduleC.GetMetadataCount);
+
+ // Load module A (available).
+ process.AddModule(moduleA);
+ resolver.OnModuleLoad(process, moduleA);
+ Assert.Equal(1, moduleA.GetMetadataCount);
+ Assert.Equal(0, moduleB.GetMetadataCount);
+ Assert.Equal(0, moduleC.GetMetadataCount);
+ VerifySignatures(requestAll, "A.F1()");
+ VerifySignatures(requestA);
+ VerifySignatures(requestB);
+ VerifySignatures(requestC);
+
+ // Load module B (missing).
+ process.AddModule(moduleB);
+ resolver.OnModuleLoad(process, moduleB);
+ Assert.Equal(1, moduleA.GetMetadataCount);
+ Assert.Equal(1, moduleB.GetMetadataCount);
+ Assert.Equal(0, moduleC.GetMetadataCount);
+ VerifySignatures(requestAll, "A.F1()");
+ VerifySignatures(requestA);
+ VerifySignatures(requestB);
+ VerifySignatures(requestC);
+
+ // Load module C (available).
+ process.AddModule(moduleC);
+ resolver.OnModuleLoad(process, moduleC);
+ Assert.Equal(1, moduleA.GetMetadataCount);
+ Assert.Equal(1, moduleB.GetMetadataCount);
+ Assert.Equal(1, moduleC.GetMetadataCount);
+ VerifySignatures(requestAll, "A.F1()", "C.F1()");
+ VerifySignatures(requestA);
+ VerifySignatures(requestB);
+ VerifySignatures(requestC);
+
+ // Request to module A (available).
+ resolver.EnableResolution(process, requestA);
+ Assert.Equal(2, moduleA.GetMetadataCount);
+ Assert.Equal(1, moduleB.GetMetadataCount);
+ Assert.Equal(1, moduleC.GetMetadataCount);
+ VerifySignatures(requestAll, "A.F1()", "C.F1()");
+ VerifySignatures(requestA, "A.F2()");
+ VerifySignatures(requestB);
+ VerifySignatures(requestC);
+
+ // Request to module B (missing).
+ resolver.EnableResolution(process, requestB);
+ Assert.Equal(2, moduleA.GetMetadataCount);
+ Assert.Equal(2, moduleB.GetMetadataCount);
+ Assert.Equal(1, moduleC.GetMetadataCount);
+ VerifySignatures(requestAll, "A.F1()", "C.F1()");
+ VerifySignatures(requestA, "A.F2()");
+ VerifySignatures(requestB);
+ VerifySignatures(requestC);
+
+ // Request to module C (available).
+ resolver.EnableResolution(process, requestC);
+ Assert.Equal(2, moduleA.GetMetadataCount);
+ Assert.Equal(2, moduleB.GetMetadataCount);
+ Assert.Equal(2, moduleC.GetMetadataCount);
+ VerifySignatures(requestAll, "A.F1()", "C.F1()");
+ VerifySignatures(requestA, "A.F2()");
+ VerifySignatures(requestB);
+ VerifySignatures(requestC, "C.F4()");
+ }
+ }
+
+ [Fact]
+ public void ModuleName()
+ {
+ var sourceA =
+@"public struct A
+{
+ static void F() { }
+}";
+ var nameA = GetUniqueName();
+ var compilationA = CreateCompilationWithMscorlib(sourceA, assemblyName: nameA);
+ var imageA = compilationA.EmitToArray();
+ var refA = AssemblyMetadata.CreateFromImage(imageA).GetReference();
+ var sourceB =
+@"class B
+{
+ static void F(A a) { }
+ static void Main() { }
+}";
+ var nameB = GetUniqueName();
+ var compilationB = CreateCompilationWithMscorlib(sourceB, assemblyName: nameB, options: TestOptions.DebugExe, references: new[] { refA });
+ var imageB = compilationB.EmitToArray();
+ using (var process = new Process(new Module(imageA, nameA + ".dll"), new Module(imageB, nameB + ".exe")))
+ {
+ var signature = MemberSignatureParser.Parse("F");
+ var resolver = new Resolver();
+
+ // No module name.
+ var request = new Request("", signature);
+ resolver.EnableResolution(process, request);
+ VerifySignatures(request, "A.F()", "B.F(A)");
+
+ // DLL module name, uppercase.
+ request = new Request(nameA.ToUpper() + ".DLL", signature);
+ resolver.EnableResolution(process, request);
+ VerifySignatures(request, "A.F()");
+
+ // EXE module name.
+ request = new Request(nameB + ".EXE", signature);
+ resolver.EnableResolution(process, request);
+ VerifySignatures(request, "B.F(A)");
+
+ // EXE module name, lowercase.
+ request = new Request(nameB.ToLower() + ".exe", signature);
+ resolver.EnableResolution(process, request);
+ VerifySignatures(request, "B.F(A)");
+
+ // EXE module name, no extension.
+ request = new Request(nameB, signature);
+ resolver.EnableResolution(process, request);
+ VerifySignatures(request);
+ }
+ }
+
+ [Fact]
+ public void Arrays()
+ {
+ var source =
+@"class A
+{
+}
+class B
+{
+ static void F(A o) { }
+ static void F(A[] o) { }
+ static void F(A[,,] o) { }
+ static void F(A[,,][] o) { }
+ static void F(A[][,,] o) { }
+ static void F(A[][][] o) { }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "B.F(A)", "B.F(A[])", "B.F(A[,,])", "B.F(A[][,,])", "B.F(A[,,][])", "B.F(A[][][])");
+ Resolve(process, resolver, "F(A)", "B.F(A)");
+ Resolve(process, resolver, "F(A[])", "B.F(A[])");
+ Resolve(process, resolver, "F(A[][])");
+ Resolve(process, resolver, "F(A[,])");
+ Resolve(process, resolver, "F(A[,,])", "B.F(A[,,])");
+ Resolve(process, resolver, "F(A[,,][])", "B.F(A[,,][])");
+ Resolve(process, resolver, "F(A[][,,])", "B.F(A[][,,])");
+ Resolve(process, resolver, "F(A[,][,,])");
+ Resolve(process, resolver, "F(A[][][])", "B.F(A[][][])");
+ Resolve(process, resolver, "F(A[][][][])");
+ }
+ }
+
+ [Fact]
+ public void Pointers()
+ {
+ var source =
+@"class C
+{
+ static unsafe void F(int*[] p) { }
+ static unsafe void F(int** q) { }
+}";
+ var compilation = CreateCompilationWithMscorlib(source, options: TestOptions.UnsafeDebugDll);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "C.F(System.Int32*[])", "C.F(System.Int32**)");
+ Resolve(process, resolver, "F(int)");
+ Resolve(process, resolver, "F(int*)");
+ Resolve(process, resolver, "F(int[])");
+ Resolve(process, resolver, "F(int*[])", "C.F(System.Int32*[])");
+ Resolve(process, resolver, "F(int**)", "C.F(System.Int32**)");
+ Resolve(process, resolver, "F(Int32**)", "C.F(System.Int32**)");
+ Resolve(process, resolver, "F(C)");
+ Resolve(process, resolver, "F(C*)");
+ }
+ }
+
+ [Fact]
+ public void Nullable()
+ {
+ var source =
+@"struct S
+{
+ void F(S? o) { }
+ static void F(int?[] o) { }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "S.F(System.Nullable)", "S.F(System.Nullable[])");
+ Resolve(process, resolver, "F(S)");
+ Resolve(process, resolver, "F(S?)", "S.F(System.Nullable)");
+ Resolve(process, resolver, "F(S??)");
+ Resolve(process, resolver, "F(int?[])", "S.F(System.Nullable[])");
+ }
+ }
+
+ [Fact]
+ public void ByRef()
+ {
+ var source =
+@"class @ref { }
+class @out { }
+class C
+{
+ static void F(@out a, @ref b) { }
+ static void F(ref @out a, out @ref b) { b = null; }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "C.F(out, ref)", "C.F(ref out, ref ref)");
+ Assert.Null(MemberSignatureParser.Parse("F(ref, out)"));
+ Assert.Null(MemberSignatureParser.Parse("F(ref ref, out out)"));
+ Resolve(process, resolver, "F(@out, @ref)", "C.F(out, ref)");
+ Resolve(process, resolver, "F(@out, out @ref)");
+ Resolve(process, resolver, "F(ref @out, @ref)");
+ Resolve(process, resolver, "F(ref @out, out @ref)", "C.F(ref out, ref ref)");
+ Resolve(process, resolver, "F(out @out, ref @ref)", "C.F(ref out, ref ref)");
+ }
+ }
+
+ [Fact]
+ public void Properties()
+ {
+ var source =
+@"abstract class A
+{
+ abstract internal object P { get; set; }
+}
+class B
+{
+ object P { get; set; }
+}
+class C
+{
+ static object P { get; }
+}
+class D
+{
+ int P { set { } }
+}
+interface I
+{
+ object P { get; set; }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "P", "B.get_P()", "B.set_P(System.Object)", "C.get_P()", "D.set_P(System.Int32)");
+ Resolve(process, resolver, "A.P");
+ Resolve(process, resolver, "B.P", "B.get_P()", "B.set_P(System.Object)");
+ Resolve(process, resolver, "B.P()");
+ Resolve(process, resolver, "B.P(object)");
+ Resolve(process, resolver, "B.P");
+ Resolve(process, resolver, "C.P", "C.get_P()");
+ Resolve(process, resolver, "C.P()");
+ Resolve(process, resolver, "D.P", "D.set_P(System.Int32)");
+ Resolve(process, resolver, "D.P()");
+ Resolve(process, resolver, "D.P(object)");
+ Resolve(process, resolver, "get_P", "B.get_P()", "C.get_P()");
+ Resolve(process, resolver, "set_P", "B.set_P(System.Object)", "D.set_P(System.Int32)");
+ Resolve(process, resolver, "B.get_P()", "B.get_P()");
+ Resolve(process, resolver, "B.set_P", "B.set_P(System.Object)");
+ }
+ }
+
+ [Fact]
+ public void Constructors()
+ {
+ var source =
+@"class A
+{
+ static A() { }
+ A() { }
+ A(object o) { }
+}
+class B
+{
+}
+class C
+{
+}
+class D
+{
+ static object A => null;
+ static void B() { }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "A", "A..ctor()", "A..ctor(System.Object)", "D.get_A()");
+ Resolve(process, resolver, "A.A", "A..ctor()", "A..ctor(System.Object)");
+ Resolve(process, resolver, "B", "B..ctor()");
+ Resolve(process, resolver, "B", "D.B()");
+ Resolve(process, resolver, "C", "C..ctor()");
+ Resolve(process, resolver, "C");
+ Resolve(process, resolver, "C.C", "C..ctor()");
+ Assert.Null(MemberSignatureParser.Parse(".ctor"));
+ Assert.Null(MemberSignatureParser.Parse("A..ctor"));
+ }
+ }
+
+ [Fact]
+ public void GenericMethods()
+ {
+ var source =
+@"class A
+{
+ static void F() { }
+ void F() { }
+ static void F() { }
+}
+class A
+{
+ static void F() { }
+ void F() { }
+}
+class B : A
+{
+ static void F() { }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ // Note, Dev14 matches type ignoring type parameters. For instance,
+ // "A.F" will bind to A.F() and A.F(), and "A.F" will bind to A.F()
+ // and A.F. However, Dev14 does expect method type parameters to
+ // match. Here, we expect both type and method parameters to match.
+ var resolver = new Resolver();
+ Resolve(process, resolver, "A.F", "A.F()");
+ Resolve(process, resolver, "A.F()", "A.F()");
+ Resolve(process, resolver, "A.F", "A.F()");
+ Resolve(process, resolver, "A.F", "A.F()");
+ Assert.Null(MemberSignatureParser.Parse("A.F<>()"));
+ Assert.Null(MemberSignatureParser.Parse("A.F<,>()"));
+ Resolve(process, resolver, "A.F", "A.F()");
+ Resolve(process, resolver, "A<_>.F<_>");
+ Resolve(process, resolver, "A<_>.F<_, _>", "A.F()");
+ Resolve(process, resolver, "B.F()");
+ Resolve(process, resolver, "B.F()", "B.F()");
+ Resolve(process, resolver, "B.F()");
+ }
+ }
+
+ [Fact]
+ public void Namespaces()
+ {
+ var source =
+@"namespace N
+{
+ namespace M
+ {
+ class A
+ {
+ static void F() { }
+ }
+ }
+}
+namespace N
+{
+ class B
+ {
+ static void F() { }
+ }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "N.B.F()", "N.M.A.F()");
+ Resolve(process, resolver, "A.F", "N.M.A.F()");
+ Resolve(process, resolver, "N.A.F");
+ Resolve(process, resolver, "M.A.F", "N.M.A.F()");
+ Resolve(process, resolver, "N.M.A.F", "N.M.A.F()");
+ Resolve(process, resolver, "N.B.F", "N.B.F()");
+ }
+ }
+
+ [Fact]
+ public void NestedTypes()
+ {
+ var source =
+@"class A
+{
+ class B
+ {
+ static void F() { }
+ }
+ class B
+ {
+ static void F() { }
+ }
+}
+class B
+{
+ static void F() { }
+}
+namespace N
+{
+ class A
+ {
+ class B
+ {
+ static void F() { }
+ }
+ class B
+ {
+ static void F() { }
+ }
+ }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ // See comment in GenericMethods regarding differences with Dev14.
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "B.F()", "A.B.F()", "N.A.B.F()");
+ Resolve(process, resolver, "F", "A.B.F()", "N.A.B.F()");
+ Resolve(process, resolver, "A.F");
+ Resolve(process, resolver, "A.B.F", "A.B.F()");
+ Resolve(process, resolver, "A.B.F");
+ Resolve(process, resolver, "A.B.F", "A.B.F()");
+ Resolve(process, resolver, "A.B.F", "N.A.B.F()");
+ Resolve(process, resolver, "A.B.F", "N.A.B.F()");
+ Resolve(process, resolver, "B.F", "B.F()", "A.B.F()");
+ Resolve(process, resolver, "B.F", "N.A.B.F()");
+ Resolve(process, resolver, "B.F", "N.A.B.F()");
+ Resolve(process, resolver, "B.F", "A.B.F()");
+ Assert.Null(MemberSignatureParser.Parse("A+B.F"));
+ Assert.Null(MemberSignatureParser.Parse("A.B`1.F"));
+ }
+ }
+
+ [Fact]
+ public void NamespacesAndTypes()
+ {
+ var source =
+@"namespace A.B
+{
+ class T { }
+ class C
+ {
+ static void F(C c) { }
+ static void F(A.B.C c) { }
+ }
+}
+namespace A
+{
+ class B
+ {
+ internal class C
+ {
+ static void F(C c) { }
+ static void F(A.B.C c) { }
+ }
+ }
+}
+class A
+{
+ internal class B
+ {
+ internal class C
+ {
+ static void F(C c) { }
+ static void F(A.B.C c) { }
+ }
+ }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "F(C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "F(B.C)", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "F(B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "A.B.C.F", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "A.B.C.F", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "A.B.C.F", "A.B.C.F(A.B.C)", "A.B.C.F(A.B.C)");
+ Resolve(process, resolver, "B.C.F(B.C)", "A.B.C.F(A.B.C)");
+ }
+ }
+
+ [Fact]
+ public void NamespacesAndTypes_More()
+ {
+ var source =
+@"namespace A1.B
+{
+ class C
+ {
+ static void F(C c) { }
+ }
+}
+namespace A2
+{
+ class B
+ {
+ internal class C
+ {
+ static void F(C c) { }
+ static void F(A1.B.C c) { }
+ }
+ }
+}
+class A3
+{
+ internal class B
+ {
+ internal class C
+ {
+ static void F(C c) { }
+ static void F(A2.B.C c) { }
+ }
+ }
+}
+namespace B
+{
+ class C
+ {
+ static void F(C c) { }
+ static void F(A3.B.C c) { }
+ }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F(C)", "B.C.F(B.C)", "B.C.F(A3.B.C)", "A1.B.C.F(A1.B.C)", "A2.B.C.F(A2.B.C)", "A2.B.C.F(A1.B.C)", "A3.B.C.F(A3.B.C)", "A3.B.C.F(A2.B.C)");
+ Resolve(process, resolver, "B.C.F(B.C)", "B.C.F(B.C)", "B.C.F(A3.B.C)", "A1.B.C.F(A1.B.C)", "A2.B.C.F(A2.B.C)", "A2.B.C.F(A1.B.C)", "A3.B.C.F(A3.B.C)", "A3.B.C.F(A2.B.C)");
+ Resolve(process, resolver, "B.C.F(A1.B.C)", "A1.B.C.F(A1.B.C)", "A2.B.C.F(A1.B.C)");
+ Resolve(process, resolver, "B.C.F(A2.B.C)", "A2.B.C.F(A2.B.C)", "A3.B.C.F(A2.B.C)");
+ Resolve(process, resolver, "B.C.F(A3.B.C)", "B.C.F(A3.B.C)", "A3.B.C.F(A3.B.C)");
+ }
+ }
+
+ [Fact]
+ public void TypeParameters()
+ {
+ var source =
+@"class A
+{
+ class B
+ {
+ static void F(B t) { }
+ static void F(B u) { }
+ }
+}
+class A
+{
+ class B
+ {
+ static void F(T t) { }
+ static void F(A u) { }
+ static void F(B v) { }
+ }
+}";
+ var compilation = CreateCompilationWithMscorlib(source);
+ using (var process = new Process(new Module(compilation.EmitToArray())))
+ {
+ var resolver = new Resolver();
+ Resolve(process, resolver, "F", "A.B.F(A.B)", "A.B.F(A.B)", "A.B.F(T)", "A.B.F(A)", "A.B.F(A.B)");
+ Resolve(process, resolver, "B.F", "A.B.F(A.B)", "A.B.F(A.B)", "A.B.F(T)", "A.B.F(A)", "A.B.F(A.B)");
+ Resolve(process, resolver, "F(B)", "A.B.F(A.B)");
+ Resolve(process, resolver, "F(B)"); // No T in signature to bind to.
+ Resolve(process, resolver, "F(B)"); // No U in signature to bind to.
+ Resolve(process, resolver, "B.F(B)", "A.B.F