Skip to content

Instantly share code, notes, and snippets.

@bjorn-ali-goransson
Created February 11, 2020 15:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjorn-ali-goransson/b4d9e0656e12c54a3ca9674bfeed8f3e to your computer and use it in GitHub Desktop.
Save bjorn-ali-goransson/b4d9e0656e12c54a3ca9674bfeed8f3e to your computer and use it in GitHub Desktop.
Reference implementation from C# in a Nutshell
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
namespace Cloudy.CMS.UI.ContentAppSupport.NameExpressionParserSupport
{
public class Disassembler // http://www.albahari.com/nutshell/cs4ch18.aspx
{
public static string Disassemble(MethodBase method)
{
return new Disassembler(method).Dis();
}
static Dictionary<short, OpCode> _opcodes = new Dictionary<short, OpCode>();
static Disassembler()
{
foreach (FieldInfo fi in typeof(OpCodes).GetFields
(BindingFlags.Public | BindingFlags.Static))
if (typeof(OpCode).IsAssignableFrom(fi.FieldType))
{
OpCode code = (OpCode)fi.GetValue(null);
if (code.OpCodeType != OpCodeType.Nternal)
_opcodes.Add(code.Value, code);
}
}
StringBuilder _output;
Module _module;
byte[] _il;
int _pos;
Disassembler(MethodBase method)
{
_module = method.DeclaringType.Module;
_il = method.GetMethodBody().GetILAsByteArray();
}
string Dis()
{
_output = new StringBuilder();
while (_pos < _il.Length) DisassembleNextInstruction();
return _output.ToString();
}
void DisassembleNextInstruction()
{
int opStart = _pos;
OpCode code = ReadOpCode();
string operand = ReadOperand(code);
_output.AppendFormat("IL_{0:X4}: {1,-12} {2}",
opStart, code.Name, operand);
_output.AppendLine();
}
string ReadOperand(OpCode c)
{
int operandLength =
c.OperandType == OperandType.InlineNone
? 0 :
c.OperandType == OperandType.ShortInlineBrTarget
|| c.OperandType == OperandType.ShortInlineI
|| c.OperandType == OperandType.ShortInlineVar
? 1 :
c.OperandType == OperandType.InlineVar
? 2 :
c.OperandType == OperandType.InlineI8
|| c.OperandType == OperandType.InlineR
? 8 :
c.OperandType == OperandType.InlineSwitch
? 4 * (BitConverter.ToInt32(_il, _pos) + 1)
: 4;
if (_pos + operandLength > _il.Length)
throw new Exception("Unexpected end of IL");
string result = FormatOperand(c, operandLength);
if (result == null)
{
result = "";
for (int i = 0; i < operandLength; i++)
result += _il[_pos + i].ToString("X2") + " ";
}
_pos += operandLength;
return result;
}
OpCode ReadOpCode()
{
byte byteCode = _il[_pos++];
if (_opcodes.ContainsKey(byteCode)) return _opcodes[byteCode];
if (_pos == _il.Length)
throw new Exception("Cannot find opcode " + byteCode);
short shortCode = (short)(byteCode * 256 + _il[_pos++]);
if (!_opcodes.ContainsKey(shortCode))
throw new Exception("Cannot find opcode " + shortCode);
return _opcodes[shortCode];
}
string FormatOperand(OpCode c, int operandLength)
{
if (operandLength == 0) return "";
if (operandLength == 4)
return Get4ByteOperand(c);
else if (c.OperandType == OperandType.ShortInlineBrTarget)
return GetShortRelativeTarget();
else if (c.OperandType == OperandType.InlineSwitch)
return GetSwitchTarget(operandLength);
else
return null;
}
string Get4ByteOperand(OpCode c)
{
int intOp = BitConverter.ToInt32(_il, _pos);
switch (c.OperandType)
{
case OperandType.InlineTok:
case OperandType.InlineMethod:
case OperandType.InlineField:
case OperandType.InlineType:
MemberInfo mi;
try { mi = _module.ResolveMember(intOp); }
catch { return null; }
if (mi == null) return null;
if (mi.ReflectedType != null)
return mi.ReflectedType.FullName + "." + mi.Name;
else if (mi is Type)
return ((Type)mi).FullName;
else
return mi.Name;
case OperandType.InlineString:
string s = _module.ResolveString(intOp);
if (s != null) s = "\"" + s + "\"";
return s;
case OperandType.InlineBrTarget:
return "IL_" + (_pos + intOp + 4).ToString("X4");
default:
return null;
}
}
string GetShortRelativeTarget()
{
return "IL_" + (_pos + (sbyte)_il[_pos] + 1).ToString("X4");
}
string GetSwitchTarget(int operandLength)
{
int targetCount = BitConverter.ToInt32(_il, _pos);
string[] targets = new string[targetCount];
for (int i = 0; i < targetCount; i++)
{
int ilTarget = BitConverter.ToInt32(_il, _pos + (i + 1) * 4);
targets[i] = "IL_" + (_pos + ilTarget + operandLength).ToString("X4");
}
return "(" + string.Join(", ", targets) + ")";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment