using System; using System.Linq; using CPU = Indy.IL2CPU.Assembler; using CPUx86 = Indy.IL2CPU.Assembler.X86; using System.Reflection; using Indy.IL2CPU.Assembler; namespace Indy.IL2CPU.IL.X86 { [OpCode(OpCodeEnum.Callvirt, true)] public class Callvirt : Op { private readonly int mMethodIdentifier; private readonly string mNormalAddress; private readonly string mMethodDescription; private readonly int mThisOffset; private readonly int mArgumentCount; private readonly int mReturnSize; private readonly string mLabelName; private readonly MethodInformation mCurrentMethodInfo; private readonly MethodInformation mTargetMethodInfo; private readonly int mCurrentILOffset; private readonly int mExtraStackSpace; public Callvirt(ILReader aReader, MethodInformation aMethodInfo) : base(aReader, aMethodInfo) { mLabelName = GetInstructionLabel(aReader); mCurrentMethodInfo = aMethodInfo; MethodBase xMethod = aReader.OperandValueMethod; if (xMethod == null) { throw new Exception("Unable to determine Method!"); } MethodBase xMethodDef = xMethod; mMethodDescription = CPU.Label.GenerateLabelName(xMethodDef); mTargetMethodInfo = Engine.GetMethodInfo(xMethodDef, xMethodDef, mMethodDescription, null, aMethodInfo.DebugMode); if (xMethodDef.IsStatic || !xMethodDef.IsVirtual || xMethod.IsFinal) { Engine.QueueMethod(xMethodDef); mNormalAddress = CPU.Label.GenerateLabelName(xMethodDef); } mMethodIdentifier = Engine.GetMethodIdentifier(xMethodDef); Engine.QueueMethod(VTablesImplRefs.GetMethodAddressForTypeRef); mArgumentCount = mTargetMethodInfo.Arguments.Length; mReturnSize = mTargetMethodInfo.ReturnSize; mThisOffset = mTargetMethodInfo.Arguments[0].Offset; if (mTargetMethodInfo.ExtraStackSize > 0) { mThisOffset -= mTargetMethodInfo.ExtraStackSize; } mCurrentILOffset = aReader.Position; mExtraStackSpace = mTargetMethodInfo.ExtraStackSize; } public override void DoAssemble() { Action xEmitCleanup = delegate() { foreach (MethodInformation.Argument xArg in mTargetMethodInfo.Arguments) { new CPUx86.Add("esp", xArg.Size.ToString()); } }; if (!String.IsNullOrEmpty(mNormalAddress)) { EmitCompareWithNull(Assembler, mCurrentMethodInfo, "[esp + 0x" + mThisOffset.ToString("X") + "]", mLabelName, mLabelName + "_AfterNullRefCheck", xEmitCleanup, mCurrentILOffset); new CPU.Label(mLabelName + "_AfterNullRefCheck"); if (mExtraStackSpace > 0) { new CPUx86.Sub("esp", mExtraStackSpace.ToString()); } new CPUx86.Call(mNormalAddress); } else { /* * On the stack now: * $esp Params * $esp + mThisOffset This */ //Assembler.Add(new CPUx86.Pop("eax")); //Assembler.Add(new CPUx86.Pushd("eax")); EmitCompareWithNull(Assembler, mCurrentMethodInfo, "[esp + 0x" + mThisOffset.ToString("X") + "]", mLabelName, mLabelName + "_AfterNullRefCheck", xEmitCleanup, mCurrentILOffset); new CPU.Label(mLabelName + "_AfterNullRefCheck"); new CPUx86.Move(CPUx86.Registers.EAX, "[esp + 0x" + mThisOffset.ToString("X") + "]"); new CPUx86.Pushd(CPUx86.Registers.AtEAX); new CPUx86.Pushd("0" + mMethodIdentifier.ToString("X") + "h"); new CPUx86.Call(CPU.Label.GenerateLabelName(VTablesImplRefs.GetMethodAddressForTypeRef)); /* * On the stack now: * $esp Params * $esp + mThisOffset This * * EAX contains the method to call */ Call.EmitExceptionLogic(Assembler, mCurrentILOffset, mCurrentMethodInfo, mLabelName + "_AfterAddressCheck", true, xEmitCleanup); new CPU.Label(mLabelName + "_AfterAddressCheck"); if (mTargetMethodInfo.Arguments[0].ArgumentType == typeof(object)) { /* * On the stack now: * $esp method to call * $esp + 4 Params * $esp + mThisOffset + 4 This */ new CPUx86.Move("eax", "[esp + " + (mThisOffset + 4) + "]"); new CPUx86.Compare("dword [eax + 4]", ((int)InstanceTypeEnum.BoxedValueType).ToString()); /* * On the stack now: * $esp Params * $esp + mThisOffset This * * EAX contains the method to call */ new CPUx86.JumpIfNotEqual(mLabelName + "_NOT_BOXED_THIS"); new CPUx86.Pop("ecx"); /* * On the stack now: * $esp Params * $esp + mThisOffset This * * ECX contains the method to call */ new CPUx86.Move("eax", "[esp + " + (mThisOffset) + "]"); /* * On the stack now: * $esp Params * $esp + mThisOffset This * * ECX contains the method to call * EAX contains $This, but boxed */ new CPUx86.Add("eax", ObjectImpl.FieldDataOffset.ToString()); new CPUx86.Move("[esp + " + mThisOffset + "]", "eax"); /* * On the stack now: * $esp Params * $esp + mThisOffset Pointer to address inside box * * ECX contains the method to call */ new CPUx86.Push("ecx"); /* * On the stack now: * $esp Method to call * $esp + 4 Params * $esp + mThisOffset + 4 This */ } new CPU.Label(mLabelName + "_NOT_BOXED_THIS"); new CPUx86.Pop("eax"); if (mExtraStackSpace > 0) { new CPUx86.Sub("esp", mExtraStackSpace.ToString()); } new CPUx86.Call("eax"); new CPU.Label(mLabelName + "__AFTER_NOT_BOXED_THIS"); } new CPUx86.Test(CPUx86.Registers.ECX, 2); new CPUx86.JumpIfNotEqual(MethodFooterOp.EndOfMethodLabelNameException); new CPU.Comment("Argument Count = " + mArgumentCount.ToString()); for (int i = 0; i < mArgumentCount; i++) { Assembler.StackContents.Pop(); } Assembler.StackContents.Push(new StackContent(mReturnSize)); } } }