using System; using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp; internal sealed class IteratorAndAsyncCaptureWalker : DefiniteAssignmentPass { private sealed class OutsideVariablesUsedInside : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator { private readonly HashSet _localsInScope; private readonly IteratorAndAsyncCaptureWalker _analyzer; private readonly MethodSymbol _topLevelMethod; private readonly IteratorAndAsyncCaptureWalker _parent; public OutsideVariablesUsedInside(IteratorAndAsyncCaptureWalker analyzer, MethodSymbol topLevelMethod, IteratorAndAsyncCaptureWalker parent) : base(parent._recursionDepth) { _analyzer = analyzer; _topLevelMethod = topLevelMethod; _localsInScope = new HashSet(); _parent = parent; } protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException() { return _parent.ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException(); } public override BoundNode VisitBlock(BoundBlock node) { AddVariables(node.Locals); return base.VisitBlock(node); } private void AddVariables(ImmutableArray locals) { ImmutableArray.Enumerator enumerator = locals.GetEnumerator(); while (enumerator.MoveNext()) { LocalSymbol current = enumerator.Current; AddVariable(current); } } public override BoundNode VisitCatchBlock(BoundCatchBlock node) { AddVariables(node.Locals); return base.VisitCatchBlock(node); } private void AddVariable(Symbol local) { if ((object)local != null) { _localsInScope.Add(local); } } public override BoundNode VisitSequence(BoundSequence node) { AddVariables(node.Locals); return base.VisitSequence(node); } public override BoundNode VisitThisReference(BoundThisReference node) { Capture(_topLevelMethod.ThisParameter, node.Syntax); return base.VisitThisReference(node); } public override BoundNode VisitBaseReference(BoundBaseReference node) { Capture(_topLevelMethod.ThisParameter, node.Syntax); return base.VisitBaseReference(node); } public override BoundNode VisitLocal(BoundLocal node) { Capture(node.LocalSymbol, node.Syntax); return base.VisitLocal(node); } public override BoundNode VisitParameter(BoundParameter node) { Capture(node.ParameterSymbol, node.Syntax); return base.VisitParameter(node); } private void Capture(Symbol s, SyntaxNode syntax) { if ((object)s != null && !_localsInScope.Contains(s)) { _analyzer.CaptureVariable(s, syntax); } } } private readonly OrderedSet _variablesToHoist = new OrderedSet(); private MultiDictionary _lazyDisallowedCaptures; private bool _seenYieldInCurrentTry; private readonly Dictionary _boundRefLocalInitializers = new Dictionary(); private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbol method, BoundNode node, HashSet initiallyAssignedVariables) : base(compilation, method, node, EmptyStructTypeCache.CreateNeverEmpty(), trackUnassignments: true, initiallyAssignedVariables) { } public static OrderedSet Analyze(CSharpCompilation compilation, MethodSymbol method, BoundNode node, DiagnosticBag diagnostics) { //IL_0022: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Invalid comparison between Unknown and I4 //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Invalid comparison between Unknown and I4 //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Invalid comparison between Unknown and I4 HashSet hashSet = UnassignedVariablesWalker.Analyze(compilation, method, node, convertInsufficientExecutionStackExceptionToCancelledByStackGuardException: true); IteratorAndAsyncCaptureWalker iteratorAndAsyncCaptureWalker = new IteratorAndAsyncCaptureWalker(compilation, method, node, hashSet); iteratorAndAsyncCaptureWalker._convertInsufficientExecutionStackExceptionToCancelledByStackGuardException = true; bool badRegion = false; iteratorAndAsyncCaptureWalker.Analyze(ref badRegion); if (!method.IsStatic && (int)method.ContainingType.TypeKind == 10) { iteratorAndAsyncCaptureWalker.CaptureVariable(method.ThisParameter, node.Syntax); } MultiDictionary lazyDisallowedCaptures = iteratorAndAsyncCaptureWalker._lazyDisallowedCaptures; ArrayBuilder.VariableIdentifier> val = iteratorAndAsyncCaptureWalker.variableBySlot; if (lazyDisallowedCaptures != null) { foreach (KeyValuePair> item in lazyDisallowedCaptures) { Symbol key = item.Key; TypeSymbol typeSymbol = (((int)key.Kind == 8) ? ((LocalSymbol)key).Type : ((ParameterSymbol)key).Type); if (key is SynthesizedLocal synthesizedLocal && (int)synthesizedLocal.SynthesizedKind == 28) { diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, synthesizedLocal.GetFirstLocation(), synthesizedLocal.TypeWithAnnotations); continue; } Enumerator enumerator2 = item.Value.GetEnumerator(); try { while (enumerator2.MoveNext()) { CSharpSyntaxNode cSharpSyntaxNode = (CSharpSyntaxNode)(object)enumerator2.Current; diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, ((SyntaxNode)cSharpSyntaxNode).Location, typeSymbol); } } finally { ((IDisposable)enumerator2/*cast due to constrained. prefix*/).Dispose(); } } } OrderedSet val2 = new OrderedSet(); if ((int)((CompilationOptions)compilation.Options).OptimizationLevel != 1) { Enumerator.VariableIdentifier> enumerator3 = val.GetEnumerator(); while (enumerator3.MoveNext()) { Symbol symbol = enumerator3.Current.Symbol; if ((object)symbol != null && HoistInDebugBuild(symbol)) { val2.Add(symbol); } } } val2.AddRange((IEnumerable)iteratorAndAsyncCaptureWalker._variablesToHoist); iteratorAndAsyncCaptureWalker.Free(); return val2; } private static bool HoistInDebugBuild(Symbol symbol) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!(symbol is ParameterSymbol parameterSymbol)) { if (symbol is LocalSymbol { IsConst: false, IsPinned: false, IsRef: false } localSymbol) { return SynthesizedLocalKindExtensions.MustSurviveStateMachineSuspension(localSymbol.SynthesizedKind) && !localSymbol.Type.IsRestrictedType(); } return false; } return !parameterSymbol.Type.IsRestrictedType(); } private void MarkLocalsUnassigned() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_0068: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < variableBySlot.Count; i++) { Symbol symbol = variableBySlot[i].Symbol; if ((object)symbol == null) { continue; } SymbolKind kind = symbol.Kind; if ((int)kind != 6) { if ((int)kind != 8) { if ((int)kind != 13) { throw ExceptionUtilities.UnexpectedValue((object)symbol.Kind); } SetSlotState(i, assigned: false); } else if (!((LocalSymbol)symbol).IsConst) { SetSlotState(i, assigned: false); } } else if (!((FieldSymbol)symbol).IsConst) { SetSlotState(i, assigned: false); } } } public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { base.VisitAwaitExpression(node); MarkLocalsUnassigned(); return null; } public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node) { base.VisitYieldReturnStatement(node); MarkLocalsUnassigned(); _seenYieldInCurrentTry = true; return null; } protected override ImmutableArray Scan(ref bool badRegion) { _variablesToHoist.Clear(); _lazyDisallowedCaptures?.Clear(); return base.Scan(ref badRegion); } private void CaptureVariable(Symbol variable, SyntaxNode syntax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 BoundExpression value; if ((((int)variable.Kind == 8) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type).IsRestrictedType()) { (_lazyDisallowedCaptures ?? (_lazyDisallowedCaptures = new MultiDictionary())).Add(variable, syntax); } else if (_variablesToHoist.Add(variable) && variable is LocalSymbol key && _boundRefLocalInitializers.TryGetValue(key, out value)) { CaptureRefInitializer(value, syntax); } } private void CaptureRefInitializer(BoundExpression variableInitializer, SyntaxNode syntax) { if (variableInitializer is BoundLocal boundLocal) { LocalSymbol localSymbol = boundLocal.LocalSymbol; CaptureVariable(localSymbol, syntax); } else if (variableInitializer is BoundParameter boundParameter) { ParameterSymbol parameterSymbol = boundParameter.ParameterSymbol; CaptureVariable(parameterSymbol, syntax); } else { if (!(variableInitializer is BoundFieldAccess boundFieldAccess)) { return; } FieldSymbol fieldSymbol = boundFieldAccess.FieldSymbol; if ((object)fieldSymbol == null || fieldSymbol.IsStatic) { return; } NamedTypeSymbol containingType = fieldSymbol.ContainingType; if ((object)containingType != null && containingType.IsValueType) { BoundExpression receiverOpt = boundFieldAccess.ReceiverOpt; if (receiverOpt != null) { CaptureRefInitializer(receiverOpt, syntax); } } } } protected override void EnterParameter(ParameterSymbol parameter) { GetOrCreateSlot(parameter); } protected override void ReportUnassigned(Symbol symbol, SyntaxNode node, int slot, bool skipIfUseBeforeDeclaration) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 SymbolKind kind = symbol.Kind; if ((int)kind != 6) { if ((int)kind != 8 && (int)kind != 13) { return; } } else { symbol = GetNonMemberSymbol(slot); } CaptureVariable(symbol, node); } protected override void VisitLvalueParameter(BoundParameter node) { TryHoistTopLevelParameter(node); base.VisitLvalueParameter(node); } public override BoundNode VisitParameter(BoundParameter node) { TryHoistTopLevelParameter(node); return base.VisitParameter(node); } private void TryHoistTopLevelParameter(BoundParameter node) { if (node.ParameterSymbol.ContainingSymbol == topLevelMethod) { CaptureVariable(node.ParameterSymbol, node.Syntax); } } public override BoundNode VisitFieldAccess(BoundFieldAccess node) { if (node.ReceiverOpt != null && node.ReceiverOpt.Kind == BoundKind.ThisReference) { ParameterSymbol thisParameter = topLevelMethod.ThisParameter; CaptureVariable(thisParameter, node.Syntax); } return base.VisitFieldAccess(node); } public override BoundNode VisitThisReference(BoundThisReference node) { CaptureVariable(topLevelMethod.ThisParameter, node.Syntax); return base.VisitThisReference(node); } public override BoundNode VisitBaseReference(BoundBaseReference node) { CaptureVariable(topLevelMethod.ThisParameter, node.Syntax); return base.VisitBaseReference(node); } public override BoundNode VisitTryStatement(BoundTryStatement node) { bool seenYieldInCurrentTry = _seenYieldInCurrentTry; _seenYieldInCurrentTry = false; base.VisitTryStatement(node); _seenYieldInCurrentTry |= seenYieldInCurrentTry; return null; } protected override void VisitFinallyBlock(BoundStatement finallyBlock, ref LocalState unsetInFinally) { if (_seenYieldInCurrentTry) { new OutsideVariablesUsedInside(this, topLevelMethod, this).Visit(finallyBlock); } base.VisitFinallyBlock(finallyBlock, ref unsetInFinally); } public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { base.VisitAssignmentOperator(node); if (node != null && node.IsRef && node.Left is BoundLocal boundLocal) { LocalSymbol localSymbol = boundLocal.LocalSymbol; if ((object)localSymbol != null && localSymbol.IsCompilerGenerated) { _boundRefLocalInitializers[localSymbol] = node.Right; } } return null; } }