/// <summary> /// Begins a catch block for the given exception type in the given exception block. /// /// The given exception block must still be open. /// </summary> public CatchBlock BeginCatchBlock(ExceptionBlock forTry, Type exceptionType) { if (exceptionType == null) { throw new ArgumentNullException("exceptionType"); } if (forTry == null) { throw new ArgumentNullException("forTry"); } if (((IOwned)forTry).Owner != this) { FailOwnership(forTry); } if (CurrentExceptionBlock.Count > 0 && forTry != CurrentExceptionBlock.Peek()) { throw new InvalidOperationException("Cannot start CatchBlock on " + forTry + " while inner ExceptionBlock is still open"); } if (!typeof(Exception).IsAssignableFrom(exceptionType)) { throw new ArgumentException("BeginCatchBlock expects a type descending from Exception, found " + exceptionType, "exceptionType"); } var currentlyOpen = CatchBlocks.Where(c => c.Key.ExceptionBlock == forTry && c.Value.Item2 == -1).Select(s => s.Key).SingleOrDefault(); if (currentlyOpen != null) { throw new InvalidOperationException("Cannot start a new catch block, " + currentlyOpen + " has not been ended"); } if (MustMark) { MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel"))); } UpdateState(Wrap(new[] { new StackTransition(0) }, "BeginCatchBlock")); var tryBlock = TryBlocks[forTry]; if (tryBlock.Item2 != -1) { throw new SigilVerificationException("BeginCatchBlock expects an unclosed exception block, but " + forTry + " is already closed", IL.Instructions(AllLocals)); } IL.BeginCatchBlock(exceptionType); UpdateState(Wrap(StackTransition.Push(exceptionType), "BeginCatchBlock")); var ret = new CatchBlock(exceptionType, forTry); CatchBlocks[ret] = SigilTuple.Create(IL.Index, -1); return(ret); }
/// <summary> /// Begins a finally block on the given exception block. /// /// Only one finally block can be defined per exception block, and the block cannot appear within a catch block. /// /// The given exception block must still be open. /// </summary> public FinallyBlock BeginFinallyBlock(ExceptionBlock forTry) { if (forTry == null) { throw new ArgumentNullException("forTry"); } if (((IOwned)forTry).Owner != this) { FailOwnership(forTry); } var tryBlock = TryBlocks[forTry]; if (tryBlock.Item2 != -1) { throw new InvalidOperationException("BeginFinallyBlock expects an unclosed exception block, but " + forTry + " is already closed"); } if (CurrentExceptionBlock.Count > 0 && forTry != CurrentExceptionBlock.Peek()) { throw new InvalidOperationException("Cannot begin FinallyBlock on " + forTry + " while inner ExceptionBlock " + CurrentExceptionBlock.Peek() + " is still open"); } if (FinallyBlocks.Any(kv => kv.Key.ExceptionBlock == forTry)) { throw new InvalidOperationException("There can only be one finally block per ExceptionBlock, and one is already defined for " + forTry); } if (MustMark) { MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel"))); } UpdateState(Wrap(new[] { new StackTransition(0) }, "BeginFinallyBlock")); var ret = new FinallyBlock(forTry); IL.BeginFinallyBlock(); FinallyBlocks[ret] = SigilTuple.Create(IL.Index, -1); return(ret); }
/// <summary> /// Ends the given exception block. /// /// All catch and finally blocks associated with the given exception block must be ended before this method is called. /// </summary> public Emit <DelegateType> EndExceptionBlock(ExceptionBlock forTry) { if (forTry == null) { throw new ArgumentNullException("forTry"); } if (((IOwned)forTry).Owner != this) { FailOwnership(forTry); } var location = TryBlocks[forTry]; // Can't close the same exception block twice if (location.Item2 != -1) { throw new InvalidOperationException("ExceptionBlock has already been ended"); } if (CurrentExceptionBlock.Count > 0 && forTry != CurrentExceptionBlock.Peek()) { throw new InvalidOperationException("Cannot end outer ExceptionBlock " + forTry + " while inner EmitExceptionBlock " + CurrentExceptionBlock.Peek() + " is open"); } // Can't close an exception block while there are outstanding catch blocks foreach (var kv in CatchBlocks.AsEnumerable()) { if (kv.Key.ExceptionBlock != forTry) { continue; } if (kv.Value.Item2 == -1) { throw new InvalidOperationException("Cannot end ExceptionBlock, CatchBlock " + kv.Key + " has not been ended"); } } foreach (var kv in FinallyBlocks.AsEnumerable()) { if (kv.Key.ExceptionBlock != forTry) { continue; } if (kv.Value.Item2 == -1) { throw new InvalidOperationException("Cannot end ExceptionBlock, FinallyBlock " + kv.Key + " has not been ended"); } } if (!CatchBlocks.Any(k => k.Key.ExceptionBlock == forTry) && !FinallyBlocks.Any(k => k.Key.ExceptionBlock == forTry)) { throw new InvalidOperationException("Cannot end ExceptionBlock without defining at least one of a catch or finally block"); } IL.EndExceptionBlock(); TryBlocks[forTry] = SigilTuple.Create(location.Item1, IL.Index); Marks[forTry.Label] = IL.Index; CurrentExceptionBlock.Pop(); if (MustMark) { MarkLabel(DefineLabel(AutoNamer.Next(this, "__autolabel"))); } return(this); }