Files
2026-03-30 20:11:57 +07:00

236 lines
9.2 KiB
C#

//
// Author:
// Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2015 Jb Evain
// Copyright (c) 2008 - 2011 Novell, Inc.
//
// Licensed under the MIT/X11 license.
//
using MonoFN.Cecil.Cil;
using MonoFN.Collections.Generic;
using System;
namespace MonoFN.Cecil.Rocks
{
#if UNITY_EDITOR
public
#endif
interface IILVisitor
{
void OnInlineNone(OpCode opcode);
void OnInlineSByte(OpCode opcode, sbyte value);
void OnInlineByte(OpCode opcode, byte value);
void OnInlineInt32(OpCode opcode, int value);
void OnInlineInt64(OpCode opcode, long value);
void OnInlineSingle(OpCode opcode, float value);
void OnInlineDouble(OpCode opcode, double value);
void OnInlineString(OpCode opcode, string value);
void OnInlineBranch(OpCode opcode, int offset);
void OnInlineSwitch(OpCode opcode, int[] offsets);
void OnInlineVariable(OpCode opcode, VariableDefinition variable);
void OnInlineArgument(OpCode opcode, ParameterDefinition parameter);
void OnInlineSignature(OpCode opcode, CallSite callSite);
void OnInlineType(OpCode opcode, TypeReference type);
void OnInlineField(OpCode opcode, FieldReference field);
void OnInlineMethod(OpCode opcode, MethodReference method);
}
#if UNITY_EDITOR
public
#endif
static class ILParser
{
private class ParseContext
{
public CodeReader Code { get; set; }
public int Position { get; set; }
public MetadataReader Metadata { get; set; }
public Collection<VariableDefinition> Variables { get; set; }
public IILVisitor Visitor { get; set; }
}
public static void Parse(MethodDefinition method, IILVisitor visitor)
{
if (method == null)
throw new ArgumentNullException("method");
if (visitor == null)
throw new ArgumentNullException("visitor");
if (!method.HasBody || !method.HasImage)
throw new ArgumentException();
method.Module.Read(method, (m, _) =>
{
ParseMethod(m, visitor);
return true;
});
}
private static void ParseMethod(MethodDefinition method, IILVisitor visitor)
{
ParseContext context = CreateContext(method, visitor);
CodeReader code = context.Code;
byte flags = code.ReadByte();
switch (flags & 0x3)
{
case 0x2: // tiny
int code_size = flags >> 2;
ParseCode(code_size, context);
break;
case 0x3: // fat
code.Advance(-1);
ParseFatMethod(context);
break;
default:
throw new NotSupportedException();
}
code.MoveBackTo(context.Position);
}
private static ParseContext CreateContext(MethodDefinition method, IILVisitor visitor)
{
CodeReader code = method.Module.Read(method, (_, reader) => reader.code);
int position = code.MoveTo(method);
return new()
{
Code = code,
Position = position,
Metadata = code.reader,
Visitor = visitor
};
}
private static void ParseFatMethod(ParseContext context)
{
CodeReader code = context.Code;
code.Advance(4);
int code_size = code.ReadInt32();
MetadataToken local_var_token = code.ReadToken();
if (local_var_token != MetadataToken.Zero)
context.Variables = code.ReadVariables(local_var_token);
ParseCode(code_size, context);
}
private static void ParseCode(int code_size, ParseContext context)
{
CodeReader code = context.Code;
MetadataReader metadata = context.Metadata;
IILVisitor visitor = context.Visitor;
int start = code.Position;
int end = start + code_size;
while (code.Position < end)
{
byte il_opcode = code.ReadByte();
OpCode opcode = il_opcode != 0xfe ? OpCodes.OneByteOpCode[il_opcode] : OpCodes.TwoBytesOpCode[code.ReadByte()];
switch (opcode.OperandType)
{
case OperandType.InlineNone:
visitor.OnInlineNone(opcode);
break;
case OperandType.InlineSwitch:
int length = code.ReadInt32();
int[] branches = new int [length];
for (int i = 0; i < length; i++)
branches[i] = code.ReadInt32();
visitor.OnInlineSwitch(opcode, branches);
break;
case OperandType.ShortInlineBrTarget:
visitor.OnInlineBranch(opcode, code.ReadSByte());
break;
case OperandType.InlineBrTarget:
visitor.OnInlineBranch(opcode, code.ReadInt32());
break;
case OperandType.ShortInlineI:
if (opcode == OpCodes.Ldc_I4_S)
visitor.OnInlineSByte(opcode, code.ReadSByte());
else
visitor.OnInlineByte(opcode, code.ReadByte());
break;
case OperandType.InlineI:
visitor.OnInlineInt32(opcode, code.ReadInt32());
break;
case OperandType.InlineI8:
visitor.OnInlineInt64(opcode, code.ReadInt64());
break;
case OperandType.ShortInlineR:
visitor.OnInlineSingle(opcode, code.ReadSingle());
break;
case OperandType.InlineR:
visitor.OnInlineDouble(opcode, code.ReadDouble());
break;
case OperandType.InlineSig:
visitor.OnInlineSignature(opcode, code.GetCallSite(code.ReadToken()));
break;
case OperandType.InlineString:
visitor.OnInlineString(opcode, code.GetString(code.ReadToken()));
break;
case OperandType.ShortInlineArg:
visitor.OnInlineArgument(opcode, code.GetParameter(code.ReadByte()));
break;
case OperandType.InlineArg:
visitor.OnInlineArgument(opcode, code.GetParameter(code.ReadInt16()));
break;
case OperandType.ShortInlineVar:
visitor.OnInlineVariable(opcode, GetVariable(context, code.ReadByte()));
break;
case OperandType.InlineVar:
visitor.OnInlineVariable(opcode, GetVariable(context, code.ReadInt16()));
break;
case OperandType.InlineTok:
case OperandType.InlineField:
case OperandType.InlineMethod:
case OperandType.InlineType:
IMetadataTokenProvider member = metadata.LookupToken(code.ReadToken());
switch (member.MetadataToken.TokenType)
{
case TokenType.TypeDef:
case TokenType.TypeRef:
case TokenType.TypeSpec:
visitor.OnInlineType(opcode, (TypeReference)member);
break;
case TokenType.Method:
case TokenType.MethodSpec:
visitor.OnInlineMethod(opcode, (MethodReference)member);
break;
case TokenType.Field:
visitor.OnInlineField(opcode, (FieldReference)member);
break;
case TokenType.MemberRef:
FieldReference field_ref = member as FieldReference;
if (field_ref != null)
{
visitor.OnInlineField(opcode, field_ref);
break;
}
MethodReference method_ref = member as MethodReference;
if (method_ref != null)
{
visitor.OnInlineMethod(opcode, method_ref);
break;
}
throw new InvalidOperationException();
}
break;
}
}
}
private static VariableDefinition GetVariable(ParseContext context, int index)
{
return context.Variables[index];
}
}
}