File: Lowering\MethodToClassRewriter\MethodToClassRewriter.MyBaseMyClassWrapper.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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
Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports System.Linq
Imports System.Text
Imports Microsoft.Cci
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.RuntimeMembers
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Emit
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend MustInherit Class MethodToClassRewriter(Of TProxy)
 
        Private Function SubstituteMethodForMyBaseOrMyClassCall(receiverOpt As BoundExpression, originalMethodBeingCalled As MethodSymbol) As MethodSymbol
            If (originalMethodBeingCalled.IsMetadataVirtual OrElse Me.IsInExpressionLambda) AndAlso
                    receiverOpt IsNot Nothing AndAlso (receiverOpt.Kind = BoundKind.MyBaseReference OrElse receiverOpt.Kind = BoundKind.MyClassReference) Then
 
                ' NOTE: We can only call a virtual method non-virtually if the type of Me reference 
                '       we pass to this method IS or INHERITS FROM the type of the method we want to call;
                '
                '       Thus, for MyBase/MyClass receivers we MAY need to replace 
                '       the method with a wrapper one to be able to call it non-virtually;
                '
                Dim callingMethodType As TypeSymbol = Me.CurrentMethod.ContainingType
                Dim topLevelMethodType As TypeSymbol = Me.TopLevelMethod.ContainingType
 
                If callingMethodType IsNot topLevelMethodType OrElse Me.IsInExpressionLambda Then
                    Dim newMethod = GetOrCreateMyBaseOrMyClassWrapperFunction(receiverOpt, originalMethodBeingCalled)
 
                    ' substitute type parameters if needed
                    If newMethod.IsGenericMethod Then
                        Debug.Assert(originalMethodBeingCalled.IsGenericMethod)
 
                        Dim typeArgs = originalMethodBeingCalled.TypeArguments
                        Debug.Assert(typeArgs.Length = newMethod.Arity)
 
                        Dim visitedTypeArgs(typeArgs.Length - 1) As TypeSymbol
                        For i = 0 To typeArgs.Length - 1
                            visitedTypeArgs(i) = VisitType(typeArgs(i))
                        Next
                        newMethod = newMethod.Construct(visitedTypeArgs.AsImmutableOrNull())
                    End If
 
                    Return newMethod
                End If
 
            End If
            Return originalMethodBeingCalled
        End Function
 
        Private Function GetOrCreateMyBaseOrMyClassWrapperFunction(receiver As BoundExpression, method As MethodSymbol) As MethodSymbol
            Debug.Assert(receiver IsNot Nothing)
            Debug.Assert(receiver.IsMyClassReference() OrElse receiver.IsMyBaseReference())
            Debug.Assert(method IsNot Nothing)
 
            method = method.ConstructedFrom()
 
            Dim methodWrapper As MethodSymbol = CompilationState.GetMethodWrapper(method)
            If methodWrapper IsNot Nothing Then
                Return methodWrapper
            End If
 
            ' Disregarding what was passed as the receiver, we only need to create one wrapper 
            ' method for a method _symbol_ being called. Thus, if topLevelMethod.ContainingType
            ' overrides the virtual method M1, 'MyClass.M1' will use a wrapper method for
            ' topLevelMethod.ContainingType.M1 method symbol and 'MyBase.M1' will use
            ' a wrapper method for topLevelMethod.ContainingType.BaseType.M1 method symbol.
            ' In case topLevelMethod.ContainingType DOES NOT override M1, both 'MyClass.M1' and 
            ' 'MyBase.M1' will use a wrapper method for topLevelMethod.ContainingType.BaseType.M1.
 
            Dim containingType As NamedTypeSymbol = Me.TopLevelMethod.ContainingType
            Dim methodContainingType As NamedTypeSymbol = method.ContainingType
 
            Dim isMyBase As Boolean = Not methodContainingType.Equals(containingType)
            Debug.Assert(isMyBase OrElse receiver.Kind = BoundKind.MyClassReference)
 
            Dim syntax As SyntaxNode = Me.CurrentMethod.Syntax
 
            ' generate and register wrapper method
            Dim wrapperMethodName As String = GeneratedNames.MakeBaseMethodWrapperName(method.Name, isMyBase)
            Dim wrapperMethod As New SynthesizedWrapperMethod(DirectCast(containingType, InstanceTypeSymbol), method, wrapperMethodName, syntax)
 
            ' register a new method
            If Me.CompilationState.ModuleBuilderOpt IsNot Nothing Then
                Me.CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(containingType, wrapperMethod.GetCciAdapter())
            End If
 
            ' generate method body
            Dim wrappedMethod As MethodSymbol = wrapperMethod.WrappedMethod
            Debug.Assert(wrappedMethod.ConstructedFrom() Is method)
 
            Dim boundArguments(wrapperMethod.ParameterCount - 1) As BoundExpression
            For argIndex = 0 To wrapperMethod.ParameterCount - 1
                Dim parameterSymbol As ParameterSymbol = wrapperMethod.Parameters(argIndex)
                boundArguments(argIndex) = New BoundParameter(syntax, parameterSymbol, isLValue:=parameterSymbol.IsByRef, type:=parameterSymbol.Type)
            Next
 
            Dim meParameter As ParameterSymbol = wrapperMethod.MeParameter
            Dim newReceiver As BoundExpression = Nothing
            If isMyBase Then
                newReceiver = New BoundMyBaseReference(syntax, meParameter.Type)
            Else
                newReceiver = New BoundMyClassReference(syntax, meParameter.Type)
            End If
 
            Dim boundCall As New BoundCall(syntax, wrappedMethod, Nothing, newReceiver,
                                           boundArguments.AsImmutableOrNull, Nothing, wrappedMethod.ReturnType)
 
            Dim boundMethodBody As BoundStatement = If(Not wrappedMethod.ReturnType.IsVoidType(),
                                                       DirectCast(New BoundReturnStatement(syntax, boundCall, Nothing, Nothing), BoundStatement),
                                                       New BoundBlock(syntax, Nothing, ImmutableArray(Of LocalSymbol).Empty,
                                                                      ImmutableArray.Create(Of BoundStatement)(
                                                                          New BoundExpressionStatement(syntax, boundCall),
                                                                          New BoundReturnStatement(syntax, Nothing, Nothing, Nothing))))
 
            ' add to generated method collection and return
            CompilationState.AddMethodWrapper(method, wrapperMethod, boundMethodBody)
            Return wrapperMethod
        End Function
 
        ''' <summary>
        ''' A method that wraps the call to a method through MyBase/MyClass receiver.
        ''' </summary>
        ''' <remarks>
        ''' <example>
        ''' Class A
        '''     Protected Overridable Sub F(a As Integer)
        '''     End Sub
        ''' End Class
        ''' 
        ''' Class B
        '''     Inherits A
        ''' 
        '''     Public Sub M()
        '''         Dim b As Integer = 1
        '''         Dim f As System.Action = Sub() MyBase.F(b)
        '''     End Sub
        ''' End Class
        ''' </example>
        ''' </remarks>
        Friend NotInheritable Class SynthesizedWrapperMethod
            Inherits SynthesizedMethod
 
            Private ReadOnly _wrappedMethod As MethodSymbol
            Private ReadOnly _typeMap As TypeSubstitution
            Private ReadOnly _typeParameters As ImmutableArray(Of TypeParameterSymbol)
            Private ReadOnly _parameters As ImmutableArray(Of ParameterSymbol)
            Private ReadOnly _returnType As TypeSymbol
            Private ReadOnly _locations As ImmutableArray(Of Location)
 
            ''' <summary>
            ''' Creates a symbol for a method that wraps the call to a method through MyBase/MyClass receiver.
            ''' </summary>
            ''' <param name="containingType">Type that contains wrapper method.</param>
            ''' <param name="methodToWrap">Method to wrap</param>
            ''' <param name="wrapperName">Wrapper method name</param>
            ''' <param name="syntax">Syntax node.</param>
            Friend Sub New(containingType As InstanceTypeSymbol,
                           methodToWrap As MethodSymbol,
                           wrapperName As String,
                           syntax As SyntaxNode)
 
                MyBase.New(syntax, containingType, wrapperName, False)
 
                Me._locations = ImmutableArray.Create(Of Location)(syntax.GetLocation())
 
                Me._typeMap = Nothing
 
                If Not methodToWrap.IsGenericMethod Then
                    Me._typeParameters = ImmutableArray(Of TypeParameterSymbol).Empty
                    Me._wrappedMethod = methodToWrap
                Else
                    Me._typeParameters = SynthesizedClonedTypeParameterSymbol.MakeTypeParameters(methodToWrap.OriginalDefinition.TypeParameters, Me, CreateTypeParameter)
 
                    Dim typeArgs(Me._typeParameters.Length - 1) As TypeSymbol
                    For ind = 0 To Me._typeParameters.Length - 1
                        typeArgs(ind) = Me._typeParameters(ind)
                    Next
 
                    Dim newConstructedWrappedMethod As MethodSymbol = methodToWrap.Construct(typeArgs.AsImmutableOrNull())
 
                    Me._typeMap = TypeSubstitution.Create(newConstructedWrappedMethod.OriginalDefinition,
                                                           newConstructedWrappedMethod.OriginalDefinition.TypeParameters,
                                                           typeArgs.AsImmutableOrNull())
 
                    Me._wrappedMethod = newConstructedWrappedMethod
                End If
 
                Dim params(Me._wrappedMethod.ParameterCount - 1) As ParameterSymbol
                For i = 0 To params.Length - 1
                    Dim curParam = Me._wrappedMethod.Parameters(i)
                    params(i) = SynthesizedMethod.WithNewContainerAndType(Me, curParam.Type.InternalSubstituteTypeParameters(Me._typeMap).Type, curParam)
                Next
                Me._parameters = params.AsImmutableOrNull()
 
                Me._returnType = Me._wrappedMethod.ReturnType.InternalSubstituteTypeParameters(Me._typeMap).Type
            End Sub
 
            Friend Overrides ReadOnly Property TypeMap As TypeSubstitution
                Get
                    Return Me._typeMap
                End Get
            End Property
 
            Friend Overrides Sub AddSynthesizedAttributes(moduleBuilder As PEModuleBuilder, ByRef attributes As ArrayBuilder(Of SynthesizedAttributeData))
                MyBase.AddSynthesizedAttributes(moduleBuilder, attributes)
 
                Dim compilation = Me.DeclaringCompilation
 
                AddSynthesizedAttribute(attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor))
 
                ' Dev11 emits DebuggerNonUserCode. We emit DebuggerHidden to hide the method even if JustMyCode is off.
                AddSynthesizedAttribute(attributes, compilation.SynthesizeDebuggerHiddenAttribute())
            End Sub
 
            Public ReadOnly Property WrappedMethod As MethodSymbol
                Get
                    Return Me._wrappedMethod
                End Get
            End Property
 
            Public Overrides ReadOnly Property TypeParameters As ImmutableArray(Of TypeParameterSymbol)
                Get
                    Return _typeParameters
                End Get
            End Property
 
            Public Overrides ReadOnly Property TypeArguments As ImmutableArray(Of TypeSymbol)
                Get
                    ' This is always a method definition, so the type arguments are the same as the type parameters.
                    If Arity > 0 Then
                        Return StaticCast(Of TypeSymbol).From(Me.TypeParameters)
                    Else
                        Return ImmutableArray(Of TypeSymbol).Empty
                    End If
                End Get
            End Property
 
            Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
                Get
                    Return _locations
                End Get
            End Property
 
            Public Overrides ReadOnly Property Parameters As ImmutableArray(Of ParameterSymbol)
                Get
                    Return _parameters
                End Get
            End Property
 
            Public Overrides ReadOnly Property ReturnType As TypeSymbol
                Get
                    Return _returnType
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsShared As Boolean
                Get
                    Return False
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsSub As Boolean
                Get
                    Return Me._wrappedMethod.IsSub
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsVararg As Boolean
                Get
                    Return Me._wrappedMethod.IsVararg
                End Get
            End Property
 
            Public Overrides ReadOnly Property Arity As Integer
                Get
                    Return _typeParameters.Length
                End Get
            End Property
 
            Public Overrides ReadOnly Property DeclaredAccessibility As Accessibility
                Get
                    Return Accessibility.Private
                End Get
            End Property
 
            Friend Overrides ReadOnly Property ParameterCount As Integer
                Get
                    Return Me._parameters.Length
                End Get
            End Property
 
            Friend Overrides ReadOnly Property HasSpecialName As Boolean
                Get
                    Return False
                End Get
            End Property
 
            Friend Overrides Function IsMetadataNewSlot(Optional ignoreInterfaceImplementationChanges As Boolean = False) As Boolean
                Return False
            End Function
 
            Friend Overrides ReadOnly Property GenerateDebugInfoImpl As Boolean
                Get
                    Return False
                End Get
            End Property
 
            Friend Overrides Function CalculateLocalSyntaxOffset(localPosition As Integer, localTree As SyntaxTree) As Integer
                Throw ExceptionUtilities.Unreachable
            End Function
        End Class
    End Class
End Namespace