|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Reflection.Metadata.Ecma335
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests
Imports Microsoft.VisualStudio.Debugger.Evaluation
Imports Roslyn.Test.Utilities
Imports Xunit
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.UnitTests
'// TODO: constructors
'// TODO: keyword identifiers
'// TODO: containing type and parameter types that are nested types
'// TODO: anonymous methods
'// TODO: iterator/async lambda
'// TODO: parameter generic(Of generic)
'// TODO: generic state machines (generic source method/source type)
'// TODO: string argument values
'// TODO: string argument values requiring quotes
'// TODO: argument flags == names only, types only, values only
'// TODO: generic class/method with 2 or more type parameters
'// TODO: generic argument type that is not from a referenced assembly
Public Class InstructionDecoderTests : Inherits ExpressionCompilerTestBase
<Fact>
Public Sub GetNameArgumentCounts()
Dim source = "
Imports System
Module Module1
Sub NoArgs()
End Sub
Sub OneArg(one As Int32)
End Sub
Sub TwoArgs(one As Object, two As Exception)
End Sub
End Module"
Assert.Equal(
"Module1.NoArgs()",
GetName(source, "Module1.NoArgs", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Module1.OneArg(Integer one)",
GetName(source, "Module1.OneArg", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Module1.TwoArgs(Object one, System.Exception two)",
GetName(source, "Module1.TwoArgs", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameNullable()
Dim source = "
Imports System
Module Module1
Sub M1(n As Nullable(Of Int32))
End Sub
Sub M2(n As Int64?)
End Sub
End Module"
Assert.Equal(
"Module1.M1(Integer? n)",
GetName(source, "Module1.M1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Module1.M2(Long? n)",
GetName(source, "Module1.M2", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameGenerics()
Dim source = "
Imports System
Class Class1(Of T)
Sub M1(Of U)(a As Action(Of Int32))
End Sub
Sub M2(Of U)(a As Action(Of T))
End Sub
Sub M3(Of U)(a As Action(Of U))
End Sub
End Class"
Assert.Equal(
"Class1(Of T).M1(Of U)(System.Action(Of Integer) a)",
GetName(source, "Class1.M1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Class1(Of T).M2(Of U)(System.Action(Of T) a)",
GetName(source, "Class1.M2", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Class1(Of T).M3(Of U)(System.Action(Of U) a)",
GetName(source, "Class1.M3", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Class1(Of String).M1(Of Decimal)(System.Action(Of Integer) a)",
GetName(source, "Class1.M1", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)}))
Assert.Equal(
"Class1(Of String).M2(Of Decimal)(System.Action(Of String) a)",
GetName(source, "Class1.M2", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)}))
Assert.Equal(
"Class1(Of String).M3(Of Decimal)(System.Action(Of Decimal) a)",
GetName(source, "Class1.M3", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), GetType(Decimal)}))
End Sub
<Fact>
Public Sub GetNameNullTypeArguments()
Dim source = "
Imports System
Class Class1(Of T)
Sub M(Of U)(a As Action(Of U))
End Sub
End Class"
Assert.Equal(
"Class1(Of T).M(Of U)(System.Action(Of U) a)",
GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:=New Type() {Nothing, Nothing}))
Assert.Equal(
"Class1(Of T).M(Of U)(System.Action(Of U) a)",
GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(String), Nothing}))
Assert.Equal(
"Class1(Of T).M(Of U)(System.Action(Of U) a)",
GetName(source, "Class1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={Nothing, GetType(Decimal)}))
End Sub
<Fact>
Public Sub GetNameGenericArgumentTypeNotInReferences()
Dim source = "
Class Class1
End Class"
Dim serializedTypeArgumentName = "Class1, " & NameOf(InstructionDecoderTests) & ", Culture=neutral, PublicKeyToken=null"
Assert.Equal(
"System.Collections.Generic.Comparer(Of Class1).Create(System.Comparison(Of Class1) comparison)",
GetName(source, "System.Collections.Generic.Comparer.Create", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={serializedTypeArgumentName}))
End Sub
<Fact>
Public Sub GetNameAsync()
Dim source = "
Imports System.Threading.Tasks
Module Module1
Async Function M() As Task
Await MAsync()
End Function
Async Function MAsync() As Task(Of Integer)
Return 3
End Function
End Module"
Assert.Equal(
"Module1.M()",
GetName(source, "Module1.VB$StateMachine_0_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1107977")>
Public Sub GetNameGenericAsync()
Dim source = "
Imports System.Threading.Tasks
Class C
Shared Async Function M(Of T)(x As T) As Task(Of T)
Await Task.Yield()
Return x
End Function
End Class"
Assert.Equal(
"C.M(Of Long)(Long x)",
GetName(source, "C.VB$StateMachine_1_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(Long)}))
End Sub
<Fact>
Public Sub GetNameIterator()
Dim source = "
Imports System.Collections.Generic
Module Module1
Iterator Function M() As IEnumerable(Of Integer)
Yield 1
Yield 3
Yield 5
Yield 7
End Function
End Module"
Assert.Equal(
"Module1.M()",
GetName(source, "Module1.VB$StateMachine_0_M.MoveNext", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameLambda()
Dim source = "
Module Module1
Sub M()
Dim f = Function() 3
End Sub
End Module"
Assert.Equal(
"Module1.<closure>.<lambda0-0>()",
GetName(source, "Module1._Closure$__._Lambda$__0-0", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameGenericLambda()
Dim source = "
Imports System
Class Class1(Of T)
Sub M(Of U As T)()
Dim f As Func(Of U, T) = Function(u2 As U) u2
End Sub
End Class"
Assert.Equal(
"Class1(Of System.Exception).<closure>.<lambda1-0>(System.ArgumentException u2)",
GetName(source, "Class1._Closure$__1._Lambda$__1-0", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, typeArguments:={GetType(Exception), GetType(ArgumentException)}))
End Sub
<Fact>
Public Sub GetNameOptionalParameter()
Dim source = "
Module Module1
Function M(Optional d As Date = #1/1/1970#) As Integer
Return 42
End Function
End Module"
Assert.Equal(
"Module1.M(Date d)",
GetName(source, "Module1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Module1.M(Date d = #6/23/1912#)",
GetName(source, "Module1.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types, argumentValues:={"#6/23/1912#"}))
End Sub
<Fact>
Public Sub GetNameProperties()
Dim source = "
Class Class1
Property P As Integer
Get
Return 1
End Get
Set
End Set
End Property
Default Property D(x As Object) As Integer
Get
Return 42
End Get
Set(i As Integer)
End Set
End Property
End Class"
Assert.Equal(
"Class1.get_P()",
GetName(source, "Class1.get_P", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Class1.set_P(Integer Value)",
GetName(source, "Class1.set_P", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Class1.get_D(Object x)",
GetName(source, "Class1.get_D", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
Assert.Equal(
"Class1.set_D(Object x, Integer i)",
GetName(source, "Class1.set_D", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameInterfaceImplementation()
Dim source = "
Imports System
Class C : Implements IDisposable
Sub Dispoze() Implements IDisposable.Dispose
End Sub
End Class"
Assert.Equal(
"C.Dispoze()",
GetName(source, "C.Dispoze", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameExtensionMethod()
Dim source = "
Imports System.Runtime.CompilerServices
Module Extensions
<Extension>
Sub M([Me] As String)
End Sub
End Module"
Assert.Equal(
"Extensions.M(String Me)",
GetName(source, "Extensions.M", DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types))
End Sub
<Fact>
Public Sub GetNameArgumentFlagsNone()
Dim source = "
Module Module1
Sub M1()
End Sub
Sub M2(x, y)
End Sub
End Module"
Assert.Equal(
"Module1.M1",
GetName(source, "Module1.M1", DkmVariableInfoFlags.None))
Assert.Equal(
"Module1.M2",
GetName(source, "Module1.M2", DkmVariableInfoFlags.None))
End Sub
<Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1107978")>
Public Sub GetNameRefAndOutParameters()
Dim source = "
Imports System.Runtime.InteropServices
Class C
Shared Sub M(ByRef x As Integer, <Out> ByRef y As Integer)
y = x
End Sub
End Class"
Assert.Equal(
"C.M",
GetName(source, "C.M", DkmVariableInfoFlags.None))
Assert.Equal(
"C.M(1, 2)",
GetName(source, "C.M", DkmVariableInfoFlags.None, argumentValues:={"1", "2"}))
Assert.Equal(
"C.M(Integer, Integer)",
GetName(source, "C.M", DkmVariableInfoFlags.Types))
Assert.Equal(
"C.M(x, y)",
GetName(source, "C.M", DkmVariableInfoFlags.Names))
Assert.Equal(
"C.M(Integer x, Integer y)",
GetName(source, "C.M", DkmVariableInfoFlags.Types Or DkmVariableInfoFlags.Names))
End Sub
<Fact>
Public Sub GetNameParamsParameters()
Dim source = "
Class C
Shared Sub M(ParamArray x() As Integer)
End Sub
End Class"
Assert.Equal(
"C.M(Integer() x)",
GetName(source, "C.M", DkmVariableInfoFlags.Types Or DkmVariableInfoFlags.Names))
End Sub
<Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1154945")>
Public Sub GetNameIncorrectNumberOfArgumentValues()
Dim source = "
Class C
Sub M(x As Integer, y As Integer)
End Sub
End Class"
Dim expected = "C.M(Integer x, Integer y)"
Assert.Equal(expected,
GetName(source, "C.M", DkmVariableInfoFlags.Types Or DkmVariableInfoFlags.Names, argumentValues:={}))
Assert.Equal(expected,
GetName(source, "C.M", DkmVariableInfoFlags.Types Or DkmVariableInfoFlags.Names, argumentValues:={"1"}))
Assert.Equal(expected,
GetName(source, "C.M", DkmVariableInfoFlags.Types Or DkmVariableInfoFlags.Names, argumentValues:={"1", "2", "3"}))
End Sub
<Fact>
Public Sub GetReturnTypeNamePrimitive()
Dim source = "
Class C
Function M1() As UInteger
Return 42
End Function
End Class"
Assert.Equal("UInteger", GetReturnTypeName(source, "C.M1"))
End Sub
<Fact>
Public Sub GetReturnTypeNameNested()
Dim source = "
Class C
Function M1() As N.D.E
Return Nothing
End Function
End Class
Namespace N
Class D
Friend Structure E
End Structure
End Class
End Namespace"
Assert.Equal("N.D.E", GetReturnTypeName(source, "C.M1"))
End Sub
<Fact>
Public Sub GetReturnTypeNameGenericOfPrimitive()
Dim source = "
Imports System
Class C
Function M1() As Action(Of Int32)
Return Nothing
End Function
End Class"
Assert.Equal("System.Action(Of Integer)", GetReturnTypeName(source, "C.M1"))
End Sub
<Fact>
Public Sub GetReturnTypeNameGenericOfNested()
Dim source = "
Imports System
Class C
Function M1() As Action(Of D)
Return Nothing
End Function
Class D
End Class
End Class"
Assert.Equal("System.Action(Of C.D)", GetReturnTypeName(source, "C.M1"))
End Sub
<Fact>
Public Sub GetReturnTypeNameGenericOfGeneric()
Dim source = "
Imports System
Class C
Function M1(Of T)() As Action(Of Func(Of T))
Return Nothing
End Function
End Class"
Assert.Equal("System.Action(Of System.Func(Of Object))", GetReturnTypeName(source, "C.M1", typeArguments:={GetType(Object)}))
End Sub
Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, Optional typeArguments() As Type = Nothing, Optional argumentValues() As String = Nothing) As String
Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray()
Return GetName(source, methodName, argumentFlags, serializedTypeArgumentNames, argumentValues)
End Function
Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, typeArguments() As String, Optional argumentValues() As String = Nothing) As String
Debug.Assert((argumentFlags And (DkmVariableInfoFlags.Names Or DkmVariableInfoFlags.Types)) = argumentFlags,
"Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags)
Dim instructionDecoder = VisualBasicInstructionDecoder.Instance
Dim method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder)
Dim includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types)
Dim includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names)
Dim builder As ArrayBuilder(Of String) = Nothing
If argumentValues IsNot Nothing Then
builder = ArrayBuilder(Of String).GetInstance()
builder.AddRange(argumentValues)
End If
Dim name = instructionDecoder.GetName(method, includeParameterTypes, includeParameterNames, builder)
If builder IsNot Nothing Then
builder.Free()
End If
Return name
End Function
Private Function GetReturnTypeName(source As String, methodName As String, Optional typeArguments() As Type = Nothing) As String
Dim instructionDecoder = VisualBasicInstructionDecoder.Instance
Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray()
Dim method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder)
Return instructionDecoder.GetReturnTypeName(method)
End Function
Private Function GetConstructedMethod(source As String, methodName As String, serializedTypeArgumentNames() As String, instructionDecoder As VisualBasicInstructionDecoder) As MethodSymbol
Dim compilation = CreateEmptyCompilationWithReferences(
{VisualBasicSyntaxTree.ParseText(source)},
references:={MscorlibRef_v4_0_30316_17626, MsvbRef_v4_0_30319_17929},
options:=TestOptions.DebugDll,
assemblyName:=NameOf(InstructionDecoderTests))
Dim runtime = CreateRuntimeInstance(compilation)
Dim moduleInstances = runtime.Modules
Dim blocks = moduleInstances.SelectAsArray(Function(m) m.MetadataBlock)
compilation = blocks.ToCompilation()
Dim frame = DirectCast(GetMethodOrTypeBySignature(compilation, methodName), PEMethodSymbol)
' Once we have the method token, we want to look up the method (again)
' using the same helper as the product code. This helper will also map
' async/ iterator "MoveNext" methods to the original source method.
Dim method As MethodSymbol = compilation.GetSourceMethod(
DirectCast(frame.ContainingModule, PEModuleSymbol).Module.GetModuleVersionIdOrThrow(),
frame.Handle)
If serializedTypeArgumentNames IsNot Nothing Then
Assert.NotEmpty(serializedTypeArgumentNames)
Dim typeParameters = instructionDecoder.GetAllTypeParameters(method)
Assert.NotEmpty(typeParameters)
' Use the same helper method as the FrameDecoder to get the TypeSymbols for the
' generic type arguments (rather than using EETypeNameDecoder directly).
Dim typeArgumentSymbols = instructionDecoder.GetTypeSymbols(compilation, method, serializedTypeArgumentNames)
If Not typeArgumentSymbols.IsEmpty Then
method = instructionDecoder.ConstructMethod(method, typeParameters, typeArgumentSymbols)
End If
End If
Return method
End Function
End Class
End Namespace
|