mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
288 lines
14 KiB
C#
288 lines
14 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Cosmos.Assembler;
|
|
using CPUx86 = Cosmos.Assembler.x86;
|
|
using Cosmos.IL2CPU.ILOpCodes;
|
|
using System.Reflection;
|
|
|
|
using Cosmos.IL2CPU.Plugs.System;
|
|
using XSharp.Compiler;
|
|
using SysReflection = System.Reflection;
|
|
|
|
namespace Cosmos.IL2CPU.X86.IL
|
|
{
|
|
[Cosmos.IL2CPU.OpCode(ILOpCode.Code.Newobj)]
|
|
public class Newobj : ILOp
|
|
{
|
|
public Newobj(Cosmos.Assembler.Assembler aAsmblr)
|
|
: base(aAsmblr)
|
|
{
|
|
}
|
|
|
|
public override void Execute(MethodInfo aMethod, ILOpCode aOpCode)
|
|
{
|
|
OpMethod xMethod = (OpMethod)aOpCode;
|
|
string xCurrentLabel = GetLabel(aMethod, aOpCode);
|
|
var xType = xMethod.Value.DeclaringType;
|
|
|
|
Assemble(Assembler, aMethod, xMethod, xCurrentLabel, xType, xMethod.Value);
|
|
}
|
|
|
|
public static void Assemble(Cosmos.Assembler.Assembler aAssembler, MethodInfo aMethod, OpMethod xMethod, string currentLabel, Type objectType, MethodBase constructor)
|
|
{
|
|
// call cctor:
|
|
if (aMethod != null)
|
|
{
|
|
var xCctor = (objectType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic) ?? new ConstructorInfo[0]).SingleOrDefault();
|
|
if (xCctor != null)
|
|
{
|
|
new CPUx86.Call
|
|
{
|
|
DestinationLabel = LabelName.Get(xCctor)
|
|
};
|
|
ILOp.EmitExceptionLogic(aAssembler, aMethod, xMethod, true, null, ".AfterCCTorExceptionCheck");
|
|
new Label(".AfterCCTorExceptionCheck");
|
|
}
|
|
}
|
|
|
|
if (objectType.IsValueType)
|
|
{
|
|
#region Valuetypes
|
|
|
|
XS.Comment("ValueType");
|
|
/*
|
|
* Current sitation on stack:
|
|
* $ESP Arg
|
|
* $ESP+.. other items
|
|
*
|
|
* What should happen:
|
|
* + The stack should be increased to allow space to contain:
|
|
* + .ctor arguments
|
|
* + struct _pointer_ (ref to start of emptied space)
|
|
* + empty space for struct
|
|
* + arguments should be copied to the new place
|
|
* + old place where arguments were should be cleared
|
|
* + pointer should be set
|
|
* + call .ctor
|
|
*/
|
|
|
|
// Size of return value - we need to make room for this on the stack.
|
|
uint xStorageSize = Align(SizeOfType(objectType), 4);
|
|
XS.Comment("StorageSize: " + xStorageSize);
|
|
if (xStorageSize == 0)
|
|
{
|
|
throw new Exception("ValueType storage size cannot be 0.");
|
|
}
|
|
|
|
//var xStorageSize = aCtorDeclTypeInfo.StorageSize;
|
|
|
|
uint xArgSize = 0;
|
|
var xParameterList = constructor.GetParameters();
|
|
foreach (var xParam in xParameterList)
|
|
{
|
|
xArgSize = xArgSize + Align(SizeOfType(xParam.ParameterType), 4);
|
|
}
|
|
XS.Comment("ArgSize: " + xArgSize);
|
|
|
|
// Set ESP so we can push the struct ptr
|
|
int xShift = (int)(xArgSize - xStorageSize);
|
|
XS.Comment("Shift: " + xShift);
|
|
if (xShift < 0)
|
|
{
|
|
XS.Sub(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.ESP), (uint)Math.Abs(xShift));
|
|
}
|
|
else if (xShift > 0)
|
|
{
|
|
XS.Add(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.ESP), (uint)xShift);
|
|
}
|
|
|
|
// push struct ptr
|
|
XS.Push(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.ESP));
|
|
|
|
// Shift args
|
|
foreach (var xParam in xParameterList)
|
|
{
|
|
uint xArgSizeForThis = Align(SizeOfType(xParam.ParameterType), 4);
|
|
for (int i = 1; i <= xArgSizeForThis / 4; i++)
|
|
{
|
|
new CPUx86.Push { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, DestinationDisplacement = (int)xStorageSize };
|
|
}
|
|
}
|
|
|
|
new Call(aAssembler).Execute(aMethod, xMethod);
|
|
|
|
// Need to put these *after* the call because the Call pops the args from the stack
|
|
// and we have mucked about on the stack, so this makes it right before the next
|
|
// op.
|
|
|
|
#endregion Valuetypes
|
|
}
|
|
else
|
|
{
|
|
// If not ValueType, then we need gc
|
|
|
|
var xParams = constructor.GetParameters();
|
|
|
|
// array length + 8
|
|
bool xHasCalcSize = false;
|
|
|
|
#region Special string handling
|
|
// try calculating size:
|
|
if (constructor.DeclaringType == typeof(string))
|
|
{
|
|
if (xParams.Length == 1
|
|
&& xParams[0].ParameterType == typeof(char[]))
|
|
{
|
|
xHasCalcSize = true;
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, SourceReg = CPUx86.RegistersEnum.ESP, SourceIsIndirect = true };
|
|
|
|
// EAX contains a memory handle now, lets dereference it to a pointer
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, SourceReg = CPUx86.RegistersEnum.EAX, SourceIsIndirect = true };
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, SourceReg = CPUx86.RegistersEnum.EAX, SourceIsIndirect = true, SourceDisplacement = 8 };
|
|
XS.Set(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EDX), 2);
|
|
XS.Multiply(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EDX));
|
|
XS.Push(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
}
|
|
else if (xParams.Length == 3
|
|
&& (xParams[0].ParameterType == typeof(char[]) || xParams[0].ParameterType == typeof(char*))
|
|
&& xParams[1].ParameterType == typeof(int)
|
|
&& xParams[2].ParameterType == typeof(int))
|
|
{
|
|
xHasCalcSize = true;
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, SourceReg = CPUx86.RegistersEnum.ESP, SourceIsIndirect = true };
|
|
XS.ShiftLeft(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX), 1);
|
|
XS.Push(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
}
|
|
else if (xParams.Length == 2
|
|
&& xParams[0].ParameterType == typeof(char)
|
|
&& xParams[1].ParameterType == typeof(int))
|
|
{
|
|
xHasCalcSize = true;
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, SourceReg = CPUx86.RegistersEnum.ESP, SourceIsIndirect = true };
|
|
XS.ShiftLeft(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX), 1);
|
|
XS.Push(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
}
|
|
else
|
|
{
|
|
throw new NotImplementedException("In NewObj, a string ctor implementation is missing!");
|
|
}
|
|
}
|
|
#endregion Special string handling
|
|
|
|
uint xMemSize = GetStorageSize(objectType);
|
|
int xExtraSize = 12; // additional size for set values after alloc
|
|
new CPUx86.Push { DestinationValue = (uint)(xMemSize + xExtraSize) };
|
|
if (xHasCalcSize)
|
|
{
|
|
XS.Pop(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
new CPUx86.Add { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, SourceReg = CPUx86.RegistersEnum.EAX };
|
|
}
|
|
|
|
// todo: probably we want to check for exceptions after calling Alloc
|
|
new CPUx86.Call { DestinationLabel = LabelName.Get(GCImplementationRefs.AllocNewObjectRef) };
|
|
new Label(".AfterAlloc");
|
|
new CPUx86.Push { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true };
|
|
new CPUx86.Push { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true };
|
|
|
|
// it's on the stack now 3 times. Once from the Alloc return value, twice from the pushes
|
|
|
|
//? ?? uint xObjSize;// = 0;
|
|
//int xGCFieldCount = ( from item in aCtorDeclTypeInfo.Fields.Values
|
|
//where item.NeedsGC
|
|
//select item ).Count();
|
|
|
|
//int xGCFieldCount = ( from item in aCtorDeclTypeInfo.Fields.Values
|
|
//where item.NeedsGC
|
|
//select item ).Count();
|
|
int xGCFieldCount = objectType.GetFields().Count(x => x.FieldType.IsValueType);
|
|
|
|
// todo: use a cleaner approach here. this class shouldnt assemble the string
|
|
string strTypeId = GetTypeIDLabel(constructor.DeclaringType);
|
|
|
|
XS.Pop(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, SourceReg = CPUx86.RegistersEnum.EAX, SourceIsIndirect = true };
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EBX, SourceRef = ElementReference.New(strTypeId), SourceIsIndirect = true };
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, DestinationIsIndirect = true, SourceReg = CPUx86.RegistersEnum.EBX };
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, DestinationIsIndirect = true, DestinationDisplacement = 4, SourceValue = (uint)InstanceTypeEnum.NormalObject, Size = 32 };
|
|
new CPUx86.Mov { DestinationReg = CPUx86.RegistersEnum.EAX, DestinationIsIndirect = true, DestinationDisplacement = 8, SourceValue = (uint)xGCFieldCount, Size = 32 };
|
|
uint xSize = (uint)(from item in xParams
|
|
let xQSize = Align(SizeOfType(item.ParameterType), 4)
|
|
select (int)xQSize).Take(xParams.Length).Sum();
|
|
|
|
foreach (var xParam in xParams)
|
|
{
|
|
uint xParamSize = Align(SizeOfType(xParam.ParameterType), 4);
|
|
new Comment(aAssembler, String.Format("Arg {0}: {1}", xParam.Name, xParamSize));
|
|
for (int i = 0; i < xParamSize; i += 4)
|
|
{
|
|
new CPUx86.Push { DestinationReg = CPUx86.RegistersEnum.ESP, DestinationIsIndirect = true, DestinationDisplacement = (int)(xSize + 4) };
|
|
}
|
|
}
|
|
|
|
new CPUx86.Call { DestinationLabel = LabelName.Get(constructor) };
|
|
// should the complete error handling happen by ILOp.EmitExceptionLogic?
|
|
if (aMethod != null)
|
|
{
|
|
// todo: only happening for real methods now, not for ctor's ?
|
|
XS.Test(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.ECX), 2);
|
|
string xNoErrorLabel = currentLabel + ".NoError" + LabelName.LabelCount.ToString();
|
|
new CPUx86.ConditionalJump { Condition = CPUx86.ConditionalTestEnum.Equal, DestinationLabel = xNoErrorLabel };
|
|
|
|
//for( int i = 1; i < aCtorMethodInfo.Arguments.Length; i++ )
|
|
//{
|
|
// new CPUx86.Add
|
|
// {
|
|
// DestinationReg = CPUx86.Registers.ESP,
|
|
// SourceValue = ( aCtorMethodInfo.Arguments[ i ].Size % 4 == 0
|
|
// ? aCtorMethodInfo.Arguments[ i ].Size
|
|
// : ( ( aCtorMethodInfo.Arguments[ i ].Size / 4 ) * 4 ) + 1 )
|
|
// };
|
|
//}
|
|
PushAlignedParameterSize(constructor);
|
|
|
|
// an exception occurred, we need to cleanup the stack, and jump to the exit
|
|
XS.Add(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.ESP), 4);
|
|
|
|
//new Comment(aAssembler, "[ Newobj.Execute cleanup start count = " + aAssembler.Stack.Count.ToString() + " ]");
|
|
//foreach( var xStackInt in Assembler.Stack )
|
|
//{
|
|
// new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = ( uint )xStackInt.Size };
|
|
//}
|
|
|
|
new Comment(aAssembler, "[ Newobj.Execute cleanup end ]");
|
|
Jump_Exception(aMethod);
|
|
new Label(xNoErrorLabel);
|
|
}
|
|
XS.Pop(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
|
|
//for( int i = 1; i < aCtorMethodInfo.Arguments.Length; i++ )
|
|
//{
|
|
// new CPUx86.Add
|
|
// {
|
|
// DestinationReg = CPUx86.Registers.ESP,
|
|
// SourceValue = ( aCtorMethodInfo.Arguments[ i ].Size % 4 == 0
|
|
// ? aCtorMethodInfo.Arguments[ i ].Size
|
|
// : ( ( aCtorMethodInfo.Arguments[ i ].Size / 4 ) * 4 ) + 1 )
|
|
// };
|
|
//}
|
|
PushAlignedParameterSize(constructor);
|
|
|
|
XS.Push(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.EAX));
|
|
}
|
|
}
|
|
|
|
private static void PushAlignedParameterSize(MethodBase aMethod)
|
|
{
|
|
ParameterInfo[] xParams = aMethod.GetParameters();
|
|
|
|
uint xSize;
|
|
XS.Comment("[ Newobj.PushAlignedParameterSize start count = " + xParams.Length.ToString() + " ]");
|
|
for (int i = 0; i < xParams.Length; i++)
|
|
{
|
|
xSize = SizeOfType(xParams[i].ParameterType);
|
|
XS.Add(XSRegisters.OldToNewRegister(CPUx86.RegistersEnum.ESP), Align(xSize, 4));
|
|
}
|
|
XS.Comment("[ Newobj.PushAlignedParameterSize end ]");
|
|
}
|
|
}
|
|
}
|