public override ScopeInfo OpenScope( ScopeType scopeType, Microsoft.Cci.ITypeReference exceptionType, ExceptionHandlerScope currentExceptionHandler ) { Debug.Assert( ((_handlers.Count == 0) && (scopeType == ScopeType.Try)) || ( (_handlers.Count > 0) && ( (scopeType == ScopeType.Catch) || (scopeType == ScopeType.Filter) || (scopeType == ScopeType.Finally) || (scopeType == ScopeType.Fault) ) ) ); Debug.Assert(currentExceptionHandler == _containingHandler); var handler = new ExceptionHandlerScope(this, scopeType, exceptionType); _handlers.Add(handler); return(handler); }
public BasicBlockWithHandlerScope( ILBuilder builder, ExceptionHandlerScope enclosingHandler ) : base(builder) { this.enclosingHandler = enclosingHandler; }
private bool ForwardLabelsAllowLeaving() { bool madeChanges = false; SmallDictionary <object, LabelInfo> .KeyCollection labels = _labelInfos.Keys; bool done; do { done = true; foreach (object label in labels) { LabelInfo labelInfo = _labelInfos[label]; if (labelInfo.targetOfConditionalBranches) { // only unconditional labels can be forwarded as a leave continue; } BasicBlock targetBlock = labelInfo.bb; Debug.Assert(!IsSpecialEndHandlerBlock(targetBlock)); if (targetBlock.HasNoRegularInstructions) { BasicBlock targetsTarget = null; switch (targetBlock.BranchCode) { case ILOpCode.Br: targetsTarget = targetBlock.BranchBlock; break; case ILOpCode.Nop: targetsTarget = targetBlock.NextBlock; break; } if ((targetsTarget != null) && (targetsTarget != targetBlock)) { ExceptionHandlerScope currentHandler = targetBlock.EnclosingHandler; ExceptionHandlerScope newHandler = targetsTarget.EnclosingHandler; // can skip the jump if it is in the same try scope if (CanMoveLabelToAnotherHandler(currentHandler, newHandler)) { _labelInfos[label] = labelInfo.WithNewTarget(targetsTarget); madeChanges = true; // since we modified at least one label we want to try again. done = false; } } } } } while (!done); return(madeChanges); }
public ExceptionHandlerLeaderBlock( ILBuilder builder, ExceptionHandlerScope enclosingHandler, BlockType type ) : base(builder, enclosingHandler) { _type = type; }
private static ScopeBounds GetBounds(ExceptionHandlerScope scope) { var scopes = ArrayBuilder <Cci.LocalScope> .GetInstance(); var result = scope.GetLocalScopes(scopes, edgeInclusive: false); scopes.Free(); return(result); }
private static ScopeBounds GetBounds(ExceptionHandlerScope scope) { ArrayBuilder <Cci.LocalScope> scopes = ArrayBuilder <Cci.LocalScope> .GetInstance(); ScopeBounds result = scope.GetLocalScopes(scopes); scopes.Free(); return(result); }
public virtual BasicBlock CreateBlock(ILBuilder builder) { ExceptionHandlerScope enclosingHandler = builder.EnclosingExceptionHandler; BasicBlock block = enclosingHandler == null? AllocatePooledBlock(builder) : new BasicBlockWithHandlerScope(builder, enclosingHandler); AddBlock(block); return(block); }
private bool ForwardLabelsNoLeaving() { bool madeChanges = false; SmallDictionary <object, LabelInfo> .KeyCollection labels = _labelInfos.Keys; bool done; do { done = true; foreach (object label in labels) { LabelInfo labelInfo = _labelInfos[label]; BasicBlock targetBlock = labelInfo.bb; Debug.Assert(!IsSpecialEndHandlerBlock(targetBlock)); if (targetBlock.HasNoRegularInstructions) { BasicBlock targetsTarget = null; switch (targetBlock.BranchCode) { case ILOpCode.Br: targetsTarget = targetBlock.BranchBlock; break; case ILOpCode.Nop: targetsTarget = targetBlock.NextBlock; break; } if ((targetsTarget != null) && (targetsTarget != targetBlock)) { ExceptionHandlerScope currentHandler = targetBlock.EnclosingHandler; ExceptionHandlerScope newHandler = targetsTarget.EnclosingHandler; // forward the label if can be done without leaving current handler if (currentHandler == newHandler) { _labelInfos[label] = labelInfo.WithNewTarget(targetsTarget); madeChanges = true; // since we modified at least one label we want to try again. done = false; } } } } } while (!done); return(madeChanges); }
// if branch is blocked by a nonterminating finally, // returns label for a landing block used as a target of blocked branches // Otherwise returns null private static object BlockedBranchDestination(BasicBlock src, BasicBlock dest) { ExceptionHandlerScope srcHandler = src.EnclosingHandler; // most common case - we are not in an exception handler. if (srcHandler == null) { return(null); } return(BlockedBranchDestinationSlow(dest.EnclosingHandler, srcHandler)); }
internal void CloseScope(ILBuilder builder) { ScopeInfo scope = _scopes.Pop(); scope.CloseScope(builder); if (scope.IsExceptionHandler) { _enclosingExceptionHandler = GetEnclosingExceptionHandler(); } Debug.Assert(_enclosingExceptionHandler == GetEnclosingExceptionHandler()); }
internal void CloseScope(ILBuilder builder) { var scope = scopes.Pop(); scope.CloseScope(builder); if (scope.IsExceptionHandler) { this.enclosingExceptionHandler = GetEnclosingExceptionHandler(); } Debug.Assert(this.enclosingExceptionHandler == GetEnclosingExceptionHandler()); }
private static bool CanMoveLabelToAnotherHandler( ExceptionHandlerScope currentHandler, ExceptionHandlerScope newHandler ) { // Generally, assuming already valid code that contains "LABEL1: goto LABEL2" // we can substitute LABEL1 for LABEL2 so that the branches go directly to // the final destination. // Technically we can allow "moving" a label to any scope that contains the current one // However we should be careful with the cases when current label is protected by a // catch clause. // // [COMPAT] // If we move a label out of catch-protected try clause, we could be forcing JIT to inject // it back since, in the case of Thread.Abort, the re-throwing of the exception needs // to happen around this leave instruction which we would be removing. // In addition to just extra work on the JIT side, handling of this case appears to be // very delicate and there are known cases where JITs did not handle this particular // scenario correctly resulting in various violations of Thread.Abort behavior. // We cannot rely on these JIT issues being fixed in the end user environment. // // Considering that we are only winning a single LEAVE here, it seems reasonable to // just disallow labels to move outside of a catch-protected regions. // no handler means outermost scope (method level) if (newHandler == null && currentHandler.ContainingExceptionScope.FinallyOnly()) { return(true); } // check if the target handler contains current handler. do { if (currentHandler == newHandler) { return(true); } var containerScope = currentHandler.ContainingExceptionScope; if (!containerScope.FinallyOnly()) { // this may move the label outside of catch-protected region // we will disallow that. return(false); } currentHandler = containerScope.ContainingHandler; } while (currentHandler != null); return(false); }
internal ScopeInfo OpenScope(ScopeType scopeType, Cci.ITypeReference exceptionType) { ScopeInfo scope = CurrentScope.OpenScope(scopeType, exceptionType, _enclosingExceptionHandler); _scopes.Push(scope); if (scope.IsExceptionHandler) { _enclosingExceptionHandler = (ExceptionHandlerScope)scope; } Debug.Assert(_enclosingExceptionHandler == GetEnclosingExceptionHandler()); return(scope); }
public override ScopeInfo OpenScope( ScopeType scopeType, Cci.ITypeReference exceptionType, ExceptionHandlerScope currentExceptionHandler) { ScopeInfo scope = base.OpenScope(scopeType, exceptionType, currentExceptionHandler); if (_nestedScopes == null) { _nestedScopes = ImmutableArray.CreateBuilder <ScopeInfo>(1); } _nestedScopes.Add(scope); return(scope); }
public virtual ScopeInfo OpenScope(ScopeType scopeType, Microsoft.Cci.ITypeReference exceptionType, ExceptionHandlerScope currentHandler) { if (scopeType == ScopeType.TryCatchFinally) { return(new ExceptionHandlerContainerScope(currentHandler)); } else { Debug.Assert(scopeType == ScopeType.Variable || scopeType == ScopeType.StateMachineVariable); return(new LocalScopeInfo()); } }
internal ScopeInfo OpenScope(ScopeType scopeType, Microsoft.Cci.ITypeReference exceptionType) { var scope = CurrentScope.OpenScope(scopeType, exceptionType, this.enclosingExceptionHandler); scopes.Push(scope); if (scope.IsExceptionHandler) { this.enclosingExceptionHandler = (ExceptionHandlerScope)scope; } Debug.Assert(this.enclosingExceptionHandler == GetEnclosingExceptionHandler()); return(scope); }
/// <summary> /// Marks blocks that are recursively reachable from the given block. /// </summary> private static void MarkReachableFrom(ArrayBuilder <BasicBlock> reachableBlocks, BasicBlock block) { tryAgain: if (block != null && block.Reachability == Reachability.NotReachable) { block.Reachability = Reachability.Reachable; ILOpCode branchCode = block.BranchCode; if (branchCode == ILOpCode.Nop && block.Type == BlockType.Normal) { block = block.NextBlock; goto tryAgain; } if (branchCode.CanFallThrough()) { PushReachableBlockToProcess(reachableBlocks, block.NextBlock); } else { // If this block is an "endfinally" block, then clear // the reachability of the following special block. if (branchCode == ILOpCode.Endfinally) { ExceptionHandlerScope enclosingFinally = block.EnclosingHandler; enclosingFinally?.UnblockFinally(); } } switch (block.Type) { case BlockType.Switch: MarkReachableFromSwitch(reachableBlocks, block); break; case BlockType.Try: MarkReachableFromTry(reachableBlocks, block); break; default: MarkReachableFromBranch(reachableBlocks, block); break; } } }
private static object BlockedBranchDestinationSlow( ExceptionHandlerScope destHandler, ExceptionHandlerScope srcHandler ) { ScopeInfo destHandlerScope = null; if (destHandler != null) { destHandlerScope = destHandler.ContainingExceptionScope; } // go from the source out until no longer crossing any finally blocks // between source and destination // if any finally blocks found in the process, check if they are blocking while (srcHandler != destHandler) { // branches within same ContainingExceptionScope do not go through finally. // only checking that source and destination are within the same handler would miss the case // of branching from catch into corresponding try. if (srcHandler.ContainingExceptionScope == destHandlerScope) { break; } if (srcHandler.Type == ScopeType.Try) { var handlerBlock = srcHandler.LeaderBlock.NextExceptionHandler; if (handlerBlock.Type == BlockType.Finally) { var blockedDest = handlerBlock.EnclosingHandler.BlockedByFinallyDestination; if (blockedDest != null) { return(blockedDest); } } } srcHandler = srcHandler.ContainingExceptionScope.ContainingHandler; } return(null); }
private static bool InSameOrOuterHandler(ExceptionHandlerScope currentHandler, ExceptionHandlerScope newHandler) { // no handler means outermost scope (method level) if (newHandler == null) { return(true); } // check if the target handler contains current handler. do { if (currentHandler == newHandler) { return(true); } currentHandler = currentHandler.ContainingExceptionScope.ContainingHandler; } while (currentHandler != null); return(false); }
public override void CloseScope(ILBuilder builder) { Debug.Assert(_handlers.Count > 1); // Fix up the NextExceptionHandler reference of each leader block. ExceptionHandlerScope tryScope = _handlers[0]; ExceptionHandlerLeaderBlock previousBlock = tryScope.LeaderBlock; for (int i = 1; i < _handlers.Count; i++) { ExceptionHandlerScope handlerScope = _handlers[i]; ExceptionHandlerLeaderBlock nextBlock = handlerScope.LeaderBlock; previousBlock.NextExceptionHandler = nextBlock; previousBlock = nextBlock; } // Generate label for try/catch "leave" target. builder.MarkLabel(_endLabel); // hide the following code, since it could be reached through the label above. builder.DefineHiddenSequencePoint(); Debug.Assert(builder._currentBlock == builder._labelInfos[_endLabel].bb); if (_handlers[1].Type == ScopeType.Finally) { // Generate "nop" branch to itself. If this block is unreachable // (because the finally block does not complete), the "nop" will be // replaced by Br_s. On the other hand, if this block is reachable, // the "nop" will be skipped so any "leave" instructions jumping // to this block will jump to the next instead. builder.EmitBranch(ILOpCode.Nop, _endLabel); _handlers[1].SetBlockedByFinallyDestination(_endLabel); } }
private static bool InSameOrOuterHandler(ExceptionHandlerScope currentHandler, ExceptionHandlerScope newHandler) { // no handler means outermost scope (method level) if (newHandler == null) { return true; } // check if the target handler contains current handler. do { if (currentHandler == newHandler) { return true; } currentHandler = currentHandler.ContainingExceptionScope.ContainingHandler; } while (currentHandler != null); return false; }
internal override void GetExceptionHandlerRegions(ArrayBuilder <Cci.ExceptionHandlerRegion> regions) { Debug.Assert(_handlers.Count > 1); ExceptionHandlerScope tryScope = null; ScopeBounds tryBounds = new ScopeBounds(); foreach (ExceptionHandlerScope handlerScope in _handlers) { // Partition I, section 12.4.2.5: // The ordering of the exception clauses in the Exception Handler Table is important. If handlers are nested, // the most deeply nested try blocks shall come before the try blocks that enclose them. // // so we collect the inner regions first. handlerScope.GetExceptionHandlerRegions(regions); ScopeBounds handlerBounds = GetBounds(handlerScope); if (tryScope == null) { // the first scope that we see should be Try. Debug.Assert(handlerScope.Type == ScopeType.Try); tryScope = handlerScope; tryBounds = handlerBounds; Reachability reachability = tryScope.LeaderBlock.Reachability; Debug.Assert((reachability == Reachability.Reachable) || (reachability == Reachability.NotReachable)); // All handler blocks should have same reachability. Debug.Assert(_handlers.All(h => (h.LeaderBlock.Reachability == reachability))); if (reachability != Reachability.Reachable) { return; } } else { Cci.ExceptionHandlerRegion region; switch (handlerScope.Type) { case ScopeType.Finally: region = new Cci.ExceptionHandlerRegionFinally(tryBounds.Begin, tryBounds.End, handlerBounds.Begin, handlerBounds.End); break; case ScopeType.Fault: region = new Cci.ExceptionHandlerRegionFault(tryBounds.Begin, tryBounds.End, handlerBounds.Begin, handlerBounds.End); break; case ScopeType.Catch: region = new Cci.ExceptionHandlerRegionCatch(tryBounds.Begin, tryBounds.End, handlerBounds.Begin, handlerBounds.End, handlerScope.ExceptionType); break; case ScopeType.Filter: region = new Cci.ExceptionHandlerRegionFilter(tryBounds.Begin, tryBounds.End, handlerScope.FilterHandlerStart, handlerBounds.End, handlerBounds.Begin); break; default: throw ExceptionUtilities.UnexpectedValue(handlerScope.Type); } regions.Add(region); } } }
public ExceptionHandlerContainerScope(ExceptionHandlerScope containingHandler) { _handlers = ImmutableArray.CreateBuilder <ExceptionHandlerScope>(2); _containingHandler = containingHandler; _endLabel = new object(); }
private static object BlockedBranchDestinationSlow(ExceptionHandlerScope destHandler, ExceptionHandlerScope srcHandler) { ScopeInfo destHandlerScope = null; if (destHandler != null) { destHandlerScope = destHandler.ContainingExceptionScope; } // go from the source out until we no longer crossing any finallies // between source and destination // if any finallies found in the process, check if they are blocking while (srcHandler != destHandler) { // branches within same ContainingExceptionScope do not go through finally. // only checking that source and destination are within the same handler would miss the case // of branching from catch into corresponding try. if (srcHandler.ContainingExceptionScope == destHandlerScope) { break; } if (srcHandler.Type == ScopeType.Try) { var handlerBlock = srcHandler.LeaderBlock.NextExceptionHandler; if (handlerBlock.Type == BlockType.Finally) { var blockedDest = handlerBlock.EnclosingHandler.BlockedByFinallyDestination; if (blockedDest != null) { return blockedDest; } } } srcHandler = srcHandler.ContainingExceptionScope.ContainingHandler; } return null; }
private static bool CanMoveLabelToAnotherHandler(ExceptionHandlerScope currentHandler, ExceptionHandlerScope newHandler) { // Generally, assuming already valid code that contains "LABEL1: goto LABEL2" // we can substitute LABEL1 for LABEL2 so that the branches go directly to // the final destination. // Technically we can allow "moving" a label to any scope that contains the current one // However we should be careful with the cases when current label is protected by a // catch clause. // // [COMPAT] // If we move a label out of catch-protected try clause, we could be forcing JIT to inject // it back since, in the case of Thread.Abort, the re-throwing of the exception needs // to happen around this leave instruction which we would be removing. // In addition to just extra work on the JIT side, handling of this case appears to be // very delicate and there are known cases where JITs did not handle this particular // scenario correctly resulting in various violations of Thread.Abort behavior. // We cannot rely on these JIT issues being fixed in the end user environment. // // Considering that we are only winning a single LEAVE here, it seems reasonable to // just disallow labels to move outside of a catch-protected regions. // no handler means outermost scope (method level) if (newHandler == null && currentHandler.ContainingExceptionScope.FinallyOnly()) { return true; } // check if the target handler contains current handler. do { if (currentHandler == newHandler) { return true; } var containerScope = currentHandler.ContainingExceptionScope; if (!containerScope.FinallyOnly()) { // this may move the label outside of catch-protected region // we will disallow that. return false; } currentHandler = containerScope.ContainingHandler; } while (currentHandler != null); return false; }
public BasicBlockWithHandlerScope(ILBuilder builder, ExceptionHandlerScope enclosingHandler) : base(builder) { this.enclosingHandler = enclosingHandler; }
public ExceptionHandlerLeaderBlock(ILBuilder builder, ExceptionHandlerScope enclosingHandler, BlockType type) : base(builder, enclosingHandler) { _type = type; }
public SwitchBlock(ILBuilder builder, ExceptionHandlerScope enclosingHandler) : base(builder, enclosingHandler) { this.SetBranchCode(ILOpCode.Switch); }
public ExceptionHandlerAttribute(Type ExceptionType, ExceptionHandlerScope Scope = ExceptionHandlerScope.Global) { this.Scope = Scope; this.ExceptionType = ExceptionType; }