mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-21 05:18:38 +00:00
362 lines
No EOL
15 KiB
C#
362 lines
No EOL
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
|
|
namespace Cosmos.IL2CPU {
|
|
public class ILReader {
|
|
// We split this into two arrays since we have to read
|
|
// a byte at a time anways. In the future if we need to
|
|
// back to a unifed array, instead of 64k entries
|
|
// we can change it to a signed int, and then add x0200 to the value.
|
|
// This will reduce array size down to 768 entries.
|
|
protected OpCode[] mOpCodesLo = new OpCode[256];
|
|
protected OpCode[] mOpCodesHi = new OpCode[256];
|
|
|
|
public ILReader() {
|
|
LoadOpCodes();
|
|
}
|
|
|
|
protected void LoadOpCodes() {
|
|
foreach (var xField in typeof(OpCodes).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)) {
|
|
var xOpCode = (OpCode)xField.GetValue(null);
|
|
var xValue = (ushort)xOpCode.Value;
|
|
if (xValue <= 0xFF) {
|
|
mOpCodesLo[xValue] = xOpCode;
|
|
} else {
|
|
mOpCodesHi[xValue & 0xFF] = xOpCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void CheckBranch(int aTarget, int aMethodSize) {
|
|
if (aTarget < 0 || aTarget >= aMethodSize) {
|
|
throw new Exception("Branch jumps outside method.");
|
|
}
|
|
}
|
|
|
|
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...
|
|
// MtW: how about externs (pinvoke, etc)
|
|
if (xBody == null) {
|
|
return null;
|
|
}
|
|
|
|
var xIL = xBody.GetILAsByteArray();
|
|
int xPos = 0;
|
|
while (xPos < xIL.Length) {
|
|
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: {
|
|
// 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, 0);
|
|
break;
|
|
case ILOpCode.Code.Ldarg_1:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, 1);
|
|
break;
|
|
case ILOpCode.Code.Ldarg_2:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, 2);
|
|
break;
|
|
case ILOpCode.Code.Ldarg_3:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, 3);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_0:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 0);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_1:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 1);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_2:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 2);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_3:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 3);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_4:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 4);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_5:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 5);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_6:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 6);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_7:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 7);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_8:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 8);
|
|
break;
|
|
case ILOpCode.Code.Ldc_I4_M1:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, 0xFFFFFFFF);
|
|
break;
|
|
case ILOpCode.Code.Ldloc_0:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, 0);
|
|
break;
|
|
case ILOpCode.Code.Ldloc_1:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, 1);
|
|
break;
|
|
case ILOpCode.Code.Ldloc_2:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, 2);
|
|
break;
|
|
case ILOpCode.Code.Ldloc_3:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, 3);
|
|
break;
|
|
case ILOpCode.Code.Stloc_0:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, 0);
|
|
break;
|
|
case ILOpCode.Code.Stloc_1:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, 1);
|
|
break;
|
|
case ILOpCode.Code.Stloc_2:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, 2);
|
|
break;
|
|
case ILOpCode.Code.Stloc_3:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, 3);
|
|
break;
|
|
default:
|
|
xILOpCode = new ILOpCodes.OpNone(xOpCodeVal, xOpPos);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case OperandType.ShortInlineBrTarget: {
|
|
// 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, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Bge_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Bge_Un_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge_Un, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Bgt_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Bgt_Un_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt_Un, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Ble_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Ble_Un_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble_Un, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Blt_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Blt_Un_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt_Un, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Bne_Un_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bne_Un, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Br_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Br, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Brfalse_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brfalse, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Brtrue_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brtrue, xOpPos, xTarget);
|
|
break;
|
|
case ILOpCode.Code.Leave_S:
|
|
xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Leave, xOpPos, xTarget);
|
|
break;
|
|
default:
|
|
xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xTarget);
|
|
break;
|
|
}
|
|
xPos = xPos + 1;
|
|
break;
|
|
}
|
|
case OperandType.InlineBrTarget: {
|
|
int xTarget = xPos + 4 + (Int32)ReadUInt32(xIL, xPos);
|
|
CheckBranch(xTarget, xIL.Length);
|
|
xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xTarget);
|
|
xPos = xPos + 4;
|
|
break;
|
|
}
|
|
|
|
case OperandType.ShortInlineI:
|
|
switch (xOpCodeVal) {
|
|
case ILOpCode.Code.Ldc_I4_S:
|
|
xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xIL[xPos]);
|
|
break;
|
|
default:
|
|
xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xIL[xPos]);
|
|
break;
|
|
}
|
|
xPos = xPos + 1;
|
|
break;
|
|
case OperandType.InlineI:
|
|
xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, ReadUInt32(xIL, xPos));
|
|
xPos = xPos + 4;
|
|
break;
|
|
case OperandType.InlineI8:
|
|
xILOpCode = new ILOpCodes.OpInt64(xOpCodeVal, xOpPos, ReadUInt64(xIL, xPos));
|
|
xPos = xPos + 8;
|
|
break;
|
|
|
|
case OperandType.ShortInlineR:
|
|
xILOpCode = new ILOpCodes.OpSingle(xOpCodeVal, xOpPos, BitConverter.ToSingle(xIL, xPos));
|
|
xPos = xPos + 4;
|
|
break;
|
|
case OperandType.InlineR:
|
|
xILOpCode = new ILOpCodes.OpDouble(xOpCodeVal, xOpPos, BitConverter.ToDouble(xIL, xPos));
|
|
xPos = xPos + 8;
|
|
break;
|
|
|
|
// The operand is a 32-bit metadata token.
|
|
case OperandType.InlineField: {
|
|
var xValue = aMethod.Module.ResolveField((int)ReadUInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs);
|
|
xILOpCode = new ILOpCodes.OpField(xOpCodeVal, xOpPos, xValue);
|
|
xPos = xPos + 4;
|
|
break;
|
|
}
|
|
|
|
// The operand is a 32-bit metadata token.
|
|
case OperandType.InlineMethod: {
|
|
var xValue = aMethod.Module.ResolveMethod((int)ReadUInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs);
|
|
xILOpCode = new ILOpCodes.OpMethod(xOpCodeVal, xOpPos, xValue);
|
|
xPos = xPos + 4;
|
|
break;
|
|
}
|
|
|
|
// 32-bit metadata signature token.
|
|
case OperandType.InlineSig:
|
|
//TODO: What are these used for? A breakpoint causes no breaks with current tests...
|
|
xILOpCode = new ILOpCodes.OpSig(xOpCodeVal, xOpPos, ReadUInt32(xIL, xPos));
|
|
xPos = xPos + 4;
|
|
break;
|
|
|
|
case OperandType.InlineString:
|
|
xILOpCode = new ILOpCodes.OpString(xOpCodeVal, xOpPos, aMethod.Module.ResolveString((int)ReadUInt32(xIL, xPos)));
|
|
xPos = xPos + 4;
|
|
break;
|
|
|
|
case OperandType.InlineSwitch: {
|
|
int xCount = (int)ReadUInt32(xIL, xPos);
|
|
xPos = xPos + 4;
|
|
int xNextOpPos = xPos + xCount * 4;
|
|
int[] xBranchLocations = new int[xCount];
|
|
for (int i = 0; i < xCount; i++) {
|
|
xBranchLocations[i] = xNextOpPos + (int)ReadUInt32(xIL, xPos + i * 4);
|
|
CheckBranch(xBranchLocations[i], xIL.Length);
|
|
}
|
|
xILOpCode = new ILOpCodes.OpSwitch(xOpCodeVal, xOpPos, xBranchLocations);
|
|
xPos = xNextOpPos;
|
|
break;
|
|
}
|
|
|
|
// The operand is a FieldRef, MethodRef, or TypeRef token.
|
|
case OperandType.InlineTok:
|
|
xILOpCode = new ILOpCodes.OpToken(xOpCodeVal, xOpPos, ReadUInt32(xIL, xPos));
|
|
xPos = xPos + 4;
|
|
break;
|
|
|
|
// 32-bit metadata token.
|
|
case OperandType.InlineType: {
|
|
var xValue = aMethod.Module.ResolveType((int)ReadUInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs);
|
|
xILOpCode = new ILOpCodes.OpType(xOpCodeVal, xOpPos, xValue);
|
|
xPos = xPos + 4;
|
|
break;
|
|
}
|
|
|
|
case OperandType.ShortInlineVar:
|
|
switch (xOpCodeVal) {
|
|
case ILOpCode.Code.Ldloc_S:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xIL[xPos]);
|
|
break;
|
|
case ILOpCode.Code.Ldloca_S:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloca, xOpPos, xIL[xPos]);
|
|
break;
|
|
case ILOpCode.Code.Ldarg_S:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xIL[xPos]);
|
|
break;
|
|
case ILOpCode.Code.Ldarga_S:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarga, xOpPos, xIL[xPos]);
|
|
break;
|
|
case ILOpCode.Code.Starg_S:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Starg, xOpPos, xIL[xPos]);
|
|
break;
|
|
case ILOpCode.Code.Stloc_S:
|
|
xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xIL[xPos]);
|
|
break;
|
|
default:
|
|
xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xIL[xPos]);
|
|
break;
|
|
}
|
|
xPos = xPos + 1;
|
|
break;
|
|
case OperandType.InlineVar:
|
|
xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, ReadUInt16(xIL, xPos));
|
|
xPos = xPos + 2;
|
|
break;
|
|
|
|
default:
|
|
throw new Exception("Unknown OperandType");
|
|
}
|
|
xResult.Add(xILOpCode);
|
|
}
|
|
return xResult;
|
|
}
|
|
|
|
// We could use BitConvertor, unfortuantely they "hardcoded" endianness. Its fine for reading IL now...
|
|
// but they essentially do the same as we do, just a bit slower.
|
|
private UInt16 ReadUInt16(byte[] aBytes, int aPos) {
|
|
return (UInt16)(aBytes[aPos + 1] << 8 | aBytes[aPos]);
|
|
}
|
|
|
|
private UInt32 ReadUInt32(byte[] aBytes, int aPos) {
|
|
return (UInt32)(aBytes[aPos + 3] << 24 | aBytes[aPos + 2] << 16 | aBytes[aPos + 1] << 8 | aBytes[aPos]);
|
|
}
|
|
|
|
private UInt64 ReadUInt64(byte[] aBytes, int aPos) {
|
|
return (UInt64)(
|
|
aBytes[aPos + 7] << 56 | aBytes[aPos + 6] << 48 | aBytes[aPos + 5] << 40 | aBytes[aPos + 4] << 32
|
|
| aBytes[aPos + 3] << 24 | aBytes[aPos + 2] << 16 | aBytes[aPos + 1] << 8 | aBytes[aPos]);
|
|
}
|
|
|
|
}
|
|
} |