/// <summary> /// Injects the garbage collector related IL ops into the specified method. /// </summary> /// <param name="TheLibrary">The library being compiled.</param> /// <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 InjectGC(ILLibrary TheLibrary, Types.MethodInfo theMethodInfo, ILBlock theILBlock) { if (theMethodInfo.ApplyGC) { // Find the index of the MethodStart op int MethodStartOpPos = theILBlock.PositionOf(theILBlock.ILOps.Where(x => x is ILOps.MethodStart).First()); // Inject ops for incrementing ref. count of args at start of method int InjectIncArgsRefCountPos = MethodStartOpPos + 1; foreach (Types.VariableInfo argInfo in theMethodInfo.ArgumentInfos) { if (argInfo.TheTypeInfo.IsGCManaged) { theILBlock.ILOps.Insert(InjectIncArgsRefCountPos, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldarg, ValueBytes = BitConverter.GetBytes(argInfo.Position) }); theILBlock.ILOps.Insert(InjectIncArgsRefCountPos + 1, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.IncrementRefCountMethodAttribute)].First().UnderlyingInfo }); } } // The following two things can be done within the same loop: // Inject ops for inc./dec. ref. counts of objects when written to: // - Arguments / Locals // - Fields / Static Fields // - Elements of Arrays // Add Cleanup Block and Inject finally-block ops for it // - Also remember the op for storing return value (if any) ILPreprocessState preprocessState = new ILPreprocessState() { TheILLibrary = TheLibrary, Input = theILBlock, CurrentStackFrame = new StackFrame() }; ExceptionHandledBlock CleanupExBlock = new ExceptionHandledBlock(); for (int opIndx = 0; opIndx < theILBlock.ILOps.Count; opIndx++) { ILOp currOp = theILBlock.ILOps[opIndx]; bool IncRefCount = false; int incOpIndexBy = 0; switch ((ILOp.OpCodes)currOp.opCode.Value) { case ILOp.OpCodes.Stsfld: #region Stsfld { int metadataToken = Utilities.ReadInt32(currOp.ValueBytes, 0); System.Reflection.FieldInfo theField = theMethodInfo.UnderlyingInfo.Module.ResolveField(metadataToken); Types.TypeInfo theFieldTypeInfo = TheLibrary.GetTypeInfo(theField.FieldType); if (theFieldTypeInfo.IsGCManaged) { theILBlock.ILOps.Insert(opIndx, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldsfld, ValueBytes = currOp.ValueBytes }); theILBlock.ILOps.Insert(opIndx + 1, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); IncRefCount = true; incOpIndexBy = 2; } } #endregion break; case ILOp.OpCodes.Stloc: case ILOp.OpCodes.Stloc_0: case ILOp.OpCodes.Stloc_1: case ILOp.OpCodes.Stloc_2: case ILOp.OpCodes.Stloc_3: case ILOp.OpCodes.Stloc_S: #region Stloc { UInt16 localIndex = 0; switch ((ILOp.OpCodes)currOp.opCode.Value) { case ILOp.OpCodes.Stloc: localIndex = (UInt16)Utilities.ReadInt16(currOp.ValueBytes, 0); break; case ILOp.OpCodes.Stloc_0: localIndex = 0; break; case ILOp.OpCodes.Stloc_1: localIndex = 1; break; case ILOp.OpCodes.Stloc_2: localIndex = 2; break; case ILOp.OpCodes.Stloc_3: localIndex = 3; break; case ILOp.OpCodes.Stloc_S: localIndex = (UInt16)currOp.ValueBytes[0]; break; } Types.TypeInfo LocalTypeInfo = theMethodInfo.LocalInfos[localIndex].TheTypeInfo; if (LocalTypeInfo.IsGCManaged) { theILBlock.ILOps.Insert(opIndx, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldloc, ValueBytes = BitConverter.GetBytes(localIndex) }); theILBlock.ILOps.Insert(opIndx + 1, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); IncRefCount = true; incOpIndexBy = 2; } } #endregion break; case ILOp.OpCodes.Stfld: #region Stfld { int metadataToken = Utilities.ReadInt32(currOp.ValueBytes, 0); System.Reflection.FieldInfo theField = theMethodInfo.UnderlyingInfo.Module.ResolveField(metadataToken); Types.TypeInfo theFieldTypeInfo = TheLibrary.GetTypeInfo(theField.FieldType); if (theFieldTypeInfo.IsGCManaged) { // Items on stack: // - Object reference // - (New) Value to store // // We want to load the current value of the field // for which we must duplicate the object ref // But first, we must remove the (new) value to store // off the stack, while also storing it to put back // on the stack after so the store can continue // // So: // 1. Switch value to store and object ref on stack // 3. Duplicate the object ref // 4. Load the field value // 5. Call dec ref count // 6. Switch value to store and object ref back again //USE A SWITCH STACK ITEMS OP!! theILBlock.ILOps.Insert(opIndx, new ILOps.StackSwitch() { StackSwitch_Items = 2 }); theILBlock.ILOps.Insert(opIndx + 1, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Dup }); theILBlock.ILOps.Insert(opIndx + 2, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldfld, ValueBytes = currOp.ValueBytes }); theILBlock.ILOps.Insert(opIndx + 3, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); theILBlock.ILOps.Insert(opIndx + 4, new ILOps.StackSwitch() { StackSwitch_Items = 2 }); IncRefCount = true; incOpIndexBy = 5; } } #endregion break; case ILOp.OpCodes.Stelem: case ILOp.OpCodes.Stelem_Ref: #region Stelem / Stelem_Ref { bool doDecrement = false; bool isRefOp = false; if ((ILOp.OpCodes)currOp.opCode.Value == ILOp.OpCodes.Stelem_Ref) { doDecrement = preprocessState.CurrentStackFrame.Stack.Peek().isGCManaged; isRefOp = true; } else { int metadataToken = Utilities.ReadInt32(currOp.ValueBytes, 0); Type elementType = theMethodInfo.UnderlyingInfo.Module.ResolveType(metadataToken); doDecrement = TheLibrary.GetTypeInfo(elementType).IsGCManaged; } if (doDecrement) { // Items on stack: // - Array reference // - Index // - (New) Value to store // // We want to load the current value of the element at Index in the array // for which we must duplicate the array ref and index // But first, we must remove the (new) value to store // off the stack, while also storing it to put back // on the stack after so the store can continue // // So: // 1. Switch (rotate) 1 times the top 3 values so that index is on top // 2. Duplicate the index // 3. Switch (rotate) 2 times the top 4 values so that array ref is on top // 4. Duplicate the array ref // 5. Switch (rotate) 4 times the top 5 values so that duplicate array ref and index are on top // 6. Do LdElem op to load existing element value // 7. Call GC.DecrementRefCount // 8. Switch (rotate) 1 times the top 3 values so that the stack is in its original state // (9. Continue to increment ref count as normal) // // The following is a diagram of the stack manipulation occurring here: // Key: A=Array ref, I=Index, V=Value to store, E=Loaded element // Top-most stack item appears last // // 1) Rotate x 1 2) Duplicate 3) Rot x 2 4) Dup // A,I,V ---------> V,A,I ---------> V,A,I,I ---------> I,I,V,A ---------> I,I,V,A,A // // // 5) Rot x 4 6) Ldelem 7) Call GC (Dec) // I,I,V,A,A ---------> I,V,A,A,I ---------> I,V,A,E ---------> I,V,A // // // 8) Rot x 1 9) Dup 10) Call GC (Inc) // I,V,A ---------> A,I,V ---------> A,I,V,V ---------> A,I,V #region 1. theILBlock.ILOps.Insert(opIndx, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(3), StackSwitch_Items = 3 }); incOpIndexBy++; #endregion #region 2. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Dup }); incOpIndexBy++; #endregion #region 3. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(4), StackSwitch_Items = 4 }); incOpIndexBy++; theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(4), StackSwitch_Items = 4 }); incOpIndexBy++; #endregion #region 4. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Dup }); incOpIndexBy++; #endregion #region 5. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(5), StackSwitch_Items = 5 }); incOpIndexBy++; theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(5), StackSwitch_Items = 5 }); incOpIndexBy++; theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(5), StackSwitch_Items = 5 }); incOpIndexBy++; theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(5), StackSwitch_Items = 5 }); incOpIndexBy++; #endregion #region 6. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOp() { opCode = isRefOp ? System.Reflection.Emit.OpCodes.Ldelem_Ref : System.Reflection.Emit.OpCodes.Ldelem, ValueBytes = currOp.ValueBytes }); incOpIndexBy++; #endregion #region 7. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); incOpIndexBy++; #endregion #region 8. theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOps.StackSwitch() { ValueBytes = BitConverter.GetBytes(3), StackSwitch_Items = 3 }); incOpIndexBy++; #endregion IncRefCount = true; } } #endregion break; case ILOp.OpCodes.Starg: case ILOp.OpCodes.Starg_S: #region Starg { UInt16 index = (ILOp.OpCodes)currOp.opCode.Value == ILOp.OpCodes.Starg_S ? (UInt16)currOp.ValueBytes[0] : (UInt16)Utilities.ReadInt16(currOp.ValueBytes, 0); if (theMethodInfo.ArgumentInfos[index].TheTypeInfo.IsGCManaged) { theILBlock.ILOps.Insert(opIndx, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldarg, ValueBytes = BitConverter.GetBytes(index) }); theILBlock.ILOps.Insert(opIndx + 1, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); IncRefCount = true; incOpIndexBy = 2; } } #endregion break; } if (IncRefCount && !preprocessState.CurrentStackFrame.Stack.Peek().isNewGCObject) { theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Dup }); incOpIndexBy++; theILBlock.ILOps.Insert(opIndx + incOpIndexBy, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.IncrementRefCountMethodAttribute)].First().UnderlyingInfo }); incOpIndexBy++; } // If op changed if (theILBlock.ILOps[opIndx] != currOp) { theILBlock.ILOps[opIndx].Offset = currOp.Offset; theILBlock.ILOps[opIndx].BytesSize = currOp.BytesSize; } // <= is correct. E.g. if 1 extra op added, incOpIndex=1 so <= results in currOp processed // + extra op processed bool UseNextOpAsCleanupStart = false; for (int i = 0; i <= incOpIndexBy; i++) { currOp = theILBlock.ILOps[opIndx]; if (UseNextOpAsCleanupStart) { UseNextOpAsCleanupStart = false; CleanupExBlock.Offset = currOp.Offset; } if (currOp is ILOps.MethodStart) { TargetArchitecture.MethodStartOp.PerformStackOperations(preprocessState, theILBlock.ILOps[opIndx]); UseNextOpAsCleanupStart = true; } else if (currOp is ILOps.MethodEnd) { TargetArchitecture.MethodEndOp.PerformStackOperations(preprocessState, theILBlock.ILOps[opIndx]); } else if (currOp is ILOps.StackSwitch) { TargetArchitecture.StackSwitchOp.PerformStackOperations(preprocessState, theILBlock.ILOps[opIndx]); } else { // Leave unsupported ops for the IL Scanner to deal with (or later code e.g. castclass op) if (TargetArchitecture.TargetILOps.ContainsKey((ILOp.OpCodes)currOp.opCode.Value)) { ILOp ConverterOp = TargetArchitecture.TargetILOps[(ILOp.OpCodes)currOp.opCode.Value]; ConverterOp.PerformStackOperations(preprocessState, currOp); } } if (i > 0) { opIndx++; } } } if (theMethodInfo.ArgumentInfos.Count > 0 || theMethodInfo.LocalInfos.Count > 0) { bool AddCleanupBlock = false; foreach (Types.VariableInfo anArgInfo in theMethodInfo.ArgumentInfos) { if (anArgInfo.TheTypeInfo.IsGCManaged) { AddCleanupBlock = true; break; } } if (!AddCleanupBlock) { foreach (Types.VariableInfo aLocInfo in theMethodInfo.LocalInfos) { if (aLocInfo.TheTypeInfo.IsGCManaged) { AddCleanupBlock = true; break; } } } if (AddCleanupBlock) { ILOp lastOp = theILBlock.ILOps.Last(); int lastOpOffset = lastOp.Offset; int lastOpIndex = theILBlock.ILOps.Count - 1; bool MethodHasReturnValue = false; // If there is a return value, we will need to temp store it if (theMethodInfo.UnderlyingInfo is System.Reflection.MethodInfo) { Type returnType = ((System.Reflection.MethodInfo)theMethodInfo.UnderlyingInfo).ReturnType; //Void return type = no return value if (returnType != typeof(void)) { // Add local variable for storing return value int lastLocalOffset = theMethodInfo.LocalInfos.Count > 0 ? theMethodInfo.LocalInfos.Last().Offset : 0; int lastLocalSize = theMethodInfo.LocalInfos.Count > 0 ? theMethodInfo.LocalInfos.Last().TheTypeInfo.SizeOnStackInBytes : 0; theMethodInfo.LocalInfos.Add(new Types.VariableInfo() { UnderlyingType = returnType, TheTypeInfo = TheLibrary.GetTypeInfo(returnType), Position = theMethodInfo.LocalInfos.Count, Offset = lastLocalOffset + lastLocalSize }); // Add op for storing return value, update op offsets theILBlock.ILOps.Insert(lastOpIndex, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Stloc, Offset = lastOpOffset, BytesSize = lastOp.BytesSize, ValueBytes = BitConverter.GetBytes(theMethodInfo.LocalInfos.Count - 1) }); lastOpIndex++; MethodHasReturnValue = true; } } ILOp leaveOp; // Add the Leave op of the try-block theILBlock.ILOps.Insert(lastOpIndex, leaveOp = new ILOp() { opCode = System.Reflection.Emit.OpCodes.Leave, Offset = lastOpOffset, BytesSize = lastOp.BytesSize, ValueBytes = BitConverter.GetBytes(0) }); lastOpIndex++; FinallyBlock CleanupFinallyBlock = new FinallyBlock() { Offset = lastOpOffset + lastOp.BytesSize, Length = 0 }; CleanupExBlock.Length = lastOpOffset - CleanupExBlock.Offset; CleanupExBlock.FinallyBlocks.Add(CleanupFinallyBlock); int cleanupOpsOffset = lastOpOffset + 1; // Add cleanup code for local variables (including the return value local) foreach (Types.VariableInfo aLocInfo in theMethodInfo.LocalInfos) { if (aLocInfo.TheTypeInfo.IsGCManaged) { theILBlock.ILOps.Insert(lastOpIndex, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldloc, Offset = cleanupOpsOffset, BytesSize = 1, ValueBytes = BitConverter.GetBytes(aLocInfo.Position) }); cleanupOpsOffset++; lastOpIndex++; theILBlock.ILOps.Insert(lastOpIndex, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, Offset = cleanupOpsOffset, BytesSize = 1, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); cleanupOpsOffset++; lastOpIndex++; CleanupFinallyBlock.Length += 2; } } // Add cleanup code for arguments foreach (Types.VariableInfo anArgInfo in theMethodInfo.ArgumentInfos) { if (anArgInfo.TheTypeInfo.IsGCManaged) { theILBlock.ILOps.Insert(lastOpIndex, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldarg, Offset = cleanupOpsOffset, BytesSize = 1, ValueBytes = BitConverter.GetBytes(anArgInfo.Position) }); cleanupOpsOffset++; lastOpIndex++; theILBlock.ILOps.Insert(lastOpIndex, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Call, Offset = cleanupOpsOffset, BytesSize = 1, MethodToCall = ILLibrary.SpecialMethods[typeof(Attributes.DecrementRefCountMethodAttribute)].First().UnderlyingInfo }); cleanupOpsOffset++; lastOpIndex++; CleanupFinallyBlock.Length += 2; } } // Add end finally op theILBlock.ILOps.Insert(lastOpIndex, leaveOp.LoadAtILOpAfterOp = new ILOp() { opCode = System.Reflection.Emit.OpCodes.Endfinally, Offset = cleanupOpsOffset, BytesSize = 1 }); cleanupOpsOffset++; lastOpIndex++; CleanupFinallyBlock.Length++; // Add restore return value op if (MethodHasReturnValue) { theILBlock.ILOps.Insert(lastOpIndex, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Ldloc, Offset = cleanupOpsOffset, BytesSize = 1, ValueBytes = BitConverter.GetBytes(theMethodInfo.LocalInfos.Count - 1) }); cleanupOpsOffset++; lastOpIndex++; } // Add ex block to the method theILBlock.ExceptionHandledBlocks.Add(CleanupExBlock); // Replace any Ret ops contained within Cleanup Block with: // - Op to store return value (if any) // - Leave op bool Inside = false; for(int opIndx = 0; opIndx < theILBlock.ILOps.Count; opIndx++) { if (theILBlock.ILOps[opIndx].Offset > CleanupExBlock.Offset + CleanupExBlock.Length) { break; } else if (theILBlock.ILOps[opIndx].Offset >= CleanupExBlock.Offset) { Inside = true; } if (Inside && (ILOp.OpCodes)theILBlock.ILOps[opIndx].opCode.Value == ILOp.OpCodes.Ret) { ILOp ARetOp = theILBlock.ILOps[opIndx]; theILBlock.ILOps.RemoveAt(opIndx); if (MethodHasReturnValue) { theILBlock.ILOps.Insert(opIndx, new ILOp() { opCode = System.Reflection.Emit.OpCodes.Stloc, Offset = ARetOp.Offset, ValueBytes = BitConverter.GetBytes(theMethodInfo.LocalInfos.Count - 1) }); theILBlock.ILOps.Insert(opIndx + 1, new ILOp() { Offset = ARetOp.Offset, opCode = System.Reflection.Emit.OpCodes.Leave, ValueBytes = BitConverter.GetBytes(0), LoadAtILOpAfterOp = leaveOp.LoadAtILOpAfterOp }); } else { theILBlock.ILOps.Insert(opIndx, new ILOp() { Offset = ARetOp.Offset, opCode = System.Reflection.Emit.OpCodes.Leave, ValueBytes = BitConverter.GetBytes(0), LoadAtILOpAfterOp = leaveOp.LoadAtILOpAfterOp }); } } } } } } }
/// <summary> /// Reads a non-plugged method. /// </summary> /// <param name="aMethodInfo">The method to read.</param> /// <returns>The new IL block for the method.</returns> public static ILBlock ReadNonPlugged(Types.MethodInfo aMethodInfo) { ILBlock result = new ILBlock() { TheMethodInfo = aMethodInfo }; MethodBody methodBody = aMethodInfo.MethodBody; //Method body for something like [DelegateType].Invoke() is null. // So just return an empty method if that is this case. if (methodBody != null) { foreach (LocalVariableInfo aLocal in methodBody.LocalVariables) { aMethodInfo.LocalInfos.Add(new Types.VariableInfo() { UnderlyingType = aLocal.LocalType, Position = aLocal.LocalIndex }); } byte[] ILBytes = methodBody.GetILAsByteArray(); int ILBytesPos = 0; while (ILBytesPos < ILBytes.Length) { OpCode currOpCode; ushort currOpCodeID = 0; int currOpBytesSize = 0; if (ILBytes[ILBytesPos] == 0xFE) { currOpCodeID = (ushort)(0xFE00 + (short)ILBytes[ILBytesPos + 1]); currOpBytesSize += 2; } else { currOpCodeID = (ushort)ILBytes[ILBytesPos]; currOpBytesSize++; } currOpCode = AllOpCodes[currOpCodeID]; int operandSize = 0; switch (currOpCode.OperandType) { #region Operands case OperandType.InlineBrTarget: operandSize = 4; break; case OperandType.InlineField: operandSize = 4; break; case OperandType.InlineI: operandSize = 4; break; case OperandType.InlineI8: operandSize = 8; break; case OperandType.InlineMethod: operandSize = 4; break; case OperandType.InlineNone: //No operands = no op size break; case OperandType.InlineR: operandSize = 8; break; case OperandType.InlineSig: operandSize = 4; break; case OperandType.InlineString: operandSize = 4; break; case OperandType.InlineSwitch: { uint count = Utilities.ReadUInt32(ILBytes, ILBytesPos + currOpBytesSize); currOpBytesSize += 4; operandSize = (int)(count * 4); } break; case OperandType.InlineTok: operandSize = 4; break; case OperandType.InlineType: operandSize = 4; break; case OperandType.InlineVar: operandSize = 2; break; case OperandType.ShortInlineBrTarget: operandSize = 1; break; case OperandType.ShortInlineI: operandSize = 1; break; case OperandType.ShortInlineR: operandSize = 4; break; case OperandType.ShortInlineVar: operandSize = 1; break; default: throw new Exception("Unrecognised operand type!"); #endregion } MethodBase methodToCall = null; byte[] valueBytes = new byte[operandSize]; if (operandSize > 0) { Array.Copy(ILBytes, ILBytesPos + currOpBytesSize, valueBytes, 0, operandSize); if (currOpCode.Equals(OpCodes.Call) || currOpCode.Equals(OpCodes.Calli) || currOpCode.Equals(OpCodes.Callvirt) || currOpCode.Equals(OpCodes.Ldftn) || currOpCode.Equals(OpCodes.Newobj)) { int MethodMetadataToken = Utilities.ReadInt32(valueBytes, 0); methodToCall = aMethodInfo.UnderlyingInfo.Module.ResolveMethod(MethodMetadataToken); } } result.ILOps.Add(new ILOp() { opCode = currOpCode, Offset = ILBytesPos, BytesSize = currOpBytesSize + operandSize, ValueBytes = valueBytes, MethodToCall = methodToCall }); ILBytesPos += currOpBytesSize + operandSize; } foreach (ExceptionHandlingClause aClause in methodBody.ExceptionHandlingClauses) { ExceptionHandledBlock exBlock = result.GetExactExceptionHandledBlock(aClause.TryOffset); if (exBlock == null) { exBlock = new ExceptionHandledBlock(); exBlock.Offset = aClause.TryOffset; exBlock.Length = aClause.TryLength; result.ExceptionHandledBlocks.Add(exBlock); } switch (aClause.Flags) { case ExceptionHandlingClauseOptions.Fault: case ExceptionHandlingClauseOptions.Clause: { CatchBlock catchBlock = new CatchBlock() { Offset = aClause.HandlerOffset, Length = aClause.HandlerLength, //Though not used, we may as well set it anyway FilterType = aClause.CatchType }; exBlock.CatchBlocks.Add(catchBlock); } break; case ExceptionHandlingClauseOptions.Finally: { FinallyBlock finallyBlock = new FinallyBlock() { Offset = aClause.HandlerOffset, Length = aClause.HandlerLength }; exBlock.FinallyBlocks.Add(finallyBlock); } break; default: Logger.LogError("IL0010", "", 0, "Exception handling clause not supported! Type: " + aClause.Flags.ToString()); break; } } } return(result); }
/// <summary> /// Reads a non-plugged method. /// </summary> /// <param name="aMethodInfo">The method to read.</param> /// <returns>The new IL block for the method.</returns> public static ILBlock ReadNonPlugged(Types.MethodInfo aMethodInfo) { ILBlock result = new ILBlock() { TheMethodInfo = aMethodInfo }; MethodBody methodBody = aMethodInfo.MethodBody; //Method body for something like [DelegateType].Invoke() is null. // So just return an empty method if that is this case. if (methodBody != null) { foreach (LocalVariableInfo aLocal in methodBody.LocalVariables) { aMethodInfo.LocalInfos.Add(new Types.VariableInfo() { UnderlyingType = aLocal.LocalType, Position = aLocal.LocalIndex }); } byte[] ILBytes = methodBody.GetILAsByteArray(); int ILBytesPos = 0; while (ILBytesPos < ILBytes.Length) { OpCode currOpCode; ushort currOpCodeID = 0; int currOpBytesSize = 0; if (ILBytes[ILBytesPos] == 0xFE) { currOpCodeID = (ushort)(0xFE00 + (short)ILBytes[ILBytesPos + 1]); currOpBytesSize += 2; } else { currOpCodeID = (ushort)ILBytes[ILBytesPos]; currOpBytesSize++; } currOpCode = AllOpCodes[currOpCodeID]; int operandSize = 0; switch (currOpCode.OperandType) { #region Operands case OperandType.InlineBrTarget: operandSize = 4; break; case OperandType.InlineField: operandSize = 4; break; case OperandType.InlineI: operandSize = 4; break; case OperandType.InlineI8: operandSize = 8; break; case OperandType.InlineMethod: operandSize = 4; break; case OperandType.InlineNone: //No operands = no op size break; case OperandType.InlineR: operandSize = 8; break; case OperandType.InlineSig: operandSize = 4; break; case OperandType.InlineString: operandSize = 4; break; case OperandType.InlineSwitch: { uint count = Utilities.ReadUInt32(ILBytes, ILBytesPos + currOpBytesSize); currOpBytesSize += 4; operandSize = (int)(count * 4); } break; case OperandType.InlineTok: operandSize = 4; break; case OperandType.InlineType: operandSize = 4; break; case OperandType.InlineVar: operandSize = 2; break; case OperandType.ShortInlineBrTarget: operandSize = 1; break; case OperandType.ShortInlineI: operandSize = 1; break; case OperandType.ShortInlineR: operandSize = 4; break; case OperandType.ShortInlineVar: operandSize = 1; break; default: throw new Exception("Unrecognised operand type!"); #endregion } MethodBase methodToCall = null; byte[] valueBytes = new byte[operandSize]; if (operandSize > 0) { Array.Copy(ILBytes, ILBytesPos + currOpBytesSize, valueBytes, 0, operandSize); if (currOpCode.Equals(OpCodes.Call) || currOpCode.Equals(OpCodes.Calli) || currOpCode.Equals(OpCodes.Callvirt) || currOpCode.Equals(OpCodes.Ldftn) || currOpCode.Equals(OpCodes.Newobj)) { int MethodMetadataToken = Utilities.ReadInt32(valueBytes, 0); methodToCall = aMethodInfo.UnderlyingInfo.Module.ResolveMethod(MethodMetadataToken); } } result.ILOps.Add(new ILOp() { opCode = currOpCode, Offset = ILBytesPos, BytesSize = currOpBytesSize + operandSize, ValueBytes = valueBytes, MethodToCall = methodToCall }); ILBytesPos += currOpBytesSize + operandSize; } foreach (ExceptionHandlingClause aClause in methodBody.ExceptionHandlingClauses) { ExceptionHandledBlock exBlock = result.GetExactExceptionHandledBlock(aClause.TryOffset); if (exBlock == null) { exBlock = new ExceptionHandledBlock(); exBlock.Offset = aClause.TryOffset; exBlock.Length = aClause.TryLength; result.ExceptionHandledBlocks.Add(exBlock); } switch (aClause.Flags) { case ExceptionHandlingClauseOptions.Fault: case ExceptionHandlingClauseOptions.Clause: { CatchBlock catchBlock = new CatchBlock() { Offset = aClause.HandlerOffset, Length = aClause.HandlerLength, //Though not used, we may as well set it anyway FilterType = aClause.CatchType }; exBlock.CatchBlocks.Add(catchBlock); } break; case ExceptionHandlingClauseOptions.Finally: { FinallyBlock finallyBlock = new FinallyBlock() { Offset = aClause.HandlerOffset, Length = aClause.HandlerLength }; exBlock.FinallyBlocks.Add(finallyBlock); } break; default: Logger.LogError("IL0010", "", 0, "Exception handling clause not supported! Type: " + aClause.Flags.ToString()); break; } } } return result; }