public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if (node.ExceptionFilterOpt == null) { return(base.VisitCatchBlock(node)); } if (node.ExceptionFilterOpt.ConstantValue?.BooleanValue == false) { return(null); } BoundExpression rewrittenExceptionSourceOpt = (BoundExpression)this.Visit(node.ExceptionSourceOpt); BoundExpression rewrittenFilter = (BoundExpression)this.Visit(node.ExceptionFilterOpt); BoundBlock rewrittenBody = (BoundBlock)this.Visit(node.Body); TypeSymbol rewrittenExceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); // EnC: We need to insert a hidden sequence point to handle function remapping in case // the containing method is edited while methods invoked in the condition are being executed. if (rewrittenFilter != null && !node.WasCompilerGenerated && this.Instrument) { rewrittenFilter = _instrumenter.InstrumentCatchClauseFilter(node, rewrittenFilter, _factory); } return(node.Update( node.Locals, rewrittenExceptionSourceOpt, rewrittenExceptionTypeOpt, rewrittenFilter, rewrittenBody, node.IsSynthesizedAsyncCatchAll)); }
protected override void VisitCatchBlock(BoundCatchBlock catchBlock, ref LocalState finallyState) { var oldPending = SavePending(); // we do not support branches into a catch block base.VisitCatchBlock(catchBlock, ref finallyState); RestorePending(oldPending); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { AddAll(node.Locals); base.VisitCatchBlock(node); RemoveAll(node.Locals); return(null); }
public virtual BoundExpression InstrumentCatchClauseFilter(BoundCatchBlock original, BoundExpression rewrittenFilter, SyntheticBoundNodeFactory factory) { Debug.Assert(!original.WasCompilerGenerated); Debug.Assert(original.Syntax.Kind() == SyntaxKind.CatchClause); Debug.Assert(((CatchClauseSyntax)original.Syntax).Filter != null); Debug.Assert(factory != null); return(rewrittenFilter); }
private static bool IsCatchingPossible(BoundCatchBlock catchBlock, TypeSymbol exception) { // Because IsCatching is called before Debug.Assert((object)catchBlock.ExceptionTypeOpt != null); HashSet <DiagnosticInfo> useSiteDiagnostics = null; return(catchBlock.ExceptionTypeOpt.IsEqualToOrDerivedFrom(exception, TypeCompareKind.ConsiderEverything, ref useSiteDiagnostics)); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { var oldScope = _currentScope; _currentScope = CreateOrReuseScope(node, node.Locals); var result = base.VisitCatchBlock(node); _currentScope = oldScope; return(result); }
public override BoundExpression InstrumentCatchClauseFilter(BoundCatchBlock original, BoundExpression rewrittenFilter, SyntheticBoundNodeFactory factory) { rewrittenFilter = base.InstrumentCatchClauseFilter(original, rewrittenFilter, factory); // EnC: We need to insert a hidden sequence point to handle function remapping in case // the containing method is edited while methods invoked in the condition are being executed. CatchFilterClauseSyntax filterClause = ((CatchClauseSyntax)original.Syntax).Filter; return(AddConditionSequencePoint(new BoundSequencePointExpression(filterClause, rewrittenFilter, rewrittenFilter.Type), filterClause, factory)); }
protected override void VisitCatchBlock(BoundCatchBlock catchBlock, ref LocalState finallyState) { if (IsInside) { var local = catchBlock.Locals.FirstOrDefault(); if (local?.DeclarationKind == LocalDeclarationKind.CatchVariable) { _variablesDeclared.Add(local); } } base.VisitCatchBlock(catchBlock, ref finallyState); }
private static bool IsCatching(BoundCatchBlock catchBlock, TypeSymbol exception) { // If it is catching all exceptions if ((object)catchBlock.ExceptionTypeOpt == null) { return(true); } HashSet <DiagnosticInfo> useSiteDiagnostics = null; if (exception.IsEqualToOrDerivedFrom(catchBlock.ExceptionTypeOpt, TypeCompareKind.ConsiderEverything, ref useSiteDiagnostics)) { return(catchBlock.ExceptionFilterOpt == null); } return(false); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if (!node.Locals.IsDefaultOrEmpty) { // Yield/await aren't supported in catch block atm, but we need to rewrite the type // of the variables owned by the catch block. Note that one of these variables might be a closure frame reference. var newLocals = RewriteLocalList(node.Locals); return(node.Update( newLocals, (BoundExpression)this.Visit(node.ExceptionSourceOpt), this.VisitType(node.ExceptionTypeOpt), (BoundExpression)this.Visit(node.ExceptionFilterOpt), (BoundBlock)this.Visit(node.Body))); } return(base.VisitCatchBlock(node)); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if ((object)node.LocalOpt != null) { // Yield/await aren't supported in catch block atm, but we need to rewrite the type // of the variable owned by the catch block. Note that this variable might be a closure frame reference. LocalSymbol newLocal; TryRewriteLocal(node.LocalOpt, out newLocal); return(node.Update( newLocal, (BoundExpression)this.Visit(node.ExceptionSourceOpt), this.VisitType(node.ExceptionTypeOpt), (BoundExpression)this.Visit(node.ExceptionFilterOpt), (BoundBlock)this.Visit(node.Body))); } return(base.VisitCatchBlock(node)); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { var origSeenAwait = _seenAwait; _seenAwait = false; var result = base.VisitCatchBlock(node); if (_seenAwait) { var awaitContainingCatches = _awaitContainingCatches; if (awaitContainingCatches == null) { _awaitContainingCatches = awaitContainingCatches = new HashSet <BoundCatchBlock>(); } _awaitContainingCatches.Add(node); } _seenAwait |= origSeenAwait; return(result); }
internal void Parse(BoundCatchBlock boundCatchBlock) { if (boundCatchBlock == null) { throw new ArgumentNullException(); } if (boundCatchBlock.ExceptionTypeOpt != null) { this.ExceptionTypeOpt = boundCatchBlock.ExceptionTypeOpt; } if (boundCatchBlock.ExceptionSourceOpt != null) { this.ExceptionSourceOpt = Deserialize(boundCatchBlock.ExceptionSourceOpt) as Expression; } if (boundCatchBlock.ExceptionFilterOpt != null) { this.ExceptionFilterOpt = Deserialize(boundCatchBlock.ExceptionFilterOpt) as Expression; } Statements = Deserialize(boundCatchBlock.Body); }
/// <remarks> /// The interesting part in the following method is the support for exception filters. /// === Example: /// /// try /// { /// TryBlock /// } /// catch (ExceptionType ex) when (Condition) /// { /// Handler /// } /// /// gets emitted as something like ===> /// /// Try /// TryBlock /// Filter /// var tmp = Pop() as {ExceptionType} /// if (tmp == null) /// { /// Push 0 /// } /// else /// { /// ex = tmp /// Push Condition ? 1 : 0 /// } /// End Filter // leaves 1 or 0 on the stack /// Catch // gets called after finalization of nested exception frames if condition above produced 1 /// Pop // CLR pushes the exception object again /// variable ex can be used here /// Handler /// EndCatch /// </remarks> private void EmitCatchBlock(BoundCatchBlock catchBlock) { object typeCheckFailedLabel = null; _builder.AdjustStack(1); // Account for exception on the stack. // 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 == null) { var exceptionType = ((object)catchBlock.ExceptionTypeOpt != null) ? _module.Translate(catchBlock.ExceptionTypeOpt, catchBlock.Syntax, _diagnostics) : _module.GetSpecialType(SpecialType.System_Object, catchBlock.Syntax, _diagnostics); _builder.OpenLocalScope(ScopeType.Catch, exceptionType); if (catchBlock.IsSynthesizedAsyncCatchAll) { Debug.Assert(_asyncCatchHandlerOffset < 0); // only one expected _asyncCatchHandlerOffset = _builder.AllocateILMarker(); } // Dev12 inserts the sequence point on catch clause without a filter, just before // the exception object is assigned to the variable. // // Also in Dev12 the exception variable scope span starts right after the stloc instruction and // ends right before leave instruction. So when stopped at the sequence point Dev12 inserts, // the exception variable is not visible. if (_emitPdbSequencePoints) { var syntax = catchBlock.Syntax as CatchClauseSyntax; if (syntax != null) { TextSpan spSpan; var declaration = syntax.Declaration; if (declaration == null) { spSpan = syntax.CatchKeyword.Span; } else { spSpan = TextSpan.FromBounds(syntax.SpanStart, syntax.Declaration.Span.End); } this.EmitSequencePoint(catchBlock.SyntaxTree, spSpan); } } } else { _builder.OpenLocalScope(ScopeType.Filter); // Filtering starts with simulating regular catch through a // type check. If this is not our type then we are done. var typeCheckPassedLabel = new object(); typeCheckFailedLabel = new object(); if ((object)catchBlock.ExceptionTypeOpt != null) { var exceptionType = _module.Translate(catchBlock.ExceptionTypeOpt, catchBlock.Syntax, _diagnostics); _builder.EmitOpCode(ILOpCode.Isinst); _builder.EmitToken(exceptionType, catchBlock.Syntax, _diagnostics); _builder.EmitOpCode(ILOpCode.Dup); _builder.EmitBranch(ILOpCode.Brtrue, typeCheckPassedLabel); _builder.EmitOpCode(ILOpCode.Pop); _builder.EmitIntConstant(0); _builder.EmitBranch(ILOpCode.Br, typeCheckFailedLabel); } else { // no formal exception type means we always pass the check } _builder.MarkLabel(typeCheckPassedLabel); } foreach (var local in catchBlock.Locals) { var declaringReferences = local.DeclaringSyntaxReferences; var localSyntax = !declaringReferences.IsEmpty ? (CSharpSyntaxNode)declaringReferences[0].GetSyntax() : catchBlock.Syntax; DefineLocal(local, localSyntax); } var exceptionSourceOpt = catchBlock.ExceptionSourceOpt; if (exceptionSourceOpt != null) { // 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 (!exceptionSourceOpt.Type.IsVerifierReference()) { Debug.Assert(exceptionSourceOpt.Type.IsTypeParameter()); // only expecting type parameters _builder.EmitOpCode(ILOpCode.Unbox_any); EmitSymbolToken(exceptionSourceOpt.Type, exceptionSourceOpt.Syntax); } BoundExpression exceptionSource = exceptionSourceOpt; while (exceptionSource.Kind == BoundKind.Sequence) { var seq = (BoundSequence)exceptionSource; EmitSideEffects(seq); exceptionSource = seq.Value; } switch (exceptionSource.Kind) { case BoundKind.Local: var exceptionSourceLocal = (BoundLocal)exceptionSource; Debug.Assert(exceptionSourceLocal.LocalSymbol.RefKind == RefKind.None); if (!IsStackLocal(exceptionSourceLocal.LocalSymbol)) { _builder.EmitLocalStore(GetLocal(exceptionSourceLocal)); } break; case BoundKind.FieldAccess: var left = (BoundFieldAccess)exceptionSource; Debug.Assert(!left.FieldSymbol.IsStatic, "Not supported"); Debug.Assert(!left.ReceiverOpt.Type.IsTypeParameter()); var stateMachineField = left.FieldSymbol as StateMachineFieldSymbol; if (((object)stateMachineField != null) && (stateMachineField.SlotIndex >= 0)) { _builder.DefineUserDefinedStateMachineHoistedLocal(stateMachineField.SlotIndex); } // When assigning to a field // we need to push param address below the exception var temp = AllocateTemp(exceptionSource.Type, exceptionSource.Syntax); _builder.EmitLocalStore(temp); var receiverTemp = EmitReceiverRef(left.ReceiverOpt); Debug.Assert(receiverTemp == null); _builder.EmitLocalLoad(temp); FreeTemp(temp); EmitFieldStore(left); break; default: throw ExceptionUtilities.UnexpectedValue(exceptionSource.Kind); } } else { _builder.EmitOpCode(ILOpCode.Pop); } // Emit the actual filter expression, if we have one, and normalize // results. if (catchBlock.ExceptionFilterOpt != null) { 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 already been stored to the // variable by the filter. _builder.EmitOpCode(ILOpCode.Pop); } EmitBlock(catchBlock.Body); _builder.CloseLocalScope(); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if (!node.Locals.IsDefaultOrEmpty) { // Yield/await aren't supported in catch block atm, but we need to rewrite the type // of the variables owned by the catch block. Note that one of these variables might be a closure frame reference. var newLocals = RewriteLocals(node.Locals); return node.Update( newLocals, (BoundExpression)this.Visit(node.ExceptionSourceOpt), this.VisitType(node.ExceptionTypeOpt), (BoundExpression)this.Visit(node.ExceptionFilterOpt), (BoundBlock)this.Visit(node.Body), node.IsSynthesizedAsyncCatchAll); } return base.VisitCatchBlock(node); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { EnsureOnlyEvalStack(); var locals = node.Locals; var exceptionSourceOpt = node.ExceptionSourceOpt; DeclareLocals(locals, stack: 0); if (exceptionSourceOpt != null) { // runtime pushes the exception object this.evalStack++; this.counter++; // We consume it by writing into the exception source. if (exceptionSourceOpt.Kind == BoundKind.Local) { RecordVarWrite(((BoundLocal)exceptionSourceOpt).LocalSymbol); } else { int prevStack = this.evalStack; exceptionSourceOpt = VisitExpression(exceptionSourceOpt, ExprContext.AssignmentTarget); Debug.Assert(evalStack == prevStack + (LhsUsesStackWhenAssignedTo(exceptionSourceOpt, ExprContext.AssignmentTarget) ? 1 : 0)); this.evalStack = prevStack; } this.evalStack--; this.counter++; } BoundExpression boundFilter; if (node.ExceptionFilterOpt != null) { boundFilter = (BoundExpression)this.Visit(node.ExceptionFilterOpt); // the value of filter expression is consumed by the VM this.evalStack--; this.counter++; // variables allocated on stack in a filter can't be used in the catch handler EnsureOnlyEvalStack(); } else { boundFilter = null; } var boundBlock = (BoundBlock)this.Visit(node.Body); var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); return node.Update(locals, exceptionSourceOpt, exceptionTypeOpt, boundFilter, boundBlock); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if (!_analysis.CatchContainsAwait(node)) { var origCurrentAwaitCatchFrame = _currentAwaitCatchFrame; _currentAwaitCatchFrame = null; var result = base.VisitCatchBlock(node); _currentAwaitCatchFrame = origCurrentAwaitCatchFrame; return(result); } var currentAwaitCatchFrame = _currentAwaitCatchFrame; if (currentAwaitCatchFrame == null) { Debug.Assert(node.Syntax.IsKind(SyntaxKind.CatchClause)); var tryStatementSyntax = (TryStatementSyntax)node.Syntax.Parent; currentAwaitCatchFrame = _currentAwaitCatchFrame = new AwaitCatchFrame(_F, tryStatementSyntax); } var catchType = node.ExceptionTypeOpt ?? _F.SpecialType(SpecialType.System_Object); var catchTemp = _F.SynthesizedLocal(catchType); var storePending = _F.AssignmentExpression( _F.Local(currentAwaitCatchFrame.pendingCaughtException), _F.Convert(currentAwaitCatchFrame.pendingCaughtException.Type.TypeSymbol, _F.Local(catchTemp))); var setPendingCatchNum = _F.Assignment( _F.Local(currentAwaitCatchFrame.pendingCatch), _F.Literal(currentAwaitCatchFrame.handlers.Count + 1)); // catch (ExType exTemp) // { // pendingCaughtException = exTemp; // catchNo = X; // } BoundCatchBlock catchAndPend; ImmutableArray <LocalSymbol> handlerLocals; var filterOpt = node.ExceptionFilterOpt; if (filterOpt == null) { // store pending exception // as the first statement in a catch catchAndPend = node.Update( ImmutableArray.Create(catchTemp), _F.Local(catchTemp), catchType, exceptionFilterOpt: null, body: _F.Block( _F.HiddenSequencePoint(), _F.ExpressionStatement(storePending), setPendingCatchNum), isSynthesizedAsyncCatchAll: node.IsSynthesizedAsyncCatchAll); // catch locals live on the synthetic catch handler block handlerLocals = node.Locals; } else { handlerLocals = ImmutableArray <LocalSymbol> .Empty; // catch locals move up into hoisted locals // since we might need to access them from both the filter and the catch foreach (var local in node.Locals) { currentAwaitCatchFrame.HoistLocal(local, _F); } // store pending exception // as the first expression in a filter var sourceOpt = node.ExceptionSourceOpt; var rewrittenFilter = (BoundExpression)this.Visit(filterOpt); var newFilter = sourceOpt == null? _F.MakeSequence( storePending, rewrittenFilter) : _F.MakeSequence( storePending, AssignCatchSource((BoundExpression)this.Visit(sourceOpt), currentAwaitCatchFrame), rewrittenFilter); catchAndPend = node.Update( ImmutableArray.Create(catchTemp), _F.Local(catchTemp), catchType, exceptionFilterOpt: newFilter, body: _F.Block( _F.HiddenSequencePoint(), setPendingCatchNum), isSynthesizedAsyncCatchAll: node.IsSynthesizedAsyncCatchAll); } var handlerStatements = ArrayBuilder <BoundStatement> .GetInstance(); handlerStatements.Add(_F.HiddenSequencePoint()); if (filterOpt == null) { var sourceOpt = node.ExceptionSourceOpt; if (sourceOpt != null) { BoundExpression assignSource = AssignCatchSource((BoundExpression)this.Visit(sourceOpt), currentAwaitCatchFrame); handlerStatements.Add(_F.ExpressionStatement(assignSource)); } } handlerStatements.Add((BoundStatement)this.Visit(node.Body)); var handler = _F.Block( handlerLocals, handlerStatements.ToImmutableAndFree() ); currentAwaitCatchFrame.handlers.Add(handler); return(catchAndPend); }
/// <summary> /// Returns true if a catch contains awaits /// </summary> internal bool CatchContainsAwait(BoundCatchBlock node) { return(_awaitContainingCatches != null && _awaitContainingCatches.Contains(node)); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { AddVariables(node.Locals); return(base.VisitCatchBlock(node)); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { EnsureOnlyEvalStack(); var local = node.LocalOpt; var exceptionSourceOpt = node.ExceptionSourceOpt; if ((object)local != null) { DeclareLocal(local, stack: 0); } if (exceptionSourceOpt != null) { // runtime pushes the exception object PushEvalStack(null, ExprContext.Value); _counter++; // We consume it by writing into the exception source. if (exceptionSourceOpt.Kind == BoundKind.Local) { RecordVarWrite(((BoundLocal)exceptionSourceOpt).LocalSymbol); } else { int prevStack = StackDepth(); exceptionSourceOpt = VisitExpression(exceptionSourceOpt, ExprContext.AssignmentTarget); SetStackDepth(prevStack); } PopEvalStack(); _counter++; } BoundExpression boundFilter; if (node.ExceptionFilterOpt != null) { boundFilter = (BoundExpression)this.Visit(node.ExceptionFilterOpt); // the value of filter expression is consumed by the VM PopEvalStack(); _counter++; // variables allocated on stack in a filter can't be used in the catch handler EnsureOnlyEvalStack(); } else { boundFilter = null; } var boundBlock = (BoundBlock)this.Visit(node.Body); var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); return node.Update(local, exceptionSourceOpt, exceptionTypeOpt, boundFilter, boundBlock, node.IsSynthesizedAsyncCatchAll); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { if ((object)node.LocalOpt != null) { // Yield/await aren't supported in catch block atm, but we need to rewrite the type // of the variable owned by the catch block. Note that this variable might be a closure frame reference. LocalSymbol newLocal; TryRewriteLocal(node.LocalOpt, out newLocal); return node.Update( newLocal, (BoundExpression)this.Visit(node.ExceptionSourceOpt), this.VisitType(node.ExceptionTypeOpt), (BoundExpression)this.Visit(node.ExceptionFilterOpt), (BoundBlock)this.Visit(node.Body), node.IsSynthesizedAsyncCatchAll); } return base.VisitCatchBlock(node); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { var exceptionSource = node.ExceptionSourceOpt; var type = node.ExceptionTypeOpt; var filter = node.ExceptionFilterOpt; var body = node.Body; if (exceptionSource != null) { // runtime pushes the exception object _nodeCounter++; if (exceptionSource.Kind == BoundKind.Local) { var sourceLocal = ((BoundLocal)exceptionSource).LocalSymbol; LocalDefUseInfo locInfo; // If catch is the last access, we do not need to store the exception object. if (_info.TryGetValue(sourceLocal, out locInfo) && IsLastAccess(locInfo, _nodeCounter)) { exceptionSource = null; } } else { exceptionSource = (BoundExpression)Visit(exceptionSource); } // we consume it by writing into the local _nodeCounter++; } if (filter != null) { filter = (BoundExpression)this.Visit(filter); // the value of filter expression is consumed by the VM _nodeCounter++; } body = (BoundBlock)this.Visit(body); type = this.VisitType(type); return node.Update(node.LocalOpt, exceptionSource, type, filter, body, node.IsSynthesizedAsyncCatchAll); }
public override BoundExpression InstrumentCatchClauseFilter(BoundCatchBlock original, BoundExpression rewrittenFilter, SyntheticBoundNodeFactory factory) { return(Previous.InstrumentCatchClauseFilter(original, rewrittenFilter, factory)); }