229 lines
6.5 KiB
C#
229 lines
6.5 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 {
|
|
|
|
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;
|
|
});
|
|
}
|
|
|
|
static void ParseMethod (MethodDefinition method, IILVisitor visitor)
|
|
{
|
|
var context = CreateContext (method, visitor);
|
|
var code = context.Code;
|
|
|
|
var 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);
|
|
}
|
|
|
|
static ParseContext CreateContext (MethodDefinition method, IILVisitor visitor)
|
|
{
|
|
var code = method.Module.Read (method, (_, reader) => reader.code);
|
|
var position = code.MoveTo (method);
|
|
|
|
return new ParseContext {
|
|
Code = code,
|
|
Position = position,
|
|
Metadata = code.reader,
|
|
Visitor = visitor,
|
|
};
|
|
}
|
|
|
|
static void ParseFatMethod (ParseContext context)
|
|
{
|
|
var code = context.Code;
|
|
|
|
code.Advance (4);
|
|
var code_size = code.ReadInt32 ();
|
|
var local_var_token = code.ReadToken ();
|
|
|
|
if (local_var_token != MetadataToken.Zero)
|
|
context.Variables = code.ReadVariables (local_var_token);
|
|
|
|
ParseCode (code_size, context);
|
|
}
|
|
|
|
static void ParseCode (int code_size, ParseContext context)
|
|
{
|
|
var code = context.Code;
|
|
var metadata = context.Metadata;
|
|
var visitor = context.Visitor;
|
|
|
|
var start = code.Position;
|
|
var end = start + code_size;
|
|
|
|
while (code.Position < end) {
|
|
var il_opcode = code.ReadByte ();
|
|
var 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:
|
|
var length = code.ReadInt32 ();
|
|
var 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:
|
|
var 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:
|
|
var field_ref = member as FieldReference;
|
|
if (field_ref != null) {
|
|
visitor.OnInlineField (opcode, field_ref);
|
|
break;
|
|
}
|
|
|
|
var method_ref = member as MethodReference;
|
|
if (method_ref != null) {
|
|
visitor.OnInlineMethod (opcode, method_ref);
|
|
break;
|
|
}
|
|
|
|
throw new InvalidOperationException ();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static VariableDefinition GetVariable (ParseContext context, int index)
|
|
{
|
|
return context.Variables [index];
|
|
}
|
|
}
|
|
}
|