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; public Callvirt(ILReader aReader, MethodInformation aMethodInfo) : base(aReader, aMethodInfo) { mLabelName = GetInstructionLabel(aReader); mCurrentMethodInfo = aMethodInfo; int xThisOffSet = (from item in aMethodInfo.Locals select item.Offset + item.Size).LastOrDefault(); 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); 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; mCurrentILOffset = aReader.Position; } 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"); new CPUx86.Call(mNormalAddress); } else { if (Assembler.InMetalMode) { throw new Exception("Virtual methods not supported in Metal mode! (Called method = '" + mMethodDescription + "')"); } //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)); Call.EmitExceptionLogic(Assembler, mCurrentILOffset, mCurrentMethodInfo, mLabelName + "_AfterAddressCheck", true, xEmitCleanup); new CPU.Label(mLabelName + "_AfterAddressCheck"); if (mTargetMethodInfo.Arguments[0].ArgumentType == typeof(object)) { new CPUx86.Push("eax"); new CPUx86.Move("eax", "[esp + " + (mThisOffset + 4) + "]"); new CPUx86.Compare("dword [eax + 4]", ((int)InstanceTypeEnum.BoxedValueType).ToString()); new CPUx86.Pop("eax"); new CPUx86.JumpIfNotEquals(mLabelName + "_NOT_BOXED_THIS"); new CPUx86.Push("eax"); new CPUx86.Move("eax", "[esp + " + (mThisOffset + 4) + "]"); new CPUx86.Add("eax", ObjectImpl.FieldDataOffset.ToString()); new CPUx86.Pushd("[eax]"); for (int i = mThisOffset; i > 0; i -= 4) { new CPUx86.Pushd("[esp + " + mThisOffset + "]"); } new CPUx86.Move("eax", "esp"); new CPUx86.Add("eax", (mThisOffset).ToString()); new CPUx86.Pushd(CPUx86.Registers.EAX); new CPUx86.Move("eax", "[esp + " + (mThisOffset + 8) + "]"); new CPUx86.Call(CPUx86.Registers.EAX); new CPUx86.Add("esp", "8"); for (int i = mThisOffset; i >= 0; i -= 4) { new CPUx86.Add("esp", "4"); } new CPUx86.JumpAlways(mLabelName + "__AFTER_NOT_BOXED_THIS"); } new CPU.Label(mLabelName + "_NOT_BOXED_THIS"); new CPUx86.Call("eax"); new CPU.Label(mLabelName + "__AFTER_NOT_BOXED_THIS"); } if (!Assembler.InMetalMode) { new CPUx86.Test("ecx", "2"); new CPUx86.JumpIfNotEquals(MethodFooterOp.EndOfMethodLabelNameException); } new CPU.Comment("Argument Count = " + mArgumentCount.ToString()); for (int i = 0; i < mArgumentCount; i++) { Assembler.StackContents.Pop(); } if (mReturnSize == 0) { return; } if (mReturnSize <= 4) { new CPUx86.Pushd(CPUx86.Registers.EAX); Assembler.StackContents.Push(new StackContent(mReturnSize, mCurrentMethodInfo.ReturnType)); return; } if (mReturnSize <= 8) { new CPUx86.Pushd(CPUx86.Registers.EBX); new CPUx86.Pushd(CPUx86.Registers.EAX); Assembler.StackContents.Push(new StackContent(mReturnSize, mCurrentMethodInfo.ReturnType)); return; } } } }