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];
 | |
| 		}
 | |
| 	}
 | |
| }
 |