/// <summary> /// Injects the try-catch-finally related IL ops into the specified method. /// </summary> /// <param name="theMethodInfo">The method to inject ops into.</param> /// <param name="theILBlock">The IL block for the method to inject ops into.</param> private static void InjectTryCatchFinally(Types.MethodInfo theMethodInfo, ILBlock theILBlock) { // Replace Leave and Leave_S ops for (int i = 0; i < theILBlock.ILOps.Count; i++) { ILOp theOp = theILBlock.ILOps[i]; if ((ILOp.OpCodes)theOp.opCode.Value == ILOp.OpCodes.Leave || (ILOp.OpCodes)theOp.opCode.Value == ILOp.OpCodes.Leave_S) { theILBlock.ILOps.RemoveAt(i); int ILOffset = 0; if (theOp.LoadAtILOpAfterOp == null) { if ((int)theOp.opCode.Value == (int)ILOp.OpCodes.Leave) { ILOffset = BitConverter.ToInt32(theOp.ValueBytes, 0); } else { ILOffset = (int)theOp.ValueBytes[0]; } } theILBlock.ILOps.Insert(i, new ILOp() { Offset = theOp.Offset, BytesSize = theOp.BytesSize, opCode = System.Reflection.Emit.OpCodes.Ldftn, LoadAtILOffset = theOp.NextOffset + ILOffset, LoadAtILOpAfterOp = theOp.LoadAtILOpAfterOp, MethodToCall = theILBlock.TheMethodInfo.UnderlyingInfo }); theILBlock.ILOps.Insert(i + 1, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.ExceptionsHandleLeaveMethodAttribute)].First().UnderlyingInfo }); i++; } else if ((int)theOp.opCode.Value == (int)ILOp.OpCodes.Endfinally) { //Endfinally is for leaving a (critical) finally section //We handle it by a higher-level implementation rather than // leaving it to each architecture to implement. //Endfinally is handled by inserting a call to the Exceptions.HandleEndFinally method theILBlock.ILOps.RemoveAt(i); ILOp newOp; theILBlock.ILOps.Insert(i, newOp = new ILOp() { Offset = theOp.Offset, BytesSize = theOp.BytesSize, opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.ExceptionsHandleEndFinallyMethodAttribute)].First().UnderlyingInfo }); //Replace references to this endfinally op for (int j = 0; j < theILBlock.ILOps.Count; j++) { if (theILBlock.ILOps[j].LoadAtILOpAfterOp == theOp) { theILBlock.ILOps[j].LoadAtILOpAfterOp = newOp; } } } } foreach (ExceptionHandledBlock exBlock in theILBlock.ExceptionHandledBlocks) { #region Try-sections // Insert the start of try-block // Also remember that other ops (e.g. branch and leave) can hold references // to the first op INSIDE a try block, even if they themselves are OUTSIDE // the block. This means we need to correct which op has the Offset value // set so those ops point at the new start op of the try block. //Consists of adding a new ExceptionHandlerInfos // built from the info in exBlock so we: // - Add infos for all finally blocks first // - Then add infos for all catch blocks // Since finally code is always run after catch code in C#, // by adding catch handlers after finally handlers, they // appear as the inner-most exception handlers and so get // run before finally handlers. //To add a new ExceptionHandlerInfo we must set up args for // calling Exceptions.AddExceptionHandlerInfo: // 1. We load a pointer to the handler // - This is calculated from an offset from the start of the function to the handler // 2. We load a pointer to the filter // - This is calculated from an offset from the start of the function to the filter // Note: Filter has not been implemented as an actual filter. // At the moment, 0x00000000 indicates a finally handler, // 0xFFFFFFFF indicates no filter block // (i.e. an unfiltered catch handler) // 0xXXXXXXXX has undetermined behaviour! //For each finally block: int insertPos = theILBlock.PositionOf(theILBlock.At(exBlock.Offset)); foreach (FinallyBlock finBlock in exBlock.FinallyBlocks) { // 1. Load the pointer to the handler code: theILBlock.ILOps.Insert(insertPos++, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldftn, MethodToCall = theMethodInfo.UnderlyingInfo, LoadAtILOffset = finBlock.Offset }); theILBlock.ILOps.Insert(insertPos++, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldc_I4, ValueBytes = BitConverter.GetBytes(0x00000000) }); theILBlock.ILOps.Insert(insertPos++, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.AddExceptionHandlerInfoMethodAttribute)].First().UnderlyingInfo }); } foreach (CatchBlock catchBlock in exBlock.CatchBlocks) { theILBlock.ILOps.Insert(insertPos++, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldftn, MethodToCall = theMethodInfo.UnderlyingInfo, LoadAtILOffset = catchBlock.Offset }); theILBlock.ILOps.Insert(insertPos++, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldc_I4, ValueBytes = BitConverter.GetBytes(0xFFFFFFFF) }); theILBlock.ILOps.Insert(insertPos++, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.AddExceptionHandlerInfoMethodAttribute)].First().UnderlyingInfo }); } #endregion } }
/// <summary> /// Handles catch handles (of exception blocks) for the specified method. /// </summary> /// <param name="theMethodInfo">The method to handle.</param> /// <param name="theILBlock">The IL block for the method to handle.</param> private static void DealWithCatchHandlers(Types.MethodInfo theMethodInfo, ILBlock theILBlock) { foreach (ExceptionHandledBlock exBlock in theILBlock.ExceptionHandledBlocks) { #region Catch-sections // Strip the first pop of catch-handler foreach (CatchBlock aCatchBlock in exBlock.CatchBlocks) { ILOp catchPopOp = theILBlock.At(aCatchBlock.Offset); int pos = theILBlock.PositionOf(catchPopOp); theILBlock.ILOps.RemoveAt(pos); theILBlock.ILOps.Insert(pos, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Nop, Offset = catchPopOp.Offset, BytesSize = catchPopOp.BytesSize }); } #endregion } }