Cosmos/source/Cosmos.IL2CPU/IL/Call.cs
José Pedro eff94b0f11 Improved code organization.
Added method tests.
Implemented Rethrow opcode.
2017-01-27 17:01:51 +00:00

156 lines
5.7 KiB
C#

using System;
using System.Reflection;
using Cosmos.Assembler;
using Cosmos.IL2CPU.ILOpCodes;
using XSharp.Compiler;
using static XSharp.Compiler.XSRegisters;
using SysReflection = System.Reflection;
namespace Cosmos.IL2CPU.X86.IL
{
[Cosmos.IL2CPU.OpCode(ILOpCode.Code.Call)]
public class Call : ILOp
{
public Call(Cosmos.Assembler.Assembler aAsmblr)
: base(aAsmblr)
{
}
public static uint GetStackSizeToReservate(MethodBase aMethod, Type aType = null)
{
var xMethodInfo = aMethod as SysReflection.MethodInfo;
uint xReturnSize = 0;
if (xMethodInfo != null)
{
xReturnSize = SizeOfType(xMethodInfo.ReturnType);
}
if (xReturnSize == 0)
{
return 0;
}
// todo: implement exception support
int xExtraStackSize = (int)Align(xReturnSize, 4);
var xParameters = aMethod.GetParameters();
foreach (var xItem in xParameters)
{
xExtraStackSize -= (int)Align(SizeOfType(xItem.ParameterType), 4);
}
if (!xMethodInfo.IsStatic)
{
if (aType != null)
{
if (TypeIsReferenceType(aType))
{
xExtraStackSize -= GetObjectReferenceSize();
}
else
{
xExtraStackSize -= 4;
}
}
else
{
if (TypeIsReferenceType(aMethod.DeclaringType))
{
xExtraStackSize -= GetObjectReferenceSize();
}
else
{
xExtraStackSize -= 4;
}
}
}
if (xExtraStackSize > 0)
{
return (uint)xExtraStackSize;
}
return 0;
}
private static int GetObjectReferenceSize()
{
// old code, which goof up everything for structs
//return (int)Align(SizeOfType(xMethodInfo.DeclaringType), 4);
// TODO native pointer size, so that COSMOS could be 64 bit OS
return 8;
}
public override void Execute(MethodInfo aMethod, ILOpCode aOpCode)
{
var xOpMethod = aOpCode as OpMethod;
DoExecute(Assembler, aMethod, xOpMethod.Value, aOpCode, LabelName.Get(aMethod.MethodBase), DebugEnabled);
}
public static void DoExecute(Cosmos.Assembler.Assembler Assembler, MethodInfo aCurrentMethod, MethodBase aTargetMethod, ILOpCode aCurrent, string currentLabel, bool debugEnabled)
{
DoExecute(Assembler, aCurrentMethod, aTargetMethod, aCurrent, currentLabel, GetLabel(aCurrentMethod, aCurrent.NextPosition), debugEnabled);
}
public static void DoExecute(Cosmos.Assembler.Assembler Assembler, MethodInfo aCurrentMethod, MethodBase aTargetMethod, ILOpCode aOp, string currentLabel, string nextLabel, bool debugEnabled)
{
var xMethodInfo = aTargetMethod as SysReflection.MethodInfo;
string xNormalAddress = LabelName.Get(aTargetMethod);
var xParameters = aTargetMethod.GetParameters();
// todo: implement exception support
uint xExtraStackSize = GetStackSizeToReservate(aTargetMethod);
if (!aTargetMethod.IsStatic)
{
uint xThisOffset = 0;
foreach (var xItem in xParameters)
{
xThisOffset += Align(SizeOfType(xItem.ParameterType), 4);
}
var stackOffsetToCheck = xThisOffset;
if (TypeIsReferenceType(aTargetMethod.DeclaringType))
{
DoNullReferenceCheck(Assembler, debugEnabled, (int)stackOffsetToCheck + 4);
}
else
{
DoNullReferenceCheck(Assembler, debugEnabled, (int)stackOffsetToCheck);
}
}
if (xExtraStackSize > 0)
{
XS.Sub(ESP, xExtraStackSize);
}
XS.Call(xNormalAddress);
uint xReturnSize = 0;
if (xMethodInfo != null)
{
xReturnSize = SizeOfType(xMethodInfo.ReturnType);
}
if (aCurrentMethod != null)
{
EmitExceptionLogic(Assembler, aCurrentMethod, aOp, true,
delegate
{
var xStackOffsetBefore = aOp.StackOffsetBeforeExecution.Value;
uint xPopSize = 0;
foreach (var type in aOp.StackPopTypes)
{
xPopSize += Align(SizeOfType(type), 4);
}
var xResultSize = xReturnSize;
if (xResultSize % 4 != 0)
{
xResultSize += 4 - (xResultSize % 4);
}
EmitExceptionCleanupAfterCall(Assembler, xResultSize, xStackOffsetBefore, xPopSize);
}, nextLabel);
}
}
}
}