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 new 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); new 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); } new Comment("ArgSize: " + xArgSize); // Set ESP so we can push the struct ptr int xShift = (int)(xArgSize - xStorageSize); new Comment("Shift: " + xShift); if (xShift < 0) { new CPUx86.Sub { DestinationReg = CPUx86.Registers.ESP, SourceValue = (uint)Math.Abs(xShift) }; } else if (xShift > 0) { new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = (uint)xShift }; } // push struct ptr new CPUx86.Push { DestinationReg = CPUx86.Registers.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.Registers.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.Registers.EAX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true }; // EAX contains a memory handle now, lets dereference it to a pointer new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.RegistersEnum.EAX, SourceIsIndirect = true }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.EAX, SourceIsIndirect = true, SourceDisplacement = 8 }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EDX, SourceValue = 2 }; new CPUx86.Multiply { DestinationReg = CPUx86.Registers.EDX }; new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; } else if (xParams.Length == 3 && xParams[0].ParameterType == typeof(char[]) && xParams[1].ParameterType == typeof(int) && xParams[2].ParameterType == typeof(int)) { xHasCalcSize = true; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true }; new CPUx86.ShiftLeft { DestinationReg = CPUx86.Registers.EAX, SourceValue = 1 }; new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; } else if (xParams.Length == 2 && xParams[0].ParameterType == typeof(char) && xParams[1].ParameterType == typeof(int)) { xHasCalcSize = true; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.ESP, SourceIsIndirect = true }; new CPUx86.ShiftLeft { DestinationReg = CPUx86.Registers.EAX, SourceValue = 1 }; new CPUx86.Push { DestinationReg = CPUx86.Registers.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) { new CPUx86.Pop { DestinationReg = CPUx86.Registers.EAX }; new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, DestinationIsIndirect = true, SourceReg = CPUx86.Registers.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.Registers.ESP, DestinationIsIndirect = true }; new CPUx86.Push { DestinationReg = CPUx86.Registers.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); new CPUx86.Pop { DestinationReg = CPUx86.Registers.EAX }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.EAX, SourceIsIndirect = true }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EBX, SourceRef = Cosmos.Assembler.ElementReference.New(strTypeId), SourceIsIndirect = true }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, DestinationIsIndirect = true, SourceReg = CPUx86.Registers.EBX }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.EAX, DestinationIsIndirect = true, DestinationDisplacement = 4, SourceValue = (uint)InstanceTypeEnum.NormalObject, Size = 32 }; new CPUx86.Mov { DestinationReg = CPUx86.Registers.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.Registers.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 ? new CPUx86.Test { DestinationReg = CPUx86.Registers.ECX, SourceValue = 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 new CPUx86.Add { DestinationReg = CPUx86.Registers.ESP, SourceValue = 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); } new CPUx86.Pop { DestinationReg = CPUx86.Registers.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); new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; } }
public List <ILOpCode> ProcessMethod(MethodBase aMethod) { var xResult = new List <ILOpCode>(); var xBody = aMethod.GetMethodBody(); var xModule = aMethod.Module; if (aMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.Unsafe") { var xUnsafeMethod = Type.GetType("System.Runtime.CompilerServices.Unsafe, System.Runtime.CompilerServices.Unsafe") .GetMethod(aMethod.Name, Array.ConvertAll(aMethod.GetParameters(), p => p.ParameterType)); xBody = xUnsafeMethod.GetMethodBody(); xModule = xUnsafeMethod.Module; } // Cache for use in field and method resolution Type[] xTypeGenArgs = Type.EmptyTypes; Type[] xMethodGenArgs = Type.EmptyTypes; if (aMethod.DeclaringType.IsGenericType) { xTypeGenArgs = aMethod.DeclaringType.GetGenericArguments(); } if (aMethod.IsGenericMethod) { xMethodGenArgs = aMethod.GetGenericArguments(); } // Some methods return no body. Not sure why.. have to investigate // They arent abstracts or icalls... if (xBody == null) { return(null); } var xIL = xBody.GetILAsByteArray(); int xPos = 0; while (xPos < xIL.Length) { _ExceptionRegionInfo xCurrentExceptionRegion = null; #region Determine current handler // todo: add support for nested handlers using a stack or so.. foreach (_ExceptionRegionInfo xHandler in aMethod.GetExceptionRegionInfos()) { if (xHandler.TryOffset >= 0) { if (xHandler.TryOffset <= xPos && (xHandler.TryLength + xHandler.TryOffset) > xPos) { if (xCurrentExceptionRegion == null) { xCurrentExceptionRegion = xHandler; continue; } else if (xHandler.TryOffset > xCurrentExceptionRegion.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentExceptionRegion.TryLength + xCurrentExceptionRegion.TryOffset)) { // only replace if the current found handler is narrower xCurrentExceptionRegion = xHandler; continue; } } } // todo: handler offset can be 0 like try offset? if (xHandler.HandlerOffset > 0) { if (xHandler.HandlerOffset <= xPos && (xHandler.HandlerOffset + xHandler.HandlerLength) > xPos) { if (xCurrentExceptionRegion == null) { xCurrentExceptionRegion = xHandler; continue; } else if (xHandler.HandlerOffset > xCurrentExceptionRegion.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentExceptionRegion.HandlerOffset + xCurrentExceptionRegion.HandlerLength)) { // only replace if the current found handler is narrower xCurrentExceptionRegion = xHandler; continue; } } } if (xHandler.Kind.HasFlag(ExceptionRegionKind.Filter)) { if (xHandler.FilterOffset > 0) { if (xHandler.FilterOffset <= xPos) { if (xCurrentExceptionRegion == null) { xCurrentExceptionRegion = xHandler; continue; } else if (xHandler.FilterOffset > xCurrentExceptionRegion.FilterOffset) { // only replace if the current found handler is narrower xCurrentExceptionRegion = xHandler; continue; } } } } } #endregion ILOpCode.Code xOpCodeVal; OpCode xOpCode; int xOpPos = xPos; if (xIL[xPos] == 0xFE) { xOpCodeVal = (ILOpCode.Code)(0xFE00 | xIL[xPos + 1]); xOpCode = mOpCodesHi[xIL[xPos + 1]]; xPos = xPos + 2; } else { xOpCodeVal = (ILOpCode.Code)xIL[xPos]; xOpCode = mOpCodesLo[xIL[xPos]]; xPos++; } ILOpCode xILOpCode = null; switch (xOpCode.OperandType) { // No operand. case OperandType.InlineNone: { #region Inline none options // These shortcut translation regions expand shortcut ops into full ops // This elminates the amount of code required in the assemblers // by allowing them to ignore the shortcuts switch (xOpCodeVal) { case ILOpCode.Code.Ldarg_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 3, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_0: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_2: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_3: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 3, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_4: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 4, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_5: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 5, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_6: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 6, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_7: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 7, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_8: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 8, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_M1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, -1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 3, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 3, xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpNone(xOpCodeVal, xOpPos, xPos, xCurrentExceptionRegion); break; } #endregion break; } case OperandType.ShortInlineBrTarget: { #region Inline branch // By calculating target, we assume all branches are within a method // So far at least wtih csc, its true. We check it with CheckBranch // just in case. int xTarget = xPos + 1 + (sbyte)xIL[xPos]; CheckBranch(xTarget, xIL.Length); switch (xOpCodeVal) { case ILOpCode.Code.Beq_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Beq, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bge_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bge_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bgt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bgt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Ble_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Ble_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Blt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Blt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bne_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bne_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Br_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Br, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Brfalse_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brfalse, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Brtrue_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brtrue, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Leave_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Leave, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; } xPos = xPos + 1; break; #endregion } case OperandType.InlineBrTarget: { int xTarget = xPos + 4 + ReadInt32(xIL, xPos); CheckBranch(xTarget, xIL.Length); xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 4, xTarget, xCurrentExceptionRegion); xPos = xPos + 4; break; } case OperandType.ShortInlineI: switch (xOpCodeVal) { case ILOpCode.Code.Ldc_I4_S: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentExceptionRegion); break; } xPos = xPos + 1; break; case OperandType.InlineI: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineI8: xILOpCode = new ILOpCodes.OpInt64(xOpCodeVal, xOpPos, xPos + 8, ReadUInt64(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 8; break; case OperandType.ShortInlineR: xILOpCode = new ILOpCodes.OpSingle(xOpCodeVal, xOpPos, xPos + 4, BitConverter.ToSingle(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineR: xILOpCode = new ILOpCodes.OpDouble(xOpCodeVal, xOpPos, xPos + 8, BitConverter.ToDouble(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 8; break; // The operand is a 32-bit metadata token. case OperandType.InlineField: { var xValue = xModule.ResolveField(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpField(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentExceptionRegion); xPos = xPos + 4; break; } // The operand is a 32-bit metadata token. case OperandType.InlineMethod: { var xValue = xModule.ResolveMethod(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpMethod(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentExceptionRegion); xPos = xPos + 4; break; } // 32-bit metadata signature token. case OperandType.InlineSig: xILOpCode = new ILOpCodes.OpSig(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineString: xILOpCode = new ILOpCodes.OpString(xOpCodeVal, xOpPos, xPos + 4, xModule.ResolveString(ReadInt32(xIL, xPos)), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineSwitch: { int xCount = ReadInt32(xIL, xPos); xPos = xPos + 4; int xNextOpPos = xPos + xCount * 4; var xBranchLocations = new int[xCount]; for (int i = 0; i < xCount; i++) { xBranchLocations[i] = xNextOpPos + ReadInt32(xIL, xPos + i * 4); CheckBranch(xBranchLocations[i], xIL.Length); } xILOpCode = new ILOpCodes.OpSwitch(xOpCodeVal, xOpPos, xNextOpPos, xBranchLocations, xCurrentExceptionRegion); xPos = xNextOpPos; break; } // The operand is a FieldRef, MethodRef, or TypeRef token. case OperandType.InlineTok: xILOpCode = new ILOpCodes.OpToken(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xModule, xTypeGenArgs, xMethodGenArgs, xCurrentExceptionRegion); xPos = xPos + 4; break; // 32-bit metadata token. case OperandType.InlineType: { var xValue = xModule.ResolveType(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpType(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentExceptionRegion); xPos = xPos + 4; break; } case OperandType.ShortInlineVar: switch (xOpCodeVal) { case ILOpCode.Code.Ldloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloca_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloca, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarga_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarga, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Starg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Starg, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; } xPos = xPos + 1; break; case OperandType.InlineVar: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 2, ReadUInt16(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 2; break; default: throw new Exception("Unknown OperandType"); } xILOpCode.InitStackAnalysis(aMethod); xResult.Add(xILOpCode); } return(xResult); }
public List <ILOpCode> ProcessMethod(MethodBase aMethod) { var xResult = new List <ILOpCode>(); var xBody = aMethod.GetMethodBody(); var xModule = aMethod.Module; // Cache for use in field and method resolution Type[] xTypeGenArgs = Type.EmptyTypes; Type[] xMethodGenArgs = Type.EmptyTypes; if (aMethod.DeclaringType.IsGenericType) { xTypeGenArgs = aMethod.DeclaringType.GetGenericArguments(); } if (aMethod.IsGenericMethod) { xMethodGenArgs = aMethod.GetGenericArguments(); } #region Unsafe Intrinsic if (aMethod.DeclaringType.FullName == "Internal.Runtime.CompilerServices.Unsafe") { var xUnsafeType = Type.GetType("System.Runtime.CompilerServices.Unsafe, System.Runtime.CompilerServices.Unsafe"); var xUnsafeMethod = xUnsafeType.GetMethods() .Where( m => m.Name == aMethod.Name && m.GetGenericArguments().Length == aMethod.GetGenericArguments().Length && m.GetParameters().Length == aMethod.GetParameters().Length) .SingleOrDefault( m => { var xParamTypes = Array.ConvertAll(m.GetParameters(), p => p.ParameterType); var xOriginalParamTypes = Array.ConvertAll( ((MethodInfo)aMethod).GetParameters(), p => p.ParameterType); for (int i = 0; i < xParamTypes.Length; i++) { var xParamType = xParamTypes[i]; var xOriginalParamType = xOriginalParamTypes[i]; while (xParamType.HasElementType) { if (!xOriginalParamType.HasElementType) { return(false); } if ((xParamType.IsArray && !xOriginalParamType.IsArray) || (xParamType.IsByRef && !xOriginalParamType.IsByRef) || (xParamType.IsPointer && !xOriginalParamType.IsPointer)) { return(false); } xParamType = xParamType.GetElementType(); xOriginalParamType = xOriginalParamType.GetElementType(); } if (!xParamType.IsAssignableFrom(xOriginalParamType) && (!xParamType.IsGenericParameter || (xParamType.HasElementType && !xParamType.IsArray))) { return(false); } } return(true); }); if (xUnsafeMethod != null) { xBody = xUnsafeMethod.GetMethodBody(); xModule = xUnsafeMethod.Module; } } #endregion #region ByReference Intrinsic if (aMethod.DeclaringType.IsGenericType && aMethod.DeclaringType.GetGenericTypeDefinition().FullName == "System.ByReference`1") { var valueField = aMethod.DeclaringType.GetField("_value", BindingFlags.Instance | BindingFlags.NonPublic); switch (aMethod.Name) { case ".ctor": // push $this xResult.Add(new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, 0, 1, 0, null)); // push value (arg 1) xResult.Add(new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, 1, 2, 1, null)); // store value into $this._value xResult.Add(new ILOpCodes.OpField(ILOpCode.Code.Stfld, 2, 8, valueField, null)); // return xResult.Add(new ILOpCodes.OpNone(ILOpCode.Code.Ret, 8, 9, null)); break; case "get_Value": // push $this xResult.Add(new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, 0, 1, 0, null)); // push $this._value xResult.Add(new ILOpCodes.OpField(ILOpCode.Code.Ldfld, 1, 6, valueField, null)); // return xResult.Add(new ILOpCodes.OpNone(ILOpCode.Code.Ret, 6, 7, null)); break; default: throw new NotImplementedException($"ByReference intrinsic method '{aMethod}' not implemented!"); } foreach (var op in xResult) { op.InitStackAnalysis(aMethod); } return(xResult); } #endregion #region RuntimeTypeHandle if (aMethod.DeclaringType.Name == "RuntimeType") { if (aMethod.Name == ".ctor") { var op = new ILOpCodes.OpNone(ILOpCode.Code.Ret, 0, 1, null); op.InitStackAnalysis(aMethod); xResult.Add(op); return(xResult); } } if (aMethod.DeclaringType.Name == "TypeImpl") { if (aMethod.Name == "CreateRuntimeTypeHandle") { // the idea of this method is to first create a RuntimeType object, set its handle and then create a RuntimeTypeHandle from it // we are manually coding in il here since we have to call a internal method on an internal class var runtimeType = Type.GetType("System.RuntimeType"); var ctor = runtimeType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { }, null); xResult.Add(new ILOpCodes.OpMethod(ILOpCode.Code.Newobj, 0, 1, ctor, null) { StackPopTypes = Array.Empty <Type>(), StackPushTypes = new[] { runtimeType }, }); xResult.Add(new ILOpCodes.OpNone(ILOpCode.Code.Dup, 1, 2, null) { StackPopTypes = new[] { runtimeType }, StackPushTypes = new[] { runtimeType, runtimeType } }); xResult.Add(new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, 2, 3, 0, null) { StackPopTypes = Array.Empty <Type>(), StackPushTypes = new[] { typeof(int) }, }); var m_handle = runtimeType.GetField("m_handle", BindingFlags.Instance | BindingFlags.NonPublic); xResult.Add(new ILOpCodes.OpField(ILOpCode.Code.Stfld, 3, 4, m_handle, null) { StackPopTypes = new[] { typeof(int), runtimeType }, StackPushTypes = Array.Empty <Type>(), }); var runtimeTypeHandle = Type.GetType("System.RuntimeTypeHandle"); ctor = runtimeTypeHandle.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { runtimeType }, null); xResult.Add(new ILOpCodes.OpMethod(ILOpCode.Code.Newobj, 4, 5, ctor, null) { StackPopTypes = new[] { runtimeType }, StackPushTypes = new[] { runtimeTypeHandle }, }); xResult.Add(new ILOpCodes.OpNone(ILOpCode.Code.Ret, 5, 6, null) { StackPopTypes = Array.Empty <Type>(), StackPushTypes = Array.Empty <Type>(), }); return(xResult); } } #endregion #region ArrayPool ("hacked" generic plug) if (aMethod.DeclaringType.IsGenericType && aMethod.DeclaringType.GetGenericTypeDefinition().FullName == "System.Buffers.ArrayPool`1") { if (aMethod.Name == ".cctor") { var op = new ILOpCodes.OpNone(ILOpCode.Code.Ret, 0, 1, null); op.InitStackAnalysis(aMethod); xResult.Add(op); return(xResult); } } #endregion #region RuntimeHelpers if (aMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers") { if (aMethod.Name == "IsBitwiseEquatable") { // This is a generic method so we emit true or false depending on the type ILOpCode op; if (ILOp.IsIntegralTypeOrPointer(xMethodGenArgs[0])) { op = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, 0, 1, 1, null); } else { op = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, 0, 1, 1, null); } op.InitStackAnalysis(aMethod); xResult.Add(op); op = new ILOpCodes.OpNone(ILOpCode.Code.Ret, 1, 2, null); op.InitStackAnalysis(aMethod); xResult.Add(op); return(xResult); } } #endregion // Some methods return no body. Not sure why.. have to investigate // They arent abstracts or icalls... if (xBody == null) { return(null); } var xIL = xBody.GetILAsByteArray(); int xPos = 0; while (xPos < xIL.Length) { _ExceptionRegionInfo xCurrentExceptionRegion = null; #region Determine current handler // todo: add support for nested handlers using a stack or so.. foreach (_ExceptionRegionInfo xHandler in aMethod.GetExceptionRegionInfos()) { if (xHandler.TryOffset >= 0) { if (xHandler.TryOffset <= xPos && (xHandler.TryLength + xHandler.TryOffset) > xPos) { if (xCurrentExceptionRegion == null) { xCurrentExceptionRegion = xHandler; continue; } else if (xHandler.TryOffset > xCurrentExceptionRegion.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentExceptionRegion.TryLength + xCurrentExceptionRegion.TryOffset)) { // only replace if the current found handler is narrower xCurrentExceptionRegion = xHandler; continue; } } } // todo: handler offset can be 0 like try offset? if (xHandler.HandlerOffset > 0) { if (xHandler.HandlerOffset <= xPos && (xHandler.HandlerOffset + xHandler.HandlerLength) > xPos) { if (xCurrentExceptionRegion == null) { xCurrentExceptionRegion = xHandler; continue; } else if (xHandler.HandlerOffset > xCurrentExceptionRegion.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentExceptionRegion.HandlerOffset + xCurrentExceptionRegion.HandlerLength)) { // only replace if the current found handler is narrower xCurrentExceptionRegion = xHandler; continue; } } } if (xHandler.Kind.HasFlag(ExceptionRegionKind.Filter)) { if (xHandler.FilterOffset > 0) { if (xHandler.FilterOffset <= xPos) { if (xCurrentExceptionRegion == null) { xCurrentExceptionRegion = xHandler; continue; } else if (xHandler.FilterOffset > xCurrentExceptionRegion.FilterOffset) { // only replace if the current found handler is narrower xCurrentExceptionRegion = xHandler; continue; } } } } } #endregion ILOpCode.Code xOpCodeVal; OpCode xOpCode; int xOpPos = xPos; if (xIL[xPos] == 0xFE) { xOpCodeVal = (ILOpCode.Code)(0xFE00 | xIL[xPos + 1]); xOpCode = mOpCodesHi[xIL[xPos + 1]]; xPos = xPos + 2; } else { xOpCodeVal = (ILOpCode.Code)xIL[xPos]; xOpCode = mOpCodesLo[xIL[xPos]]; xPos++; } ILOpCode xILOpCode = null; switch (xOpCode.OperandType) { // No operand. case OperandType.InlineNone: { #region Inline none options // These shortcut translation regions expand shortcut ops into full ops // This elminates the amount of code required in the assemblers // by allowing them to ignore the shortcuts switch (xOpCodeVal) { case ILOpCode.Code.Ldarg_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 3, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_0: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_2: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_3: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 3, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_4: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 4, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_5: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 5, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_6: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 6, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_7: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 7, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_8: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 8, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldc_I4_M1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, -1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 3, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 0, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 1, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 2, xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 3, xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpNone(xOpCodeVal, xOpPos, xPos, xCurrentExceptionRegion); break; } #endregion break; } case OperandType.ShortInlineBrTarget: { #region Inline branch // By calculating target, we assume all branches are within a method // So far at least wtih csc, its true. We check it with CheckBranch // just in case. int xTarget = xPos + 1 + (sbyte)xIL[xPos]; CheckBranch(xTarget, xIL.Length); switch (xOpCodeVal) { case ILOpCode.Code.Beq_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Beq, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bge_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bge_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bgt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bgt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Ble_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Ble_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Blt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Blt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Bne_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bne_Un, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Br_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Br, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Brfalse_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brfalse, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Brtrue_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brtrue, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; case ILOpCode.Code.Leave_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Leave, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 1, xTarget, xCurrentExceptionRegion); break; } xPos = xPos + 1; break; #endregion } case OperandType.InlineBrTarget: { int xTarget = xPos + 4 + ReadInt32(xIL, xPos); CheckBranch(xTarget, xIL.Length); xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 4, xTarget, xCurrentExceptionRegion); xPos = xPos + 4; break; } case OperandType.ShortInlineI: switch (xOpCodeVal) { case ILOpCode.Code.Ldc_I4_S: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentExceptionRegion); break; } xPos = xPos + 1; break; case OperandType.InlineI: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineI8: xILOpCode = new ILOpCodes.OpInt64(xOpCodeVal, xOpPos, xPos + 8, ReadUInt64(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 8; break; case OperandType.ShortInlineR: xILOpCode = new ILOpCodes.OpSingle(xOpCodeVal, xOpPos, xPos + 4, BitConverter.ToSingle(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineR: xILOpCode = new ILOpCodes.OpDouble(xOpCodeVal, xOpPos, xPos + 8, BitConverter.ToDouble(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 8; break; // The operand is a 32-bit metadata token. case OperandType.InlineField: { var xValue = xModule.ResolveField(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpField(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentExceptionRegion); xPos = xPos + 4; break; } // The operand is a 32-bit metadata token. case OperandType.InlineMethod: { var xValue = xModule.ResolveMethod(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpMethod(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentExceptionRegion); xPos = xPos + 4; break; } // 32-bit metadata signature token. case OperandType.InlineSig: xILOpCode = new ILOpCodes.OpSig(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineString: xILOpCode = new ILOpCodes.OpString(xOpCodeVal, xOpPos, xPos + 4, xModule.ResolveString(ReadInt32(xIL, xPos)), xCurrentExceptionRegion); xPos = xPos + 4; break; case OperandType.InlineSwitch: { int xCount = ReadInt32(xIL, xPos); xPos = xPos + 4; int xNextOpPos = xPos + xCount * 4; var xBranchLocations = new int[xCount]; for (int i = 0; i < xCount; i++) { xBranchLocations[i] = xNextOpPos + ReadInt32(xIL, xPos + i * 4); CheckBranch(xBranchLocations[i], xIL.Length); } xILOpCode = new ILOpCodes.OpSwitch(xOpCodeVal, xOpPos, xNextOpPos, xBranchLocations, xCurrentExceptionRegion); xPos = xNextOpPos; break; } // The operand is a FieldRef, MethodRef, or TypeRef token. case OperandType.InlineTok: xILOpCode = new ILOpCodes.OpToken(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xModule, xTypeGenArgs, xMethodGenArgs, xCurrentExceptionRegion); xPos = xPos + 4; break; // 32-bit metadata token. case OperandType.InlineType: { var xValue = xModule.ResolveType(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpType(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentExceptionRegion); xPos = xPos + 4; break; } case OperandType.ShortInlineVar: switch (xOpCodeVal) { case ILOpCode.Code.Ldloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Ldloca_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloca, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Ldarga_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarga, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Starg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Starg, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; case ILOpCode.Code.Stloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; default: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 1, xIL[xPos], xCurrentExceptionRegion); break; } xPos = xPos + 1; break; case OperandType.InlineVar: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 2, ReadUInt16(xIL, xPos), xCurrentExceptionRegion); xPos = xPos + 2; break; default: throw new Exception("Unknown OperandType"); } xILOpCode.InitStackAnalysis(aMethod); xResult.Add(xILOpCode); } return(xResult); }
public List<ILOpCode> ProcessMethod(MethodBase aMethod) { var xResult = new List<ILOpCode>(); var xBody = aMethod.GetMethodBody(); // Cache for use in field and method resolution Type[] xTypeGenArgs = null; Type[] xMethodGenArgs = null; if (aMethod.DeclaringType.IsGenericType) { xTypeGenArgs = aMethod.DeclaringType.GetGenericArguments(); } if (aMethod.IsGenericMethod) { xMethodGenArgs = aMethod.GetGenericArguments(); } // Some methods return no body. Not sure why.. have to investigate // They arent abstracts or icalls... if (xBody == null) { return null; } var xIL = xBody.GetILAsByteArray(); int xPos = 0; while (xPos < xIL.Length) { ExceptionHandlingClause xCurrentHandler = null; #region Determine current handler // todo: add support for nested handlers using a stack or so.. foreach (ExceptionHandlingClause xHandler in xBody.ExceptionHandlingClauses) { if (xHandler.TryOffset > 0) { if (xHandler.TryOffset <= xPos && (xHandler.TryLength + xHandler.TryOffset + 1) > xPos) // + 1 because index should be less than the try { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.TryOffset > xCurrentHandler.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentHandler.TryLength + xCurrentHandler.TryOffset)) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } if (xHandler.HandlerOffset > 0) { if (xHandler.HandlerOffset <= xPos && (xHandler.HandlerOffset + xHandler.HandlerLength + 1) > xPos) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.HandlerOffset > xCurrentHandler.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentHandler.HandlerOffset + xCurrentHandler.HandlerLength)) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } if ((xHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0) { if (xHandler.FilterOffset > 0) { if (xHandler.FilterOffset <= xPos) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.FilterOffset > xCurrentHandler.FilterOffset) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } } } #endregion ILOpCode.Code xOpCodeVal; OpCode xOpCode; int xOpPos = xPos; if (xIL[xPos] == 0xFE) { xOpCodeVal = (ILOpCode.Code)(0xFE00 | xIL[xPos + 1]); xOpCode = mOpCodesHi[xIL[xPos + 1]]; xPos = xPos + 2; } else { xOpCodeVal = (ILOpCode.Code)xIL[xPos]; xOpCode = mOpCodesLo[xIL[xPos]]; xPos++; } ILOpCode xILOpCode = null; switch (xOpCode.OperandType) { // No operand. case OperandType.InlineNone: { #region Inline none options // These shortcut translation regions expand shortcut ops into full ops // This elminates the amount of code required in the assemblers // by allowing them to ignore the shortcuts switch (xOpCodeVal) { case ILOpCode.Code.Ldarg_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Ldarg_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Ldarg_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Ldarg_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 3, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_0: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_2: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_3: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 3, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_4: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 4, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_5: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 5, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_6: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 6, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_7: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 7, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_8: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 8, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_M1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, -1, xCurrentHandler); break; case ILOpCode.Code.Ldloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Ldloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Ldloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Ldloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 3, xCurrentHandler); break; case ILOpCode.Code.Stloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Stloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Stloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Stloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 3, xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpNone(xOpCodeVal, xOpPos, xPos, xCurrentHandler); break; } #endregion break; } case OperandType.ShortInlineBrTarget: { #region Inline branch // By calculating target, we assume all branches are within a method // So far at least wtih csc, its true. We check it with CheckBranch // just in case. int xTarget = xPos + 1 + (sbyte)xIL[xPos]; CheckBranch(xTarget, xIL.Length); switch (xOpCodeVal) { case ILOpCode.Code.Beq_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Beq, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bge_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bge_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bgt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bgt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Ble_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Ble_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Blt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Blt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bne_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bne_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Br_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Br, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Brfalse_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brfalse, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Brtrue_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brtrue, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Leave_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Leave, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; } xPos = xPos + 1; break; #endregion } case OperandType.InlineBrTarget: { int xTarget = xPos + 4 + ReadInt32(xIL, xPos); CheckBranch(xTarget, xIL.Length); xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 4, xTarget, xCurrentHandler); xPos = xPos + 4; break; } case OperandType.ShortInlineI: switch (xOpCodeVal) { case ILOpCode.Code.Ldc_I4_S: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentHandler); break; } xPos = xPos + 1; break; case OperandType.InlineI: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineI8: xILOpCode = new ILOpCodes.OpInt64(xOpCodeVal, xOpPos, xPos + 8, ReadUInt64(xIL, xPos), xCurrentHandler); xPos = xPos + 8; break; case OperandType.ShortInlineR: xILOpCode = new ILOpCodes.OpSingle(xOpCodeVal, xOpPos, xPos + 4, BitConverter.ToSingle(xIL, xPos), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineR: xILOpCode = new ILOpCodes.OpDouble(xOpCodeVal, xOpPos, xPos + 8, BitConverter.ToDouble(xIL, xPos), xCurrentHandler); xPos = xPos + 8; break; // The operand is a 32-bit metadata token. case OperandType.InlineField: { var xValue = aMethod.Module.ResolveField(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpField(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentHandler); xPos = xPos + 4; break; } // The operand is a 32-bit metadata token. case OperandType.InlineMethod: { var xValue = aMethod.Module.ResolveMethod(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpMethod(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentHandler); xPos = xPos + 4; break; } // 32-bit metadata signature token. case OperandType.InlineSig: xILOpCode = new ILOpCodes.OpSig(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineString: xILOpCode = new ILOpCodes.OpString(xOpCodeVal, xOpPos, xPos + 4, aMethod.Module.ResolveString(ReadInt32(xIL, xPos)), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineSwitch: { int xCount = ReadInt32(xIL, xPos); xPos = xPos + 4; int xNextOpPos = xPos + xCount * 4; var xBranchLocations = new int[xCount]; for (int i = 0; i < xCount; i++) { xBranchLocations[i] = xNextOpPos + ReadInt32(xIL, xPos + i * 4); CheckBranch(xBranchLocations[i], xIL.Length); } xILOpCode = new ILOpCodes.OpSwitch(xOpCodeVal, xOpPos, xNextOpPos, xBranchLocations, xCurrentHandler); xPos = xNextOpPos; break; } // The operand is a FieldRef, MethodRef, or TypeRef token. case OperandType.InlineTok: xILOpCode = new ILOpCodes.OpToken(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), aMethod.Module, xTypeGenArgs, xMethodGenArgs, xCurrentHandler); xPos = xPos + 4; break; // 32-bit metadata token. case OperandType.InlineType: { var xValue = aMethod.Module.ResolveType(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpType(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentHandler); xPos = xPos + 4; break; } case OperandType.ShortInlineVar: switch (xOpCodeVal) { case ILOpCode.Code.Ldloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Ldloca_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloca, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Ldarg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Ldarga_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarga, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Starg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Starg, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Stloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; } xPos = xPos + 1; break; case OperandType.InlineVar: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 2, ReadUInt16(xIL, xPos), xCurrentHandler); xPos = xPos + 2; break; default: throw new Exception("Unknown OperandType"); } xILOpCode.InitStackAnalysis(aMethod); xResult.Add(xILOpCode); } return xResult; }
public static void Assemble(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) { XS.Call(LabelName.Get(xCctor)); EmitExceptionLogic(aAssembler, aMethod, xMethod, true, null, ".AfterCCTorExceptionCheck"); XS.Label(".AfterCCTorExceptionCheck"); } } if (objectType.IsValueType) { #region Valuetypes XS.Comment("ValueType"); XS.Comment("Type: " + objectType); /* * 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(ESP, (uint)Math.Abs(xShift)); } else if (xShift > 0) { XS.Add(ESP, (uint)xShift); } // push struct ptr XS.Push(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; XS.Set(EAX, ESP, sourceDisplacement: 4, sourceIsIndirect: true); // address XS.Set(EAX, EAX, sourceDisplacement: 8, sourceIsIndirect: true); // element count XS.Set(EDX, 2); // element size XS.Multiply(EDX); XS.Push(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; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.ShiftLeft(EAX, 1); XS.Push(EAX); } else if (xParams.Length == 2 && xParams[0].ParameterType == typeof(char) && xParams[1].ParameterType == typeof(int)) { xHasCalcSize = true; XS.Set(EAX, ESP, sourceIsIndirect: true); XS.ShiftLeft(EAX, 1); XS.Push(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 XS.Push((uint)(xMemSize + xExtraSize)); if (xHasCalcSize) { XS.Pop(EAX); XS.Add(ESP, EAX, destinationIsIndirect: true); } // todo: probably we want to check for exceptions after calling Alloc XS.Call(LabelName.Get(GCImplementationRefs.AllocNewObjectRef)); XS.Label(".AfterAlloc"); XS.Push(ESP, isIndirect: true); XS.Push(ESP, isIndirect: true); // it's on the stack now 3 times. Once from the Alloc return value, twice from the pushes // todo: use a cleaner approach here. this class shouldnt assemble the string string strTypeId = GetTypeIDLabel(constructor.DeclaringType); XS.Pop(EAX); XS.Set(EBX, strTypeId, sourceIsIndirect: true); XS.Set(EAX, EBX, destinationIsIndirect: true); XS.Set(EAX, (uint)InstanceTypeEnum.NormalObject, destinationDisplacement: 4, destinationIsIndirect: true, size: RegisterSize.Int32); XS.Set(EAX, xMemSize, destinationDisplacement: 8, destinationIsIndirect: true, size: RegisterSize.Int32); uint xSize = (uint)(from item in xParams let xQSize = Align(SizeOfType(item.ParameterType), 4) select (int)xQSize).Take(xParams.Length).Sum(); XS.Push(0); foreach (var xParam in xParams) { uint xParamSize = Align(SizeOfType(xParam.ParameterType), 4); XS.Comment($"Arg {xParam.Name}: {xParamSize}"); for (int i = 0; i < xParamSize; i += 4) { XS.Push(ESP, isIndirect: true, displacement: (int)(xSize + 8)); } } XS.Call(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(ECX, 2); string xNoErrorLabel = currentLabel + ".NoError" + LabelName.LabelCount.ToString(); XS.Jump(CPUx86.ConditionalTestEnum.Equal, xNoErrorLabel); PushAlignedParameterSize(constructor); // an exception occurred, we need to cleanup the stack, and jump to the exit XS.Add(ESP, 4); new Comment(aAssembler, "[ Newobj.Execute cleanup end ]"); Jump_Exception(aMethod); XS.Label(xNoErrorLabel); } XS.Pop(EAX); PushAlignedParameterSize(constructor); XS.Push(EAX); XS.Push(0); } }