/// <summary> /// Pops a size from the stack, allocates size bytes on the local dynamic memory pool, and pushes a pointer to the allocated block. /// /// LocalAllocate can only be called if the stack is empty aside from the size value. /// /// Memory allocated with LocalAllocate is released when the current method ends execution. /// </summary> public Emit <DelegateType> LocalAllocate() { if (CatchBlocks.Any(c => c.Value.Item2 == -1)) { throw new InvalidOperationException("LocalAllocate cannot be used in a catch block"); } if (FinallyBlocks.Any(f => f.Value.Item2 == -1)) { throw new InvalidOperationException("LocalAllocate cannot be used in a finally block"); } if (!AllowsUnverifiableCIL) { FailUnverifiable("LocalAllocate"); } UpdateState(Wrap(new[] { new StackTransition(1) }, "LocalAllocate")); var transitions = new[] { new StackTransition(new [] { typeof(int) }, new [] { typeof(NativeIntType) }), new StackTransition(new [] { typeof(NativeIntType) }, new [] { typeof(NativeIntType) }) }; UpdateState(OpCodes.Localloc, Wrap(transitions, "LocalAllocate")); return(this); }
private void ValidateTryCatchFinallyBlocks() { foreach (var kv in TryBlocks.AsEnumerable()) { if (kv.Value.Item2 == -1) { throw new SigilVerificationException( "Unended ExceptionBlock " + kv.Key, IL.Instructions(AllLocals) ); } } foreach (var kv in CatchBlocks.AsEnumerable()) { if (kv.Value.Item2 == -1) { throw new Exception("Invalid State, all ExceptionBlocks are ended but CatchBlock " + kv.Key + " isn't ended"); } } foreach (var kv in FinallyBlocks.AsEnumerable()) { if (kv.Value.Item2 == -1) { throw new Exception("Invalid State, all ExceptionBlocks are ended but FinallyBlock " + kv.Key + " isn't ended"); } } }
/// <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> /// Transfers control to another method. /// /// The parameters and calling convention of method must match the current one's. /// /// The stack must be empty to jump. /// /// Like the branching instructions, Jump cannot leave exception blocks. /// </summary> public Emit <DelegateType> Jump(MethodInfo method) { if (method == null) { throw new ArgumentNullException("method"); } if (method.CallingConvention != CallingConventions) { throw new ArgumentException("Jump expected a calling convention of " + CallingConventions + ", found " + method.CallingConvention); } var paras = method.GetParameters(); if (paras.Length != ParameterTypes.Length) { throw new ArgumentException("Jump expected a method with " + ParameterTypes.Length + " parameters, found " + paras.Length); } if (!AllowsUnverifiableCIL) { FailUnverifiable("Jump"); } if (CatchBlocks.Any(t => t.Value.Item2 == -1)) { throw new InvalidOperationException("Jump cannot transfer control from a catch block"); } if (FinallyBlocks.Any(t => t.Value.Item2 == -1)) { throw new InvalidOperationException("Jump cannot transfer control from a finally block"); } if (TryBlocks.Any(t => t.Value.Item2 == -1)) { throw new InvalidOperationException("Jump cannot transfer control from an exception block"); } UpdateState(Wrap(new[] { new StackTransition(0) }, "Jump")); for (var i = 0; i < paras.Length; i++) { var shouldBe = paras[i].ParameterType; var actuallyIs = ParameterTypes[i]; if (!shouldBe.IsAssignableFrom(actuallyIs)) { throw new SigilVerificationException("Jump expected the #" + i + " parameter to be assignable from " + actuallyIs + ", but found " + shouldBe, IL.Instructions(AllLocals)); } } UpdateState(OpCodes.Jmp, method, TypeHelpers.EmptyTypes, Wrap(StackTransition.None(), "Jump")); return(this); }
public override int GetHashCode() { unchecked { var hashCode = 37 + Body.GetHashCode(); hashCode = (hashCode * 397) ^ CatchBlocks.GetHashCode(); hashCode = (hashCode * 397) ^ Finally.GetHashCode(); return(hashCode); } }
/// <summary> /// <para>Leave an exception or catch block, branching to the given label.</para> /// <para>This instruction empties the stack.</para> /// </summary> public Emit <DelegateType> Leave(Label label) { if (label == null) { throw new ArgumentNullException("label"); } if (((IOwned)label).Owner != this) { if (((IOwned)label).Owner is DisassembledOperations <DelegateType> ) { return(Leave(label.Name)); } FailOwnership(label); } if (!TryBlocks.Any(t => t.Value.Item2 == -1) && !CatchBlocks.Any(c => c.Value.Item2 == -1)) { throw new InvalidOperationException("Leave can only be used within an exception or catch block"); } // Note that Leave *always* nuked the stack; nothing survies exiting an exception block UpdateOpCodeDelegate update; UpdateState(OpCodes.Leave, label, Wrap(new[] { new StackTransition(new [] { typeof(PopAllType) }, TypeHelpers.EmptyTypes) }, "Leave"), out update); Branches.Add(SigilTuple.Create(OpCodes.Leave, label, IL.Index)); BranchPatches[IL.Index] = SigilTuple.Create(label, update, OpCodes.Leave); MustMark = true; var valid = CurrentVerifiers.UnconditionalBranch(label); if (!valid.Success) { throw new SigilVerificationException("Leave", valid, IL.Instructions(AllLocals)); } return(this); }
/// <summary> /// From within a catch block, rethrows the exception that caused the catch block to be entered. /// </summary> public Emit <DelegateType> ReThrow() { if (!CatchBlocks.Any(c => c.Value.Item2 == -1)) { throw new InvalidOperationException("ReThrow is only legal in a catch block"); } UpdateState(OpCodes.Rethrow, Wrap(StackTransition.None(), "ReThrow")); UpdateState(Wrap(new[] { new StackTransition(new[] { typeof(PopAllType) }, Type.EmptyTypes) }, "ReThrow")); Throws.Add(IL.Index); MustMark = true; var verify = CurrentVerifiers.ReThrow(); if (!verify.Success) { throw new SigilVerificationException("ReThrow", verify, IL.Instructions(AllLocals)); } return(this); }
private bool Equals(TryBlock other) { return(Body.Equals(other.Body) && CatchBlocks.Equals(other.CatchBlocks) && Finally.Equals(other.Finally)); }
private void ValidateTryCatchFinallyBranches() { foreach (var branch in Branches.AsEnumerable()) { var instr = BranchPatches[branch.Item3]; var toLabel = branch.Item2; var fromIndex = branch.Item3; var toIndex = Marks[toLabel]; var fromTryBlocks = TryBlocks.Where(t => fromIndex >= t.Value.Item1 && fromIndex <= t.Value.Item2).ToList(); var fromCatchBlocks = CatchBlocks.Where(c => fromIndex >= c.Value.Item1 && fromIndex <= c.Value.Item2).ToList(); var fromFinallyBlocks = FinallyBlocks.Where(f => fromIndex >= f.Value.Item1 && fromIndex <= f.Value.Item2).ToList(); var toTryBlocks = TryBlocks.Where(t => toIndex >= t.Value.Item1 && toIndex <= t.Value.Item2).ToList(); var toCatchBlocks = CatchBlocks.Where(c => toIndex >= c.Value.Item1 && toIndex <= c.Value.Item2).ToList(); var toFinallyBlocks = FinallyBlocks.Where(f => toIndex >= f.Value.Item1 && toIndex <= f.Value.Item2).ToList(); var fromTryBlock = fromTryBlocks.OrderByDescending(t => t.Value.Item1).Select(t => t.Key).FirstOrDefault(); var fromCatchBlock = fromCatchBlocks.OrderByDescending(c => c.Value.Item1).Select(c => c.Key).FirstOrDefault(); var fromFinallyBlock = fromFinallyBlocks.OrderByDescending(f => f.Value.Item1).Select(f => f.Key).FirstOrDefault(); var toTryBlock = toTryBlocks.OrderByDescending(t => t.Value.Item1).Select(t => t.Key).FirstOrDefault(); var toCatchBlock = toCatchBlocks.OrderByDescending(c => c.Value.Item1).Select(c => c.Key).FirstOrDefault(); var toFinallyBlock = toFinallyBlocks.OrderByDescending(f => f.Value.Item1).Select(f => f.Key).FirstOrDefault(); // Nothing funky going on, carry on if (fromTryBlock == null && fromCatchBlock == null && fromFinallyBlock == null && toTryBlock == null && toCatchBlock == null && toFinallyBlock == null) { continue; } if (fromCatchBlock != null && toCatchBlock != fromCatchBlock) { if (instr.Item3 != OpCodes.Leave) { throw new SigilVerificationException( "Cannot branch from inside " + fromCatchBlock + " to outside, exit the ExceptionBlock first", IL.Instructions(AllLocals) ); } } if (fromFinallyBlock != null && toFinallyBlock != fromFinallyBlock) { throw new SigilVerificationException( "Cannot branch from inside " + fromFinallyBlock + " to outside, exit the ExceptionBlock first", IL.Instructions(AllLocals) ); } if (toFinallyBlock != null && fromFinallyBlock != toFinallyBlock) { throw new SigilVerificationException( "Cannot branch into a FinallyBlock", IL.Instructions(AllLocals) ); } if (fromTryBlock != null && toTryBlock != fromTryBlock) { if (instr.Item3 != OpCodes.Leave) { throw new SigilVerificationException( "Cannot branch from inside " + fromTryBlock + " to outside, exit the ExceptionBlock first", IL.Instructions(AllLocals) ); } } } }
/// <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); }