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(A.B)"); + Resolve(process, resolver, "B.F(B)", "A.B.F(A.B)"); + Resolve(process, resolver, "B.F(T)"); // No T in signature to bind to. + Resolve(process, resolver, "B.F(A)", "A.B.F(A)"); + Resolve(process, resolver, "B.F(B)", "A.B.F(A.B)"); + Resolve(process, resolver, "B.F(B)", "A.B.F(A.B)"); + Resolve(process, resolver, "A.B.F(X)", "A.B.F(T)"); + Resolve(process, resolver, "A.B.F(B)", "A.B.F(A.B)"); + } + } + + [Fact] + public void TypeReferences() + { + var sourceA = +@"public class A +{ + public class B + { + static void F() { } + } +} +namespace N +{ + public class C + { + } +}"; + var compilationA = CreateCompilationWithMscorlib(sourceA); + var bytesA = compilationA.EmitToArray(); + var refA = AssemblyMetadata.CreateFromImage(bytesA).GetReference(); + var sourceB = +@"class D +{ + static void F(N.C.B[]> b) { } +}"; + var compilationB = CreateCompilationWithMscorlib(sourceB, references: new[] { refA }); + var bytesB = compilationB.EmitToArray(); + using (var process = new Process(new Module(bytesA), new Module(bytesB))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "A.B.F()", "D.F(N.C.B[]>)"); + Resolve(process, resolver, "F(C)"); // No type argument for C<> + Resolve(process, resolver, "F(C)"); // Incorrect type argument for C<> + Resolve(process, resolver, "F(C>)"); // No array qualifier + Resolve(process, resolver, "F(C[]>)"); // Incorrect type argument for B<> + Resolve(process, resolver, "F(C[]>)", "D.F(N.C.B[]>)"); + Resolve(process, resolver, "F(N.C[]>)", "D.F(N.C.B[]>)"); + Resolve(process, resolver, "D.F", "D.F(N.C.B[]>)"); + Resolve(process, resolver, "D.F(C[]>)"); // No nested type B + Resolve(process, resolver, "D.F(C.B[]>)", "D.F(N.C.B[]>)"); + Resolve(process, resolver, "D.F(C.B[]>)"); // Incorrect type argument for B<>. + } + } + + [Fact] + public void Keywords_MethodName() + { + var source = +@"namespace @namespace +{ + struct @struct + { + object @public => 1; + } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Assert.Null(MemberSignatureParser.Parse("public")); + Assert.Null(MemberSignatureParser.Parse("namespace.@struct.@public")); + Assert.Null(MemberSignatureParser.Parse("@namespace.struct.@public")); + Assert.Null(MemberSignatureParser.Parse("@namespace.@struct.public")); + Resolve(process, resolver, "@public", "namespace.struct.get_public()"); + Resolve(process, resolver, "@namespace.@struct.@public", "namespace.struct.get_public()"); + } + } + + [Fact] + public void Keywords_MethodTypeParameter() + { + var source = +@"class @class<@in> +{ + static void F<@out>(@in i, @out o) { } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("class<@in>.F<@out>")); + Assert.Null(MemberSignatureParser.Parse("@class.F<@out>")); + Assert.Null(MemberSignatureParser.Parse("@class<@in>.F")); + Resolve(process, resolver, "F<@out>", "class.F(in, out)"); + Resolve(process, resolver, "F<@in>", "class.F(in, out)"); + Resolve(process, resolver, "@class<@in>.F<@out>", "class.F(in, out)"); + Resolve(process, resolver, "@class<@this>.F<@base>", "class.F(in, out)"); + Resolve(process, resolver, "@class.F", "class.F(in, out)"); + } + } + + [Fact] + public void Keywords_ParameterName() + { + var source = +@"namespace @namespace +{ + struct @struct + { + } +} +class C +{ + static void F(@namespace.@struct s) { } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Assert.Null(MemberSignatureParser.Parse("F(struct)")); + Assert.Null(MemberSignatureParser.Parse("F(namespace.@struct)")); + Resolve(process, resolver, "F(@struct)", "C.F(namespace.struct)"); + Resolve(process, resolver, "F(@namespace.@struct)", "C.F(namespace.struct)"); + } + } + + [Fact] + public void Keywords_ParameterTypeArgument() + { + var source = +@"class @this +{ + internal class @base + { + } +} +class @class +{ +} +class C +{ + static void F(@class<@this.@base> c) { } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Assert.Null(MemberSignatureParser.Parse("F(@class)")); + Assert.Null(MemberSignatureParser.Parse("F(@class)")); + Assert.Null(MemberSignatureParser.Parse("F(@class<@this.base>)")); + Resolve(process, resolver, "F(@class<@base>)", "C.F(class)"); + Resolve(process, resolver, "F(@class<@this.@base>)", "C.F(class)"); + } + } + + [Fact] + public void EscapedNames() + { + var source = +@"class @object { } +class Object { } +class C +{ + static void F(@object o) { } + static void F(Object o) { } + static void F(object o) { } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "C.F(object)", "C.F(Object)", "C.F(System.Object)"); + Resolve(process, resolver, "F(object)", "C.F(System.Object)"); + Resolve(process, resolver, "F(Object)", "C.F(Object)", "C.F(System.Object)"); + Resolve(process, resolver, "F(System.Object)", "C.F(System.Object)"); + Resolve(process, resolver, "F(@object)", "C.F(object)"); + Resolve(process, resolver, "F(@Object)", "C.F(Object)", "C.F(System.Object)"); + } + } + + [Fact] + public void SpecialTypes() + { + var source = +@"class C +{ +} +class C +{ + static void F(bool a, char b, sbyte c, byte d) { } + static void F(short a, ushort b, int c, uint d) { } + static void F(C o) { } + static void F(C o) { } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F(bool, char, sbyte, byte)", "C.F(System.Boolean, System.Char, System.SByte, System.Byte)"); + Resolve(process, resolver, "F(System.Int16, System.UInt16, System.Int32, System.UInt32)", "C.F(System.Int16, System.UInt16, System.Int32, System.UInt32)"); + Resolve(process, resolver, "F(C)", "C.F(C)"); + Resolve(process, resolver, "F(C)", "C.F(C)"); + Resolve(process, resolver, "F(bool, char, sbyte)"); + Resolve(process, resolver, "F(C)"); + } + } + + [Fact] + public void SpecialTypes_More() + { + var source = +@"class C +{ + static void F(System.IntPtr p) { } + static void F(System.UIntPtr p) { } + static void F(System.TypedReference r) { } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F(object)"); + Resolve(process, resolver, "F(IntPtr)", "C.F(System.IntPtr)"); + Resolve(process, resolver, "F(UIntPtr)", "C.F(System.UIntPtr)"); + Resolve(process, resolver, "F(TypedReference)", "C.F(System.TypedReference)"); + } + } + + // Binding to "dynamic" type refs is not supported. + // This is consistent with Dev14. + [Fact] + public void Dynamic() + { + var source = +@"class C +{ +} +class C +{ + static void F(dynamic d) { } + static void F(C d) { } +}"; + var compilation = CreateCompilationWithMscorlib(source, references: new[] { CSharpRef, SystemCoreRef }); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "C.F(System.Object)", "C.F(C)"); + Resolve(process, resolver, "F(object)", "C.F(System.Object)"); + Resolve(process, resolver, "F(C)", "C.F(C)"); + Resolve(process, resolver, "F(dynamic)"); + Resolve(process, resolver, "F(C)"); + } + } + + [Fact] + public void Iterator() + { + var source = +@"class C +{ + static System.Collections.IEnumerable F() { yield break; } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "C.F()"); + } + } + + [Fact] + public void Async() + { + var source = +@"using System.Threading.Tasks; +class C +{ + static async Task F() + { + await Task.Delay(0); + } +}"; + var compilation = CreateCompilationWithMscorlib46(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "C.F()"); + } + } + + [Fact(Skip = "global:: not supported")] + public void Global() + { + var source = +@"class C +{ + static void F(N.C o) { } + static void F(global::C o) { } +} +namespace N +{ + class C + { + static void F(N.C o) { } + static void F(global::C o) { } + } +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "C.F(C)", "C.F(N.C)", "C.F(C)", "N.C.F(N.C)", "N.C.F(C)"); + Resolve(process, resolver, "C.F(N.C)", "C.F(N.C)", "N.C.F(N.C)"); + Resolve(process, resolver, "global::C.F(C)", "C.F(N.C)", "C.F(C)"); // Dev14 does not bind global:: + Resolve(process, resolver, "C.F(global::C)", "C.F(C)", "N.C.F(C)"); // Dev14 does not bind global:: + } + } + + // Since MetadataDecoder does not load referenced + // assemblies, the arity or a type reference is determined + // by the type name: e.g.: "C`2". If the arity from the name is + // different from the number of generic type arguments, the + // method signature containing the type reference is ignored. + [Fact] + public void UnexpectedArity() + { + var sourceA = +@".class public A { }"; + var sourceB = +@"class B +{ + static void F(object o) { } + static void F(A a) { } +}"; + ImmutableArray bytesA; + ImmutableArray pdbA; + EmitILToArray(sourceA, appendDefaultHeader: true, includePdb: false, assemblyBytes: out bytesA, pdbBytes: out pdbA); + var refA = AssemblyMetadata.CreateFromImage(bytesA).GetReference(); + var compilationB = CreateCompilationWithMscorlib(sourceB, references: new[] { refA }); + var bytesB = compilationB.EmitToArray(); + using (var process = new Process(new Module(bytesB))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "B.F(System.Object)", "B.F([notsupported])"); + Resolve(process, resolver, "F(A)"); + } + } + + /// + /// Should not resolve to P/Invoke methods. + /// + [Fact] + public void PInvoke() + { + var source = +@"using System.Runtime.InteropServices; +class A +{ + [DllImport(""extern.dll"")] + public static extern int F(); +} +class B +{ + public static int F() => 0; +}"; + var compilation = CreateCompilationWithMscorlib(source); + using (var process = new Process(new Module(compilation.EmitToArray()))) + { + var resolver = new Resolver(); + Resolve(process, resolver, "F", "B.F()"); + } + } + + private static void Resolve(Process process, Resolver resolver, string str, params string[] expectedSignatures) + { + var signature = MemberSignatureParser.Parse(str); + Assert.NotNull(signature); + var request = new Request(null, signature); + resolver.EnableResolution(process, request); + VerifySignatures(request, expectedSignatures); + } + + private static void VerifySignatures(Request request, params string[] expectedSignatures) + { + var actualSignatures = request.GetResolvedAddresses().Select(a => GetMethodSignature(a.Module, a.Token)); + AssertEx.Equal(expectedSignatures, actualSignatures); + } + + private static string GetMethodSignature(Module module, int token) + { + var reader = module.GetMetadataInternal(); + return GetMethodSignature(reader, MetadataTokens.MethodDefinitionHandle(token)); + } + + private static string GetMethodSignature(MetadataReader reader, MethodDefinitionHandle handle) + { + var methodDef = reader.GetMethodDefinition(handle); + var builder = new StringBuilder(); + var typeDef = reader.GetTypeDefinition(methodDef.GetDeclaringType()); + var allTypeParameters = typeDef.GetGenericParameters(); + AppendTypeName(builder, reader, typeDef); + builder.Append('.'); + builder.Append(reader.GetString(methodDef.Name)); + var methodTypeParameters = methodDef.GetGenericParameters(); + AppendTypeParameters(builder, DecodeTypeParameters(reader, offset: 0, typeParameters: methodTypeParameters)); + var decoder = new MetadataDecoder( + reader, + GetTypeParameterNames(reader, allTypeParameters), + 0, + GetTypeParameterNames(reader, methodTypeParameters)); + try + { + AppendParameters(builder, decoder.DecodeParameters(methodDef)); + } + catch (NotSupportedException) + { + builder.Append("([notsupported])"); + } + return builder.ToString(); + } + + private static ImmutableArray GetTypeParameterNames(MetadataReader reader, GenericParameterHandleCollection handles) + { + return ImmutableArray.CreateRange(handles.Select(h => reader.GetString(reader.GetGenericParameter(h).Name))); + } + + private static void AppendTypeName(StringBuilder builder, MetadataReader reader, TypeDefinition typeDef) + { + var declaringTypeHandle = typeDef.GetDeclaringType(); + int declaringTypeArity; + if (declaringTypeHandle.IsNil) + { + declaringTypeArity = 0; + var namespaceName = reader.GetString(typeDef.Namespace); + if (!string.IsNullOrEmpty(namespaceName)) + { + builder.Append(namespaceName); + builder.Append('.'); + } + } + else + { + var declaringType = reader.GetTypeDefinition(declaringTypeHandle); + declaringTypeArity = declaringType.GetGenericParameters().Count; + AppendTypeName(builder, reader, declaringType); + builder.Append('.'); + } + var typeName = reader.GetString(typeDef.Name); + int index = typeName.IndexOf('`'); + if (index >= 0) + { + typeName = typeName.Substring(0, index); + } + builder.Append(typeName); + AppendTypeParameters(builder, DecodeTypeParameters(reader, declaringTypeArity, typeDef.GetGenericParameters())); + } + + private static void AppendTypeParameters(StringBuilder builder, ImmutableArray typeParameters) + { + if (typeParameters.Length > 0) + { + builder.Append('<'); + AppendCommaSeparatedList(builder, typeParameters, (b, t) => b.Append(t)); + builder.Append('>'); + } + } + + private static void AppendParameters(StringBuilder builder, ImmutableArray parameters) + { + builder.Append('('); + AppendCommaSeparatedList(builder, parameters, AppendParameter); + builder.Append(')'); + } + + private static void AppendParameter(StringBuilder builder, ParameterSignature signature) + { + if (signature.IsByRef) + { + builder.Append("ref "); + } + AppendType(builder, signature.Type); + } + + private static void AppendType(StringBuilder builder, TypeSignature signature) + { + switch (signature.Kind) + { + case TypeSignatureKind.GenericType: + { + var genericName = (GenericTypeSignature)signature; + AppendType(builder, genericName.QualifiedName); + AppendTypeArguments(builder, genericName.TypeArguments); + } + break; + case TypeSignatureKind.QualifiedType: + { + var qualifiedName = (QualifiedTypeSignature)signature; + var qualifier = qualifiedName.Qualifier; + if (qualifier != null) + { + AppendType(builder, qualifier); + builder.Append('.'); + } + builder.Append(qualifiedName.Name); + } + break; + case TypeSignatureKind.ArrayType: + { + var arrayType = (ArrayTypeSignature)signature; + AppendType(builder, arrayType.ElementType); + builder.Append('['); + builder.Append(',', arrayType.Rank - 1); + builder.Append(']'); + } + break; + case TypeSignatureKind.PointerType: + AppendType(builder, ((PointerTypeSignature)signature).PointedAtType); + builder.Append('*'); + break; + default: + throw new System.NotImplementedException(); + } + } + + private static void AppendTypeArguments(StringBuilder builder, ImmutableArray typeArguments) + { + if (typeArguments.Length > 0) + { + builder.Append('<'); + AppendCommaSeparatedList(builder, typeArguments, AppendType); + builder.Append('>'); + } + } + + private static void AppendCommaSeparatedList(StringBuilder builder, ImmutableArray items, Action appendItem) + { + bool any = false; + foreach (var item in items) + { + if (any) + { + builder.Append(", "); + } + appendItem(builder, item); + any = true; + } + } + + private static ImmutableArray DecodeTypeParameters(MetadataReader reader, int offset, GenericParameterHandleCollection typeParameters) + { + int arity = typeParameters.Count - offset; + Debug.Assert(arity >= 0); + if (arity == 0) + { + return ImmutableArray.Empty; + } + var builder = ImmutableArray.CreateBuilder(arity); + for (int i = 0; i < arity; i++) + { + var handle = typeParameters[offset + i]; + var typeParameter = reader.GetGenericParameter(handle); + builder.Add(reader.GetString(typeParameter.Name)); + } + return builder.ToImmutable(); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/CSharpParsingTests.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/CSharpParsingTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..d8657ebc0faf42cb37896c706d81a3468cca9775 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/CSharpParsingTests.cs @@ -0,0 +1,418 @@ +// 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 Roslyn.Test.Utilities; +using System.Collections.Immutable; +using System.Linq; +using Xunit; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + public class CSharpParsingTests : CSharpTestBase + { + [Fact] + public void Parsing() + { + // Method name only. + VerifySignature("F", + SignatureNameOnly( + Name("F"))); + // Method name and empty parameters. + VerifySignature("F()", + Signature( + Name("F"))); + // Method name and parameters. + VerifySignature("F(A, B)", + Signature( + Name("F"), + Identifier("A"), + Identifier("B"))); + // Type and method name. + VerifySignature("C.F", + SignatureNameOnly( + Qualified(Name("C"), "F"))); + // Qualified type and method name. + VerifySignature("A.B.F", + SignatureNameOnly( + Qualified( + Qualified( + Name("A"), + "B"), + "F"))); + // Generic types and method names. + VerifySignature("A.B.F", + SignatureNameOnly( + Generic( + Qualified( + Generic( + Qualified( + Generic( + Name("A"), + "T"), + "B"), + "U"), + "F"), + "V"))); + } + + [Fact] + public void Spaces() + { + VerifySignature(" \tC . F ( System.Object\t,object) ", + Signature( + Qualified(Name("C"), "F"), + Qualified("System", "Object"), + Qualified("System", "Object"))); + } + + [Fact] + public void ArraysAndPointers() + { + VerifySignature("F(C*)", + Signature( + Name("F"), + Pointer( + Identifier("C")))); + VerifySignature("F(C**)", + Signature( + Name("F"), + Pointer( + Pointer( + Identifier("C"))))); + VerifySignature("F(C*[,,,])", + Signature( + Name("F"), + Array( + Pointer( + Identifier("C")), + 4))); + VerifySignature("F(C[,][]*)", + Signature( + Name("F"), + Pointer( + Array( + Array( + Identifier("C"), + 2), + 1)))); + VerifySignature("F(C*)", + Signature( + Name("F"), + Pointer( + Generic( + Identifier("C"), + Identifier("T"))))); + VerifySignature("F(C)", + Signature( + Name("F"), + Generic( + Identifier("C"), + Pointer( + Array( + Identifier("T"), + 2))))); + } + + [Fact] + public void ParseErrors() + { + Assert.Null(MemberSignatureParser.Parse("AB")); + Assert.Null(MemberSignatureParser.Parse("A<>")); + Assert.Null(MemberSignatureParser.Parse("A")); + Assert.Null(MemberSignatureParser.Parse("A.")); + Assert.Null(MemberSignatureParser.Parse("A.")); + Assert.Null(MemberSignatureParser.Parse("A+B")); + Assert.Null(MemberSignatureParser.Parse("F(")); + Assert.Null(MemberSignatureParser.Parse("F())")); + Assert.Null(MemberSignatureParser.Parse("F(]")); + Assert.Null(MemberSignatureParser.Parse("F(,B)")); + Assert.Null(MemberSignatureParser.Parse("F(A,)")); + Assert.Null(MemberSignatureParser.Parse("F<")); + Assert.Null(MemberSignatureParser.Parse("F<()")); + Assert.Null(MemberSignatureParser.Parse("F>")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("1")); + } + + [Fact] + public void ByRef() + { + VerifySignature("F(ref A, out B)", + Signature( + Name("F"), + Identifier("A"), + Identifier("B"))); + Assert.Null(MemberSignatureParser.Parse("F(ref out C)")); + Assert.Null(MemberSignatureParser.Parse("F(ref)")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F(C)")); + Assert.Null(MemberSignatureParser.Parse("F(C)")); + } + + // Special types are treated as keywords in names, + // but not recognized as special types. + [Fact] + public void SpecialTypes_Names() + { + // Method name only. + Assert.Null(MemberSignatureParser.Parse("int")); + Assert.Null(MemberSignatureParser.Parse("params")); + VerifySignature("@int", + SignatureNameOnly( + Name("int"))); + // Type and method name. + Assert.Null(MemberSignatureParser.Parse("@object.int")); + Assert.Null(MemberSignatureParser.Parse("@public.private")); + VerifySignature("@object.@int", + SignatureNameOnly( + Qualified( + Name("object"), + "int"))); + // Type parameters. + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + Assert.Null(MemberSignatureParser.Parse("F")); + VerifySignature("F<@void, @bool, @char, @sbyte, @byte, @short, @ushort, @int, @uint, @long, @ulong, @float, @double, @string, @object, @decimal>()", + Signature( + Generic(Name("F"), + "void", + "bool", + "char", + "sbyte", + "byte", + "short", + "ushort", + "int", + "uint", + "long", + "ulong", + "float", + "double", + "string", + "object", + "decimal"))); + } + + // Special types are recognized in type references. + [Fact] + public void SpecialTypes_TypeReferences() + { + // Parameters. + VerifySignature("F(void, bool, char, sbyte, byte, short, ushort, int, uint, long, ulong, float, double, string, object, decimal)", + Signature( + Name("F"), + Qualified("System", "Void"), + Qualified("System", "Boolean"), + Qualified("System", "Char"), + Qualified("System", "SByte"), + Qualified("System", "Byte"), + Qualified("System", "Int16"), + Qualified("System", "UInt16"), + Qualified("System", "Int32"), + Qualified("System", "UInt32"), + Qualified("System", "Int64"), + Qualified("System", "UInt64"), + Qualified("System", "Single"), + Qualified("System", "Double"), + Qualified("System", "String"), + Qualified("System", "Object"), + Qualified("System", "Decimal"))); + // Type arguments. + VerifySignature("F(C)", + Signature( + Name("F"), + Generic( + Identifier("C"), + Qualified("System", "Void"), + Qualified("System", "Int32"), + Qualified("System", "String"), + Qualified("System", "Object")))); + // Not special types. + VerifySignature("F(Void, Int32, @string, @object)", + Signature( + Name("F"), + Identifier("Void"), + Identifier("Int32"), + Identifier("string"), + Identifier("object"))); + // dynamic is not special. + VerifySignature("F(dynamic)", + Signature( + Name("F"), + Identifier("dynamic"))); + } + + [Fact] + public void EscapedNames() + { + VerifySignature("@F", + SignatureNameOnly( + Name("F"))); + VerifySignature("@_", + SignatureNameOnly( + Name("_"))); + VerifySignature("@int", + SignatureNameOnly( + Name("int"))); + VerifySignature("A.B.@int", + SignatureNameOnly( + Qualified( + Qualified( + Name("A"), + "B"), + "int"))); + VerifySignature("F(@int)", + Signature( + Name("F"), + Identifier("int"))); + VerifySignature("F(System.@int)", + Signature( + Name("F"), + Qualified( + Identifier("System"), + "int"))); + VerifySignature("A<@object>.B<@int>.F<@void>", + SignatureNameOnly( + Generic( + Qualified( + Generic( + Qualified( + Generic( + Name("A"), + "object"), + "B"), + "int"), + "F"), + "void"))); + VerifySignature("F(C)", + Signature( + Name("F"), + Generic( + Identifier("C"), + Qualified("System", "Int32"), + Identifier("void")))); + Assert.Null(MemberSignatureParser.Parse("@")); + Assert.Null(MemberSignatureParser.Parse("@1")); + Assert.Null(MemberSignatureParser.Parse("@@F")); + Assert.Null(MemberSignatureParser.Parse("@F@")); + Assert.Null(MemberSignatureParser.Parse("@")); + Assert.Null(MemberSignatureParser.Parse("@.F")); + Assert.Null(MemberSignatureParser.Parse("@()")); + Assert.Null(MemberSignatureParser.Parse("F(@)")); + Assert.Null(MemberSignatureParser.Parse("F(A, @)")); + Assert.Null(MemberSignatureParser.Parse("F<@>")); + Assert.Null(MemberSignatureParser.Parse("F")); + } + + private static RequestSignature SignatureNameOnly(Name name) + { + return new RequestSignature(name, default(ImmutableArray)); + } + + private static RequestSignature Signature(Name name) + { + return new RequestSignature(name, ImmutableArray.Empty); + } + + private static RequestSignature Signature(Name name, params TypeSignature[] parameterTypes) + { + return Signature(name, parameterTypes.Select(t => new ParameterSignature(t, isByRef: false)).ToArray()); + } + + private static RequestSignature Signature(Name name, params ParameterSignature[] parameters) + { + return new RequestSignature(name, ImmutableArray.CreateRange(parameters)); + } + + private static QualifiedName Name(string name) + { + return new QualifiedName(null, name); + } + + private static GenericName Generic(QualifiedName name, params string[] typeArguments) + { + Assert.True(typeArguments.Length > 0); + return new GenericName(name, ImmutableArray.CreateRange(typeArguments)); + } + + private static QualifiedName Qualified(Name left, string right) + { + return new QualifiedName(left, right); + } + + private static QualifiedTypeSignature Identifier(string name) + { + return new QualifiedTypeSignature(null, name); + } + + private static GenericTypeSignature Generic(QualifiedTypeSignature name, params TypeSignature[] typeArguments) + { + Assert.True(typeArguments.Length > 0); + return new GenericTypeSignature(name, ImmutableArray.CreateRange(typeArguments)); + } + + private static QualifiedTypeSignature Qualified(TypeSignature left, string right) + { + return new QualifiedTypeSignature(left, right); + } + + private static QualifiedTypeSignature Qualified(params string[] names) + { + QualifiedTypeSignature signature = null; + foreach (var name in names) + { + signature = new QualifiedTypeSignature(signature, name); + } + return signature; + } + + private static ArrayTypeSignature Array(TypeSignature elementType, int rank) + { + return new ArrayTypeSignature(elementType, rank); + } + + private static PointerTypeSignature Pointer(TypeSignature pointedAtType) + { + return new PointerTypeSignature(pointedAtType); + } + + private static void VerifySignature(string str, RequestSignature expectedSignature) + { + var actualSignature = MemberSignatureParser.Parse(str); + if (expectedSignature == null) + { + Assert.Null(actualSignature); + } + else + { + Assert.NotNull(actualSignature); + Assert.Equal(expectedSignature.MemberName, actualSignature.MemberName, NameComparer.Instance); + if (expectedSignature.Parameters.IsDefault) + { + Assert.True(actualSignature.Parameters.IsDefault); + } + else + { + AssertEx.Equal( + expectedSignature.Parameters, + actualSignature.Parameters, + comparer: ParameterComparer.Instance, + itemInspector: p => p.Type.GetDebuggerDisplay()); + } + } + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj b/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj new file mode 100644 index 0000000000000000000000000000000000000000..8eff6d8e277caa4c8b3795f8d007dcb88666c0a4 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj @@ -0,0 +1,75 @@ + + + + + true + AnyCPU + {DD317BE1-42A1-4795-B1D4-F370C40D649A} + Library + Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests + Roslyn.ExpressionEvaluator.FunctionResolver.UnitTests + true + v4.6 + UnitTest + + + + + {7a4b2176-7bfd-4b75-a61a-e25a1fdd0a1e} + CSharpCompilerTestUtilities.Desktop + + + {f7712928-1175-47b3-8819-ee086753dee2} + TestUtilities.FX45 + + + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} + CodeAnalysis + + + {B501A547-C911-4A05-AC6E-274A50DFF30E} + CSharpCodeAnalysis + + + {2523d0e6-df32-4a3e-8ae0-a19bffae2ef6} + BasicCodeAnalysis + + + {6FC8E6F5-659C-424D-AEB5-331B95883E29} + FunctionResolver + + + {AFDE6BEA-5038-4A4A-A88E-DBD2E4088EED} + PdbUtilities + + + {76C6F005-C89D-4348-BB4A-391898DBEB52} + TestUtilities.Desktop + + + {7fe6b002-89d8-4298-9b1b-0b5c247dd1fd} + CompilerTestResources + + + + true + + + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/Module.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Module.cs new file mode 100644 index 0000000000000000000000000000000000000000..af4ee1fa37d58f8e7a2bbf7c51df52101ed120ac --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Module.cs @@ -0,0 +1,53 @@ +// 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; +using System.Reflection.PortableExecutable; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal sealed class Module : IDisposable + { + private readonly string _name; + private readonly PEReader _reader; + private int _getMetadataCount; + + internal Module(ImmutableArray bytes, string name = null) + { + _name = name; + _reader = bytes.IsDefault ? null : new PEReader(bytes); + } + + internal string Name => _name; + + internal int GetMetadataCount => _getMetadataCount; + + internal MetadataReader GetMetadata() + { + _getMetadataCount++; + return GetMetadataInternal(); + } + + internal MetadataReader GetMetadataInternal() + { + if (_reader == null) + { + return null; + } + unsafe + { + var block = _reader.GetMetadata(); + return new MetadataReader(block.Pointer, block.Length); + } + } + + void IDisposable.Dispose() + { + if (_reader != null) + { + _reader.Dispose(); + } + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/NameComparer.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/NameComparer.cs new file mode 100644 index 0000000000000000000000000000000000000000..2a07b80662aeb2c19f58b34d268c081f6b918cb2 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/NameComparer.cs @@ -0,0 +1,53 @@ +// 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.Linq; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal sealed class NameComparer : IEqualityComparer + { + internal static readonly NameComparer Instance = new NameComparer(); + + public bool Equals(Name x, Name y) + { + if (x == null) + { + return y == null; + } + if (y == null) + { + return false; + } + if (x.Kind != y.Kind) + { + return false; + } + switch (x.Kind) + { + case NameKind.QualifiedName: + { + var xQualified = (QualifiedName)x; + var yQualified = (QualifiedName)y; + return Equals(xQualified.Qualifier, yQualified.Qualifier) && + Equals(xQualified.Name, yQualified.Name); + } + case NameKind.GenericName: + { + var xGeneric = (GenericName)x; + var yGeneric = (GenericName)y; + return Equals(xGeneric.QualifiedName, yGeneric.QualifiedName) && + xGeneric.TypeParameters.SequenceEqual(yGeneric.TypeParameters); + } + default: + throw new NotImplementedException(); + } + } + + public int GetHashCode(Name obj) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/ParameterComparer.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/ParameterComparer.cs new file mode 100644 index 0000000000000000000000000000000000000000..f3b581dee9d18511c2109916a0650def699733af --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/ParameterComparer.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal sealed class ParameterComparer : IEqualityComparer + { + internal static readonly ParameterComparer Instance = new ParameterComparer(); + + public bool Equals(ParameterSignature x, ParameterSignature y) + { + return TypeComparer.Instance.Equals(x.Type, y.Type); + } + + public int GetHashCode(ParameterSignature obj) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/Process.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Process.cs new file mode 100644 index 0000000000000000000000000000000000000000..51c34103c29579df1aab157d328acdc190195210 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Process.cs @@ -0,0 +1,50 @@ +// 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; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal sealed class Process : IDisposable + { + private readonly bool _shouldEnable; + private readonly List _modules; + private int _shouldEnableRequests; + + internal Process(params Module[] modules) : this(true, modules) + { + } + + internal Process(bool shouldEnable, params Module[] modules) + { + _shouldEnable = shouldEnable; + _modules = new List(modules); + } + + internal int ShouldEnableRequests => _shouldEnableRequests; + + internal bool ShouldEnableFunctionResolver() + { + _shouldEnableRequests++; + return _shouldEnable; + } + + internal void AddModule(Module module) + { + _modules.Add(module); + } + + internal Module[] GetModules() + { + return _modules.ToArray(); + } + + void IDisposable.Dispose() + { + foreach (var module in _modules) + { + ((IDisposable)module).Dispose(); + } + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/Request.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Request.cs new file mode 100644 index 0000000000000000000000000000000000000000..ac74ba57f62f73ed7e0bd7d41fb1c05fc31f1d60 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Request.cs @@ -0,0 +1,48 @@ +// 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.Generic; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal struct Address + { + internal readonly Module Module; + internal readonly int Token; + internal readonly int Version; + internal readonly int ILOffset; + + internal Address(Module module, int token, int version, int ilOffset) + { + Module = module; + Token = token; + Version = version; + ILOffset = ilOffset; + } + } + + internal sealed class Request + { + internal readonly List
_resolvedAddresses; + + internal Request(string moduleName, RequestSignature signature) + { + ModuleName = moduleName; + Signature = signature; + _resolvedAddresses = new List
(); + } + + internal readonly string ModuleName; + internal readonly RequestSignature Signature; + + internal void OnFunctionResolved(Module module, int token, int version, int ilOffset) + { + _resolvedAddresses.Add(new Address(module, token, version, ilOffset)); + } + + internal ImmutableArray
GetResolvedAddresses() + { + return ImmutableArray.CreateRange(_resolvedAddresses); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/Resolver.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Resolver.cs new file mode 100644 index 0000000000000000000000000000000000000000..2ead5f708e65c4d43bfa579fa722e983cb210651 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/Resolver.cs @@ -0,0 +1,79 @@ +// 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.Generic; +using System.Reflection.Metadata; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal sealed class Resolver : FunctionResolverBase + { + private readonly Dictionary> _requests; + + internal Resolver() + { + _requests = new Dictionary>(); + } + + internal new void EnableResolution(Process process, Request request) + { + List requests; + if (!_requests.TryGetValue(process, out requests)) + { + requests = new List(); + _requests.Add(process, requests); + } + requests.Add(request); + base.EnableResolution(process, request); + } + + internal override bool ShouldEnableFunctionResolver(Process process) + { + return process.ShouldEnableFunctionResolver(); + } + + internal override IEnumerable GetAllModules(Process process) + { + return process.GetModules(); + } + + internal override string GetModuleName(Module module) + { + return module.Name; + } + + internal override MetadataReader GetModuleMetadata(Module module) + { + return module.GetMetadata(); + } + + internal override Request[] GetRequests(Process process) + { + List requests; + if (!_requests.TryGetValue(process, out requests)) + { + return new Request[0]; + } + return requests.ToArray(); + } + + internal override string GetRequestModuleName(Request request) + { + return request.ModuleName; + } + + internal override RequestSignature GetParsedSignature(Request request) + { + return request.Signature; + } + + internal override void OnFunctionResolved(Module module, Request request, int token, int version, int ilOffset) + { + request.OnFunctionResolved(module, token, version, ilOffset); + } + + private void OnModuleLoaded(object sender, Module e) + { + OnModuleLoad((Process)sender, e); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/TypeComparer.cs b/src/ExpressionEvaluator/Core/Test/FunctionResolver/TypeComparer.cs new file mode 100644 index 0000000000000000000000000000000000000000..d7675643d3319a9e1c055cfbd3e099a9f75cc755 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/TypeComparer.cs @@ -0,0 +1,66 @@ +// 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.Linq; + +namespace Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests +{ + internal sealed class TypeComparer : IEqualityComparer + { + internal static readonly TypeComparer Instance = new TypeComparer(); + + public bool Equals(TypeSignature x, TypeSignature y) + { + if (x == null) + { + return y == null; + } + if (y == null) + { + return false; + } + if (x.Kind != y.Kind) + { + return false; + } + switch (x.Kind) + { + case TypeSignatureKind.QualifiedType: + { + var xQualified = (QualifiedTypeSignature)x; + var yQualified = (QualifiedTypeSignature)y; + return Equals(xQualified.Qualifier, yQualified.Qualifier) && + Equals(xQualified.Name, yQualified.Name); + } + case TypeSignatureKind.GenericType: + { + var xGeneric = (GenericTypeSignature)x; + var yGeneric = (GenericTypeSignature)y; + return Equals(xGeneric.QualifiedName, yGeneric.QualifiedName) && + xGeneric.TypeArguments.SequenceEqual(yGeneric.TypeArguments, this); + } + case TypeSignatureKind.ArrayType: + { + var xArray = (ArrayTypeSignature)x; + var yArray = (ArrayTypeSignature)y; + return Equals(xArray.ElementType, yArray.ElementType) && + xArray.Rank == yArray.Rank; + } + case TypeSignatureKind.PointerType: + { + var xPointer = (PointerTypeSignature)x; + var yPointer = (PointerTypeSignature)y; + return Equals(xPointer.PointedAtType, yPointer.PointedAtType); + } + default: + throw new NotImplementedException(); + } + } + + public int GetHashCode(TypeSignature obj) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/project.json b/src/ExpressionEvaluator/Core/Test/FunctionResolver/project.json new file mode 100644 index 0000000000000000000000000000000000000000..875a59704cb6de7496bba156d73f353d2be7c466 --- /dev/null +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/project.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "Microsoft.VisualStudio.Debugger.Engine": "14.3.25422", + "xunit": "2.1.0", + "xunit.runner.console": "2.2.0-beta1-build3239" + }, + "frameworks": { + "net46": { } + }, + "runtimes": { + "win7": { } + } +} \ No newline at end of file diff --git a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrModuleInstance.cs b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrModuleInstance.cs index 9f6649c020bcfba325aca4a219c55fd46e5550f6..b7773cd3eea27c8f836667d1504435686135b317 100644 --- a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrModuleInstance.cs +++ b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrModuleInstance.cs @@ -38,6 +38,8 @@ public DkmClrRuntimeInstance RuntimeInstance get { return _runtimeInstance; } } + public DkmProcess Process => _runtimeInstance.Process; + public DkmClrType ResolveTypeName(string typeName, ReadOnlyCollection typeArguments) { var type = this.Assembly.GetType(typeName); diff --git a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmProcess.cs b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmProcess.cs index 2e5c45ca009c3e6af1c8f11e301f43cc6d099572..edff411866843198b56636d69104fde2f6be67bf 100644 --- a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmProcess.cs +++ b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmProcess.cs @@ -12,10 +12,6 @@ public class DkmProcess private readonly bool _nativeDebuggingEnabled; - public DkmProcess() - { - } - public DkmProcess(bool enableNativeDebugging) { _nativeDebuggingEnabled = enableNativeDebugging; diff --git a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmRuntimeInstance.cs b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmRuntimeInstance.cs index c16fbb1cdfb4165ed202f7d4b9f390e3b0233ac7..dea025cc253b28dc5f8ffe68fae9d6219120152f 100644 --- a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmRuntimeInstance.cs +++ b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmRuntimeInstance.cs @@ -8,16 +8,11 @@ namespace Microsoft.VisualStudio.Debugger { public class DkmRuntimeInstance { - internal static readonly DkmProcess DefaultProcess = new DkmProcess(); - internal static readonly DkmProcess ProcessWithNativeDebugging = new DkmProcess(enableNativeDebugging: true); - public readonly DkmProcess Process; public DkmRuntimeInstance(bool enableNativeDebugging) { - Process = enableNativeDebugging ? - ProcessWithNativeDebugging : - DefaultProcess; + Process = new DkmProcess(enableNativeDebugging); } } } diff --git a/src/ExpressionEvaluator/Package/AssemblyRedirects.cs b/src/ExpressionEvaluator/Package/AssemblyRedirects.cs index ed1395891e009af9c78d5d65ae20e87561136c42..9cf42197b03536dfac7322bba21f0da8974ec766 100644 --- a/src/ExpressionEvaluator/Package/AssemblyRedirects.cs +++ b/src/ExpressionEvaluator/Package/AssemblyRedirects.cs @@ -5,6 +5,7 @@ [assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler.dll")] [assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider.dll")] [assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.ExpressionEvaluator.ExpressionCompiler.dll")] +[assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver.dll")] [assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider.dll")] [assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ExpressionCompiler.dll")] [assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider.dll")] diff --git a/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj b/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj index 59a9f036b24ed2a90bf0f01eee8301694ff914aa..b701e237d0af37846b460a37aab411b17a92829d 100644 --- a/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj +++ b/src/ExpressionEvaluator/Package/ExpressionEvaluatorPackage.csproj @@ -47,6 +47,13 @@ DebugSymbolsProjectOutputGroup true + + {6FC8E6F5-659C-424D-AEB5-331B95883E29} + FunctionResolver + BuiltProjectOutputGroup%3bVsdConfigOutputGroup + DebugSymbolsProjectOutputGroup + true + {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0} ResultProvider.Portable diff --git a/src/ExpressionEvaluator/Package/source.extension.vsixmanifest b/src/ExpressionEvaluator/Package/source.extension.vsixmanifest index 37b8e70f50219101326c8f4a5a00dd39e2bfef6c..106a24d88d15f7b35d316d84dd837482fa47e00c 100644 --- a/src/ExpressionEvaluator/Package/source.extension.vsixmanifest +++ b/src/ExpressionEvaluator/Package/source.extension.vsixmanifest @@ -17,6 +17,7 @@ + @@ -30,6 +31,7 @@ + diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/BasicExpressionCompiler.vbproj b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/BasicExpressionCompiler.vbproj index 85763a8403b30d06aef1c3761a91cda6d3c3cafc..c4d3e3dba50fd3d8f1ccbe0c3d16aed8b4de3719 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/BasicExpressionCompiler.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/BasicExpressionCompiler.vbproj @@ -99,9 +99,6 @@ - - - diff --git a/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb b/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb index 686eb6681fab09b755c08d0f2b301145fc869b34..95e6a03ea01ea564873adb715612eb8d215c5a41 100644 --- a/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb +++ b/src/Setup/DevDivInsertionFiles/BuildDevDivInsertionFiles.vb @@ -297,6 +297,7 @@ Public Class BuildDevDivInsertionFiles "Microsoft.CodeAnalysis.EditorFeatures.dll", "Microsoft.CodeAnalysis.EditorFeatures.Text.dll", "Microsoft.CodeAnalysis.ExpressionEvaluator.ExpressionCompiler.dll", + "Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver.dll", "Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider.dll", "Microsoft.CodeAnalysis.Features.dll", "Microsoft.CodeAnalysis.InteractiveEditorFeatures.dll", @@ -835,6 +836,7 @@ Public Class BuildDevDivInsertionFiles add("Dlls\BasicResultProvider.Portable\Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider.vsdconfig") add("Dlls\CSharpExpressionCompiler\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler.vsdconfig") add("Dlls\CSharpResultProvider.Portable\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider.vsdconfig") + add("Dlls\FunctionResolver\Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver.vsdconfig") add("Dlls\MSBuildTask\Microsoft.CSharp.Core.targets") add("Dlls\MSBuildTask\Microsoft.VisualBasic.Core.targets") add("Dlls\CSharpCompilerTestUtilities\Roslyn.Compilers.CSharp.Test.Utilities.dll") diff --git a/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj b/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj index 028ecbd52faf928992a5e15804927910534a4c23..6206ef630f0889c4e374863cf955801e517b7194 100644 --- a/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj +++ b/src/Test/Utilities/Desktop/TestUtilities.Desktop.csproj @@ -142,6 +142,7 @@ + diff --git a/src/Test/Utilities/Portable.FX45/TestUtilities.FX45.csproj b/src/Test/Utilities/Portable.FX45/TestUtilities.FX45.csproj index eb957b47901375419ff577664af4796e4bbbf3e0..ca501615e2785add027130f2cc412bfe3cc04b5f 100644 --- a/src/Test/Utilities/Portable.FX45/TestUtilities.FX45.csproj +++ b/src/Test/Utilities/Portable.FX45/TestUtilities.FX45.csproj @@ -95,6 +95,7 @@ + diff --git a/src/Test/Utilities/Portable/TestUtilities.csproj b/src/Test/Utilities/Portable/TestUtilities.csproj index fd5712d4d174992dc86a05fd64bdb5b22d6b7d7e..2b33fac95816aab1fe0782c05b45497b31cbd8e7 100644 --- a/src/Test/Utilities/Portable/TestUtilities.csproj +++ b/src/Test/Utilities/Portable/TestUtilities.csproj @@ -79,6 +79,7 @@ +