/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb
2 388 строк
111 KB
AlekseyTs
VB: EmitDelegateCreation - account for the fact that static methods can be marked virtual in metadata (#79697)
31 июл 2025, 20:05
Не верифицирован
31 июл 2025, 20:05
981ac6e
Код
Авторство
О чём код?
' 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.Collections.Immutable Imports System.Reflection.Metadata Imports System.Runtime.CompilerServices Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.CodeGen Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen Partial Friend Class CodeGenerator Private _recursionDepth As Integer Private Class EmitCancelledException Inherits Exception End Class Private Enum UseKind Unused UsedAsValue UsedAsAddress End Enum Private Sub EmitExpression(expression As BoundExpression, used As Boolean) If expression Is Nothing Then Return End If Dim constantValue = expression.ConstantValueOpt If constantValue IsNot Nothing Then If Not used Then ' unused constants have no side-effects. Return End If If constantValue.IsDecimal OrElse constantValue.IsDateTime Then ' Decimal/DateTime literal fields like Decimal.One should be emitted as fields. Debug.Assert(expression.Kind = BoundKind.FieldAccess) Else EmitConstantExpression(expression.Type, constantValue, used, expression.Syntax) Return End If End If _recursionDepth += 1 If _recursionDepth > 1 Then StackGuard.EnsureSufficientExecutionStack(_recursionDepth) EmitExpressionCore(expression, used) Else EmitExpressionCoreWithStackGuard(expression, used) End If _recursionDepth -= 1 End Sub Private Sub EmitExpressionCoreWithStackGuard(expression As BoundExpression, used As Boolean) Debug.Assert(_recursionDepth = 1) Try EmitExpressionCore(expression, used) Debug.Assert(_recursionDepth = 1) Catch ex As InsufficientExecutionStackException _diagnostics.Add(ERRID.ERR_TooLongOrComplexExpression, BoundTreeVisitor.CancelledByStackGuardException.GetTooLongOrComplexExpressionErrorLocation(expression)) Throw New EmitCancelledException() End Try End Sub Private Sub EmitExpressionCore(expression As BoundExpression, used As Boolean) Select Case expression.Kind Case BoundKind.AssignmentOperator EmitAssignmentExpression(DirectCast(expression, BoundAssignmentOperator), used) Case BoundKind.Call EmitCallExpression(DirectCast(expression, BoundCall), If(used, UseKind.UsedAsValue, UseKind.Unused)) Case BoundKind.TernaryConditionalExpression EmitTernaryConditionalExpression(DirectCast(expression, BoundTernaryConditionalExpression), used) Case BoundKind.BinaryConditionalExpression EmitBinaryConditionalExpression(DirectCast(expression, BoundBinaryConditionalExpression), used) Case BoundKind.ObjectCreationExpression EmitObjectCreationExpression(DirectCast(expression, BoundObjectCreationExpression), used) Case BoundKind.ArrayCreation EmitArrayCreationExpression(DirectCast(expression, BoundArrayCreation), used) Case BoundKind.ArrayLength EmitArrayLengthExpression(DirectCast(expression, BoundArrayLength), used) Case BoundKind.Conversion EmitConversionExpression(DirectCast(expression, BoundConversion), used) Case BoundKind.DirectCast EmitDirectCastExpression(DirectCast(expression, BoundDirectCast), used) Case BoundKind.TryCast EmitTryCastExpression(DirectCast(expression, BoundTryCast), used) Case BoundKind.TypeOf EmitTypeOfExpression(DirectCast(expression, BoundTypeOf), used) Case BoundKind.Local EmitLocalLoad(DirectCast(expression, BoundLocal), used) Case BoundKind.Parameter If used Then ' unused parameter has no side-effects EmitParameterLoad(DirectCast(expression, BoundParameter)) End If Case BoundKind.Dup EmitDupExpression(DirectCast(expression, BoundDup), used) Case BoundKind.FieldAccess EmitFieldLoad(DirectCast(expression, BoundFieldAccess), used) Case BoundKind.ArrayAccess EmitArrayElementLoad(DirectCast(expression, BoundArrayAccess), used) Case BoundKind.MeReference, BoundKind.MyClassReference If used Then ' unused Me/MyClass has no side-effects EmitMeOrMyClassReferenceExpression(expression) End If Case BoundKind.MyBaseReference If used Then ' unused base has no side-effects _builder.EmitOpCode(ILOpCode.Ldarg_0) End If Case BoundKind.Sequence EmitSequenceExpression(DirectCast(expression, BoundSequence), used) Case BoundKind.SequencePointExpression EmitSequencePointExpression(DirectCast(expression, BoundSequencePointExpression), used) Case BoundKind.UnaryOperator EmitUnaryOperatorExpression(DirectCast(expression, BoundUnaryOperator), used) Case BoundKind.BinaryOperator EmitBinaryOperatorExpression(DirectCast(expression, BoundBinaryOperator), used) Case BoundKind.DelegateCreationExpression EmitDelegateCreationExpression(DirectCast(expression, BoundDelegateCreationExpression), used) Case BoundKind.GetType EmitGetType(DirectCast(expression, BoundGetType), used) Case BoundKind.FieldInfo EmitFieldInfoExpression(DirectCast(expression, BoundFieldInfo), used) Case BoundKind.MethodInfo EmitMethodInfoExpression(DirectCast(expression, BoundMethodInfo), used) Case BoundKind.ReferenceAssignment EmitReferenceAssignment(DirectCast(expression, BoundReferenceAssignment), used) Case BoundKind.ValueTypeMeReference ' We want to restrict the usage of BoundValueTypeMeReference to the very minimum possible, ' which is to be able to pass Me reference of value type as ByRef argument in compiler ' generated code, this is why we specifically prohibit emitting this as value Throw ExceptionUtilities.UnexpectedValue(expression.Kind) Case BoundKind.LoweredConditionalAccess EmitConditionalAccess(DirectCast(expression, BoundLoweredConditionalAccess), used) Case BoundKind.ConditionalAccessReceiverPlaceholder EmitConditionalAccessReceiverPlaceholder(DirectCast(expression, BoundConditionalAccessReceiverPlaceholder), used) Case BoundKind.ComplexConditionalAccessReceiver EmitComplexConditionalAccessReceiver(DirectCast(expression, BoundComplexConditionalAccessReceiver), used) Case BoundKind.PseudoVariable EmitPseudoVariableValue(DirectCast(expression, BoundPseudoVariable), used) Case BoundKind.ModuleVersionId Debug.Assert(used) EmitModuleVersionIdLoad(DirectCast(expression, BoundModuleVersionId)) Case BoundKind.ModuleVersionIdString Debug.Assert(used) EmitModuleVersionIdStringLoad(DirectCast(expression, BoundModuleVersionIdString)) Case BoundKind.InstrumentationPayloadRoot Debug.Assert(used) EmitInstrumentationPayloadRootLoad(DirectCast(expression, BoundInstrumentationPayloadRoot)) Case BoundKind.MethodDefIndex Debug.Assert(used) EmitMethodDefIndexExpression(DirectCast(expression, BoundMethodDefIndex)) Case BoundKind.MaximumMethodDefIndex Debug.Assert(used) EmitMaximumMethodDefIndexExpression(DirectCast(expression, BoundMaximumMethodDefIndex)) Case BoundKind.SourceDocumentIndex Debug.Assert(used) EmitSourceDocumentIndex(DirectCast(expression, BoundSourceDocumentIndex)) Case Else ' Code gen should not be invoked if there are errors. ' Debug.Assert(expression.Kind <> BoundKind.BadExpression AndAlso expression.Kind <> BoundKind.Parenthesized) Throw ExceptionUtilities.UnexpectedValue(expression.Kind) Return End Select End Sub Private Sub EmitConditionalAccessReceiverPlaceholder(expression As BoundConditionalAccessReceiverPlaceholder, used As Boolean) Debug.Assert(Not expression.Type.IsValueType) If used AndAlso Not expression.Type.IsReferenceType Then EmitLoadIndirect(expression.Type, expression.Syntax) End If EmitPopIfUnused(used) End Sub Private Sub EmitComplexConditionalAccessReceiver(expression As BoundComplexConditionalAccessReceiver, used As Boolean) Debug.Assert(Not expression.Type.IsReferenceType) Debug.Assert(Not expression.Type.IsValueType) Dim receiverType = expression.Type Dim whenValueTypeLabel As New Object() Dim doneLabel As New Object() EmitInitObj(receiverType, True, expression.Syntax) EmitBox(receiverType, expression.Syntax) _builder.EmitBranch(ILOpCode.Brtrue, whenValueTypeLabel) EmitExpression(expression.ReferenceTypeReceiver, used) _builder.EmitBranch(ILOpCode.Br, doneLabel) _builder.AdjustStack(-1) _builder.MarkLabel(whenValueTypeLabel) EmitExpression(expression.ValueTypeReceiver, used) _builder.MarkLabel(doneLabel) End Sub Private Sub EmitConditionalAccess(conditional As BoundLoweredConditionalAccess, used As Boolean) Debug.Assert(conditional.WhenNullOpt IsNot Nothing OrElse Not used) Dim receiverTemp As LocalDefinition = Nothing Dim receiver As BoundExpression = conditional.Receiver Dim receiverType As TypeSymbol = receiver.Type If receiverType.IsNullableType() Then If conditional.CaptureReceiver Then EmitExpression(receiver, used:=True) receiverTemp = AllocateTemp(receiverType, receiver.Syntax) _builder.EmitLocalStore(receiverTemp) _builder.EmitLocalAddress(receiverTemp) Else receiverTemp = EmitAddress(receiver, addressKind:=AddressKind.Immutable) End If Dim doneLabel = New Object() Dim consequenceLabel = New Object() Dim hasValue = DirectCast(Me._module.Compilation.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_HasValue), MethodSymbol) Debug.Assert(hasValue IsNot Nothing) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=0) EmitSymbolToken(hasValue.AsMember(DirectCast(receiverType, NamedTypeSymbol)), conditional.Syntax) _builder.EmitBranch(ILOpCode.Brtrue, consequenceLabel) If conditional.WhenNullOpt IsNot Nothing Then EmitExpression(conditional.WhenNullOpt, used) Else Debug.Assert(Not used) End If _builder.EmitBranch(ILOpCode.Br, doneLabel) If used Then ' If we get to consequenceLabel, we should not have WhenFalse on stack, adjust for that. _builder.AdjustStack(-1) End If _builder.MarkLabel(consequenceLabel) If receiverTemp Is Nothing Then receiverTemp = EmitAddress(receiver, addressKind:=AddressKind.Immutable) Debug.Assert(receiverTemp Is Nothing) Else _builder.EmitLocalAddress(receiverTemp) End If EmitExpression(conditional.WhenNotNull, used) _builder.MarkLabel(doneLabel) Else Debug.Assert(Not receiverType.IsValueType) Dim temp As LocalDefinition = Nothing ' labels Dim whenNotNullLabel As New Object() Dim doneLabel As New Object() ' we need a copy if we deal with nonlocal value (to capture the value) ' Or if we have a ref-constrained T (to do box just once) Debug.Assert(Not (Not receiverType.IsReferenceType AndAlso Not receiverType.IsValueType AndAlso Not DirectCast(receiverType, TypeParameterSymbol).HasInterfaceConstraint) OrElse ' This could be a nullable value type, which must be copied in order to not mutate the original value (receiverType.IsReferenceType AndAlso receiverType.TypeKind = TypeKind.TypeParameter) OrElse conditional.CaptureReceiver) Dim nullCheckOnCopy = conditional.CaptureReceiver If nullCheckOnCopy Then receiverTemp = EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly) If Not receiverType.IsReferenceType Then If receiverTemp Is Nothing Then ' unconstrained case needs to handle case where T is actually a struct. ' such values are never nulls ' we will emit a check for such case, but the check is really a JIT-time ' constant since JIT will know if T is a struct or not. ' ' if ((object)default(T) != null) ' { ' goto whenNotNull ' } ' else ' { ' temp = receiverRef ' receiverRef = ref temp ' } EmitInitObj(receiverType, True, receiver.Syntax) EmitBox(receiverType, receiver.Syntax) _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel) EmitLoadIndirect(receiverType, receiver.Syntax) temp = AllocateTemp(receiverType, receiver.Syntax) _builder.EmitLocalStore(temp) _builder.EmitLocalAddress(temp) _builder.EmitLocalLoad(temp) EmitBox(receiverType, receiver.Syntax) ' here we have loaded a ref to a temp And its boxed value { &T, O } Else ' We are calling the expression on a copy of the target anyway, ' so even if T is a struct, we don't need to make sure we call the expression on the original target. ' We currently have an address on the stack. Duplicate it, and load the value of the address. _builder.EmitOpCode(ILOpCode.Dup) EmitLoadIndirect(receiverType, receiver.Syntax) EmitBox(receiverType, receiver.Syntax) End If Else _builder.EmitOpCode(ILOpCode.Dup) ' here we have loaded two copies of a reference { O, O } End If Else EmitExpression(receiver, True) If Not receiverType.IsReferenceType Then EmitBox(receiverType, receiver.Syntax) End If ' here we have loaded just { O } ' we have the most trivial case where we can just reload O when needed End If _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel) If nullCheckOnCopy Then _builder.EmitOpCode(ILOpCode.Pop) End If If conditional.WhenNullOpt IsNot Nothing Then EmitExpression(conditional.WhenNullOpt, used) Else Debug.Assert(Not used) End If _builder.EmitBranch(ILOpCode.Br, doneLabel) If used Then ' If we get to whenNotNullLabel, we should not have WhenNullOpt on stack, adjust for that. _builder.AdjustStack(-1) End If If nullCheckOnCopy Then ' whenNull branch pops copy of the receiver off the stack when nullCheckOnCopy ' however on this branch we still have the stack as it was and need ' to adjust stack depth accordingly. _builder.AdjustStack(+1) End If _builder.MarkLabel(whenNotNullLabel) If Not nullCheckOnCopy Then Debug.Assert(receiverTemp Is Nothing) receiverTemp = EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly) Debug.Assert(receiverTemp Is Nothing OrElse receiver.IsDefaultValue()) End If EmitExpression(conditional.WhenNotNull, used) _builder.MarkLabel(doneLabel) If temp IsNot Nothing Then FreeTemp(temp) End If End If If receiverTemp IsNot Nothing Then FreeTemp(receiverTemp) End If End Sub Private Sub EmitComplexConditionalAccessReceiverAddress(expression As BoundComplexConditionalAccessReceiver) Debug.Assert(Not expression.Type.IsReferenceType) Debug.Assert(Not expression.Type.IsValueType) Dim receiverType = expression.Type Dim whenValueTypeLabel As New Object() Dim doneLabel As New Object() EmitInitObj(receiverType, True, expression.Syntax) EmitBox(receiverType, expression.Syntax) _builder.EmitBranch(ILOpCode.Brtrue, whenValueTypeLabel) Dim receiverTemp = EmitAddress(expression.ReferenceTypeReceiver, addressKind:=AddressKind.ReadOnly) Debug.Assert(receiverTemp Is Nothing) _builder.EmitBranch(ILOpCode.Br, doneLabel) _builder.AdjustStack(-1) _builder.MarkLabel(whenValueTypeLabel) EmitReceiverRef(expression.ValueTypeReceiver, isAccessConstrained:=True, addressKind:=AddressKind.ReadOnly) _builder.MarkLabel(doneLabel) End Sub Private Sub EmitDelegateCreationExpression(expression As BoundDelegateCreationExpression, used As Boolean) Dim invoke = DirectCast(expression.Method, MethodSymbol) EmitDelegateCreation(expression.ReceiverOpt, invoke, expression.Type, used, expression.Syntax) End Sub Private Sub EmitLocalLoad(local As BoundLocal, used As Boolean) If IsStackLocal(local.LocalSymbol) Then ' local should be already on the stack EmitPopIfUnused(used) Else If used Then ' unused local has no side-effects _builder.EmitLocalLoad(GetLocal(local)) Else ' do nothing. Unused local load has no side-effects. Return End If End If If used AndAlso local.LocalSymbol.IsByRef Then EmitLoadIndirect(local.Type, local.Syntax) End If End Sub Private Sub EmitDelegateCreation(receiver As BoundExpression, method As MethodSymbol, delegateType As TypeSymbol, used As Boolean, syntaxNode As SyntaxNode) Dim isStatic = receiver Is Nothing OrElse method.IsShared If Not used Then If Not isStatic Then EmitExpression(receiver, False) End If Return End If If isStatic Then _builder.EmitNullConstant() Else EmitExpression(receiver, True) If Not IsVerifierReference(receiver.Type) Then EmitBox(receiver.Type, receiver.Syntax) End If End If ' Metadata Spec (II.14.6): ' Delegates shall be declared sealed. ' The Invoke method shall be virtual. ' Dev11 VB uses ldvirtftn for delegate methods, we emit ldftn to be consistent with C#. If Not method.IsShared AndAlso method.IsMetadataVirtual AndAlso Not method.ContainingType.IsDelegateType() AndAlso Not receiver.SuppressVirtualCalls Then _builder.EmitOpCode(ILOpCode.Dup) _builder.EmitOpCode(ILOpCode.Ldvirtftn) Else _builder.EmitOpCode(ILOpCode.Ldftn) End If Dim targetMethod = If(method.CallsiteReducedFromMethod, method) If Not isStatic AndAlso targetMethod.ContainingType.IsNullableType Then Debug.Assert(targetMethod.IsOverrides, "Nullable cannot be truly boxed therefore delegates of methods that do not override cannot be created") targetMethod = method.OverriddenMethod End If EmitSymbolToken(targetMethod, syntaxNode) ' TODO: check the ctor signature, and recover more gracefully from failure to ' find a single constructor with the correct signature Dim ctor = DirectCast(delegateType.GetMembers(WellKnownMemberNames.InstanceConstructorName).Single(), MethodSymbol) _builder.EmitOpCode(ILOpCode.Newobj, -1) EmitSymbolToken(ctor, syntaxNode) End Sub Private Sub EmitMeOrMyClassReferenceExpression(thisRef As BoundExpression) Debug.Assert(thisRef.Kind = BoundKind.MeReference OrElse thisRef.Kind = BoundKind.MyClassReference) Dim thisType = thisRef.Type Debug.Assert(thisType.TypeKind <> TypeKind.TypeParameter) _builder.EmitOpCode(ILOpCode.Ldarg_0) If thisType.IsValueType Then _builder.EmitOpCode(ILOpCode.Ldobj) EmitSymbolToken(thisRef.Type, thisRef.Syntax) End If End Sub Private Sub EmitPseudoVariableValue(expression As BoundPseudoVariable, used As Boolean) EmitExpression(expression.EmitExpressions.GetValue(expression, _diagnostics), used) End Sub Private Sub EmitSequenceExpression(sequence As BoundSequence, used As Boolean) Dim hasLocals As Boolean = Not sequence.Locals.IsEmpty If hasLocals Then _builder.OpenLocalScope() For Each local In sequence.Locals Me.DefineLocal(local, sequence.Syntax) Next End If Me.EmitSideEffects(sequence.SideEffects) Debug.Assert(sequence.ValueOpt IsNot Nothing OrElse sequence.Type.SpecialType = SpecialType.System_Void) Me.EmitExpression(sequence.ValueOpt, used) If hasLocals Then _builder.CloseLocalScope() For Each local In sequence.Locals Me.FreeLocal(local) Next End If End Sub Private Sub EmitSideEffects(sideEffects As ImmutableArray(Of BoundExpression)) If Not sideEffects.IsDefaultOrEmpty Then For Each se In sideEffects EmitExpression(se, False) Next End If End Sub Private Sub EmitExpressions(expressions As ImmutableArray(Of BoundExpression), used As Boolean) Dim i As Integer = 0 While i < expressions.Length Dim expression = expressions(i) EmitExpression(expression, used) i = i + 1 End While End Sub Private Sub EmitArguments(arguments As ImmutableArray(Of BoundExpression), parameters As ImmutableArray(Of ParameterSymbol)) Debug.Assert(arguments.Length = parameters.Length) For i = 0 To arguments.Length - 1 Dim argument = arguments(i) Dim parameter = parameters(i) If parameter.IsByRef Then Dim temp = EmitAddress(argument, AddressKind.Writeable) Debug.Assert(temp Is Nothing, "passing args byref should not clone them into temps. That should be done in rewriter.") Else EmitExpression(argument, True) End If Next End Sub Private Sub EmitArrayElementLoad(arrayAccess As BoundArrayAccess, used As Boolean) EmitExpression(arrayAccess.Expression, True) EmitExpressions(arrayAccess.Indices, True) If DirectCast(arrayAccess.Expression.Type, ArrayTypeSymbol).IsSZArray Then Dim elementType = arrayAccess.Type If elementType.IsEnumType() Then elementType = (DirectCast(elementType, NamedTypeSymbol)).EnumUnderlyingType End If Select Case elementType.PrimitiveTypeCode Case Microsoft.Cci.PrimitiveTypeCode.Int8 _builder.EmitOpCode(ILOpCode.Ldelem_i1) Case Microsoft.Cci.PrimitiveTypeCode.Boolean, Microsoft.Cci.PrimitiveTypeCode.UInt8 _builder.EmitOpCode(ILOpCode.Ldelem_u1) Case Microsoft.Cci.PrimitiveTypeCode.Int16 _builder.EmitOpCode(ILOpCode.Ldelem_i2) Case Microsoft.Cci.PrimitiveTypeCode.Char, Microsoft.Cci.PrimitiveTypeCode.UInt16 _builder.EmitOpCode(ILOpCode.Ldelem_u2) Case Microsoft.Cci.PrimitiveTypeCode.Int32 _builder.EmitOpCode(ILOpCode.Ldelem_i4) Case Microsoft.Cci.PrimitiveTypeCode.UInt32 _builder.EmitOpCode(ILOpCode.Ldelem_u4) Case Microsoft.Cci.PrimitiveTypeCode.Int64, Microsoft.Cci.PrimitiveTypeCode.UInt64 _builder.EmitOpCode(ILOpCode.Ldelem_i8) Case Microsoft.Cci.PrimitiveTypeCode.IntPtr, Microsoft.Cci.PrimitiveTypeCode.UIntPtr, Microsoft.Cci.PrimitiveTypeCode.Pointer _builder.EmitOpCode(ILOpCode.Ldelem_i) Case Microsoft.Cci.PrimitiveTypeCode.Float32 _builder.EmitOpCode(ILOpCode.Ldelem_r4) Case Microsoft.Cci.PrimitiveTypeCode.Float64 _builder.EmitOpCode(ILOpCode.Ldelem_r8) Case Else If IsVerifierReference(elementType) Then _builder.EmitOpCode(ILOpCode.Ldelem_ref) Else If used Then _builder.EmitOpCode(ILOpCode.Ldelem) Else ' no need to read whole element of nontrivial type/size here ' just take a reference to an element for array access side-effects If elementType.TypeKind = TypeKind.TypeParameter Then _builder.EmitOpCode(ILOpCode.Readonly) End If _builder.EmitOpCode(ILOpCode.Ldelema) End If EmitSymbolToken(elementType, arrayAccess.Expression.Syntax) End If End Select Else _builder.EmitArrayElementLoad(_module.Translate(DirectCast(arrayAccess.Expression.Type, ArrayTypeSymbol)), arrayAccess.Expression.Syntax) End If EmitPopIfUnused(used) End Sub Private Sub EmitFieldLoad(fieldAccess As BoundFieldAccess, used As Boolean) Dim field = fieldAccess.FieldSymbol 'TODO: For static field access this may require ..ctor to run. Is this a side-effect? ' Accessing unused instance field on a struct is a noop. Just emit the receiver. If Not used AndAlso Not field.IsShared AndAlso fieldAccess.ReceiverOpt.Type.IsVerifierValue() Then EmitExpression(fieldAccess.ReceiverOpt, used:=False) Return End If Dim specType = field.Type.SpecialType If field.IsConst AndAlso specType <> SpecialType.System_Decimal AndAlso specType <> SpecialType.System_DateTime Then ' constant fields are not really fields and should not get here Throw ExceptionUtilities.Unreachable Else If field.IsShared Then EmitStaticFieldLoad(field, used, fieldAccess.Syntax) Else EmitInstanceFieldLoad(fieldAccess, used) End If End If End Sub Private Sub EmitDupExpression(dupExpression As BoundDup, used As Boolean) If Not dupExpression.IsReference Then ' unused dup is noop If used Then _builder.EmitOpCode(ILOpCode.Dup) End If Else _builder.EmitOpCode(ILOpCode.Dup) ' must read in case if it is a null ref EmitLoadIndirect(dupExpression.Type, dupExpression.Syntax) EmitPopIfUnused(used) End If End Sub Private Sub EmitStaticFieldLoad(field As FieldSymbol, used As Boolean, syntaxNode As SyntaxNode) 'TODO: this may require ..ctor to run. Is this a side-effect? _builder.EmitOpCode(ILOpCode.Ldsfld) EmitSymbolToken(field, syntaxNode) EmitPopIfUnused(used) End Sub Private Sub EmitInstanceFieldLoad(fieldAccess As BoundFieldAccess, used As Boolean) 'TODO: access to a field on this/base has no side-effects. Dim field As FieldSymbol = fieldAccess.FieldSymbol Dim receiver = fieldAccess.ReceiverOpt 'ldfld can work with structs directly (taking address is optional) 'taking address is typically cheaper, but not for homeless exprs 'those would need to be loaded anyways. If FieldLoadMustUseRef(receiver) OrElse FieldLoadPrefersRef(receiver) Then If Not EmitFieldLoadReceiverAddress(receiver) Then ' Since we are simply loading the field value, the receiver reference is not going to be mutated. Dim temp = EmitReceiverRef(receiver, isAccessConstrained:=False, addressKind:=AddressKind.Immutable) Debug.Assert(temp Is Nothing OrElse receiver.Type.IsEnumType, "temp is unexpected, just reading a field") End If Else EmitExpression(receiver, True) End If _builder.EmitOpCode(ILOpCode.Ldfld) EmitSymbolToken(field, fieldAccess.Syntax) EmitPopIfUnused(used) End Sub ' In special case of loading the sequence of field accesses we can perform all the ' necessary field loads using the following IL: ' ' <expr>.a.b...y.z ' | ' V ' Unbox -or- Load.Ref (<expr>) ' Ldflda a ' Ldflda b ' ... ' Ldflda y ' Ldfld z ' ' Returns 'true' if the receiver was actually emitted this way Private Function EmitFieldLoadReceiverAddress(receiver As BoundExpression) As Boolean If receiver Is Nothing OrElse receiver.Type.IsReferenceType Then Return False ElseIf receiver.Kind = BoundKind.DirectCast AndAlso IsUnboxingDirectCast(DirectCast(receiver, BoundDirectCast)) Then EmitExpression(DirectCast(receiver, BoundDirectCast).Operand, True) Me._builder.EmitOpCode(ILOpCode.Unbox) EmitSymbolToken(receiver.Type, receiver.Syntax) Return True ElseIf receiver.Kind = BoundKind.FieldAccess Then Dim fieldAccess = DirectCast(receiver, BoundFieldAccess) Dim field As FieldSymbol = fieldAccess.FieldSymbol If Not field.IsShared AndAlso EmitFieldLoadReceiverAddress(fieldAccess.ReceiverOpt) Then Me._builder.EmitOpCode(ILOpCode.Ldflda) EmitSymbolToken(field, fieldAccess.Syntax) Return True End If End If Return False End Function ' ldfld can work with structs directly or with their addresses ' In some cases it results in same native code emitted, but in some cases JIT pushes values for real ' resulting in much worse code (on x64 in particular). ' So, we will always prefer references here except when receiver is a struct non-ref local or parameter. Private Function FieldLoadPrefersRef(receiver As BoundExpression) As Boolean ' only fields of structs can be accessed via value If Not receiver.Type.IsVerifierValue() Then Return True End If ' can unbox directly into a ref. If receiver.Kind = BoundKind.DirectCast AndAlso IsUnboxingDirectCast(DirectCast(receiver, BoundDirectCast)) Then Return True End If ' can we take address at all? If Not HasHome(receiver) Then Return False End If Select Case receiver.Kind Case BoundKind.Parameter ' prefer ldarg over ldarga Return DirectCast(receiver, BoundParameter).ParameterSymbol.IsByRef Case BoundKind.Local ' prefer ldloc over ldloca Return DirectCast(receiver, BoundLocal).LocalSymbol.IsByRef Case BoundKind.Sequence Return FieldLoadPrefersRef(DirectCast(receiver, BoundSequence).ValueOpt) Case BoundKind.FieldAccess Dim fieldAccess = DirectCast(receiver, BoundFieldAccess) Return fieldAccess.FieldSymbol.IsShared OrElse FieldLoadPrefersRef(fieldAccess.ReceiverOpt) End Select Return True End Function Friend Shared Function FieldLoadMustUseRef(expr As BoundExpression) As Boolean ' NOTE: we emit a ref when receiver is an Enum. ' that is needed only when accessing value__ of the enum and that cannot be done off the enum value. If expr.Type.IsEnumType Then Return True End If ' type parameter values must be boxed to get access to fields Return expr.Type.IsTypeParameter() End Function Private Function ParameterSlot(parameter As BoundParameter) As Integer Dim sym = parameter.ParameterSymbol Dim slot As Integer = sym.Ordinal If Not sym.ContainingSymbol.IsShared Then slot = slot + 1 ' skip this End If Return slot End Function Private Sub EmitParameterLoad(parameter As BoundParameter) Dim slot As Integer = ParameterSlot(parameter) _builder.EmitLoadArgumentOpcode(slot) If parameter.ParameterSymbol.IsByRef Then Dim parameterType = parameter.ParameterSymbol.Type EmitLoadIndirect(parameterType, parameter.Syntax) End If End Sub Private Sub EmitLoadIndirect(type As TypeSymbol, syntaxNode As SyntaxNode) If type.IsEnumType() Then 'underlying primitives do not need type tokens. type = (DirectCast(type, NamedTypeSymbol)).EnumUnderlyingType End If Select Case type.PrimitiveTypeCode Case Microsoft.Cci.PrimitiveTypeCode.Int8 _builder.EmitOpCode(ILOpCode.Ldind_i1) Case Microsoft.Cci.PrimitiveTypeCode.Boolean, Microsoft.Cci.PrimitiveTypeCode.UInt8 _builder.EmitOpCode(ILOpCode.Ldind_u1) Case Microsoft.Cci.PrimitiveTypeCode.Int16 _builder.EmitOpCode(ILOpCode.Ldind_i2) Case Microsoft.Cci.PrimitiveTypeCode.Char, Microsoft.Cci.PrimitiveTypeCode.UInt16 _builder.EmitOpCode(ILOpCode.Ldind_u2) Case Microsoft.Cci.PrimitiveTypeCode.Int32 _builder.EmitOpCode(ILOpCode.Ldind_i4) Case Microsoft.Cci.PrimitiveTypeCode.UInt32 _builder.EmitOpCode(ILOpCode.Ldind_u4) Case Microsoft.Cci.PrimitiveTypeCode.Int64, Microsoft.Cci.PrimitiveTypeCode.UInt64 _builder.EmitOpCode(ILOpCode.Ldind_i8) Case Microsoft.Cci.PrimitiveTypeCode.IntPtr, Microsoft.Cci.PrimitiveTypeCode.UIntPtr, Microsoft.Cci.PrimitiveTypeCode.Pointer _builder.EmitOpCode(ILOpCode.Ldind_i) Case Microsoft.Cci.PrimitiveTypeCode.Float32 _builder.EmitOpCode(ILOpCode.Ldind_r4) Case Microsoft.Cci.PrimitiveTypeCode.Float64 _builder.EmitOpCode(ILOpCode.Ldind_r8) Case Else If IsVerifierReference(type) Then _builder.EmitOpCode(ILOpCode.Ldind_ref) Else _builder.EmitOpCode(ILOpCode.Ldobj) EmitSymbolToken(type, syntaxNode) End If End Select End Sub ''' <summary> ''' Used to decide if we need to emit call or callvirt. ''' It basically checks if the receiver expression cannot be null, but it is not 100% precise. ''' There are cases where it really can be null, but we do not care. ''' </summary> Private Function CanUseCallOnRefTypeReceiver(receiver As BoundExpression) As Boolean ' It seems none of the ways that could produce a receiver typed as a type param ' can guarantee that it is not null. If receiver.Type.IsTypeParameter() Then Return False End If Debug.Assert(IsVerifierReference(receiver.Type), "this is not a reference") Debug.Assert(receiver.Kind <> BoundKind.MyBaseReference, "MyBase should always use call") Debug.Assert(receiver.Kind <> BoundKind.MyClassReference, "MyClass should always use call") Dim constVal = receiver.ConstantValueOpt If constVal IsNot Nothing Then ' only when this is a constant Nothing, we need a callvirt Return Not constVal.IsNothing End If Select Case receiver.Kind Case BoundKind.ArrayCreation Return True Case BoundKind.ObjectCreationExpression 'NOTE: there are cases involving ProxyAttribute 'where newobj may produce null Return True Case BoundKind.DirectCast Dim convOperand = DirectCast(receiver, BoundDirectCast).Operand If Not IsVerifierReference(convOperand.Type) Then 'this is boxing 'NOTE: it can produce null for Nullable, but any call through that 'will result in null reference exceptions anyways. Return True Else Return CanUseCallOnRefTypeReceiver(convOperand) End If Case BoundKind.MeReference, BoundKind.MyBaseReference, BoundKind.MyClassReference 'NOTE: these actually can be null if called from a different language 'if that has already happen, we will just propagate the behavior. Return True Case BoundKind.DelegateCreationExpression, BoundKind.AddressOfOperator Return True Case BoundKind.Sequence Dim seqValue = DirectCast(receiver, BoundSequence).ValueOpt Return seqValue IsNot Nothing AndAlso CanUseCallOnRefTypeReceiver(seqValue) Case BoundKind.AssignmentOperator Dim rhs = DirectCast(receiver, BoundAssignmentOperator).Right Return CanUseCallOnRefTypeReceiver(rhs) Case BoundKind.GetType Return True Case BoundKind.FieldAccess Return DirectCast(receiver, BoundFieldAccess).FieldSymbol.IsCapturedFrame Case BoundKind.ConditionalAccessReceiverPlaceholder, BoundKind.ComplexConditionalAccessReceiver Return True 'TODO: there must be more non-null cases. End Select Return False End Function ''' <summary> ''' checks if receiver is effectively ldarg.0 ''' </summary> Private Function IsMeReceiver(receiver As BoundExpression) As Boolean Select Case receiver.Kind Case BoundKind.MeReference, BoundKind.MyClassReference Return True Case BoundKind.Sequence Dim seqValue = DirectCast(receiver, BoundSequence).ValueOpt Return IsMeReceiver(seqValue) End Select Return False End Function Private Enum CallKind [Call] CallVirt ConstrainedCallVirt End Enum Private Sub EmitCallExpression([call] As BoundCall, useKind As UseKind) Dim method = [call].Method Dim receiver = [call].ReceiverOpt Debug.Assert([call].MethodGroupOpt Is Nothing) Debug.Assert(Not Me._module.AllowOmissionOfConditionalCalls OrElse Not method.CallsAreOmitted([call].Syntax, [call].SyntaxTree)) ' is this a call to a default struct constructor? ' this happens in struct non-parameterless constructors calling ' Me.New() If method.IsDefaultValueTypeConstructor() Then EmitInitObjOnTarget(receiver) Return End If Dim arguments = [call].Arguments Dim stackBehavior = (If(method.IsSub, 0, 1)) - arguments.Length Dim callKind As CallKind Dim tempOpt As LocalDefinition = Nothing If method.IsShared Then callKind = CallKind.Call Else stackBehavior = stackBehavior - 1 Dim receiverType = receiver.Type If IsVerifierReference(receiverType) Then tempOpt = EmitReceiverRef(receiver, isAccessConstrained:=False, addressKind:=AddressKind.ReadOnly) 'TODO: it is ok to use Call with final methods. ' Dev10 does not do this, but perhaps we should. ' Call/Callvirt is decided in the following order: ' 1) MyBase/MyClass calls must use "call" ' 2) virtual methods must use "callvirt" ' 3) nonvirtual methods use "callvirt" too for the null check semantics. ' 3.a In some cases CanUseCallOnRefTypeReceiver returns true which means that ' null check is unnecessary and we can use "call" If receiver.SuppressVirtualCalls OrElse (Not method.IsMetadataVirtual AndAlso CanUseCallOnRefTypeReceiver(receiver)) Then callKind = CallKind.Call Else callKind = CallKind.CallVirt End If ElseIf IsVerifierValue(receiverType) Then Dim methodContainingType = method.ContainingType If IsVerifierValue(methodContainingType) AndAlso MayUseCallForStructMethod(method) Then ' NOTE: this should be either a method which overrides some abstract method or ' does not override anything (with few exceptions, see MayUseCallForStructMethod); ' otherwise we should not use direct 'call' and must use constrained call; ' calling a method defined in a value type considered a "write" to the target unless the target is "Me" Debug.Assert(TypeSymbol.Equals(receiverType, method.ContainingType, TypeCompareKind.ConsiderEverything)) tempOpt = EmitReceiverRef( receiver, isAccessConstrained:=False, addressKind:=If(IsMeReceiver(receiver), AddressKind.ReadOnly, AddressKind.Writeable)) callKind = CallKind.Call Else If method.IsMetadataVirtual Then ' When calling a method that is virtual in metadata on a struct receiver, ' we use a constrained virtual call. If possible, it will skip boxing. tempOpt = EmitReceiverRef(receiver, isAccessConstrained:=True, addressKind:=AddressKind.ReadOnly) callKind = CallKind.ConstrainedCallVirt ' In case we plan to use 'callvirt' we need to be sure the method we call ' is not the one from the structure, so we take the one we override If IsVerifierValue(methodContainingType) Then ' NOTE: the most overridden method needs to be used to match Dev10 behavior While method.OverriddenMethod IsNot Nothing method = method.OverriddenMethod End While End If Else ' calling a method defined in a base class. EmitExpression(receiver, True) EmitBox(receiverType, receiver.Syntax) callKind = CallKind.Call End If End If Else tempOpt = EmitGenericReceiver([call], callKind) End If End If ' Devirtualizing of calls to effectively sealed methods. If callKind = CallKind.CallVirt AndAlso method.ContainingModule Is Me._method.ContainingModule Then ' NOTE: we check that we call method in same module just to be sure ' that it cannot be recompiled as not final and make our call not verifiable. ' such change by adversarial user would arguably be a compat break, but better be safe... ' In reality we would typically have one method calling another method in the same class (one GetEnumerator calling another). ' Other scenarios are uncommon since base class cannot be sealed and ' referring to a derived type in a different module is not an easy thing to do. If IsMeReceiver(receiver) AndAlso method.ContainingType.IsNotInheritable Then ' special case for target is in a sealed class and "this" receiver. Debug.Assert(receiver.Type.IsVerifierReference()) callKind = CallKind.Call ElseIf method.IsMetadataFinal AndAlso CanUseCallOnRefTypeReceiver(receiver) Then ' special case for calling 'final' virtual method on reference receiver Debug.Assert(receiver.Type.IsVerifierReference()) callKind = CallKind.Call End If End If EmitArguments(arguments, method.Parameters) Select Case callKind Case CallKind.Call _builder.EmitOpCode(ILOpCode.Call, stackBehavior) Case CallKind.CallVirt _builder.EmitOpCode(ILOpCode.Callvirt, stackBehavior) Case CallKind.ConstrainedCallVirt _builder.EmitOpCode(ILOpCode.Constrained) EmitSymbolToken(receiver.Type, receiver.Syntax) _builder.EmitOpCode(ILOpCode.Callvirt, stackBehavior) End Select EmitSymbolToken(method, [call].Syntax) If Not method.IsSub Then EmitPopIfUnused(useKind <> UseKind.Unused) ElseIf _ilEmitStyle = ILEmitStyle.Debug Then Debug.Assert(useKind = UseKind.Unused, "Using the return value of a void method.") Debug.Assert(_method.GenerateDebugInfo, "Implied by emitSequencePoints") ' DevDiv #15135. When a method like System.Diagnostics.Debugger.Break() is called, the ' debugger sees an event indicating that a user break (vs a breakpoint) has occurred. ' When this happens, it uses ICorDebugILFrame.GetIP(out uint, out CorDebugMappingResult) ' to determine the current instruction pointer. This method returns the instruction ' *after* the call. The source location is then given by the last sequence point before ' or on this instruction. As a result, if the instruction after the call has its own ' sequence point, then that sequence point will be used to determine the source location ' and the debugging experience will be disrupted. The easiest way to ensure that the next ' instruction does not have a sequence point is to insert a nop. Obviously, we only do this ' if debugging is enabled and optimization is disabled. ' From CodeGenerator::GenerateCall: ' The IP always points to the location where execution will return ' So we generate a NOP here so that the IP is still associated with ' the call statement and the user can double click in the callstack ' window and take him to the calling line. ' CONSIDER: The native compiler does not appear to consider whether we are optimizing or emitting debug info. ' CONSIDER: The native compiler also checks !(tree->flags & EXF_NODEBUGINFO). We don't have ' this mutable bit on our bound nodes, so we can't exactly match the behavior. We might be ' able to approximate the native behavior by inspecting call.WasCompilerGenerated, but it is ' Not in a reliable state after lowering. _builder.EmitOpCode(ILOpCode.Nop) End If If useKind = UseKind.UsedAsValue AndAlso method.ReturnsByRef Then EmitLoadIndirect(method.ReturnType, [call].Syntax) ElseIf useKind = UseKind.UsedAsAddress Then Debug.Assert(method.ReturnsByRef) End If FreeOptTemp(tempOpt) ' Dev10 #850039 Check if we must disable inlining and optimization for the enclosing proc. If Me._checkCallsForUnsafeJITOptimization AndAlso method.IsDefinition Then Dim disableJITOptimization As Boolean = False If method.ContainingSymbol Is Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_ErrObject) Then If String.Equals(method.Name, "Raise", StringComparison.Ordinal) Then disableJITOptimization = True End If ElseIf method.ContainingSymbol Is Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_CompilerServices_ProjectData) Then If String.Equals(method.Name, "EndApp", StringComparison.Ordinal) Then disableJITOptimization = True End If ElseIf method.ContainingSymbol Is Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_ApplicationServices_ApplicationBase) Then If String.Equals(method.Name, "Info", StringComparison.Ordinal) Then disableJITOptimization = True End If ElseIf method.ContainingSymbol Is Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_ApplicationServices_WindowsFormsApplicationBase) Then If String.Equals(method.Name, "Run", StringComparison.Ordinal) Then disableJITOptimization = True End If ElseIf method.ContainingSymbol Is Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_FileSystem) Then Select Case method.Name Case "Dir", "EOF", "FileAttr", "FileClose", "FileCopy", "FileGet", "FileGetObject", "FileOpen", "FilePut", "FilePutObject", "FileWidth", "FreeFile", "Input", "InputString", "Kill", "LineInput", "Loc", "Lock", "LOF", "Print", "PrintLine", "Rename", "Reset", "Seek", "SetAttr", "Unlock", "Write", "WriteLine" disableJITOptimization = True End Select End If If disableJITOptimization Then Me._checkCallsForUnsafeJITOptimization = False Me._module.SetDisableJITOptimization(Me._method) End If End If End Sub <MethodImpl(MethodImplOptions.NoInlining)> Private Function EmitGenericReceiver([call] As BoundCall, <Out> ByRef callKind As CallKind) As LocalDefinition Dim receiver = [call].ReceiverOpt Dim receiverType = receiver.Type ' receiver is generic and method must come from the base or an interface or a generic constraint ' if the receiver is actually a value type it would need to be boxed. ' let .constrained sort this out. Debug.Assert(Not receiverType.IsReferenceType OrElse receiver.Kind <> BoundKind.ComplexConditionalAccessReceiver) callKind = If(receiverType.IsReferenceType AndAlso (receiver.Kind = BoundKind.ConditionalAccessReceiverPlaceholder OrElse Not AllowedToTakeRef(receiver, AddressKind.ReadOnly) OrElse (Not ReceiverIsKnownToReferToTempIfReferenceType(receiver) AndAlso Not IsSafeToDereferenceReceiverRefAfterEvaluatingArguments([call].Arguments))), CallKind.CallVirt, CallKind.ConstrainedCallVirt) Dim tempOpt As LocalDefinition = EmitReceiverRef(receiver, isAccessConstrained:=callKind = CallKind.ConstrainedCallVirt, addressKind:=AddressKind.ReadOnly) If callKind = CallKind.ConstrainedCallVirt AndAlso tempOpt Is Nothing AndAlso Not receiverType.IsValueType AndAlso Not ReceiverIsKnownToReferToTempIfReferenceType(receiver) AndAlso Not IsSafeToDereferenceReceiverRefAfterEvaluatingArguments([call].Arguments) Then ' A case where T is actually a class must be handled specially. ' Taking a reference to a class instance is fragile because the value behind the ' reference might change while arguments are evaluated. However, the call should be ' performed on the instance that is behind reference at the time we push the ' reference to the stack. So, for a class we need to emit a reference to a temporary ' location, rather than to the original location ' ' Struct values are never nulls. ' We will emit a check for such case, but the check is really a JIT-time ' constant since JIT will know if T is a struct or not. ' ' if ((object)default(T) == null) ' { ' temp = receiverRef ' receiverRef = ref temp ' } Dim whenNotNullLabel As Object = Nothing If Not receiverType.IsReferenceType Then ' if ((object)default(T) == null) EmitInitObj(receiverType, True, receiver.Syntax) EmitBox(receiverType, receiver.Syntax) whenNotNullLabel = New Object() _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel) End If ' temp = receiverRef ' receiverRef = ref temp EmitLoadIndirect(receiverType, receiver.Syntax) tempOpt = AllocateTemp(receiverType, receiver.Syntax) _builder.EmitLocalStore(tempOpt) _builder.EmitLocalAddress(tempOpt) If whenNotNullLabel IsNot Nothing Then _builder.MarkLabel(whenNotNullLabel) End If End If Return tempOpt End Function Friend Shared Function IsPossibleReferenceTypeReceiverOfConstrainedCall(receiver As BoundExpression) As Boolean Dim receiverType = receiver.Type If receiverType.IsVerifierReference() OrElse receiverType.IsVerifierValue() Then Return False End If Return Not receiverType.IsValueType End Function Friend Shared Function ReceiverIsKnownToReferToTempIfReferenceType(receiver As BoundExpression) As Boolean Dim sequence = TryCast(receiver, BoundSequence) While sequence IsNot Nothing receiver = sequence.ValueOpt sequence = TryCast(receiver, BoundSequence) End While If TypeOf receiver Is BoundComplexConditionalAccessReceiver Then Return True End If Dim placeholder = TryCast(receiver, BoundConditionalAccessReceiverPlaceholder) If placeholder IsNot Nothing AndAlso placeholder.Capture Then Return True End If Return False End Function Friend Shared Function IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(arguments As ImmutableArray(Of BoundExpression)) As Boolean Return arguments.All(Function(a) If a.ConstantValueOpt IsNot Nothing Then Return True End If Select Case a.Kind Case BoundKind.Local, BoundKind.Parameter, BoundKind.MeReference, BoundKind.MyBaseReference, BoundKind.MyClassReference Return True End Select Return False End Function) End Function ''' <summary> ''' Used to decide if we need to emit 'call' or 'callvirt' for structure method. ''' It basically checks if the method overrides any other and method's defining type ''' is not a 'special' or 'special-by-ref' type. ''' </summary> Private Function MayUseCallForStructMethod(method As MethodSymbol) As Boolean Debug.Assert(IsVerifierValue(method.ContainingType), "this is not a value type") If Not method.IsMetadataVirtual Then Return True End If Dim overriddenMethod = method.OverriddenMethod If overriddenMethod Is Nothing OrElse overriddenMethod.IsMustOverride Then Return True End If Dim containingType = method.ContainingType ' NOTE: current implementation of IsIntrinsicType treats DateTime as an ' intrinsic type which differs from C# version ' NOTE: VB Dev10 uses 'constrained'/'callvirt' for calls to methods of restricted types ' (those with IsRestrictedType = True); Roslyn uses 'call' to match C# behavior Return containingType.IsIntrinsicType OrElse containingType.IsRestrictedType End Function Private Sub EmitTypeOfExpression(expression As BoundTypeOf, used As Boolean, Optional optimize As Boolean = False) Dim operand = expression.Operand Debug.Assert(operand.Type.IsReferenceType AndAlso Not operand.Type.IsTypeParameter(), "operand.Type.IsReferenceType") EmitExpression(operand, True) If used Then Dim typeFrom = operand.Type Dim typeTo = expression.TargetType _builder.EmitOpCode(ILOpCode.Isinst) EmitSymbolToken(typeTo, expression.Syntax) ' If this expression is the condition of an If we can save these instructions ' and let the parent condition emit branches instead If Not optimize Then _builder.EmitOpCode(ILOpCode.Ldnull) If expression.IsTypeOfIsNotExpression Then _builder.EmitOpCode(ILOpCode.Ceq) Else _builder.EmitOpCode(ILOpCode.Cgt_un) End If End If End If EmitPopIfUnused(used) End Sub ''' <summary> ''' Emit code for a ternary conditional operator. ''' </summary> ''' <remarks> ''' if (b, x, y) becomes ''' push b ''' if pop then goto CONSEQUENCE ''' push y ''' goto DONE ''' CONSEQUENCE: ''' push x ''' DONE: ''' </remarks> Private Sub EmitTernaryConditionalExpression(expr As BoundTernaryConditionalExpression, used As Boolean) Debug.Assert(expr.ConstantValueOpt Is Nothing, "Constant value should have been emitted directly") ' Generate branchless IL for If(b, 1, 0). Dim isOneWhenTrue = False Dim isOneWhenFalse = False If used AndAlso _ilEmitStyle <> ILEmitStyle.Debug AndAlso (IsSimpleType(expr.Type.PrimitiveTypeCode) OrElse expr.Type.PrimitiveTypeCode = Cci.PrimitiveTypeCode.Char) AndAlso If(expr.WhenTrue.ConstantValueOpt?.IsIntegralValueZeroOrOne(isOneWhenTrue), False) AndAlso If(expr.WhenFalse.ConstantValueOpt?.IsIntegralValueZeroOrOne(isOneWhenFalse), False) AndAlso isOneWhenTrue <> isOneWhenFalse AndAlso TryEmitComparison(expr.Condition, sense:=isOneWhenTrue) Then Dim toType = expr.Type.PrimitiveTypeCode If toType <> Cci.PrimitiveTypeCode.Boolean Then _builder.EmitNumericConversion(Cci.PrimitiveTypeCode.Int32, toType, checked:=False) End If Return End If Dim consequenceLabel = New Object() Dim doneLabel = New Object() EmitCondBranch(expr.Condition, consequenceLabel, sense:=True) EmitExpression(expr.WhenFalse, used) ' ' III.1.8.1.3 Merging stack states ' . . . ' Let T be the type from the slot on the newly computed state and S ' be the type from the corresponding slot on the previously stored state. The merged type, U, shall ' be computed as follows (recall that S := T is the compatibility function defined ' in §III.1.8.1.2.2): ' 1. if S := T then U=S ' 2. Otherwise, if T := S then U=T ' 3. Otherwise, if S and T are both object types, then let V be the closest common supertype of S and T then U=V. ' 4. Otherwise, the merge shall fail. ' ' The issue is that, if the types don't match exactly but share an interface, ' there's no guarantee that the runtime will be able to find the interface. ' However, if you convert one of the types to the interface type, then it will ' work for both expression, and either (1) or (2) will succeed. ' This explains both (a) why this only applies to interfaces and (b) why it's ' okay to only explicitly convert one branch. ' Dim mergeTypeOfAlternative As TypeSymbol = StackMergeType(expr.WhenFalse) If (used) Then If (IsVarianceCast(expr.Type, mergeTypeOfAlternative)) Then EmitStaticCast(expr.Type, expr.Syntax) mergeTypeOfAlternative = expr.Type End If End If _builder.EmitBranch(ILOpCode.Br, doneLabel) If (used) Then ' If we get to consequenceLabel, we should not have WhenFalse on stack, adjust for that. _builder.AdjustStack(-1) End If _builder.MarkLabel(consequenceLabel) EmitExpression(expr.WhenTrue, used) If (used) Then Dim mergeTypeOfConsequence As TypeSymbol = StackMergeType(expr.WhenTrue) If (IsVarianceCast(expr.Type, mergeTypeOfConsequence)) Then EmitStaticCast(expr.Type, expr.Syntax) mergeTypeOfConsequence = expr.Type ElseIf (expr.Type.IsInterfaceType() AndAlso Not TypeSymbol.Equals(expr.Type, mergeTypeOfAlternative, TypeCompareKind.ConsiderEverything) AndAlso Not TypeSymbol.Equals(expr.Type, mergeTypeOfConsequence, TypeCompareKind.ConsiderEverything)) Then EmitStaticCast(expr.Type, expr.Syntax) End If End If _builder.MarkLabel(doneLabel) End Sub ''' <summary> ''' Emit code for a null-coalescing operator. ''' </summary> ''' <remarks> ''' if(x, y) becomes ''' push x ''' dup x ''' if pop isnot null goto LEFT_NOT_NULL ''' pop ''' push y ''' LEFT_NOT_NULL: ''' </remarks> Private Sub EmitBinaryConditionalExpression(expr As BoundBinaryConditionalExpression, used As Boolean) Debug.Assert(expr.ConvertedTestExpression Is Nothing, "coalesce with nontrivial test conversions are lowered into ternary.") Debug.Assert(TypeSymbol.Equals(expr.Type, expr.ElseExpression.Type, TypeCompareKind.ConsiderEverything)) Debug.Assert(Not expr.Type.IsValueType) EmitExpression(expr.TestExpression, used:=True) ' See the notes about verification type merges in EmitConditionalOperator Dim mergeTypeOfLeftValue As TypeSymbol = StackMergeType(expr.TestExpression) If (used) Then If (IsVarianceCast(expr.Type, mergeTypeOfLeftValue)) Then EmitStaticCast(expr.Type, expr.Syntax) mergeTypeOfLeftValue = expr.Type End If _builder.EmitOpCode(ILOpCode.Dup) End If If (expr.Type.IsTypeParameter()) Then EmitBox(expr.Type, expr.TestExpression.Syntax) End If Dim ifLeftNotNullLabel = New Object() _builder.EmitBranch(ILOpCode.Brtrue, ifLeftNotNullLabel) If (used) Then _builder.EmitOpCode(ILOpCode.Pop) End If EmitExpression(expr.ElseExpression, used) If (used) Then Dim mergeTypeOfRightValue As TypeSymbol = StackMergeType(expr.ElseExpression) If (IsVarianceCast(expr.Type, mergeTypeOfRightValue)) Then EmitStaticCast(expr.Type, expr.Syntax) mergeTypeOfRightValue = expr.Type ElseIf (expr.Type.IsInterfaceType() AndAlso Not TypeSymbol.Equals(expr.Type, mergeTypeOfLeftValue, TypeCompareKind.ConsiderEverything) AndAlso Not TypeSymbol.Equals(expr.Type, mergeTypeOfRightValue, TypeCompareKind.ConsiderEverything)) Then EmitStaticCast(expr.Type, expr.Syntax) End If End If _builder.MarkLabel(ifLeftNotNullLabel) End Sub ' Implicit casts are not emitted. As a result verifier may operate on a different ' types from the types of operands when performing stack merges in coalesce/ternary. ' Such differences are in general irrelevant since merging rules work the same way ' for base and derived types. ' ' Situation becomes more complicated with delegates, arrays and interfaces since they ' allow implicit casts from types that do not derive from them. In such cases ' we may need to introduce static casts in the code to prod the verifier to the ' right direction ' ' This helper returns actual type of array|interface|delegate expression ignoring implicit ' casts. This would be the effective stack merge type in the verifier. ' ' NOTE: In cases where stack merge type cannot be determined, we just return null. ' We still must assume that it can be an array, delegate or interface though. Private Function StackMergeType(expr As BoundExpression) As TypeSymbol ' these cases are not interesting. Merge type is the same or derived. No difference. If (Not (expr.Type.IsArrayType OrElse expr.Type.IsInterfaceType OrElse expr.Type.IsDelegateType)) Then Return expr.Type End If ' Dig through casts. We only need to check for expressions that - ' 1) are implicit casts ' 2) may transparently return operands, so we need to dig deeper ' 3) stack values Select Case (expr.Kind) Case BoundKind.DirectCast Dim conversion = DirectCast(expr, BoundDirectCast) Dim conversionKind = conversion.ConversionKind If (Conversions.IsWideningConversion(conversionKind)) Then Return StackMergeType(conversion.Operand) End If Case BoundKind.TryCast Dim conversion = DirectCast(expr, BoundTryCast) Dim conversionKind = conversion.ConversionKind If (Conversions.IsWideningConversion(conversionKind)) Then Return StackMergeType(conversion.Operand) End If 'Case BoundKind.Conversion ' Dim conversion = DirectCast(expr, BoundConversion) ' Dim conversionKind = conversion.ConversionKind ' If (Conversions.IsWideningConversion(conversionKind)) Then ' Return StackMergeType(conversion.Operand) ' End If Case BoundKind.AssignmentOperator Dim assignment = DirectCast(expr, BoundAssignmentOperator) Return StackMergeType(assignment.Right) Case BoundKind.Sequence Dim sequence = DirectCast(expr, BoundSequence) Return StackMergeType(sequence.ValueOpt) Case BoundKind.Local Dim local = DirectCast(expr, BoundLocal) If (Me.IsStackLocal(local.LocalSymbol)) Then ' stack value, we cannot be sure what it is Return Nothing End If Case BoundKind.Dup ' stack value, we cannot be sure what it is Return Nothing End Select Return expr.Type End Function ' Although III.1.8.1.3 seems to imply that verifier understands variance casts. ' It appears that verifier/JIT gets easily confused. ' So to not rely on whether that should work or not we will flag potentially ' "complicated" casts and make them static casts to ensure we are all on ' the same page with what type should be tracked. Private Shared Function IsVarianceCast(toType As TypeSymbol, fromType As TypeSymbol) As Boolean If (TypeSymbol.Equals(toType, fromType, TypeCompareKind.ConsiderEverything)) Then Return False End If If (fromType Is Nothing) Then ' from unknown type - this could be a variance conversion. Return True End If ' while technically variance casts, array conversions do not seem to be a problem ' unless the element types are converted via variance. If (toType.IsArrayType) Then Return IsVarianceCast(DirectCast(toType, ArrayTypeSymbol).ElementType, DirectCast(fromType, ArrayTypeSymbol).ElementType) End If Return (toType.IsDelegateType() AndAlso Not TypeSymbol.Equals(toType, fromType, TypeCompareKind.ConsiderEverything)) OrElse (toType.IsInterfaceType() AndAlso fromType.IsInterfaceType() AndAlso Not fromType.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.ContainsKey(DirectCast(toType, NamedTypeSymbol))) End Function Private Sub EmitStaticCast(toType As TypeSymbol, syntax As SyntaxNode) Debug.Assert(toType.IsVerifierReference()) ' From ILGENREC::GenQMark ' See VSWhidbey Bugs #49619 and 108643. If the destination type is an interface we need ' to force a static cast to be generated for any cast result expressions. The static cast ' should be done before the unifying jump so the code is verifiable and to allow the JIT to ' optimize it away. NOTE: Since there is no staticcast instruction, we implement static cast ' with a stloc / ldloc to a temporary. ' Bug: VSWhidbey/49619 ' Bug: VSWhidbey/108643 ' Bug: DevDivBugs/42645 Dim temp = AllocateTemp(toType, syntax) _builder.EmitLocalStore(temp) _builder.EmitLocalLoad(temp) FreeTemp(temp) End Sub Private Sub EmitArrayCreationExpression(expression As BoundArrayCreation, used As Boolean) Dim arrayType = DirectCast(expression.Type, ArrayTypeSymbol) EmitExpressions(expression.Bounds, True) If arrayType.IsSZArray Then _builder.EmitOpCode(ILOpCode.Newarr) EmitSymbolToken(arrayType.ElementType, expression.Syntax) Else _builder.EmitArrayCreation(_module.Translate(arrayType), expression.Syntax) End If If expression.InitializerOpt IsNot Nothing Then EmitArrayInitializers(arrayType, expression.InitializerOpt) End If EmitPopIfUnused(used) End Sub Private Sub EmitArrayLengthExpression(expression As BoundArrayLength, used As Boolean) Debug.Assert(expression.Type.SpecialType = SpecialType.System_Int32 OrElse expression.Type.SpecialType = SpecialType.System_Int64 OrElse expression.Type.SpecialType = SpecialType.System_UIntPtr) EmitExpression(expression.Expression, used:=True) _builder.EmitOpCode(ILOpCode.Ldlen) Dim typeTo = expression.Type.PrimitiveTypeCode ' NOTE: ldlen returns native uint, but newarr takes native int, so the length value is always ' a positive native int. We can treat it as either signed or unsigned. ' We will use whatever typeTo says so we do not need to convert because of sign. Dim typeFrom = If(typeTo.IsUnsigned(), Microsoft.Cci.PrimitiveTypeCode.UIntPtr, Microsoft.Cci.PrimitiveTypeCode.IntPtr) ' NOTE In Dev10 VB this cast Is checked. ' ' Emitting checked conversion however results in redundant overflow checks on 64bit And also inhibits range check hoisting in loops. ' Therefore we will emit unchecked conversion here as C# compiler always did. _builder.EmitNumericConversion(typeFrom, typeTo, checked:=False) EmitPopIfUnused(used) End Sub Private Sub EmitObjectCreationExpression(expression As BoundObjectCreationExpression, used As Boolean) ' if there is no constructor or it is synthesized, we do not call it, call initobj instead If expression.IsDefaultValue Then EmitInitObj(expression.Type, used, expression.Syntax) Else Dim constructor As MethodSymbol = expression.ConstructorOpt EmitNewObj(expression.ConstructorOpt, expression.Arguments, used, expression.Syntax) End If End Sub Private Sub EmitInitObj(type As TypeSymbol, used As Boolean, syntaxNode As SyntaxNode) If (used) Then Dim temp = Me.AllocateTemp(type, syntaxNode) _builder.EmitLocalAddress(temp) ' ldloca temp _builder.EmitOpCode(ILOpCode.Initobj) ' initobj <MyStruct> EmitSymbolToken(type, syntaxNode) _builder.EmitLocalLoad(temp) ' ldloc temp FreeTemp(temp) End If End Sub Private Sub EmitNewObj(constructor As MethodSymbol, arguments As ImmutableArray(Of BoundExpression), used As Boolean, syntaxNode As SyntaxNode) Debug.Assert(Not constructor.IsDefaultValueTypeConstructor(), "do not call synthesized struct constructors, they do not exist") EmitArguments(arguments, constructor.Parameters) _builder.EmitOpCode(ILOpCode.Newobj, ILOpCode.Newobj.StackPushCount() - arguments.Length) EmitSymbolToken(constructor, syntaxNode) EmitPopIfUnused(used) End Sub Private Sub EmitLoadDefaultValueOfTypeParameter(type As TypeSymbol, used As Boolean, syntaxNode As SyntaxNode) Debug.Assert(type.IsTypeParameter) EmitLoadDefaultValueOfTypeFromNothingLiteral(type, used, syntaxNode) End Sub Private Sub EmitLoadDefaultValueOfTypeFromNothingLiteral(type As TypeSymbol, used As Boolean, syntaxNode As SyntaxNode) EmitInitObj(type, used, syntaxNode) End Sub Private Sub EmitInitObjOnTarget(target As BoundExpression) ' NOTE!!!: We are misusing isReadOnly here!!! ' ' We are creating a fully modifiable reference to a struct in order to initialize it. ' In fact we are going to wipe the target ' ' We are still going to say the reference is immutable. Since we are initializing, ' there is nothing to mutate. ' If we have to call an initobj a stack local, we still have to create a temp If target.Kind = BoundKind.Local AndAlso IsStackLocal(DirectCast(target, BoundLocal).LocalSymbol) Then EmitInitObj(target.Type, True, target.Syntax) Return End If ' Stack local should be on stack Debug.Assert(target.Kind <> BoundKind.Local OrElse Not IsStackLocal(DirectCast(target, BoundLocal).LocalSymbol)) EmitAddress(target, addressKind:=AddressKind.Immutable) _builder.EmitOpCode(ILOpCode.Initobj) EmitSymbolToken(target.Type, target.Syntax) End Sub Private Sub EmitConstantExpression(type As TypeSymbol, constantValue As ConstantValue, used As Boolean, syntaxNode As SyntaxNode) ' unused constant has no side-effects If used Then ' Null type parameter values must be emitted as 'initobj' rather than 'ldnull'. If ((type IsNot Nothing) AndAlso (type.TypeKind = TypeKind.TypeParameter) AndAlso constantValue.IsNull) Then EmitInitObj(type, used, syntaxNode) Else _builder.EmitConstantValue(constantValue, syntaxNode) End If End If End Sub Private Sub EmitAssignmentExpression(assignmentOperator As BoundAssignmentOperator, used As Boolean) If Me.TryEmitAssignmentInPlace(assignmentOperator, used) Then Return End If ' Assignment expression codegen has the following parts: ' ' * PreRHS: We need to emit instructions before the load of the right hand side if: ' - If the left hand side is a ref local or ref formal parameter and the right hand ' side is a value then we must put the ref on the stack early so that we can store ' indirectly into it. ' - If the left hand side is an array slot then we must evaluate the array and indices ' before we evaluate the right hand side. We ensure that the array and indices are ' on the stack when the store is executed. ' - Similarly, if the left hand side is a non-static field then its receiver must be ' evaluated before the right hand side. ' ' * RHS: There are three possible ways to do an assignment with respect to "refness", ' and all are found in the lowering of: ' ' N().s += 10; ' ' That expression is realized as ' ' ref int addr = ref N().s; ' Assign a ref on the right hand side to the left hand side. ' int sum = addr + 10; ' No refs at all; assign directly to sum. ' addr = sum; ' Assigns indirectly through the address. ' ' - If we are in the first case then assignmentOperator.RefKind is Ref and the left hand side is a ' ref local temporary. We simply assign the ref on the RHS to the storage on the LHS with no indirection. ' ' - If we are in the second case then nothing is ref; we have a value on one side an a local on the other. ' Again, there is no indirection. ' ' - If we are in the third case then we have a ref on the left and a value on the right. We must compute the ' value of the right hand side and then store it into the left hand side. ' ' * Duplication: The result of an assignment operation is the value that was assigned. It is possible that ' later codegen is expecting this value to be on the stack when we're done here. This is controlled by ' the "used" formal parameter. There are two possible cases: ' - If the preamble put stuff on the stack for the usage of the store, then we must not put an extra copy ' of the right hand side value on the stack; that will be between the value and the stuff needed to ' do the storage. In that case we put the right hand side value in a temporary and restore it later. ' - Otherwise we can just do a dup instruction; there's nothing before the dup on the stack that we'll need. ' ' * Storage: Either direct or indirect, depending. See the RHS section above for details. ' ' * Post-storage: If we stashed away the duplicated value in the temporary, we need to restore it back to the stack. Dim lhsUsesStack As Boolean = Me.EmitAssignmentPreamble(assignmentOperator.Left) Me.EmitExpression(assignmentOperator.Right, used:=True) Dim temp As LocalDefinition = Me.EmitAssignmentDuplication(assignmentOperator, used, lhsUsesStack) Me.EmitStore(assignmentOperator.Left) Me.EmitAssignmentPostfix(temp) End Sub ' sometimes it is possible and advantageous to get an address of the lHS and ' perform assignment as an in-place initialization via initobj or constructor invocation. ' ' 1) initobj ' is used when assigning default value to T that is not a verifier reference. ' ' 2) inplace ctor call ' is used when assigning a freshly created struct. "x = new S(arg)" can be ' replaced by x.S(arg) as long as partial assignment cannot be observed - ' i.e. target must not be on the heap and we should not be in a try block. Private Function TryEmitAssignmentInPlace(assignmentOperator As BoundAssignmentOperator, used As Boolean) As Boolean Dim left As BoundExpression = assignmentOperator.Left ' if result is used, and lives on heap, we must keep RHS value on the stack. ' otherwise we can try conjuring up the RHS value directly where it belongs. If used AndAlso Not Me.TargetIsNotOnHeap(left) Then Return False End If If Not SafeToGetWriteableReference(left) Then ' cannot take a ref Return False End If Dim right As BoundExpression = assignmentOperator.Right Dim rightType = right.Type If Not rightType.IsTypeParameter Then If rightType.IsReferenceType OrElse (right.ConstantValueOpt IsNot Nothing AndAlso rightType.SpecialType <> SpecialType.System_Decimal) Then ' in-place is not advantageous for reference types or constants Return False End If End If If right.IsDefaultValue() Then Me.InPlaceInit(left, used) Return True Else If right.Kind = BoundKind.ObjectCreationExpression Then ' It is desirable to do in-place ctor call if possible. ' we could do newobj/stloc, but inplace call ' produces same or better code in current JITs If Me.PartialCtorResultCannotEscape(left) Then Dim objCreation As BoundObjectCreationExpression = DirectCast(right, BoundObjectCreationExpression) Me.InPlaceCtorCall(left, objCreation, used) Return True End If End If End If Return False End Function ' because of array covariance, taking a reference to an element of ' generic array may fail even though assignment "arr(i) = Nothing" would always succeed. Private Function SafeToGetWriteableReference(left As BoundExpression) As Boolean Return AllowedToTakeRef(left, AddressKind.Writeable) AndAlso Not (left.Kind = BoundKind.ArrayAccess AndAlso left.Type.TypeKind = TypeKind.TypeParameter) End Function Private Sub InPlaceInit(target As BoundExpression, used As Boolean) Dim temp = Me.EmitAddress(target, AddressKind.Writeable) Debug.Assert(temp Is Nothing, "temp is not expected when in-place assigning") Me._builder.EmitOpCode(ILOpCode.Initobj) ' initobj <MyStruct> Me.EmitSymbolToken(target.Type, target.Syntax) If used Then Debug.Assert(Me.TargetIsNotOnHeap(target), "cannot read-back the target since it could have been modified") Me.EmitExpression(target, used = True) End If End Sub Private Sub InPlaceCtorCall(target As BoundExpression, objCreation As BoundObjectCreationExpression, used As Boolean) Dim temp = Me.EmitAddress(target, AddressKind.Writeable) Debug.Assert(temp Is Nothing, "temp is not expected when in-place assigning") Dim constructor As MethodSymbol = objCreation.ConstructorOpt Me.EmitArguments(objCreation.Arguments, constructor.Parameters) ' +1 to adjust for consumed target address Dim stackAdjustment As Integer = constructor.ParameterCount + 1 Me._builder.EmitOpCode(ILOpCode.[Call], -stackAdjustment) Me.EmitSymbolToken(constructor, objCreation.Syntax) If used Then Debug.Assert(Me.TargetIsNotOnHeap(target), "cannot read-back the target since it could have been modified") Me.EmitExpression(target, used = True) End If End Sub ' partial ctor results are not observable when target is not on the heap. ' we also must not be in a try, otherwise if ctor throws ' partially assigned value may be observed in the handler. Private Function PartialCtorResultCannotEscape(left As BoundExpression) As Boolean Return Me._tryNestingLevel = 0 AndAlso Me.TargetIsNotOnHeap(left) End Function Private Function TargetIsNotOnHeap(left As BoundExpression) As Boolean Select Case left.Kind Case BoundKind.Local Return Not DirectCast(left, BoundLocal).LocalSymbol.IsByRef Case BoundKind.Parameter Return Not DirectCast(left, BoundParameter).ParameterSymbol.IsByRef Case BoundKind.ReferenceAssignment Return False End Select Return False End Function Private Function EmitAssignmentPreamble(assignmentTarget As BoundExpression) As Boolean Dim lhsUsesStack = False Select Case assignmentTarget.Kind Case BoundKind.Local Dim boundLocal = DirectCast(assignmentTarget, BoundLocal) If boundLocal.LocalSymbol.IsByRef Then If IsStackLocal(boundLocal.LocalSymbol) Then ' the address is supposed to already be on stack Else _builder.EmitLocalLoad(GetLocal(boundLocal)) End If lhsUsesStack = True End If Case BoundKind.ReferenceAssignment EmitReferenceAssignment(DirectCast(assignmentTarget, BoundReferenceAssignment), used:=True, needReference:=True) lhsUsesStack = True Case BoundKind.FieldAccess Dim left = DirectCast(assignmentTarget, BoundFieldAccess) If Not left.FieldSymbol.IsShared Then ' we will not write to the receiver, but will write into its field. Dim temp = EmitReceiverRef(left.ReceiverOpt, isAccessConstrained:=False, addressKind:=AddressKind.ReadOnly) Debug.Assert(temp Is Nothing, "temp is unexpected when writing to a field") lhsUsesStack = True End If Case BoundKind.Parameter Dim left = DirectCast(assignmentTarget, BoundParameter) If left.ParameterSymbol.IsByRef Then _builder.EmitLoadArgumentOpcode(ParameterSlot(left)) lhsUsesStack = True End If Case BoundKind.ArrayAccess Dim left = DirectCast(assignmentTarget, BoundArrayAccess) EmitExpression(left.Expression, True) EmitExpressions(left.Indices, True) lhsUsesStack = True Case BoundKind.MeReference Dim left = DirectCast(assignmentTarget, BoundMeReference) ' why do we even need to handle this case? VB doesn't allow assigning to 'Me'. Debug.Assert(False) Dim temp = EmitAddress(left, addressKind:=AddressKind.Writeable) Debug.Assert(temp Is Nothing, "taking ref of Me should not create a temp") lhsUsesStack = True Case BoundKind.PseudoVariable EmitPseudoVariableAddress(DirectCast(assignmentTarget, BoundPseudoVariable)) lhsUsesStack = True Case BoundKind.Sequence Dim sequence = DirectCast(assignmentTarget, BoundSequence) If Not sequence.Locals.IsEmpty Then _builder.OpenLocalScope() For Each local In sequence.Locals Me.DefineLocal(local, sequence.Syntax) Next End If Me.EmitSideEffects(sequence.SideEffects) lhsUsesStack = EmitAssignmentPreamble(sequence.ValueOpt) Case BoundKind.Call Dim left = DirectCast(assignmentTarget, BoundCall) Debug.Assert(left.Method.ReturnsByRef) EmitCallExpression(left, UseKind.UsedAsAddress) lhsUsesStack = True Case BoundKind.ModuleVersionId, BoundKind.InstrumentationPayloadRoot Case Else Throw ExceptionUtilities.UnexpectedValue(assignmentTarget.Kind) End Select Return lhsUsesStack End Function Private Function EmitAssignmentDuplication(assignmentOperator As BoundAssignmentOperator, used As Boolean, lhsUsesStack As Boolean) As LocalDefinition Dim temp As LocalDefinition = Nothing If used Then Me._builder.EmitOpCode(ILOpCode.Dup) If lhsUsesStack Then temp = Me.AllocateTemp(assignmentOperator.Left.Type, assignmentOperator.Left.Syntax) Me._builder.EmitLocalStore(temp) End If End If Return temp End Function Private Sub EmitAssignmentPostfix(temp As LocalDefinition) If temp IsNot Nothing Then Me._builder.EmitLocalLoad(temp) Me.FreeTemp(temp) End If End Sub Private Sub EmitReferenceAssignment(capture As BoundReferenceAssignment, used As Boolean, Optional needReference As Boolean = False) Debug.Assert(Not needReference OrElse used) Dim temp = EmitAddress(capture.LValue, addressKind:=AddressKind.Writeable) Debug.Assert(temp Is Nothing, "reference assignment should not clone the referent") If used Then _builder.EmitOpCode(ILOpCode.Dup) End If Dim boundLocal As BoundLocal = capture.ByRefLocal Debug.Assert(boundLocal.LocalSymbol.IsByRef) If IsStackLocal(boundLocal.LocalSymbol) Then ' just leave the address on stack Else Dim local = GetLocal(boundLocal) _builder.EmitLocalStore(local) End If If used AndAlso Not needReference Then EmitLoadIndirect(capture.Type, capture.Syntax) End If End Sub Private Sub EmitStore(expression As BoundExpression) Select Case expression.Kind Case BoundKind.FieldAccess EmitFieldStore(DirectCast(expression, BoundFieldAccess)) Case BoundKind.Local Dim boundLocal = DirectCast(expression, BoundLocal) If boundLocal.LocalSymbol.IsByRef Then EmitStoreIndirect(boundLocal.LocalSymbol.Type, expression.Syntax) ElseIf IsStackLocal(boundLocal.LocalSymbol) Then ' just leave original value on stack Else Dim local = GetLocal(boundLocal) _builder.EmitLocalStore(local) End If Case BoundKind.ReferenceAssignment, BoundKind.PseudoVariable EmitStoreIndirect(expression.Type, expression.Syntax) Case BoundKind.ArrayAccess Dim array = DirectCast(expression, BoundArrayAccess).Expression Dim arrayType = DirectCast(array.Type, ArrayTypeSymbol) EmitArrayElementStore(arrayType, expression.Syntax) Case BoundKind.MeReference EmitMeStore(DirectCast(expression, BoundMeReference)) Case BoundKind.Parameter EmitParameterStore(DirectCast(expression, BoundParameter)) Case BoundKind.Sequence Dim sequence = DirectCast(expression, BoundSequence) EmitStore(sequence.ValueOpt) If Not sequence.Locals.IsEmpty Then _builder.CloseLocalScope() For Each local In sequence.Locals Me.FreeLocal(local) Next End If Case BoundKind.Call Debug.Assert(DirectCast(expression, BoundCall).Method.ReturnsByRef) EmitStoreIndirect(expression.Type, expression.Syntax) Case BoundKind.ModuleVersionId EmitModuleVersionIdStore(DirectCast(expression, BoundModuleVersionId)) Case BoundKind.InstrumentationPayloadRoot EmitInstrumentationPayloadRootStore(DirectCast(expression, BoundInstrumentationPayloadRoot)) Case Else Throw ExceptionUtilities.UnexpectedValue(expression.Kind) End Select End Sub Private Sub EmitMeStore(thisRef As BoundMeReference) Debug.Assert(thisRef.Type.IsValueType) _builder.EmitOpCode(ILOpCode.Stobj) EmitSymbolToken(thisRef.Type, thisRef.Syntax) End Sub Private Sub EmitArrayElementStore(arrayType As ArrayTypeSymbol, syntaxNode As SyntaxNode) If arrayType.IsSZArray Then EmitVectorElementStore(arrayType, syntaxNode) Else _builder.EmitArrayElementStore(_module.Translate(arrayType), syntaxNode) End If End Sub ''' <summary> ''' Emit an element store instruction for a single dimensional array. ''' </summary> Private Sub EmitVectorElementStore(arrayType As ArrayTypeSymbol, syntaxNode As SyntaxNode) Dim elementType = arrayType.ElementType If elementType.IsEnumType() Then 'underlying primitives do not need type tokens. elementType = (DirectCast(elementType, NamedTypeSymbol)).EnumUnderlyingType End If Select Case elementType.PrimitiveTypeCode Case Microsoft.Cci.PrimitiveTypeCode.Boolean, Microsoft.Cci.PrimitiveTypeCode.Int8, Microsoft.Cci.PrimitiveTypeCode.UInt8 _builder.EmitOpCode(ILOpCode.Stelem_i1) Case Microsoft.Cci.PrimitiveTypeCode.Char, Microsoft.Cci.PrimitiveTypeCode.Int16, Microsoft.Cci.PrimitiveTypeCode.UInt16 _builder.EmitOpCode(ILOpCode.Stelem_i2) Case Microsoft.Cci.PrimitiveTypeCode.Int32, Microsoft.Cci.PrimitiveTypeCode.UInt32 _builder.EmitOpCode(ILOpCode.Stelem_i4) Case Microsoft.Cci.PrimitiveTypeCode.Int64, Microsoft.Cci.PrimitiveTypeCode.UInt64 _builder.EmitOpCode(ILOpCode.Stelem_i8) Case Microsoft.Cci.PrimitiveTypeCode.IntPtr, Microsoft.Cci.PrimitiveTypeCode.UIntPtr, Microsoft.Cci.PrimitiveTypeCode.Pointer _builder.EmitOpCode(ILOpCode.Stelem_i) Case Microsoft.Cci.PrimitiveTypeCode.Float32 _builder.EmitOpCode(ILOpCode.Stelem_r4) Case Microsoft.Cci.PrimitiveTypeCode.Float64 _builder.EmitOpCode(ILOpCode.Stelem_r8) Case Else If IsVerifierReference(elementType) Then _builder.EmitOpCode(ILOpCode.Stelem_ref) Else _builder.EmitOpCode(ILOpCode.Stelem) EmitSymbolToken(elementType, syntaxNode) End If End Select End Sub Private Sub EmitFieldStore(fieldAccess As BoundFieldAccess) Dim field As FieldSymbol = fieldAccess.FieldSymbol If field.IsShared Then _builder.EmitOpCode(ILOpCode.Stsfld) Else _builder.EmitOpCode(ILOpCode.Stfld) End If EmitSymbolToken(field, fieldAccess.Syntax) End Sub Private Sub EmitParameterStore(parameter As BoundParameter) If Not parameter.ParameterSymbol.IsByRef Then Dim slot As Integer = ParameterSlot(parameter) _builder.EmitStoreArgumentOpcode(slot) Else 'NOTE: we should have the actual parameter already loaded, 'now need to do a store to where it points to EmitStoreIndirect(parameter.ParameterSymbol.Type, parameter.Syntax) End If End Sub Private Sub EmitStoreIndirect(type As TypeSymbol, syntaxNode As SyntaxNode) If type.IsEnumType() Then type = (DirectCast(type, NamedTypeSymbol)).EnumUnderlyingType End If Select Case type.PrimitiveTypeCode Case Microsoft.Cci.PrimitiveTypeCode.Boolean, Microsoft.Cci.PrimitiveTypeCode.Int8, Microsoft.Cci.PrimitiveTypeCode.UInt8 _builder.EmitOpCode(ILOpCode.Stind_i1) Case Microsoft.Cci.PrimitiveTypeCode.Char, Microsoft.Cci.PrimitiveTypeCode.Int16, Microsoft.Cci.PrimitiveTypeCode.UInt16 _builder.EmitOpCode(ILOpCode.Stind_i2) Case Microsoft.Cci.PrimitiveTypeCode.Int32, Microsoft.Cci.PrimitiveTypeCode.UInt32 _builder.EmitOpCode(ILOpCode.Stind_i4) Case Microsoft.Cci.PrimitiveTypeCode.Int64, Microsoft.Cci.PrimitiveTypeCode.UInt64 _builder.EmitOpCode(ILOpCode.Stind_i8) Case Microsoft.Cci.PrimitiveTypeCode.IntPtr, Microsoft.Cci.PrimitiveTypeCode.UIntPtr, Microsoft.Cci.PrimitiveTypeCode.Pointer _builder.EmitOpCode(ILOpCode.Stind_i) Case Microsoft.Cci.PrimitiveTypeCode.Float32 _builder.EmitOpCode(ILOpCode.Stind_r4) Case Microsoft.Cci.PrimitiveTypeCode.Float64 _builder.EmitOpCode(ILOpCode.Stind_r8) Case Else If IsVerifierReference(type) Then _builder.EmitOpCode(ILOpCode.Stind_ref) Else _builder.EmitOpCode(ILOpCode.Stobj) EmitSymbolToken(type, syntaxNode) End If End Select End Sub Private Sub EmitPopIfUnused(used As Boolean) If Not used Then _builder.EmitOpCode(ILOpCode.Pop) End If End Sub Private Sub EmitGetType(boundTypeOfOperator As BoundGetType, used As Boolean) ' an unused GetType can have side effects because it can e.g. throw a TypeLoadException Dim type As TypeSymbol = boundTypeOfOperator.SourceType.Type _builder.EmitOpCode(ILOpCode.Ldtoken) EmitSymbolToken(type, boundTypeOfOperator.SourceType.Syntax) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=0) 'argument off, return value on Dim getTypeMethod = boundTypeOfOperator.GetTypeFromHandle Debug.Assert(getTypeMethod IsNot Nothing) ' Should have been checked during binding EmitSymbolToken(getTypeMethod, boundTypeOfOperator.Syntax) EmitPopIfUnused(used) End Sub Private Sub EmitFieldInfoExpression(node As BoundFieldInfo, used As Boolean) _builder.EmitOpCode(ILOpCode.Ldtoken) EmitSymbolToken(node.Field, node.Syntax) Dim getField As MethodSymbol If Not node.Field.ContainingType.IsGenericType Then Debug.Assert(Not node.Field.ContainingType.IsAnonymousType) ' NO anonymous types field access expected _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=0) ' argument off, return value on getField = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.System_Reflection_FieldInfo__GetFieldFromHandle), MethodSymbol) Else _builder.EmitOpCode(ILOpCode.Ldtoken) EmitSymbolToken(node.Field.ContainingType, node.Syntax) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=-1) ' 2 arguments off, return value on getField = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.System_Reflection_FieldInfo__GetFieldFromHandle2), MethodSymbol) End If Debug.Assert(getField IsNot Nothing) EmitSymbolToken(getField, node.Syntax) If Not TypeSymbol.Equals(node.Type, getField.ReturnType, TypeCompareKind.ConsiderEverything) Then _builder.EmitOpCode(ILOpCode.Castclass) EmitSymbolToken(node.Type, node.Syntax) End If EmitPopIfUnused(used) End Sub Private Sub EmitMethodInfoExpression(node As BoundMethodInfo, used As Boolean) Dim method As MethodSymbol = node.Method If method.IsTupleMethod Then method = method.TupleUnderlyingMethod End If _builder.EmitOpCode(ILOpCode.Ldtoken) EmitSymbolToken(method, node.Syntax) If node.GetMethodFromHandle.ParameterCount = 1 Then Debug.Assert(Not method.ContainingType.IsGenericType AndAlso Not method.ContainingType.IsAnonymousType) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=0) ' argument off, return value on Else Debug.Assert(method.ContainingType.IsGenericType OrElse method.ContainingType.IsAnonymousType) Debug.Assert(node.GetMethodFromHandle.ParameterCount = 2) _builder.EmitOpCode(ILOpCode.Ldtoken) EmitSymbolToken(method.ContainingType, node.Syntax) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=-1) ' 2 arguments off, return value on End If EmitSymbolToken(node.GetMethodFromHandle, node.Syntax) If Not TypeSymbol.Equals(node.Type, node.GetMethodFromHandle.ReturnType, TypeCompareKind.ConsiderEverything) Then _builder.EmitOpCode(ILOpCode.Castclass) EmitSymbolToken(node.Type, node.Syntax) End If EmitPopIfUnused(used) End Sub Private Sub EmitBox(type As TypeSymbol, syntaxNode As SyntaxNode) _builder.EmitOpCode(ILOpCode.Box) EmitSymbolToken(type, syntaxNode) End Sub Private Sub EmitUnboxAny(type As TypeSymbol, syntaxNode As SyntaxNode) _builder.EmitOpCode(ILOpCode.Unbox_any) EmitSymbolToken(type, syntaxNode) End Sub Private Sub EmitMethodDefIndexExpression(node As BoundMethodDefIndex) Debug.Assert(node.Method.IsDefinition) Debug.Assert(node.Type.SpecialType = SpecialType.System_Int32) _builder.EmitOpCode(ILOpCode.Ldtoken) ' For partial methods, we emit pseudo token based on the symbol for the partial ' definition part as opposed to the symbol for the partial implementation part. ' We will need to resolve the symbol associated with each pseudo token in order ' to compute the real method definition tokens later. For partial methods, this ' resolution can only succeed if the associated symbol is the symbol for the ' partial definition and not the symbol for the partial implementation (see ' MethodSymbol.ResolvedMethodImpl()). Dim symbol = If(node.Method.PartialDefinitionPart, node.Method) EmitSymbolToken(symbol, node.Syntax, encodeAsRawDefinitionToken:=True) End Sub Private Sub EmitMaximumMethodDefIndexExpression(node As BoundMaximumMethodDefIndex) Debug.Assert(node.Type.SpecialType = SpecialType.System_Int32) _builder.EmitOpCode(ILOpCode.Ldtoken) _builder.EmitGreatestMethodToken() End Sub Private Sub EmitModuleVersionIdLoad(node As BoundModuleVersionId) _builder.EmitOpCode(ILOpCode.Ldsfld) EmitModuleVersionIdToken(node) End Sub Private Sub EmitModuleVersionIdStore(node As BoundModuleVersionId) _builder.EmitOpCode(ILOpCode.Stsfld) EmitModuleVersionIdToken(node) End Sub Private Sub EmitModuleVersionIdToken(node As BoundModuleVersionId) _builder.EmitToken(_module.GetModuleVersionId(_module.Translate(node.Type, node.Syntax, _diagnostics), node.Syntax, _diagnostics), node.Syntax) End Sub Private Sub EmitModuleVersionIdStringLoad(node As BoundModuleVersionIdString) _builder.EmitOpCode(ILOpCode.Ldstr) _builder.EmitModuleVersionIdStringToken() End Sub Private Sub EmitInstrumentationPayloadRootLoad(node As BoundInstrumentationPayloadRoot) _builder.EmitOpCode(ILOpCode.Ldsfld) EmitInstrumentationPayloadRootToken(node) End Sub Private Sub EmitInstrumentationPayloadRootStore(node As BoundInstrumentationPayloadRoot) _builder.EmitOpCode(ILOpCode.Stsfld) EmitInstrumentationPayloadRootToken(node) End Sub Private Sub EmitInstrumentationPayloadRootToken(node As BoundInstrumentationPayloadRoot) _builder.EmitToken(_module.GetInstrumentationPayloadRoot(node.AnalysisKind, _module.Translate(node.Type, node.Syntax, _diagnostics), node.Syntax, _diagnostics), node.Syntax) End Sub Private Sub EmitSourceDocumentIndex(node As BoundSourceDocumentIndex) Debug.Assert(node.Type.SpecialType = SpecialType.System_Int32) _builder.EmitOpCode(ILOpCode.Ldtoken) _builder.EmitSourceDocumentIndexToken(node.Document) End Sub End Class End Namespace