using System; using System.Collections; using System.Reflection; namespace NPhilosopher { public variant NAssembly { | Assembly { AssemblyName : string; Types : list [NAssembly]; } | Type { TypeName : string; Members : list [NAssembly]; } | Method { MethodName : string; Arguments : list [NAssembly]; ReturnType : System.Type; IL : NIL; } | Argument { ArgName : string; ArgType : System.Type; } | Other } public variant NIL { | Block { Pos : int; Insts : ArrayList; } | Inst { Pos : int; Prefix : NIL; Opcode : uint; Mnem : string; Argument : NIL; } | TokenArg | IntArg { Size : int; } | UIntArg { USize : int; } | Int { Value : int; } | UInt { UValue : uint; } | Object { OValue : object; } } public module CILReflector { public Prefixes : list [uint * string * NIL] = [ (0xFE16U, "constrained.", NIL.TokenArg()), (0xFE19U, "no.", NIL.UIntArg(8)), (0xFE1EU, "readonly.", null), (0xFE14U, "tail.", null), (0xFE12U, "unaligned.", NIL.UIntArg(8)), (0xFE13U, "volatile.", null) ]; public Opcodes : list [uint * string * NIL] = [ (0x0000U, "nop", null), (0x0001U, "break", null), (0x0002U, "ldarg.0", null), (0x0003U, "ldarg.1", null), (0x0004U, "ldarg.2", null), (0x0005U, "ldarg.3", null), (0x0006U, "ldarg.0", null), (0x0007U, "ldarg.1", null), (0x0008U, "ldarg.2", null), (0x0009U, "ldarg.3", null), (0x000AU, "stloc.0", null), (0x000BU, "stloc.1", null), (0x000CU, "stloc.2", null), (0x000DU, "stloc.3", null), (0x000EU, "ldarg.s", NIL.IntArg(8)), (0x000FU, "ldarga.s", NIL.IntArg(8)), (0x0010U, "starg.s", NIL.IntArg(8)), (0x0011U, "ldloc.s", NIL.IntArg(8)), (0x0012U, "ldloca.s", NIL.IntArg(8)), (0x0013U, "stloc.s", NIL.IntArg(8)), (0x0014U, "ldnull", null), (0x0015U, "ldc.i4.m1", null), (0x0016U, "ldc.i4.0", null), (0x0017U, "ldc.i4.1", null), (0x0018U, "ldc.i4.2", null), (0x0019U, "ldc.i4.3", null), (0x001AU, "ldc.i4.4", null), (0x001BU, "ldc.i4.5", null), (0x001CU, "ldc.i4.6", null), (0x001DU, "ldc.i4.7", null), (0x001EU, "ldc.i4.8", null), (0x001FU, "ldc.i4.s", NIL.IntArg(8)), (0x0020U, "ldc.i4", NIL.IntArg(32)), (0x0021U, "ldc.i8", NIL.IntArg(64)), //(0x0022U, "ldc.r4", NIL.FloatArg(32)), //(0x0023U, "ldc.r8", NIL.FloatArg(64)), (0x0025U, "dup", null), (0x0026U, "pop", null), (0x0027U, "jmp", NIL.TokenArg()), (0x0028U, "call", NIL.TokenArg()), (0x0029U, "calli", NIL.TokenArg()), (0x002AU, "ret", null), (0x002BU, "br.s", NIL.IntArg(8)), (0x002CU, "brfalse.s", NIL.IntArg(8)), (0x002DU, "brtrue.s", NIL.IntArg(8)), (0x002EU, "beq.s", NIL.IntArg(8)), (0x002FU, "bge.s", NIL.IntArg(8)), (0x0030U, "bgt.s", NIL.IntArg(8)), (0x0031U, "ble.s", NIL.IntArg(8)), (0x0032U, "blt.s", NIL.IntArg(8)), (0x0033U, "bne.s", NIL.IntArg(8)), (0x0034U, "bge.un.s", NIL.IntArg(8)), (0x0035U, "bgt.un.s", NIL.IntArg(8)), (0x0036U, "ble.un.s", NIL.IntArg(8)), (0x0037U, "ble.un.s", NIL.IntArg(8)), (0x0038U, "br", NIL.IntArg(32)), (0x0039U, "brfalse", NIL.IntArg(32)), (0x003AU, "brtrue", NIL.IntArg(32)), (0x003BU, "beq", NIL.IntArg(32)), (0x003CU, "bge", NIL.IntArg(32)), (0x003DU, "bgt", NIL.IntArg(32)), (0x003EU, "ble", NIL.IntArg(32)), (0x003FU, "blt", NIL.IntArg(32)), (0x0040U, "bne", NIL.IntArg(32)), (0x0041U, "bge.un", NIL.IntArg(32)), (0x0042U, "bgt.un", NIL.IntArg(32)), (0x0043U, "ble.un", NIL.IntArg(32)), (0x0044U, "blt.un", NIL.IntArg(32)), (0x0046U, "ldind.i1", null), (0x0047U, "ldind.u1", null), (0x0048U, "ldind.i2", null), (0x0049U, "ldind.u2", null), (0x004AU, "ldind.i4", null), (0x004BU, "ldind.u4", null), (0x004CU, "ldind.i8", null), (0x004DU, "ldind.i", null), (0x004EU, "ldind.r4", null), (0x004FU, "ldind.r8", null), (0x0050U, "ldind.ref", null), (0x0051U, "stind.ref", null), (0x0052U, "stind.i1", null), (0x0053U, "stind.i2", null), (0x0054U, "stind.i4", null), (0x0055U, "stind.i8", null), (0x0056U, "stind.r4", null), (0x0057U, "stind.r8", null), (0x0058U, "add", null), (0x0059U, "sub", null), (0x005AU, "mul", null), (0x005BU, "div", null), (0x005CU, "div.un", null), (0x005DU, "rem", null), (0x005EU, "rem.un", null), (0x005FU, "and", null), (0x0060U, "or", null), (0x0061U, "xor", null), (0x0062U, "shl", null), (0x0063U, "shr", null), (0x0064U, "shr.un", null), (0x0065U, "neg", null), (0x0066U, "nop", null), (0x0067U, "conv.i1", null), (0x0068U, "conv.i2", null), (0x0069U, "conv.i4", null), (0x006AU, "conv.i8", null), (0x006BU, "conv.r4", null), (0x006CU, "conv.r8", null), (0x006DU, "conv.u4", null), (0x006EU, "conv.u8", null), (0x006FU, "callvirt", NIL.TokenArg()), (0x0072U, "ldstr", NIL.TokenArg()), (0x0076U, "conv.r.un", null), (0x0082U, "conv.ovf.i1.un", null), (0x0083U, "conv.ovf.i2.un", null), (0x0084U, "conv.ovf.i4.un", null), (0x0085U, "conv.ovf.i8.un", null), (0x0086U, "conv.ovf.u1.un", null), (0x0087U, "conv.ovf.u2.un", null), (0x0088U, "conv.ovf.u4.un", null), (0x0089U, "conv.ovf.u8.un", null), (0x008AU, "conv.ovf.i.un", null), (0x008BU, "conv.ovf.u.un", null), (0x008CU, "box", NIL.TokenArg()), (0x00B3U, "conv.ovf.i1", null), (0x00B4U, "conv.ovf.u1", null), (0x00B5U, "conv.ovf.i2", null), (0x00B6U, "conv.ovf.u2", null), (0x00B7U, "conv.ovf.i4", null), (0x00B8U, "conv.ovf.u4", null), (0x00B9U, "conv.ovf.i8", null), (0x00BAU, "conv.ovf.u8", null), (0x00C3U, "ckfinite", null), (0x00D1U, "conv.u2", null), (0x00D2U, "conv.u1", null), (0x00D3U, "conv.i", null), (0x00D4U, "conv.ovf.i", null), (0x00D5U, "conv.ovf.u", null), (0x00D6U, "add.ovf", null), (0x00D7U, "add.ovf.un", null), (0x00D8U, "mul.ovf", null), (0x00D9U, "mul.ovf.un", null), (0x00DAU, "sub.ovf", null), (0x00DBU, "sub.ovf.un", null), (0x00DCU, "endfinally", null), (0x00DDU, "leave", NIL.IntArg(32)), (0x00DEU, "leave.s", NIL.IntArg(8)), (0x00DFU, "stind.i", null), (0x00E0U, "conv.u", null), (0xFE00U, "arglist", null), (0xFE01U, "ceq", null), (0xFE02U, "cgt", null), (0xFE03U, "cgt.un", null), (0xFE04U, "clt", null), (0xFE05U, "clt.un", null), (0xFE06U, "ldftn", NIL.TokenArg()), (0xFE09U, "ldarg", NIL.IntArg(16)), (0xFE0AU, "ldarga", NIL.IntArg(16)), (0xFE0BU, "starg", NIL.IntArg(16)), (0xFE0CU, "ldloc", NIL.IntArg(16)), (0xFE0DU, "ldloca", NIL.IntArg(16)), (0xFE0EU, "stloc", NIL.IntArg(16)), (0xFE0FU, "localloc", null), (0xFE11U, "endfilter", null), (0xFE18U, "initblk", null), // Once you go block, you never go back ]; public ReflectAssembly(file : string) : NAssembly { mutable types : list [NAssembly] = []; def assembly = Assembly.ReflectionOnlyLoadFrom(file); foreach(t in assembly.GetTypes()) types = NAssembly.Type(t.FullName, ReflectMembers(t)) :: types; NAssembly.Assembly(assembly.FullName, types); } public ReflectMembers(t : Type) : list[NAssembly] { mutable members : list [NAssembly] = []; def flags = ( BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic ); foreach(member in t.GetMembers(flags)) { def mem = match(member.MemberType) { | MemberTypes.Method => def method = member :> MethodInfo; NAssembly.Method( MethodName=member.Name, Arguments=ReflectMethodArguments(method), ReturnType=method.ReturnType, IL={ def body = method.GetMethodBody(); if(body == null) null; else ReflectMethodIL(body, method.Module); } ); | _ => NAssembly.Other(); }; members = mem :: members; } members; } public ReflectMethodArguments(m : MethodInfo) : list [NAssembly] { mutable args : list [NAssembly] = []; foreach(param in m.GetParameters()) args = NAssembly.Argument(param.Name, param.ParameterType) :: args; args; } public ReflectMethodIL(body : MethodBody, mod : Module) : NIL.Block { def blocks = Hashtable(); def il = body.GetILAsByteArray(); mutable next : int; mutable start : int; def U32() { (il[next] :> uint | (il[next + 1] :> uint << 8) | (il[next + 2] :> uint << 16) | (il[next + 3] :> uint << 24)); } def ParseArgument(arg) : NIL { | NIL.TokenArg => def token = U32() :> int; next += 4; match(token >> 24) { | 0x06 => NIL.Object(mod.ResolveMethod(token)); | 0x0A => NIL.Object(mod.ResolveMember(token)); | 0x70 => NIL.Object(mod.ResolveString(token)); | x => Console.WriteLine("Table: {0:X}", x); NIL.Object(null); } | NIL.IntArg(size) => def temp = match(ParseArgument(NIL.UIntArg(size))) { | NIL.UInt(val) => val :> int; | _ => 0; }; def signmask = 1U << (size - 1); NIL.Int({ if((temp :> uint & signmask) != 0) match(size) { | 8 => -(~temp & 0xFF) - 1; | 16 => -(~temp & 0xFFFF) - 1; | 24 => -(~temp) - 1; | _ => temp; } else temp; }); | NIL.UIntArg(size) => def temp = NIL.UInt( match(size) { | 8 => il[next] :> uint; | 16 => (il[next] :> uint | (il[next + 1] :> uint << 8)); | 32 => U32(); | _ => 0U; } ); next += size >> 3; temp; | _ => null; } def ParseOpcd(opcd, prefix, opcodes) { match(opcodes) { | [] => null; | (instopcd, mnem, arg) :: rest => if(opcd == instopcd) { next++; unless(opcd & 0xFF00U == 0) next++; NIL.Inst( Pos=start, Prefix=prefix, Opcode=opcd, Mnem=mnem, Argument=ParseArgument(arg) ); } else ParseOpcd(opcd, prefix, rest); } } def Parse() { def prefix = ParseOpcd( match(il[next] :> uint) { | 0xFEU => (0xFE00U | (il[next+1] :> uint)); | x => x; }, null, Prefixes ); def opcd = match(il[next] :> uint) { | 0xFEU => (0xFE00U | (il[next+1] :> uint)); | x => x; }; def inst = ParseOpcd( opcd, prefix, Opcodes ); when(inst == null) throw Exception(String.Format("Unknown opcode {0:X}", opcd)); inst; } def Process(pos) { if(blocks.ContainsKey(pos)) blocks[pos] :> NIL.Block; else { mutable inst : NIL.Inst; def insts = ArrayList(); next = pos; start = pos; while(next != -1) { inst = Parse(); _ = insts.Add(inst); match(inst) { | NIL.Inst(_, _, opcd, _, _) => match(opcd) { | 0x002AU => // ret next = -1; | _ => (); } } } def block = NIL.Block(pos, insts); blocks[pos] = block; block; } } Process(0); } private Dump(obj : NAssembly) : void { | NAssembly.Assembly(name, types) => Console.WriteLine("Assembly: {0}", name); DumpList(types); | NAssembly.Type(name, members) => Console.WriteLine("\tType: {0}", name); DumpList(members); | NAssembly.Method(name, args, returnType, il) => Console.WriteLine("\t\tMethod: {0}", name); DumpList(args); Console.WriteLine("\t\t\tReturns: {0}", returnType); match(il) { | NIL.Block(_, insts) => foreach(inst :> NIL.Inst in insts) match(inst) { | NIL.Inst(_, _, _, mnem, arg) => Console.WriteLine( "\t\t\t\t{0} {1}", mnem, match(arg) { | Int(val) => val : object; | UInt(val) => val : object; | Object(obj) => obj; | x => x; } ); } | _ => (); } | NAssembly.Argument(name, argType) => Console.WriteLine("\t\t\tArgument: {0} : {1}", name, argType); | _ => (); } private DumpList(obj : list [NAssembly]) : void { | [] => (); | head :: tail => Dump(head); DumpList(tail); } } }