/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/CodeGen/EmitStatement.vb
1 498 строк
69 KB
Copilot
Copilot: Fix culture-sensitive StringBuilder.Append and ToString calls across codebase and add banned symbols (#80800)
25 окт 2025, 02:42
Не верифицирован
25 окт 2025, 02:42
5ce2f56
Код
Авторство
О чём код?
' 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.InteropServices Imports Microsoft.CodeAnalysis.CodeGen Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen Partial Friend Class CodeGenerator Private Sub EmitStatement(statement As BoundStatement) Select Case statement.Kind Case BoundKind.Block EmitBlock(DirectCast(statement, BoundBlock)) Case BoundKind.SequencePoint EmitSequencePointStatement(DirectCast(statement, BoundSequencePoint)) Case BoundKind.SequencePointWithSpan EmitSequencePointStatement(DirectCast(statement, BoundSequencePointWithSpan)) Case BoundKind.ExpressionStatement EmitExpression((DirectCast(statement, BoundExpressionStatement)).Expression, False) Case BoundKind.NoOpStatement EmitNoOpStatement(DirectCast(statement, BoundNoOpStatement)) Case BoundKind.StatementList Dim list = DirectCast(statement, BoundStatementList) Dim n As Integer = list.Statements.Length For i = 0 To n - 1 EmitStatement(list.Statements(i)) Next Case BoundKind.ReturnStatement EmitReturnStatement(DirectCast(statement, BoundReturnStatement)) Case BoundKind.ThrowStatement EmitThrowStatement(DirectCast(statement, BoundThrowStatement)) Case BoundKind.GotoStatement EmitGotoStatement(DirectCast(statement, BoundGotoStatement)) Case BoundKind.LabelStatement EmitLabelStatement(DirectCast(statement, BoundLabelStatement)) Case BoundKind.ConditionalGoto EmitConditionalGoto(DirectCast(statement, BoundConditionalGoto)) Case BoundKind.TryStatement EmitTryStatement(DirectCast(statement, BoundTryStatement)) Case BoundKind.SelectStatement EmitSelectStatement(DirectCast(statement, BoundSelectStatement)) Case BoundKind.UnstructuredExceptionOnErrorSwitch EmitUnstructuredExceptionOnErrorSwitch(DirectCast(statement, BoundUnstructuredExceptionOnErrorSwitch)) Case BoundKind.UnstructuredExceptionResumeSwitch EmitUnstructuredExceptionResumeSwitch(DirectCast(statement, BoundUnstructuredExceptionResumeSwitch)) Case BoundKind.StateMachineScope EmitStateMachineScope(DirectCast(statement, BoundStateMachineScope)) Case Else Throw ExceptionUtilities.UnexpectedValue(statement.Kind) End Select #If DEBUG Then If Me._stackLocals Is Nothing OrElse Not Me._stackLocals.Any Then _builder.AssertStackEmpty() End If #End If End Sub Private Function EmitStatementAndCountInstructions(statement As BoundStatement) As Integer Dim n = _builder.InstructionsEmitted EmitStatement(statement) Return _builder.InstructionsEmitted - n End Function Private Sub EmitNoOpStatement(statement As BoundNoOpStatement) Select Case statement.Flavor Case NoOpStatementFlavor.Default If _ilEmitStyle = ILEmitStyle.Debug Then _builder.EmitOpCode(ILOpCode.Nop) End If Case NoOpStatementFlavor.AwaitYieldPoint Debug.Assert((_asyncYieldPoints Is Nothing) = (_asyncResumePoints Is Nothing)) If _asyncYieldPoints Is Nothing Then _asyncYieldPoints = ArrayBuilder(Of Integer).GetInstance _asyncResumePoints = ArrayBuilder(Of Integer).GetInstance End If Debug.Assert(_asyncYieldPoints.Count = _asyncResumePoints.Count) _asyncYieldPoints.Add(_builder.AllocateILMarker()) Case NoOpStatementFlavor.AwaitResumePoint Debug.Assert(_asyncYieldPoints IsNot Nothing) Debug.Assert(_asyncResumePoints IsNot Nothing) Debug.Assert((_asyncYieldPoints.Count - 1) = _asyncResumePoints.Count) _asyncResumePoints.Add(_builder.AllocateILMarker()) Case Else Throw ExceptionUtilities.UnexpectedValue(statement.Flavor) End Select End Sub Private Sub EmitTryStatement(statement As BoundTryStatement, Optional emitCatchesOnly As Boolean = False) Debug.Assert(Not statement.CatchBlocks.IsDefault) ' Stack must be empty at beginning of try block. _builder.AssertStackEmpty() ' IL requires catches and finally block to be distinct try ' blocks so if the source contained both a catch and ' a finally, nested scopes are emitted. Dim emitNestedScopes As Boolean = (Not emitCatchesOnly AndAlso (statement.CatchBlocks.Length > 0) AndAlso (statement.FinallyBlockOpt IsNot Nothing)) _builder.OpenLocalScope(ScopeType.TryCatchFinally) _builder.OpenLocalScope(ScopeType.Try) Me._tryNestingLevel += 1 If emitNestedScopes Then EmitTryStatement(statement, emitCatchesOnly:=True) Else EmitBlock(statement.TryBlock) End If Debug.Assert(Me._tryNestingLevel > 0) Me._tryNestingLevel -= 1 ' close Try scope _builder.CloseLocalScope() If Not emitNestedScopes Then For Each catchBlock In statement.CatchBlocks EmitCatchBlock(catchBlock) Next End If If Not emitCatchesOnly AndAlso (statement.FinallyBlockOpt IsNot Nothing) Then _builder.OpenLocalScope(ScopeType.Finally) EmitBlock(statement.FinallyBlockOpt) _builder.CloseLocalScope() End If _builder.CloseLocalScope() If Not emitCatchesOnly AndAlso statement.ExitLabelOpt IsNot Nothing Then _builder.MarkLabel(statement.ExitLabelOpt) End If End Sub 'The interesting part in the following method is the support for exception filters. '=== Example: ' 'Try ' <SomeCode> 'Catch ex as NullReferenceException When ex.Message isnot Nothing ' <Handler> 'End Try ' 'gets emitted as something like ===> ' 'Try ' <SomeCode> 'Filter ' Condition ' starts with exception on the stack ' Dim temp As NullReferenceException = TryCast(Pop, NullReferenceException) ' if temp is Nothing ' Push 0 ' Else ' ex = temp ' Push if ((ex.Message isnot Nothing), 1, 0) ' End If ' End Condition ' leaves 1 or 0 on the stack ' Handler ' gets called after finalization of nested exception frames if condition above produced 1 ' <Handler> ' End Handler 'End Try Private Sub EmitCatchBlock(catchBlock As BoundCatchBlock) Dim oldCatchBlock = _currentCatchBlock _currentCatchBlock = catchBlock Dim typeCheckFailedLabel As Object = Nothing Dim exceptionSource = catchBlock.ExceptionSourceOpt Dim exceptionType As Cci.ITypeReference If exceptionSource IsNot Nothing Then exceptionType = Me._module.Translate(exceptionSource.Type, exceptionSource.Syntax, _diagnostics) Else ' if type is not specified it is assumed to be System.Exception exceptionType = Me._module.Translate(Me._module.Compilation.GetWellKnownType(WellKnownType.System_Exception), catchBlock.Syntax, _diagnostics) End If ' exception on stack _builder.AdjustStack(1) If catchBlock.ExceptionFilterOpt IsNot Nothing AndAlso catchBlock.ExceptionFilterOpt.Kind = BoundKind.UnstructuredExceptionHandlingCatchFilter Then ' This is a special catch created for Unstructured Exception Handling Debug.Assert(catchBlock.LocalOpt Is Nothing) Debug.Assert(exceptionSource Is Nothing) ' ' Generate the OnError filter. ' ' The On Error filter catches an exception when a handler is active and the method ' isn't currently in the process of handling an earlier error. We know the method ' is handling an earlier error when we have a valid Resume target. ' ' The filter expression is the equivalent of: ' ' Catch e When (TypeOf e Is Exception) And (ActiveHandler <> 0) And (ResumeTarget = 0) ' Dim filter = DirectCast(catchBlock.ExceptionFilterOpt, BoundUnstructuredExceptionHandlingCatchFilter) _builder.OpenLocalScope(ScopeType.Filter) 'Determine if the exception object is or inherits from System.Exception _builder.EmitOpCode(ILOpCode.Isinst) _builder.EmitToken(exceptionType, catchBlock.Syntax) _builder.EmitOpCode(ILOpCode.Ldnull) _builder.EmitOpCode(ILOpCode.Cgt_un) ' Calculate ActiveHandler <> 0 EmitLocalLoad(filter.ActiveHandlerLocal, used:=True) _builder.EmitIntConstant(0) _builder.EmitOpCode(ILOpCode.Cgt_un) ' AND the values together. _builder.EmitOpCode(ILOpCode.And) ' Calculate ResumeTarget = 0 EmitLocalLoad(filter.ResumeTargetLocal, used:=True) _builder.EmitIntConstant(0) _builder.EmitOpCode(ILOpCode.Ceq) ' AND the values together. _builder.EmitOpCode(ILOpCode.And) ' Now we are starting the actual handler _builder.MarkFilterConditionEnd() _builder.EmitOpCode(ILOpCode.Castclass) _builder.EmitToken(exceptionType, catchBlock.Syntax) If ShouldNoteProjectErrors() Then EmitSetProjectError(catchBlock.Syntax, catchBlock.ErrorLineNumberOpt) Else _builder.EmitOpCode(ILOpCode.Pop) End If Else ' open appropriate exception handler scope. (Catch or Filter) ' if it is a Filter, emit prologue that checks if the type on the stack ' converts to what we want. If catchBlock.ExceptionFilterOpt Is Nothing Then _builder.OpenLocalScope(ScopeType.Catch, exceptionType) If catchBlock.IsSynthesizedAsyncCatchAll Then Debug.Assert(_asyncCatchHandlerOffset < 0) _asyncCatchHandlerOffset = _builder.AllocateILMarker() End If Else _builder.OpenLocalScope(ScopeType.Filter) ' Filtering starts with simulating regular Catch through an imperative type check ' If this is not our type, then we are done Dim typeCheckPassedLabel As New Object typeCheckFailedLabel = New Object _builder.EmitOpCode(ILOpCode.Isinst) _builder.EmitToken(exceptionType, catchBlock.Syntax) _builder.EmitOpCode(ILOpCode.Dup) _builder.EmitBranch(ILOpCode.Brtrue, typeCheckPassedLabel) _builder.EmitOpCode(ILOpCode.Pop) _builder.EmitIntConstant(0) _builder.EmitBranch(ILOpCode.Br, typeCheckFailedLabel) _builder.MarkLabel(typeCheckPassedLabel) End If ' define local if we have one Dim localOpt = catchBlock.LocalOpt If localOpt IsNot Nothing Then ' TODO: this local can be released when we can release named locals. Dim declNodes = localOpt.DeclaringSyntaxReferences DefineLocal(localOpt, If(Not declNodes.IsEmpty, DirectCast(declNodes(0).GetSyntax(), VisualBasicSyntaxNode), catchBlock.Syntax)) End If ' assign the exception variable if we have one If exceptionSource IsNot Nothing Then If ShouldNoteProjectErrors() Then _builder.EmitOpCode(ILOpCode.Dup) EmitSetProjectError(catchBlock.Syntax, catchBlock.ErrorLineNumberOpt) End If ' here we have our exception on the stack in a form of a reference type (O) ' it means that we have to "unbox" it before storing to the local ' if exception's type is a generic type parameter. If exceptionSource.Type.IsTypeParameter Then _builder.EmitOpCode(ILOpCode.Unbox_any) EmitSymbolToken(exceptionSource.Type, exceptionSource.Syntax) End If ' TODO: parts of the following code is common with AssignmentExpression ' the only major difference is that assignee is on the stack ' consider factoring out common code. While exceptionSource.Kind = BoundKind.Sequence Dim seq = DirectCast(exceptionSource, BoundSequence) EmitSideEffects(seq.SideEffects) If seq.ValueOpt Is Nothing Then Exit While Else exceptionSource = seq.ValueOpt End If End While Select Case exceptionSource.Kind Case BoundKind.Local Debug.Assert(Not DirectCast(exceptionSource, BoundLocal).LocalSymbol.IsByRef) _builder.EmitLocalStore(GetLocal(DirectCast(exceptionSource, BoundLocal))) Case BoundKind.Parameter Dim left = DirectCast(exceptionSource, BoundParameter) ' When assigning to a byref param ' we need to push param address below the exception If left.ParameterSymbol.IsByRef Then Dim temp = AllocateTemp(exceptionSource.Type, exceptionSource.Syntax) _builder.EmitLocalStore(temp) _builder.EmitLoadArgumentOpcode(ParameterSlot(left)) _builder.EmitLocalLoad(temp) FreeTemp(temp) End If EmitParameterStore(left) Case BoundKind.FieldAccess Dim left = DirectCast(exceptionSource, BoundFieldAccess) If Not left.FieldSymbol.IsShared Then Dim stateMachineField = TryCast(left.FieldSymbol, StateMachineFieldSymbol) If (stateMachineField IsNot Nothing) AndAlso (stateMachineField.SlotIndex >= 0) Then DefineUserDefinedStateMachineHoistedLocal(stateMachineField) End If ' When assigning to a field ' we need to push param address below the exception Dim temp = AllocateTemp(exceptionSource.Type, exceptionSource.Syntax) _builder.EmitLocalStore(temp) Dim receiver = left.ReceiverOpt ' EmitFieldReceiver will handle receivers with type parameter type, ' but we do not know of a test case that will get here with receiver ' of type T. The assert is here to catch such a case. If the assert ' fails, remove the assert and add a corresponding test case. Debug.Assert(receiver.Type.TypeKind <> TypeKind.TypeParameter) Dim temp1 = EmitReceiverRef(receiver, isAccessConstrained:=False, addressKind:=AddressKind.[ReadOnly]) Debug.Assert(temp1 Is Nothing, "temp is unexpected when assigning to a field") _builder.EmitLocalLoad(temp) End If EmitFieldStore(left) Case Else Throw ExceptionUtilities.UnexpectedValue(exceptionSource.Kind) End Select Else If ShouldNoteProjectErrors() Then EmitSetProjectError(catchBlock.Syntax, catchBlock.ErrorLineNumberOpt) Else _builder.EmitOpCode(ILOpCode.Pop) End If End If ' emit the actual filter expression, if we have one, ' and normalize results If catchBlock.ExceptionFilterOpt IsNot Nothing Then EmitCondExpr(catchBlock.ExceptionFilterOpt, True) ' Normalize the return value because values other than 0 or 1 produce unspecified results. _builder.EmitIntConstant(0) _builder.EmitOpCode(ILOpCode.Cgt_un) _builder.MarkLabel(typeCheckFailedLabel) ' Now we are starting the actual handler _builder.MarkFilterConditionEnd() 'pop the exception, it should have been already stored to the variable by the filter. _builder.EmitOpCode(ILOpCode.Pop) End If End If ' emit actual handler body ' Note that it is a block so it will introduce its own scope for locals ' it should also have access to the exception local if we have declared one ' as that is scoped to the whole Catch/Filter EmitBlock(catchBlock.Body) ' if the end of handler is reachable we should clear project errors. ' (if unreachable, this will not be emitted) If ShouldNoteProjectErrors() AndAlso (catchBlock.ExceptionFilterOpt Is Nothing OrElse catchBlock.ExceptionFilterOpt.Kind <> BoundKind.UnstructuredExceptionHandlingCatchFilter) Then EmitClearProjectError(catchBlock.Syntax) End If _builder.CloseLocalScope() _currentCatchBlock = oldCatchBlock End Sub ''' <summary> ''' Tells if we should emit [Set/Clear]ProjectErrors when entering/leaving handlers ''' </summary> Private Function ShouldNoteProjectErrors() As Boolean Return Not Me._module.SourceModule.ContainingSourceAssembly.IsVbRuntime End Function Private Sub EmitSetProjectError(syntaxNode As SyntaxNode, errorLineNumberOpt As BoundExpression) Dim setProjectErrorMethod As MethodSymbol If errorLineNumberOpt Is Nothing Then setProjectErrorMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__SetProjectError), MethodSymbol) ' consumes exception object from the stack _builder.EmitOpCode(ILOpCode.Call, -1) Else setProjectErrorMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__SetProjectError_Int32), MethodSymbol) EmitExpression(errorLineNumberOpt, used:=True) ' consumes exception object from the stack and the error line number _builder.EmitOpCode(ILOpCode.Call, -2) End If Me.EmitSymbolToken(setProjectErrorMethod, syntaxNode) End Sub Private Sub EmitClearProjectError(syntaxNode As SyntaxNode) Const clearProjectError As WellKnownMember = WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__ClearProjectError Dim clearProjectErrorMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(clearProjectError), MethodSymbol) ' void static with no arguments _builder.EmitOpCode(ILOpCode.Call, 0) Me.EmitSymbolToken(clearProjectErrorMethod, syntaxNode) End Sub ' specifies whether emitted conditional expression was a constant true/false or not a constant Private Enum ConstResKind ConstFalse ConstTrue NotAConst End Enum Private Sub EmitConditionalGoto(boundConditionalGoto As BoundConditionalGoto) Dim label As Object = boundConditionalGoto.Label Debug.Assert(label IsNot Nothing) EmitCondBranch(boundConditionalGoto.Condition, label, boundConditionalGoto.JumpIfTrue) End Sub ' 3.17 The brfalse instruction transfers control to target if value (of type int32, int64, object reference, managed 'pointer, unmanaged pointer or native int) is zero (false). If value is non-zero (true), execution continues at 'the next instruction. Private Function CanPassToBrfalse(ts As TypeSymbol) As Boolean If ts.IsEnumType Then ' valid enums are all primitives Return True End If Dim tc = ts.PrimitiveTypeCode Select Case tc Case Cci.PrimitiveTypeCode.Float32, Cci.PrimitiveTypeCode.Float64 Return False Case Cci.PrimitiveTypeCode.NotPrimitive ' if this is a generic type param, verifier will want us to box ' EmitCondBranch knows that Return ts.IsReferenceType Case Else Debug.Assert(tc <> Cci.PrimitiveTypeCode.Invalid) Debug.Assert(tc <> Cci.PrimitiveTypeCode.Void) Return True End Select End Function Private Function TryReduce(condition As BoundBinaryOperator, ByRef sense As Boolean) As BoundExpression Dim opKind = condition.OperatorKind And BinaryOperatorKind.OpMask Debug.Assert(opKind = BinaryOperatorKind.Equals OrElse opKind = BinaryOperatorKind.NotEquals OrElse opKind = BinaryOperatorKind.Is OrElse opKind = BinaryOperatorKind.IsNot) Dim nonConstOp As BoundExpression Dim constOp As ConstantValue = condition.Left.ConstantValueOpt If constOp IsNot Nothing Then nonConstOp = condition.Right Else constOp = condition.Right.ConstantValueOpt If constOp Is Nothing Then Return Nothing End If nonConstOp = condition.Left End If Dim nonConstType = nonConstOp.Type Debug.Assert(nonConstType IsNot Nothing OrElse (nonConstOp.IsNothingLiteral() AndAlso (opKind = BinaryOperatorKind.Is OrElse opKind = BinaryOperatorKind.IsNot))) If nonConstType IsNot Nothing AndAlso Not CanPassToBrfalse(nonConstType) Then Return Nothing End If Dim isBool As Boolean = nonConstType IsNot Nothing AndAlso nonConstType.PrimitiveTypeCode = Microsoft.Cci.PrimitiveTypeCode.Boolean Dim isZero As Boolean = constOp.IsDefaultValue If Not isBool AndAlso Not isZero Then Return Nothing End If If isZero Then sense = Not sense End If If opKind = BinaryOperatorKind.NotEquals OrElse opKind = BinaryOperatorKind.IsNot Then sense = Not sense End If Return nonConstOp End Function Private Const s_IL_OP_CODE_ROW_LENGTH = 4 ' // < <= > >= ' ILOpCode.Blt, ILOpCode.Ble, ILOpCode.Bgt, ILOpCode.Bge, // Signed ' ILOpCode.Bge, ILOpCode.Bgt, ILOpCode.Ble, ILOpCode.Blt, // Signed Invert ' ILOpCode.Blt_un, ILOpCode.Ble_un, ILOpCode.Bgt_un, ILOpCode.Bge_un, // Unsigned ' ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un, // Unsigned Invert ' ILOpCode.Blt, ILOpCode.Ble, ILOpCode.Bgt, ILOpCode.Bge, // Float ' ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un, // Float Invert Private Shared ReadOnly s_condJumpOpCodes As ILOpCode() = New ILOpCode() { ILOpCode.Blt, ILOpCode.Ble, ILOpCode.Bgt, ILOpCode.Bge, ILOpCode.Bge, ILOpCode.Bgt, ILOpCode.Ble, ILOpCode.Blt, ILOpCode.Blt_un, ILOpCode.Ble_un, ILOpCode.Bgt_un, ILOpCode.Bge_un, ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un, ILOpCode.Blt, ILOpCode.Ble, ILOpCode.Bgt, ILOpCode.Bge, ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un} Private Function CodeForJump(expression As BoundBinaryOperator, sense As Boolean, <Out()> ByRef revOpCode As ILOpCode) As ILOpCode Dim opIdx As Integer Dim opKind = (expression.OperatorKind And BinaryOperatorKind.OpMask) Dim operandType = expression.Left.Type Debug.Assert(operandType IsNot Nothing OrElse (expression.Left.IsNothingLiteral() AndAlso (opKind = BinaryOperatorKind.Is OrElse opKind = BinaryOperatorKind.IsNot))) If operandType IsNot Nothing AndAlso operandType.IsBooleanType() Then ' Since VB True is -1 but is stored as 1 in IL, relational operations on Boolean must ' be reversed to yield the correct results. Note that = and <> do not need reversal. Select Case opKind Case BinaryOperatorKind.LessThan opKind = BinaryOperatorKind.GreaterThan Case BinaryOperatorKind.LessThanOrEqual opKind = BinaryOperatorKind.GreaterThanOrEqual Case BinaryOperatorKind.GreaterThan opKind = BinaryOperatorKind.LessThan Case BinaryOperatorKind.GreaterThanOrEqual opKind = BinaryOperatorKind.LessThanOrEqual End Select End If Select Case opKind Case BinaryOperatorKind.IsNot ValidateReferenceEqualityOperands(expression) Return If(sense, ILOpCode.Bne_un, ILOpCode.Beq) Case BinaryOperatorKind.Is ValidateReferenceEqualityOperands(expression) Return If(sense, ILOpCode.Beq, ILOpCode.Bne_un) Case BinaryOperatorKind.Equals Return If(sense, ILOpCode.Beq, ILOpCode.Bne_un) Case BinaryOperatorKind.NotEquals Return If(sense, ILOpCode.Bne_un, ILOpCode.Beq) Case BinaryOperatorKind.LessThan opIdx = 0 Case BinaryOperatorKind.LessThanOrEqual opIdx = 1 Case BinaryOperatorKind.GreaterThan opIdx = 2 Case BinaryOperatorKind.GreaterThanOrEqual opIdx = 3 Case Else Throw ExceptionUtilities.UnexpectedValue(opKind) End Select If operandType IsNot Nothing Then If operandType.IsUnsignedIntegralType() Then opIdx += 2 * s_IL_OP_CODE_ROW_LENGTH 'unsigned Else If operandType.IsFloatingType() Then opIdx += 4 * s_IL_OP_CODE_ROW_LENGTH 'float End If End If End If Dim revOpIdx = opIdx If Not sense Then opIdx += s_IL_OP_CODE_ROW_LENGTH 'invert op Else revOpIdx += s_IL_OP_CODE_ROW_LENGTH 'invert orev End If revOpCode = s_condJumpOpCodes(revOpIdx) Return s_condJumpOpCodes(opIdx) End Function ' generate a jump to dest if (condition == sense) is true ' it is ok if lazyDest is Nothing ' if lazyDest is needed it will be initialized to a new object Private Sub EmitCondBranch(condition As BoundExpression, ByRef lazyDest As Object, sense As Boolean) _recursionDepth += 1 If _recursionDepth > 1 Then StackGuard.EnsureSufficientExecutionStack(_recursionDepth) EmitCondBranchCore(condition, lazyDest, sense) Else EmitCondBranchCoreWithStackGuard(condition, lazyDest, sense) End If _recursionDepth -= 1 End Sub Private Sub EmitCondBranchCoreWithStackGuard(condition As BoundExpression, ByRef lazyDest As Object, sense As Boolean) Debug.Assert(_recursionDepth = 1) Try EmitCondBranchCore(condition, lazyDest, sense) Debug.Assert(_recursionDepth = 1) Catch ex As InsufficientExecutionStackException _diagnostics.Add(ERRID.ERR_TooLongOrComplexExpression, BoundTreeVisitor.CancelledByStackGuardException.GetTooLongOrComplexExpressionErrorLocation(condition)) Throw New EmitCancelledException() End Try End Sub Private Sub EmitCondBranchCore(condition As BoundExpression, ByRef lazyDest As Object, sense As Boolean) oneMoreTime: Dim ilcode As ILOpCode Dim constExprValue = condition.ConstantValueOpt If constExprValue IsNot Nothing Then ' make sure that only the bool bits are set or it is a Nothing literal ' or it is a string literal in which case it is equal to True Debug.Assert(constExprValue.Discriminator = ConstantValueTypeDiscriminator.Boolean OrElse constExprValue.Discriminator = ConstantValueTypeDiscriminator.String OrElse constExprValue.IsNothing) Dim taken As Boolean = constExprValue.IsDefaultValue <> sense If taken Then lazyDest = If(lazyDest, New Object()) _builder.EmitBranch(ILOpCode.Br, lazyDest) Else ' otherwise this branch will never be taken, so just fall through... End If Return End If Select Case condition.Kind Case BoundKind.BinaryOperator Dim binOp = DirectCast(condition, BoundBinaryOperator) Dim testBothArgs As Boolean = sense Select Case binOp.OperatorKind And BinaryOperatorKind.OpMask Case BinaryOperatorKind.OrElse testBothArgs = Not testBothArgs GoTo BinaryOperatorKindLogicalAnd Case BinaryOperatorKind.AndAlso BinaryOperatorKindLogicalAnd: If testBothArgs Then ' gotoif(a != sense) fallThrough ' gotoif(b == sense) dest ' fallThrough: Dim lazyFallThrough = New Object EmitCondBranch(binOp.Left, lazyFallThrough, Not sense) EmitCondBranch(binOp.Right, lazyDest, sense) If (lazyFallThrough IsNot Nothing) Then _builder.MarkLabel(lazyFallThrough) End If Else EmitCondBranch(binOp.Left, lazyDest, sense) condition = binOp.Right GoTo oneMoreTime End If Return Case BinaryOperatorKind.IsNot, BinaryOperatorKind.Is ValidateReferenceEqualityOperands(binOp) GoTo BinaryOperatorKindEqualsNotEquals Case BinaryOperatorKind.Equals, BinaryOperatorKind.NotEquals BinaryOperatorKindEqualsNotEquals: Dim reduced = TryReduce(binOp, sense) If reduced IsNot Nothing Then condition = reduced GoTo oneMoreTime End If GoTo BinaryOperatorKindLessThan Case BinaryOperatorKind.LessThan, BinaryOperatorKind.LessThanOrEqual, BinaryOperatorKind.GreaterThan, BinaryOperatorKind.GreaterThanOrEqual BinaryOperatorKindLessThan: EmitExpression(binOp.Left, True) EmitExpression(binOp.Right, True) Dim revOpCode As ILOpCode ilcode = CodeForJump(binOp, sense, revOpCode) lazyDest = If(lazyDest, New Object()) _builder.EmitBranch(ilcode, lazyDest, revOpCode) Return End Select ' none of above. ' then it is regular binary expression - Or, And, Xor ... GoTo OtherExpressions Case BoundKind.UnaryOperator Dim unOp = DirectCast(condition, BoundUnaryOperator) If (unOp.OperatorKind = UnaryOperatorKind.Not) Then Debug.Assert(unOp.Type.IsBooleanType()) sense = Not sense condition = unOp.Operand GoTo oneMoreTime Else GoTo OtherExpressions End If Case BoundKind.TypeOf Dim typeOfExpression = DirectCast(condition, BoundTypeOf) EmitTypeOfExpression(typeOfExpression, used:=True, optimize:=True) If typeOfExpression.IsTypeOfIsNotExpression Then sense = Not sense End If ilcode = If(sense, ILOpCode.Brtrue, ILOpCode.Brfalse) lazyDest = If(lazyDest, New Object()) _builder.EmitBranch(ilcode, lazyDest) Return #If False Then Case BoundKind.AsOperator Dim asOp = DirectCast(condition, BoundIsOperator) EmitExpression(asOp.Operand, True) _builder.EmitOpCode(ILOpCode.Isinst) EmitSymbolToken(asOp.TargetType.Type) ilcode = If(sense, ILOpCode.Brtrue, ILOpCode.Brfalse) _builder.EmitBranch(ilcode, dest) Return #End If Case BoundKind.Sequence Dim sequence = DirectCast(condition, BoundSequence) EmitSequenceCondBranch(sequence, lazyDest, sense) Return Case Else OtherExpressions: EmitExpression(condition, True) Dim conditionType = condition.Type If Not conditionType.IsValueType AndAlso Not IsVerifierReference(conditionType) Then EmitBox(conditionType, condition.Syntax) End If ilcode = If(sense, ILOpCode.Brtrue, ILOpCode.Brfalse) lazyDest = If(lazyDest, New Object()) _builder.EmitBranch(ilcode, lazyDest) Return End Select End Sub <Conditional("DEBUG")> Private Sub ValidateReferenceEqualityOperands(binOp As BoundBinaryOperator) Debug.Assert(binOp.Left.IsNothingLiteral() OrElse binOp.Left.Type.SpecialType = SpecialType.System_Object OrElse binOp.WasCompilerGenerated) Debug.Assert(binOp.Right.IsNothingLiteral() OrElse binOp.Right.Type.SpecialType = SpecialType.System_Object OrElse binOp.WasCompilerGenerated) End Sub 'TODO: is this to fold value? Same in C#? Private Sub EmitSequenceCondBranch(sequence As BoundSequence, ByRef lazyDest As Object, sense 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) Me.EmitCondBranch(sequence.ValueOpt, lazyDest, sense) If hasLocals Then _builder.CloseLocalScope() For Each local In sequence.Locals Me.FreeLocal(local) Next End If End Sub Private Sub EmitLabelStatement(boundLabelStatement As BoundLabelStatement) _builder.MarkLabel(boundLabelStatement.Label) End Sub Private Sub EmitGotoStatement(boundGotoStatement As BoundGotoStatement) ' if branch leaves current Catch block we need to emit ClearProjectError() If ShouldNoteProjectErrors() Then If _currentCatchBlock IsNot Nothing AndAlso (_currentCatchBlock.ExceptionFilterOpt Is Nothing OrElse _currentCatchBlock.ExceptionFilterOpt.Kind <> BoundKind.UnstructuredExceptionHandlingCatchFilter) AndAlso Not LabelFinder.NodeContainsLabel(_currentCatchBlock, boundGotoStatement.Label) Then EmitClearProjectError(boundGotoStatement.Syntax) End If End If _builder.EmitBranch(ILOpCode.Br, boundGotoStatement.Label) End Sub ''' <summary> ''' tells if given node contains a label statement that defines given label symbol ''' </summary> Private Class LabelFinder Inherits StatementWalker Private ReadOnly _label As LabelSymbol Private _found As Boolean = False Private Sub New(label As LabelSymbol) Me._label = label End Sub Public Overrides Function Visit(node As BoundNode) As BoundNode If Not _found AndAlso TypeOf node IsNot BoundExpression Then Return MyBase.Visit(node) End If Return Nothing End Function Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode If node.Label Is Me._label Then _found = True End If Return MyBase.VisitLabelStatement(node) End Function Public Shared Function NodeContainsLabel(node As BoundNode, label As LabelSymbol) As Boolean Dim finder = New LabelFinder(label) finder.Visit(node) Return finder._found End Function End Class Private Sub EmitReturnStatement(boundReturnStatement As BoundReturnStatement) Me.EmitExpression(boundReturnStatement.ExpressionOpt, True) _builder.EmitRet(boundReturnStatement.ExpressionOpt Is Nothing) End Sub Private Sub EmitThrowStatement(boundThrowStatement As BoundThrowStatement) Dim operand = boundThrowStatement.ExpressionOpt If operand IsNot Nothing Then EmitExpression(operand, used:=True) Dim operandType = operand.Type ' "Throw Nothing" is not supported by the language ' so operand.Type should always be set. Debug.Assert(operandType IsNot Nothing) If (operandType IsNot Nothing) AndAlso (operandType.TypeKind = TypeKind.TypeParameter) Then EmitBox(operandType, operand.Syntax) End If End If _builder.EmitThrow(operand Is Nothing) End Sub Private Sub EmitSelectStatement(boundSelectStatement As BoundSelectStatement) Debug.Assert(boundSelectStatement.RecommendSwitchTable) Dim selectExpression = boundSelectStatement.ExpressionStatement.Expression Dim caseBlocks = boundSelectStatement.CaseBlocks Dim exitLabel = boundSelectStatement.ExitLabel Dim fallThroughLabel = exitLabel Debug.Assert(selectExpression.Type IsNot Nothing) Debug.Assert(caseBlocks.Any()) ' Create labels for case blocks Dim caseBlockLabels As ImmutableArray(Of GeneratedLabelSymbol) = CreateCaseBlockLabels(caseBlocks) ' Create an array of key value pairs (key: case clause constant value, value: case block label) ' for emitting switch table based header. ' This function also ensures the correct fallThroughLabel is set, i.e. case else block label if one exists, otherwise exit label. Dim caseLabelsForEmit As KeyValuePair(Of ConstantValue, Object)() = GetCaseLabelsForEmitSwitchHeader(caseBlocks, caseBlockLabels, fallThroughLabel) ' Emit switch table header EmitSwitchTableHeader(selectExpression, caseLabelsForEmit, fallThroughLabel) ' Emit case blocks EmitCaseBlocks(caseBlocks, caseBlockLabels, exitLabel) ' Emit exit label _builder.MarkLabel(exitLabel) End Sub ' Create a label for each case block Private Function CreateCaseBlockLabels(caseBlocks As ImmutableArray(Of BoundCaseBlock)) As ImmutableArray(Of GeneratedLabelSymbol) Debug.Assert(Not caseBlocks.IsEmpty) Dim caseBlockLabels = ArrayBuilder(Of GeneratedLabelSymbol).GetInstance(caseBlocks.Length) Dim cur As Integer = 0 For Each caseBlock In caseBlocks cur = cur + 1 caseBlockLabels.Add(New GeneratedLabelSymbol("Case Block " + cur.ToString(Globalization.CultureInfo.InvariantCulture))) Next Return caseBlockLabels.ToImmutableAndFree() End Function ' Creates an array of key value pairs (key: case clause constant value, value: case block label) ' for emitting switch table based header. ' This function also ensures the correct fallThroughLabel is set, i.e. case else block label if one exists, otherwise exit label. Private Function GetCaseLabelsForEmitSwitchHeader( caseBlocks As ImmutableArray(Of BoundCaseBlock), caseBlockLabels As ImmutableArray(Of GeneratedLabelSymbol), ByRef fallThroughLabel As LabelSymbol ) As KeyValuePair(Of ConstantValue, Object)() Debug.Assert(Not caseBlocks.IsEmpty) Debug.Assert(Not caseBlockLabels.IsEmpty) Debug.Assert(caseBlocks.Length = caseBlockLabels.Length) Dim labelsBuilder = ArrayBuilder(Of KeyValuePair(Of ConstantValue, Object)).GetInstance() Dim constantsSet = New HashSet(Of ConstantValue)(New SwitchConstantValueHelper.SwitchLabelsComparer()) Dim cur As Integer = 0 For Each caseBlock In caseBlocks Dim caseBlockLabel = caseBlockLabels(cur) Dim caseClauses = caseBlock.CaseStatement.CaseClauses If caseClauses.Any() Then For Each caseClause In caseClauses Dim constant As ConstantValue Select Case caseClause.Kind Case BoundKind.SimpleCaseClause Dim simpleCaseClause = DirectCast(caseClause, BoundSimpleCaseClause) Debug.Assert(simpleCaseClause.ValueOpt IsNot Nothing) Debug.Assert(simpleCaseClause.ConditionOpt Is Nothing) constant = simpleCaseClause.ValueOpt.ConstantValueOpt Case BoundKind.RelationalCaseClause Dim relationalCaseClause = DirectCast(caseClause, BoundRelationalCaseClause) Debug.Assert(relationalCaseClause.OperatorKind = BinaryOperatorKind.Equals) Debug.Assert(relationalCaseClause.ValueOpt IsNot Nothing) Debug.Assert(relationalCaseClause.ConditionOpt Is Nothing) constant = relationalCaseClause.ValueOpt.ConstantValueOpt Case BoundKind.RangeCaseClause ' TODO: For now we use IF lists if we encounter ' TODO: BoundRangeCaseClause, we should not reach here. Throw ExceptionUtilities.UnexpectedValue(caseClause.Kind) Case Else Throw ExceptionUtilities.UnexpectedValue(caseClause.Kind) End Select Debug.Assert(constant IsNot Nothing) Debug.Assert(SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(constant)) ' If we have duplicate case value constants, use the lexically first case constant. If Not constantsSet.Contains(constant) Then labelsBuilder.Add(New KeyValuePair(Of ConstantValue, Object)(constant, caseBlockLabel)) constantsSet.Add(constant) End If Next Else Debug.Assert(caseBlock.Syntax.Kind = SyntaxKind.CaseElseBlock) ' We have a case else block, update the fallThroughLabel to the corresponding caseBlockLabel fallThroughLabel = caseBlockLabel End If cur = cur + 1 Next Return labelsBuilder.ToArrayAndFree() End Function Private Sub EmitSwitchTableHeader(selectExpression As BoundExpression, caseLabels As KeyValuePair(Of ConstantValue, Object)(), fallThroughLabel As LabelSymbol) Debug.Assert(selectExpression.Type IsNot Nothing) Debug.Assert(caseLabels IsNot Nothing) If Not caseLabels.Any() Then ' No case labels, emit branch to fallThroughLabel _builder.EmitBranch(ILOpCode.Br, fallThroughLabel) Else Dim exprType = selectExpression.Type Dim syntax As SyntaxNode = selectExpression.Syntax Dim temp As LocalDefinition = Nothing If exprType.SpecialType <> SpecialType.System_String Then If selectExpression.Kind = BoundKind.Local AndAlso Not DirectCast(selectExpression, BoundLocal).LocalSymbol.IsByRef Then _builder.EmitIntegerSwitchJumpTable(caseLabels, fallThroughLabel, GetLocal(DirectCast(selectExpression, BoundLocal)), keyTypeCode:=exprType.GetEnumUnderlyingTypeOrSelf.PrimitiveTypeCode, syntax) ElseIf selectExpression.Kind = BoundKind.Parameter AndAlso Not DirectCast(selectExpression, BoundParameter).ParameterSymbol.IsByRef Then _builder.EmitIntegerSwitchJumpTable(caseLabels, fallThroughLabel, ParameterSlot(DirectCast(selectExpression, BoundParameter)), keyTypeCode:=exprType.GetEnumUnderlyingTypeOrSelf.PrimitiveTypeCode, syntax) Else EmitExpression(selectExpression, True) temp = AllocateTemp(exprType, syntax) _builder.EmitLocalStore(temp) _builder.EmitIntegerSwitchJumpTable(caseLabels, fallThroughLabel, temp, keyTypeCode:=exprType.GetEnumUnderlyingTypeOrSelf.PrimitiveTypeCode, syntax) End If Else If selectExpression.Kind = BoundKind.Local AndAlso Not DirectCast(selectExpression, BoundLocal).LocalSymbol.IsByRef Then EmitStringSwitchJumpTable(caseLabels, fallThroughLabel, GetLocal(DirectCast(selectExpression, BoundLocal)), syntax) Else EmitExpression(selectExpression, True) temp = AllocateTemp(exprType, syntax) _builder.EmitLocalStore(temp) EmitStringSwitchJumpTable(caseLabels, fallThroughLabel, temp, syntax) End If End If If temp IsNot Nothing Then FreeTemp(temp) End If End If End Sub Private Sub EmitStringSwitchJumpTable(caseLabels As KeyValuePair(Of ConstantValue, Object)(), fallThroughLabel As LabelSymbol, key As LocalDefinition, syntaxNode As SyntaxNode) Dim genHashTableSwitch As Boolean = SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch(caseLabels.Length) Dim keyHash As LocalDefinition = Nothing If genHashTableSwitch Then Dim privateImplClass = _module.GetPrivateImplClass(syntaxNode, _diagnostics) Dim stringHashMethodRef As Microsoft.Cci.IReference = privateImplClass.GetMethod(PrivateImplementationDetails.SynthesizedStringHashFunctionName) Debug.Assert(stringHashMethodRef IsNot Nothing) ' static uint ComputeStringHash(string s) ' pop 1 (s) ' push 1 (uint return value) ' stackAdjustment = (pushCount - popCount) = 0 _builder.EmitLocalLoad(key) _builder.EmitOpCode(ILOpCode.[Call], stackAdjustment:=0) _builder.EmitToken(stringHashMethodRef, syntaxNode) Dim UInt32Type = DirectCast(_module.GetSpecialType(SpecialType.System_UInt32, syntaxNode, _diagnostics).GetInternalSymbol(), TypeSymbol) keyHash = AllocateTemp(UInt32Type, syntaxNode) _builder.EmitLocalStore(keyHash) End If ' Prefer embedded version of the member if present Dim embeddedOperatorsType As NamedTypeSymbol = Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators) Dim compareStringMember As WellKnownMember = If(embeddedOperatorsType.IsErrorType AndAlso TypeOf embeddedOperatorsType Is MissingMetadataTypeSymbol, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareStringStringStringBoolean, WellKnownMember.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators__CompareStringStringStringBoolean) Dim stringCompareMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(compareStringMember), MethodSymbol) Dim stringCompareMethodRef As Cci.IReference = Me._module.Translate(stringCompareMethod, needDeclaration:=False, syntaxNodeOpt:=syntaxNode, diagnostics:=_diagnostics) Dim compareDelegate As SwitchStringJumpTableEmitter.EmitStringCompareAndBranch = Sub(keyArg, stringConstant, targetLabel) EmitStringCompareAndBranch(keyArg, syntaxNode, stringConstant, targetLabel, stringCompareMethodRef) End Sub _builder.EmitStringSwitchJumpTable( syntaxNode, caseLabels, fallThroughLabel, key, keyHash, compareDelegate, AddressOf SynthesizedStringSwitchHashMethod.ComputeStringHash) If keyHash IsNot Nothing Then FreeTemp(keyHash) End If End Sub ''' <summary> ''' Delegate to emit string compare call and conditional branch based on the compare result. ''' </summary> ''' <param name="key">Key to compare</param> ''' <param name="syntaxNode">Node for diagnostics</param> ''' <param name="stringConstant">Case constant to compare the key against</param> ''' <param name="targetLabel">Target label to branch to if key = stringConstant</param> ''' <param name="stringCompareMethodRef">String equality method</param> Private Sub EmitStringCompareAndBranch(key As LocalOrParameter, syntaxNode As SyntaxNode, stringConstant As ConstantValue, targetLabel As Object, stringCompareMethodRef As Microsoft.Cci.IReference) ' Emit compare and branch: ' If key = stringConstant Then ' Goto targetLabel ' End If Debug.Assert(stringCompareMethodRef IsNot Nothing) #If DEBUG Then Dim assertDiagnostics = DiagnosticBag.GetInstance() Debug.Assert(stringCompareMethodRef Is Me._module.Translate(DirectCast( If(TypeOf Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators) Is MissingMetadataTypeSymbol, Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareStringStringStringBoolean), Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators__CompareStringStringStringBoolean)), MethodSymbol), needDeclaration:=False, syntaxNodeOpt:=DirectCast(syntaxNode, VisualBasicSyntaxNode), diagnostics:=assertDiagnostics)) assertDiagnostics.Free() #End If ' Public Shared Function CompareString(Left As String, Right As String, TextCompare As Boolean) As Integer ' pop 3 (Left, Right, TextCompare) ' push 1 (Integer return value) ' stackAdjustment = (pushCount - popCount) = -2 ' NOTE: We generate string switch table only for Option Compare Binary, i.e. TextCompare = False _builder.EmitLoad(key) _builder.EmitConstantValue(stringConstant, syntaxNode) _builder.EmitConstantValue(ConstantValue.False, syntaxNode) _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=-2) _builder.EmitToken(stringCompareMethodRef, syntaxNode) ' CompareString returns 0 if Left and Right strings are equal. ' Branch to targetLabel if CompareString returned 0. _builder.EmitBranch(ILOpCode.Brfalse, targetLabel, ILOpCode.Brtrue) End Sub Private Sub EmitCaseBlocks(caseBlocks As ImmutableArray(Of BoundCaseBlock), caseBlockLabels As ImmutableArray(Of GeneratedLabelSymbol), exitLabel As LabelSymbol) Debug.Assert(Not caseBlocks.IsEmpty) Debug.Assert(Not caseBlockLabels.IsEmpty) Debug.Assert(caseBlocks.Length = caseBlockLabels.Length) Dim cur As Integer = 0 For Each caseBlock In caseBlocks ' Emit case block label _builder.MarkLabel(caseBlockLabels(cur)) cur = cur + 1 ' Emit case statement sequence point Dim caseStatement = caseBlock.CaseStatement If Not caseStatement.WasCompilerGenerated Then Debug.Assert(caseStatement.Syntax IsNot Nothing) If _emitPdbSequencePoints Then EmitSequencePoint(caseStatement.Syntax) End If If _ilEmitStyle = ILEmitStyle.Debug Then ' Emit nop for the case statement otherwise the above sequence point ' will get associated with the first statement in subsequent case block. ' This matches the native compiler codegen. _builder.EmitOpCode(ILOpCode.Nop) End If End If ' Emit case block body EmitBlock(caseBlock.Body) ' Emit a branch to exit label _builder.EmitBranch(ILOpCode.Br, exitLabel) Next End Sub Private Sub EmitBlock(scope As BoundBlock) Dim hasLocals As Boolean = Not scope.Locals.IsEmpty If hasLocals Then _builder.OpenLocalScope() For Each local In scope.Locals Dim declNodes = local.DeclaringSyntaxReferences Me.DefineLocal(local, If(declNodes.IsEmpty, scope.Syntax, declNodes(0).GetVisualBasicSyntax())) Next End If For Each statement In scope.Statements EmitStatement(statement) Next If hasLocals Then _builder.CloseLocalScope() 'TODO: can we free any locals here? Perhaps nameless temps? End If End Sub Private Function DefineLocal(local As LocalSymbol, syntaxNode As SyntaxNode) As LocalDefinition Dim dynamicTransformFlags = ImmutableArray(Of Boolean).Empty Dim tupleElementNames = If(Not local.IsCompilerGenerated AndAlso local.Type.ContainsTupleNames(), VisualBasicCompilation.TupleNamesEncoder.Encode(local.Type), ImmutableArray(Of String).Empty) ' We're treating constants of type Decimal and DateTime as local here to not create a new instance for each time ' the value is accessed. This means there will be one local in the scope for this constant. ' This has the side effect that this constant will later on appear in the PDB file as a common local and one is able ' to modify the value in the debugger (which is a regression from Dev10). ' To fix this while keeping the behavior of having just one local for the const, one would need to preserve the ' information that this local is a ConstantButNotMetadataConstant (update ScopeManager.DeclareLocal & LocalDefinition) ' and modify PEWriter.Initialize to DefineLocalConstant instead of DefineLocalVariable if the local is ' ConstantButNotMetadataConstant. ' See bug #11047 If local.HasConstantValue Then Dim compileTimeValue As MetadataConstant = _module.CreateConstant(local.Type, local.ConstantValue, syntaxNode, _diagnostics) Dim localConstantDef = New LocalConstantDefinition( local.Name, If(local.Locations.FirstOrDefault(), Location.None), compileTimeValue, dynamicTransformFlags:=dynamicTransformFlags, tupleElementNames:=tupleElementNames) ' Reference in the scope for debugging purpose _builder.AddLocalConstantToScope(localConstantDef) Return Nothing End If If Me.IsStackLocal(local) Then Return Nothing End If Dim translatedType = _module.Translate(local.Type, syntaxNodeOpt:=syntaxNode, diagnostics:=_diagnostics) ' Even though we don't need the token immediately, we will need it later when signature for the local is emitted. ' Also, requesting the token has side-effect of registering types used, which is critical for embedded types (NoPia, VBCore, etc). _module.GetFakeSymbolTokenForIL(translatedType, syntaxNode, _diagnostics) Dim constraints = If(local.IsByRef, LocalSlotConstraints.ByRef, LocalSlotConstraints.None) Or If(local.IsPinned, LocalSlotConstraints.Pinned, LocalSlotConstraints.None) Dim localId As LocalDebugId = Nothing Dim name As String = GetLocalDebugName(local, localId) Dim synthesizedKind = local.SynthesizedKind Dim localDef = _builder.LocalSlotManager.DeclareLocal( type:=translatedType, symbol:=local, name:=name, kind:=synthesizedKind, id:=localId, pdbAttributes:=synthesizedKind.PdbAttributes(), constraints:=constraints, dynamicTransformFlags:=dynamicTransformFlags, tupleElementNames:=tupleElementNames, isSlotReusable:=synthesizedKind.IsSlotReusable(_ilEmitStyle <> ILEmitStyle.Release)) ' If named, add it to the local debug scope. If localDef.Name IsNot Nothing Then _builder.AddLocalToScope(localDef) End If Return localDef End Function ''' <summary> ''' Gets the name And id of the local that are going to be generated into the debug metadata. ''' </summary> Private Function GetLocalDebugName(local As LocalSymbol, <Out> ByRef localId As LocalDebugId) As String localId = LocalDebugId.None If local.IsImportedFromMetadata Then Return local.Name End If ' We include function value locals in async and iterator methods so that appropriate ' errors can be reported when users attempt to refer to them. However, there's no ' reason to actually emit them into the resulting MoveNext method, because they will ' never be accessed. Unfortunately, for implementation-specific reasons, dropping them ' would be non-trivial. Instead, we drop their names so that they do not appear while ' debugging (esp in the Locals window). If local.DeclarationKind = LocalDeclarationKind.FunctionValue AndAlso TypeOf _method Is SynthesizedStateMachineMethod Then Return Nothing End If Dim localKind = local.SynthesizedKind ' only user-defined locals should be named during lowering: Debug.Assert((local.Name Is Nothing) = (localKind <> SynthesizedLocalKind.UserDefined)) If Not localKind.IsLongLived() Then Return Nothing End If If _ilEmitStyle = ILEmitStyle.Debug Then Dim syntax = local.GetDeclaratorSyntax() Dim syntaxOffset = _method.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree) Dim ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset) ' user-defined locals should have 0 ordinal Debug.Assert(ordinal = 0 OrElse localKind <> SynthesizedLocalKind.UserDefined) localId = New LocalDebugId(syntaxOffset, ordinal) End If If local.Name IsNot Nothing Then Return local.Name End If Return GeneratedNames.MakeSynthesizedLocalName(localKind, _uniqueNameId) End Function Private Function IsSlotReusable(local As LocalSymbol) As Boolean Return local.SynthesizedKind.IsSlotReusable(_ilEmitStyle <> ILEmitStyle.Release) End Function Private Sub FreeLocal(local As LocalSymbol) 'TODO: releasing locals with name NYI. 'NOTE: VB considers named local's extent to be whole method ' so releasing them may just not be possible. If local.Name Is Nothing AndAlso IsSlotReusable(local) AndAlso Not IsStackLocal(local) Then _builder.LocalSlotManager.FreeLocal(local) End If End Sub ''' <summary> ''' Gets already declared and initialized local. ''' </summary> Private Function GetLocal(localExpression As BoundLocal) As LocalDefinition Dim symbol = localExpression.LocalSymbol Return GetLocal(symbol) End Function Private Function GetLocal(symbol As LocalSymbol) As LocalDefinition Return _builder.LocalSlotManager.GetLocal(symbol) End Function ''' <summary> ''' Allocates a temp without identity. ''' </summary> Private Function AllocateTemp(type As TypeSymbol, syntaxNode As SyntaxNode) As LocalDefinition Return _builder.LocalSlotManager.AllocateSlot( Me._module.Translate(type, syntaxNodeOpt:=syntaxNode, diagnostics:=_diagnostics), LocalSlotConstraints.None) End Function ''' <summary> ''' Frees a temp without identity. ''' </summary> Private Sub FreeTemp(temp As LocalDefinition) _builder.LocalSlotManager.FreeSlot(temp) End Sub ''' <summary> ''' Frees an optional temp. ''' </summary> Private Sub FreeOptTemp(temp As LocalDefinition) If temp IsNot Nothing Then FreeTemp(temp) End If End Sub Private Sub EmitUnstructuredExceptionOnErrorSwitch(node As BoundUnstructuredExceptionOnErrorSwitch) EmitExpression(node.Value, used:=True) EmitSwitch(node.Jumps) End Sub Private Sub EmitSwitch(jumps As ImmutableArray(Of BoundGotoStatement)) Dim labels(jumps.Length - 1) As Object For i As Integer = 0 To jumps.Length - 1 labels(i) = jumps(i).Label Next _builder.EmitSwitch(labels) End Sub Private Sub EmitStateMachineScope(scope As BoundStateMachineScope) _builder.OpenLocalScope(ScopeType.StateMachineVariable) For Each field In scope.Fields Dim stateMachineField = DirectCast(field, StateMachineFieldSymbol) If stateMachineField.SlotIndex >= 0 Then DefineUserDefinedStateMachineHoistedLocal(stateMachineField) End If Next EmitStatement(scope.Statement) _builder.CloseLocalScope() End Sub Private Sub DefineUserDefinedStateMachineHoistedLocal(field As StateMachineFieldSymbol) Debug.Assert(field.SlotIndex >= 0) If _module.debugInformationFormat = DebugInformationFormat.Pdb Then 'Native PDBs: VB EE uses name mangling to match up original locals and the fields where they are hoisted 'The scoping information is passed by recording PDB scopes of "fake" locals named the same 'as the fields. These locals are not emitted to IL. ' vb\language\debugger\procedurecontext.cpp ' 813 // Since state machines lift (almost) all locals of a method, the lifted fields should ' 814 // only be shown in the debugger when the original local variable was in scope. So ' 815 // we first check if there's a local by the given name and attempt to remove it from ' 816 // m_localVariableMap. If it was present, we decode the original local's name, otherwise ' 817 // we skip loading this lifted field since it is out of scope. _builder.AddLocalToScope(New LocalDefinition( symbolOpt:=Nothing, nameOpt:=field.Name, type:=Nothing, slot:=field.SlotIndex, synthesizedKind:=SynthesizedLocalKind.EmitterTemp, id:=Nothing, pdbAttributes:=LocalVariableAttributes.None, constraints:=LocalSlotConstraints.None, dynamicTransformFlags:=Nothing, tupleElementNames:=Nothing)) Else _builder.DefineUserDefinedStateMachineHoistedLocal(field.SlotIndex) End If End Sub Private Sub EmitUnstructuredExceptionResumeSwitch(node As BoundUnstructuredExceptionResumeSwitch) ' Resume statements will branch here. Just load the resume local and ' branch to the switch table EmitLabelStatement(node.ResumeLabel) EmitExpression(node.ResumeTargetTemporary, used:=True) Dim switchLabel As New Object() _builder.EmitBranch(ILOpCode.Br_s, switchLabel) _builder.AdjustStack(-1) ' Resume Next statements will branch here. Increment the resume local and ' fall through to the switch table EmitLabelStatement(node.ResumeNextLabel) EmitExpression(node.ResumeTargetTemporary, used:=True) _builder.EmitIntConstant(1) _builder.EmitOpCode(ILOpCode.Add) ' now start generating the resume switch table _builder.MarkLabel(switchLabel) ' but first clear the resume local _builder.EmitIntConstant(0) _builder.EmitLocalStore(GetLocal(node.ResumeTargetTemporary)) EmitSwitch(node.Jumps) End Sub End Class End Namespace