using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGen; internal sealed class StackOptimizerPass1 : BoundTreeRewriter { private sealed class LocalUsedWalker : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator { private readonly LocalSymbol _local; private bool _found; internal LocalUsedWalker(LocalSymbol local, int recursionDepth) : base(recursionDepth) { _local = local; } public bool IsLocalUsedIn(BoundNode node) { _found = false; Visit(node); return _found; } public override BoundNode Visit(BoundNode node) { if (!_found) { return base.Visit(node); } return null; } public override BoundNode VisitLocal(BoundLocal node) { if (node.LocalSymbol == _local) { _found = true; } return null; } } private readonly bool _debugFriendly; private readonly ArrayBuilder<(BoundExpression, ExprContext)> _evalStack; private int _counter; private ExprContext _context; private BoundLocal _assignmentLocal; private readonly Dictionary _locals; private readonly SmallDictionary _dummyVariables = new SmallDictionary((IEqualityComparer)ReferenceEqualityComparer.Instance); public static readonly DummyLocal empty = new DummyLocal(); private int _recursionDepth; private StackOptimizerPass1(Dictionary locals, ArrayBuilder<(BoundExpression, ExprContext)> evalStack, bool debugFriendly) { _locals = locals; _evalStack = evalStack; _debugFriendly = debugFriendly; DeclareLocal(empty, 0); RecordDummyWrite(empty); } public static BoundNode Analyze(BoundNode node, Dictionary locals, bool debugFriendly) { ArrayBuilder<(BoundExpression, ExprContext)> instance = ArrayBuilder<(BoundExpression, ExprContext)>.GetInstance(); BoundNode result = new StackOptimizerPass1(locals, instance, debugFriendly).Visit(node); instance.Free(); return result; } public override BoundNode Visit(BoundNode node) { if (node is BoundExpression node2) { return VisitExpression(node2, ExprContext.Value); } return VisitStatement(node); } private BoundExpression VisitExpressionCore(BoundExpression node, ExprContext context) { ExprContext context2 = _context; int stackDepth = StackDepth(); _context = context; BoundExpression result = ((!(node.ConstantValueOpt == (ConstantValue)null)) ? node : (node = (BoundExpression)base.Visit(node))); _context = context2; _counter++; switch (context) { case ExprContext.Sideeffects: SetStackDepth(stackDepth); break; case ExprContext.Value: case ExprContext.Address: case ExprContext.Box: SetStackDepth(stackDepth); PushEvalStack(node, context); break; default: throw ExceptionUtilities.UnexpectedValue((object)context); case ExprContext.AssignmentTarget: break; } return result; } private BoundExpression VisitExpression(BoundExpression node, ExprContext context) { _recursionDepth++; BoundExpression result; if (_recursionDepth > 1) { StackGuard.EnsureSufficientExecutionStack(_recursionDepth); result = VisitExpressionCore(node, context); } else { result = VisitExpressionCoreWithStackGuard(node, context); } _recursionDepth--; return result; } private BoundExpression VisitExpressionCoreWithStackGuard(BoundExpression node, ExprContext context) { try { return VisitExpressionCore(node, context); } catch (InsufficientExecutionStackException inner) { throw new CancelledByStackGuardException(inner, node); } } protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node) { throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs", 537); } private void PushEvalStack(BoundExpression result, ExprContext context) { _evalStack.Add((result, context)); } private int StackDepth() { return _evalStack.Count; } private bool EvalStackIsEmpty() { return StackDepth() == 0; } private void SetStackDepth(int depth) { _evalStack.Clip(depth); } private void PopEvalStack() { SetStackDepth(_evalStack.Count - 1); } public BoundNode VisitStatement(BoundNode node) { return VisitSideEffect(node); } public BoundNode VisitSideEffect(BoundNode node) { int stackDepth = StackDepth(); ExprContext context = _context; BoundNode result = base.Visit(node); if (_debugFriendly) { EnsureOnlyEvalStack(); } _context = context; SetStackDepth(stackDepth); _counter++; return result; } public override BoundNode VisitConversion(BoundConversion node) { ExprContext context = ((_context == ExprContext.Sideeffects && !node.ConversionHasSideEffects()) ? ExprContext.Sideeffects : ExprContext.Value); return node.UpdateOperand(VisitExpression(node.Operand, context)); } public override BoundNode VisitPassByCopy(BoundPassByCopy node) { ExprContext context = ((_context == ExprContext.Sideeffects) ? ExprContext.Sideeffects : ExprContext.Value); return node.Update(VisitExpression(node.Expression, context), node.Type); } public override BoundNode VisitBlock(BoundBlock node) { if (node.Instrumentation != null) { DeclareLocal(node.Instrumentation.Local, 0); } DeclareLocals(node.Locals, 0); return base.VisitBlock(node); } public override BoundNode VisitSequence(BoundSequence node) { int num = StackDepth(); ImmutableArray locals = node.Locals; if (!locals.IsDefaultOrEmpty) { if (_context == ExprContext.Sideeffects) { ImmutableArray.Enumerator enumerator = locals.GetEnumerator(); while (enumerator.MoveNext()) { LocalSymbol current = enumerator.Current; if (IsNestedLocalOfCompoundOperator(current, node)) { DeclareLocal(current, num + 1); } else { DeclareLocal(current, num); } } } else { DeclareLocals(locals, num); } } ExprContext context = _context; ImmutableArray sideEffects = node.SideEffects; ArrayBuilder val = null; if (!sideEffects.IsDefault) { for (int i = 0; i < sideEffects.Length; i++) { BoundExpression boundExpression = sideEffects[i]; BoundExpression boundExpression2 = VisitExpression(boundExpression, ExprContext.Sideeffects); if (val == null && boundExpression2 != boundExpression) { val = ArrayBuilder.GetInstance(); val.AddRange(sideEffects, i); } val?.Add(boundExpression2); } } BoundExpression value = VisitExpression(node.Value, context); return node.Update(node.Locals, val?.ToImmutableAndFree() ?? sideEffects, value, node.Type); } private bool IsNestedLocalOfCompoundOperator(LocalSymbol local, BoundSequence node) { BoundExpression value = node.Value; if (value != null && value.Kind == BoundKind.Local && ((BoundLocal)value).LocalSymbol == local) { ImmutableArray sideEffects = node.SideEffects; BoundExpression boundExpression = sideEffects.LastOrDefault(); if (boundExpression != null && boundExpression.Kind == BoundKind.AssignmentOperator) { BoundAssignmentOperator boundAssignmentOperator = (BoundAssignmentOperator)boundExpression; if (IsIndirectOrInstanceFieldAssignment(boundAssignmentOperator) && boundAssignmentOperator.Right.Kind == BoundKind.Sequence) { LocalUsedWalker localUsedWalker = new LocalUsedWalker(local, _recursionDepth); for (int i = 0; i < sideEffects.Length - 1; i++) { if (localUsedWalker.IsLocalUsedIn(sideEffects[i])) { return false; } } if (localUsedWalker.IsLocalUsedIn(boundAssignmentOperator.Left)) { return false; } return true; } } } return false; } public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { return node.Update(VisitExpression(node.Expression, ExprContext.Sideeffects)); } public override BoundNode VisitLocal(BoundLocal node) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (node.ConstantValueOpt == (ConstantValue)null) { switch (_context) { case ExprContext.Address: if ((int)node.LocalSymbol.RefKind != 0) { RecordVarRead(node.LocalSymbol); } else { RecordVarRef(node.LocalSymbol); } break; case ExprContext.AssignmentTarget: _assignmentLocal = node; break; case ExprContext.Sideeffects: if ((int)node.LocalSymbol.RefKind != 0) { RecordVarRead(node.LocalSymbol); } break; case ExprContext.Value: case ExprContext.Box: RecordVarRead(node.LocalSymbol); break; } } return base.VisitLocal(node); } public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Invalid comparison between Unknown and I4 //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Invalid comparison between Unknown and I4 if (node.Left is BoundSequence boundSequence) { BoundExpression node2 = boundSequence.Update(boundSequence.Locals, boundSequence.SideEffects, node.Update(boundSequence.Value, node.Right, node.IsRef, node.Type), boundSequence.Type); node2 = (BoundExpression)Visit(node2); _counter--; return node2; } bool flag = IsIndirectAssignment(node); BoundExpression left = VisitExpression(node.Left, flag ? ExprContext.Address : ExprContext.AssignmentTarget); BoundLocal assignmentLocal = _assignmentLocal; _assignmentLocal = null; ExprContext context = ((!node.IsRef && _context != ExprContext.Address) ? ExprContext.Value : ExprContext.Address); BoundExpression right = node.Right; int num; if (right.Kind == BoundKind.ObjectCreationExpression && right.Type.IsVerifierValue()) { num = ((((BoundObjectCreationExpression)right).Constructor.ParameterCount != 0) ? 1 : 0); if (num != 0) { PushEvalStack(null, ExprContext.None); } } else { num = 0; } right = VisitExpression(node.Right, context); if (num != 0) { PopEvalStack(); } if (assignmentLocal != null) { LocalSymbol localSymbol = assignmentLocal.LocalSymbol; RefKind refKind = localSymbol.RefKind; bool flag2 = (((int)refKind == 3 || (int)refKind == 5) ? true : false); if (flag2 && (_context == ExprContext.Address || _context == ExprContext.Value)) { ShouldNotSchedule(localSymbol); } if (CanScheduleToStack(localSymbol) && assignmentLocal.Type.IsPointerOrFunctionPointer() && right.Kind == BoundKind.Conversion && ((BoundConversion)right).ConversionKind.IsPointerConversion()) { ShouldNotSchedule(localSymbol); } RecordVarWrite(localSymbol); assignmentLocal = null; } return node.Update(left, right, node.IsRef, node.Type); } internal static bool IsFixedBufferAssignmentToRefLocal(BoundExpression left, BoundExpression right, bool isRef) { if (isRef && right is BoundFieldAccess boundFieldAccess && boundFieldAccess.FieldSymbol.IsFixedSizeBuffer) { return left.Type.Equals(((PointerTypeSymbol)right.Type).PointedAtType, (TypeCompareKind)63); } return false; } private static bool IsIndirectAssignment(BoundAssignmentOperator node) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) BoundExpression left = node.Left; switch (left.Kind) { case BoundKind.ThisReference: return true; case BoundKind.Parameter: if ((int)((BoundParameter)left).ParameterSymbol.RefKind != 0) { return !node.IsRef; } return false; case BoundKind.Local: if ((int)((BoundLocal)left).LocalSymbol.RefKind != 0) { return !node.IsRef; } return false; case BoundKind.Call: return true; case BoundKind.FunctionPointerInvocation: return true; case BoundKind.ConditionalOperator: return true; case BoundKind.AssignmentOperator: return true; case BoundKind.Sequence: return false; case BoundKind.PointerIndirectionOperator: case BoundKind.RefValueOperator: case BoundKind.PseudoVariable: return true; case BoundKind.ArrayAccess: case BoundKind.InstrumentationPayloadRoot: case BoundKind.ModuleVersionId: case BoundKind.FieldAccess: return false; default: throw ExceptionUtilities.UnexpectedValue((object)left.Kind); } } private static bool IsIndirectOrInstanceFieldAssignment(BoundAssignmentOperator node) { BoundExpression left = node.Left; if (left.Kind == BoundKind.FieldAccess) { return !((BoundFieldAccess)left).FieldSymbol.IsStatic; } return IsIndirectAssignment(node); } public override BoundNode VisitCall(BoundCall node) { if (node.ReceiverOpt is BoundCall boundCall) { int stackDepth = StackDepth(); ArrayBuilder instance = ArrayBuilder.GetInstance(); ArrayBuilderExtensions.Push(instance, node); node = boundCall; while (node.ReceiverOpt is BoundCall boundCall2) { ArrayBuilderExtensions.Push(instance, node); node = boundCall2; } BoundExpression boundExpression = visitReceiver(node); while (true) { boundExpression = visitArgumentsAndUpdateCall(node, boundExpression); BoundCall boundCall3 = node; if (!ArrayBuilderExtensions.TryPop(instance, ref node)) { break; } CheckCallReceiver(boundCall3, node); _counter++; SetStackDepth(stackDepth); PushEvalStack(boundCall3, GetReceiverContext(boundCall3)); } instance.Free(); return boundExpression; } BoundExpression receiver = visitReceiver(node); return visitArgumentsAndUpdateCall(node, receiver); BoundCall visitArgumentsAndUpdateCall(BoundCall boundCall4, BoundExpression receiverOpt) { ImmutableArray arguments = VisitArguments(boundCall4.Arguments, boundCall4.Method.Parameters, boundCall4.ArgumentRefKindsOpt); return boundCall4.Update(receiverOpt, (ThreeState)0, boundCall4.Method, arguments); } BoundExpression visitReceiver(BoundCall boundCall4) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 BoundExpression receiverOpt = boundCall4.ReceiverOpt; MethodSymbol method = boundCall4.Method; if (method.RequiresInstanceReceiver) { return VisitCallOrConditionalAccessReceiver(receiverOpt, boundCall4); } _counter++; if ((method.IsAbstract || method.IsVirtual) && receiverOpt is BoundTypeExpression boundTypeExpression) { TypeSymbol type = boundTypeExpression.Type; if ((object)type != null && (int)type.TypeKind == 11) { return boundTypeExpression.Update(null, null, ImmutableArray.Empty, boundTypeExpression.TypeWithAnnotations, VisitType(boundTypeExpression.Type)); } } return null; } } private BoundExpression VisitCallOrConditionalAccessReceiver(BoundExpression receiver, BoundCall callOpt) { _ = receiver.Type; if (callOpt != null) { CheckCallReceiver(receiver, callOpt); } ExprContext receiverContext = GetReceiverContext(receiver); receiver = VisitExpression(receiver, receiverContext); return receiver; } private void CheckCallReceiver(BoundExpression receiver, BoundCall call) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 if (!CodeGenerator.IsRef(receiver) || !CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiver) || CodeGenerator.IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(call.Arguments)) { return; } BoundExpression boundExpression; for (boundExpression = receiver; boundExpression is BoundSequence boundSequence; boundExpression = boundSequence.Value) { } if (boundExpression is BoundLocal boundLocal) { LocalSymbol localSymbol = boundLocal.LocalSymbol; if ((object)localSymbol != null && (int)localSymbol.RefKind != 0) { ShouldNotSchedule(localSymbol); } } } private static ExprContext GetReceiverContext(BoundExpression receiver) { TypeSymbol type = receiver.Type; if (type.IsReferenceType) { if (type.IsTypeParameter()) { return ExprContext.Box; } return ExprContext.Value; } return ExprContext.Address; } private ImmutableArray VisitArguments(ImmutableArray arguments, ImmutableArray parameters, ImmutableArray argRefKindsOpt) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) ArrayBuilder rewrittenArguments = null; for (int i = 0; i < arguments.Length; i++) { RefKind argumentRefKind = CodeGenerator.GetArgumentRefKind(arguments, parameters, argRefKindsOpt, i); VisitArgument(arguments, ref rewrittenArguments, i, argumentRefKind); } return rewrittenArguments?.ToImmutableAndFree() ?? arguments; } private void VisitArgument(ImmutableArray arguments, ref ArrayBuilder rewrittenArguments, int i, RefKind argRefKind) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ExprContext context = (((int)argRefKind == 0) ? ExprContext.Value : ExprContext.Address); BoundExpression boundExpression = arguments[i]; BoundExpression boundExpression2 = VisitExpression(boundExpression, context); if (rewrittenArguments == null && boundExpression != boundExpression2) { rewrittenArguments = ArrayBuilder.GetInstance(); rewrittenArguments.AddRange(arguments, i); } if (rewrittenArguments != null) { rewrittenArguments.Add(boundExpression2); } } public override BoundNode VisitArgListOperator(BoundArgListOperator node) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) ArrayBuilder rewrittenArguments = null; ImmutableArray arguments = node.Arguments; ImmutableArray argumentRefKindsOpt = node.ArgumentRefKindsOpt; for (int i = 0; i < arguments.Length; i++) { RefKind argRefKind = (RefKind)((!argumentRefKindsOpt.IsDefaultOrEmpty) ? ((int)argumentRefKindsOpt[i]) : 0); VisitArgument(arguments, ref rewrittenArguments, i, argRefKind); } return node.Update(rewrittenArguments?.ToImmutableAndFree() ?? arguments, argumentRefKindsOpt, node.Type); } public override BoundNode VisitMakeRefOperator(BoundMakeRefOperator node) { BoundExpression operand = VisitExpression(node.Operand, ExprContext.Address); return node.Update(operand, node.Type); } public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) MethodSymbol constructor = node.Constructor; ImmutableArray arguments = VisitArguments(node.Arguments, constructor.Parameters, node.ArgumentRefKindsOpt); return node.Update(constructor, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.ConstantValueOpt, null, node.Type); } public override BoundNode VisitArrayAccess(BoundArrayAccess node) { ExprContext context = _context; _context = ExprContext.Value; BoundNode? result = base.VisitArrayAccess(node); _context = context; return result; } public override BoundNode VisitFieldAccess(BoundFieldAccess node) { FieldSymbol fieldSymbol = node.FieldSymbol; BoundExpression receiverOpt = node.ReceiverOpt; if (!fieldSymbol.IsStatic) { receiverOpt = (receiverOpt.Type.IsTypeParameter() ? VisitExpression(receiverOpt, ExprContext.Box) : ((!receiverOpt.Type.IsValueType || (_context != ExprContext.AssignmentTarget && _context != ExprContext.Address && !CodeGenerator.FieldLoadMustUseRef(receiverOpt))) ? VisitExpression(receiverOpt, ExprContext.Value) : VisitExpression(receiverOpt, ExprContext.Address))); } else { _counter++; receiverOpt = null; } return node.Update(receiverOpt, fieldSymbol, node.ConstantValueOpt, node.ResultKind, node.Type); } public override BoundNode VisitLabelStatement(BoundLabelStatement node) { RecordLabel(node.Label); return base.VisitLabelStatement(node); } public override BoundNode VisitLabel(BoundLabel node) { return node; } public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) { return node; } public override BoundNode VisitGotoStatement(BoundGotoStatement node) { BoundNode? result = base.VisitGotoStatement(node); RecordBranch(node.Label); return result; } public override BoundNode VisitConditionalGoto(BoundConditionalGoto node) { BoundNode? result = base.VisitConditionalGoto(node); PopEvalStack(); RecordBranch(node.Label); return result; } public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) BoundExpression expression = node.Expression; if (expression.Kind == BoundKind.Local) { LocalSymbol localSymbol = ((BoundLocal)expression).LocalSymbol; if ((int)localSymbol.RefKind == 0) { ShouldNotSchedule(localSymbol); } } expression = (BoundExpression)Visit(expression); PopEvalStack(); EnsureOnlyEvalStack(); RecordBranch(node.DefaultLabel); ImmutableArray<(ConstantValue, LabelSymbol)>.Enumerator enumerator = node.Cases.GetEnumerator(); while (enumerator.MoveNext()) { LabelSymbol item = enumerator.Current.Item2; RecordBranch(item); } return node.Update(expression, node.Cases, node.DefaultLabel, node.LengthBasedStringSwitchDataOpt); } public override BoundNode VisitConditionalOperator(BoundConditionalOperator node) { int stackDepth = StackDepth(); BoundExpression condition = VisitExpression(node.Condition, ExprContext.Value); object stackStateCookie = GetStackStateCookie(); ExprContext context = (node.IsRef ? ExprContext.Address : ExprContext.Value); SetStackDepth(stackDepth); BoundExpression consequence = VisitExpression(node.Consequence, context); EnsureStackState(stackStateCookie); SetStackDepth(stackDepth); BoundExpression alternative = VisitExpression(node.Alternative, context); EnsureStackState(stackStateCookie); return node.Update(node.IsRef, condition, consequence, alternative, node.ConstantValueOpt, node.NaturalTypeOpt, node.WasCompilerGenerated, node.Type); } public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundExpression left = node.Left; if (left.Kind != BoundKind.BinaryOperator || left.ConstantValueOpt != (ConstantValue)null) { return VisitBinaryOperatorSimple(node); } ArrayBuilder instance = ArrayBuilder.GetInstance(); ArrayBuilderExtensions.Push(instance, node); BoundBinaryOperator boundBinaryOperator = (BoundBinaryOperator)left; while (true) { ArrayBuilderExtensions.Push(instance, boundBinaryOperator); left = boundBinaryOperator.Left; if (left.Kind != BoundKind.BinaryOperator || left.ConstantValueOpt != (ConstantValue)null) { break; } boundBinaryOperator = (BoundBinaryOperator)left; } ExprContext context = _context; int stackDepth = StackDepth(); BoundExpression boundExpression = (BoundExpression)Visit(left); while (true) { boundBinaryOperator = ArrayBuilderExtensions.Pop(instance); bool num = (boundBinaryOperator.OperatorKind & BinaryOperatorKind.Logical) != 0; object cookie = null; if (num) { cookie = GetStackStateCookie(); SetStackDepth(stackDepth); } BoundExpression right = (BoundExpression)Visit(boundBinaryOperator.Right); if (num) { EnsureStackState(cookie); } TypeSymbol type = VisitType(boundBinaryOperator.Type); boundExpression = boundBinaryOperator.Update(boundBinaryOperator.OperatorKind, boundBinaryOperator.ConstantValueOpt, boundBinaryOperator.Method, boundBinaryOperator.ConstrainedToType, boundBinaryOperator.ResultKind, boundExpression, right, type); if (instance.Count == 0) { break; } _context = context; _counter++; SetStackDepth(stackDepth); PushEvalStack(boundBinaryOperator, ExprContext.Value); } instance.Free(); return boundExpression; } private BoundNode VisitBinaryOperatorSimple(BoundBinaryOperator node) { if ((node.OperatorKind & BinaryOperatorKind.Logical) != BinaryOperatorKind.Error) { int stackDepth = StackDepth(); BoundExpression left = (BoundExpression)Visit(node.Left); object stackStateCookie = GetStackStateCookie(); SetStackDepth(stackDepth); BoundExpression right = (BoundExpression)Visit(node.Right); EnsureStackState(stackStateCookie); return node.Update(node.OperatorKind, node.ConstantValueOpt, node.Method, node.ConstrainedToType, node.ResultKind, left, right, node.Type); } return base.VisitBinaryOperator(node); } public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperator node) { int stackDepth = StackDepth(); BoundExpression leftOperand = (BoundExpression)Visit(node.LeftOperand); object stackStateCookie = GetStackStateCookie(); SetStackDepth(stackDepth); BoundExpression rightOperand = (BoundExpression)Visit(node.RightOperand); EnsureStackState(stackStateCookie); return node.Update(leftOperand, rightOperand, node.LeftPlaceholder, node.LeftConversion, node.OperatorResultKind, node.Checked, node.Type); } public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { int stackDepth = StackDepth(); BoundExpression receiver = VisitCallOrConditionalAccessReceiver(node.Receiver, null); object stackStateCookie = GetStackStateCookie(); SetStackDepth(stackDepth); BoundExpression whenNotNull = (BoundExpression)Visit(node.WhenNotNull); EnsureStackState(stackStateCookie); BoundExpression boundExpression = node.WhenNullOpt; if (boundExpression != null) { SetStackDepth(stackDepth); boundExpression = (BoundExpression)Visit(boundExpression); EnsureStackState(stackStateCookie); } else { _counter++; } return node.Update(receiver, node.HasValueMethodOpt, whenNotNull, boundExpression, node.Id, node.ForceCopyOfNullableValueType, node.Type); } public override BoundNode VisitComplexConditionalReceiver(BoundComplexConditionalReceiver node) { EnsureOnlyEvalStack(); int stackDepth = StackDepth(); PushEvalStack(null, ExprContext.None); object stackStateCookie = GetStackStateCookie(); SetStackDepth(stackDepth); BoundExpression valueTypeReceiver = (BoundExpression)Visit(node.ValueTypeReceiver); EnsureStackState(stackStateCookie); SetStackDepth(stackDepth); BoundExpression boundExpression; for (boundExpression = node.ReferenceTypeReceiver; boundExpression is BoundSequence boundSequence; boundExpression = boundSequence.Value) { } if (boundExpression is BoundLocal boundLocal) { LocalSymbol localSymbol = boundLocal.LocalSymbol; if ((object)localSymbol != null) { ShouldNotSchedule(localSymbol); } } BoundExpression referenceTypeReceiver = (BoundExpression)Visit(node.ReferenceTypeReceiver); EnsureStackState(stackStateCookie); return node.Update(valueTypeReceiver, referenceTypeReceiver, node.Type); } public override BoundNode VisitUnaryOperator(BoundUnaryOperator node) { if (node.OperatorKind.IsChecked() && node.OperatorKind.Operator() == UnaryOperatorKind.UnaryMinus) { StackDepth(); PushEvalStack(new BoundDefaultExpression(node.Syntax, node.Operand.Type), ExprContext.Value); BoundExpression operand = (BoundExpression)Visit(node.Operand); return node.Update(node.OperatorKind, operand, node.ConstantValueOpt, node.MethodOpt, node.ConstrainedToTypeOpt, node.ResultKind, node.Type); } return base.VisitUnaryOperator(node); } public override BoundNode VisitTryStatement(BoundTryStatement node) { EnsureOnlyEvalStack(); BoundBlock tryBlock = (BoundBlock)Visit(node.TryBlock); ImmutableArray catchBlocks = VisitList(node.CatchBlocks); EnsureOnlyEvalStack(); BoundBlock finallyBlockOpt = (BoundBlock)Visit(node.FinallyBlockOpt); EnsureOnlyEvalStack(); return node.Update(tryBlock, catchBlocks, finallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler); } public override BoundNode VisitCatchBlock(BoundCatchBlock node) { EnsureOnlyEvalStack(); BoundExpression boundExpression = node.ExceptionSourceOpt; DeclareLocals(node.Locals, 0); if (boundExpression != null) { PushEvalStack(null, ExprContext.None); _counter++; if (boundExpression.Kind == BoundKind.Local) { RecordVarWrite(((BoundLocal)boundExpression).LocalSymbol); } else { int stackDepth = StackDepth(); boundExpression = VisitExpression(boundExpression, ExprContext.AssignmentTarget); _assignmentLocal = null; SetStackDepth(stackDepth); } PopEvalStack(); _counter++; } BoundStatementList exceptionFilterPrologueOpt; if (node.ExceptionFilterPrologueOpt != null) { EnsureOnlyEvalStack(); exceptionFilterPrologueOpt = (BoundStatementList)Visit(node.ExceptionFilterPrologueOpt); } else { exceptionFilterPrologueOpt = null; } BoundExpression exceptionFilterOpt; if (node.ExceptionFilterOpt != null) { exceptionFilterOpt = (BoundExpression)Visit(node.ExceptionFilterOpt); PopEvalStack(); _counter++; EnsureOnlyEvalStack(); } else { exceptionFilterOpt = null; } BoundBlock body = (BoundBlock)Visit(node.Body); TypeSymbol exceptionTypeOpt = VisitType(node.ExceptionTypeOpt); return node.Update(node.Locals, boundExpression, exceptionTypeOpt, exceptionFilterPrologueOpt, exceptionFilterOpt, body, node.IsSynthesizedAsyncCatchAll); } public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node) { EnsureOnlyEvalStack(); return base.VisitConvertedStackAllocExpression(node); } public override BoundNode VisitArrayInitialization(BoundArrayInitialization node) { EnsureOnlyEvalStack(); ImmutableArray initializers = node.Initializers; ArrayBuilder val = null; if (!initializers.IsDefault) { for (int i = 0; i < initializers.Length; i++) { EnsureOnlyEvalStack(); BoundExpression boundExpression = initializers[i]; BoundExpression boundExpression2 = VisitExpression(boundExpression, ExprContext.Value); if (val == null && boundExpression2 != boundExpression) { val = ArrayBuilder.GetInstance(); val.AddRange(initializers, i); } val?.Add(boundExpression2); } } return node.Update(val?.ToImmutableAndFree() ?? initializers); } public override BoundNode VisitAddressOfOperator(BoundAddressOfOperator node) { BoundExpression operand = VisitExpression(node.Operand, ExprContext.Address); return node.Update(operand, node.IsManaged, node.Type); } public override BoundNode VisitReturnStatement(BoundReturnStatement node) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) BoundExpression expressionOpt = (BoundExpression)Visit(node.ExpressionOpt); EnsureOnlyEvalStack(); return node.Update(node.RefKind, expressionOpt, node.Checked); } private void EnsureOnlyEvalStack() { RecordVarRead(empty); } private object GetStackStateCookie() { DummyLocal dummyLocal = new DummyLocal(); _dummyVariables.Add((object)dummyLocal, dummyLocal); _locals.Add(dummyLocal, LocalDefUseInfo.GetInstance(StackDepth())); RecordDummyWrite(dummyLocal); return dummyLocal; } private void EnsureStackState(object cookie) { RecordVarRead(_dummyVariables[cookie]); } private void RecordBranch(LabelSymbol label) { DummyLocal local = default(DummyLocal); if (_dummyVariables.TryGetValue((object)label, ref local)) { RecordVarRead(local); return; } local = new DummyLocal(); _dummyVariables.Add((object)label, local); _locals.Add(local, LocalDefUseInfo.GetInstance(StackDepth())); RecordDummyWrite(local); } private void RecordLabel(LabelSymbol label) { DummyLocal local = default(DummyLocal); if (_dummyVariables.TryGetValue((object)label, ref local)) { RecordVarRead(local); return; } local = empty; _dummyVariables.Add((object)label, local); RecordVarRead(local); } private void ShouldNotSchedule(LocalSymbol localSymbol) { if (_locals.TryGetValue(localSymbol, out var value)) { value.ShouldNotSchedule(); } } private void RecordVarRef(LocalSymbol local) { if (CanScheduleToStack(local)) { ShouldNotSchedule(local); } } private void RecordVarRead(LocalSymbol local) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 if (!CanScheduleToStack(local)) { return; } LocalDefUseInfo localDefUseInfo = _locals[local]; if (!localDefUseInfo.CannotSchedule) { ArrayBuilder localDefs = localDefUseInfo.LocalDefs; if (localDefs.Count == 0) { localDefUseInfo.ShouldNotSchedule(); return; } if ((int)local.SynthesizedKind != -3 && localDefUseInfo.StackAtDeclaration != StackDepth() && !EvalStackHasLocal(local)) { localDefUseInfo.ShouldNotSchedule(); return; } int num = localDefs.Count - 1; localDefs[num] = localDefs[num].WithEnd(_counter); LocalDefUseSpan localDefUseSpan = new LocalDefUseSpan(_counter); localDefs.Add(localDefUseSpan); } } private bool EvalStackHasLocal(LocalSymbol local) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) (BoundExpression, ExprContext) tuple = _evalStack.Last(); if (tuple.Item2 == (ExprContext)(((int)local.RefKind == 0) ? 2 : 3) && tuple.Item1.Kind == BoundKind.Local) { return ((BoundLocal)tuple.Item1).LocalSymbol == local; } return false; } private void RecordDummyWrite(LocalSymbol local) { LocalDefUseInfo localDefUseInfo = _locals[local]; LocalDefUseSpan localDefUseSpan = new LocalDefUseSpan(_counter); localDefUseInfo.LocalDefs.Add(localDefUseSpan); } private void RecordVarWrite(LocalSymbol local) { if (!CanScheduleToStack(local)) { return; } LocalDefUseInfo localDefUseInfo = _locals[local]; if (!localDefUseInfo.CannotSchedule) { int num = StackDepth() - 1; if (localDefUseInfo.StackAtDeclaration != num) { localDefUseInfo.ShouldNotSchedule(); return; } LocalDefUseSpan localDefUseSpan = new LocalDefUseSpan(_counter); localDefUseInfo.LocalDefs.Add(localDefUseSpan); } } private bool CanScheduleToStack(LocalSymbol local) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (local.CanScheduleToStack) { if (_debugFriendly) { return !SynthesizedLocalKindExtensions.IsLongLived(local.SynthesizedKind); } return true; } return false; } private void DeclareLocals(ImmutableArray locals, int stack) { ImmutableArray.Enumerator enumerator = locals.GetEnumerator(); while (enumerator.MoveNext()) { LocalSymbol current = enumerator.Current; DeclareLocal(current, stack); } } private void DeclareLocal(LocalSymbol local, int stack) { if ((object)local != null && CanScheduleToStack(local)) { if (!_locals.TryGetValue(local, out var value)) { _locals.Add(local, LocalDefUseInfo.GetInstance(stack)); } else if (value.StackAtDeclaration != stack) { value.ShouldNotSchedule(); } } } }