PageRenderTime 56ms CodeModel.GetById 13ms app.highlight 37ms RepoModel.GetById 2ms app.codeStats 0ms

/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs

http://github.com/icsharpcode/ILSpy
C# | 312 lines | 264 code | 12 blank | 36 comment | 4 complexity | 4d8e3db92f7c6ee0e14cb656160a41ed MD5 | raw file
  1// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2// 
  3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4// software and associated documentation files (the "Software"), to deal in the Software
  5// without restriction, including without limitation the rights to use, copy, modify, merge,
  6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7// to whom the Software is furnished to do so, subject to the following conditions:
  8// 
  9// The above copyright notice and this permission notice shall be included in all copies or
 10// substantial portions of the Software.
 11// 
 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 17// DEALINGS IN THE SOFTWARE.
 18
 19using System;
 20using System.Collections.Generic;
 21using System.Linq;
 22
 23using Mono.Cecil.Cil;
 24
 25namespace ICSharpCode.Decompiler.FlowAnalysis
 26{
 27	/// <summary>
 28	/// Additional info about opcodes.
 29	/// </summary>
 30	sealed class OpCodeInfo
 31	{
 32		public static bool IsUnconditionalBranch(OpCode opcode)
 33		{
 34			if (opcode.OpCodeType == OpCodeType.Prefix)
 35				return false;
 36			switch (opcode.FlowControl) {
 37				case FlowControl.Branch:
 38				case FlowControl.Throw:
 39				case FlowControl.Return:
 40					return true;
 41				case FlowControl.Next:
 42				case FlowControl.Call:
 43				case FlowControl.Cond_Branch:
 44					return false;
 45				default:
 46					throw new NotSupportedException(opcode.FlowControl.ToString());
 47			}
 48		}
 49		
 50		static readonly OpCodeInfo[] knownOpCodes = {
 51			#region Base Instructions
 52			new OpCodeInfo(OpCodes.Add)        { CanThrow = false },
 53			new OpCodeInfo(OpCodes.Add_Ovf)    { CanThrow = true  },
 54			new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true  },
 55			new OpCodeInfo(OpCodes.And)        { CanThrow = false },
 56			new OpCodeInfo(OpCodes.Arglist)    { CanThrow = false },
 57			new OpCodeInfo(OpCodes.Beq)        { CanThrow = false },
 58			new OpCodeInfo(OpCodes.Beq_S)      { CanThrow = false },
 59			new OpCodeInfo(OpCodes.Bge)        { CanThrow = false },
 60			new OpCodeInfo(OpCodes.Bge_S)      { CanThrow = false },
 61			new OpCodeInfo(OpCodes.Bge_Un)     { CanThrow = false },
 62			new OpCodeInfo(OpCodes.Bge_Un_S)   { CanThrow = false },
 63			new OpCodeInfo(OpCodes.Bgt)        { CanThrow = false },
 64			new OpCodeInfo(OpCodes.Bgt_S)      { CanThrow = false },
 65			new OpCodeInfo(OpCodes.Bgt_Un)     { CanThrow = false },
 66			new OpCodeInfo(OpCodes.Bgt_Un_S)   { CanThrow = false },
 67			new OpCodeInfo(OpCodes.Ble)        { CanThrow = false },
 68			new OpCodeInfo(OpCodes.Ble_S)      { CanThrow = false },
 69			new OpCodeInfo(OpCodes.Ble_Un)     { CanThrow = false },
 70			new OpCodeInfo(OpCodes.Ble_Un_S)   { CanThrow = false },
 71			new OpCodeInfo(OpCodes.Blt)        { CanThrow = false },
 72			new OpCodeInfo(OpCodes.Blt_S)      { CanThrow = false },
 73			new OpCodeInfo(OpCodes.Blt_Un)     { CanThrow = false },
 74			new OpCodeInfo(OpCodes.Blt_Un_S)   { CanThrow = false },
 75			new OpCodeInfo(OpCodes.Bne_Un)     { CanThrow = false },
 76			new OpCodeInfo(OpCodes.Bne_Un_S)   { CanThrow = false },
 77			new OpCodeInfo(OpCodes.Br)         { CanThrow = false },
 78			new OpCodeInfo(OpCodes.Br_S)       { CanThrow = false },
 79			new OpCodeInfo(OpCodes.Break)      { CanThrow = true  },
 80			new OpCodeInfo(OpCodes.Brfalse)    { CanThrow = false },
 81			new OpCodeInfo(OpCodes.Brfalse_S)  { CanThrow = false },
 82			new OpCodeInfo(OpCodes.Brtrue)     { CanThrow = false },
 83			new OpCodeInfo(OpCodes.Brtrue_S)   { CanThrow = false },
 84			new OpCodeInfo(OpCodes.Call)       { CanThrow = true  },
 85			new OpCodeInfo(OpCodes.Calli)      { CanThrow = true  },
 86			new OpCodeInfo(OpCodes.Ceq)        { CanThrow = false },
 87			new OpCodeInfo(OpCodes.Cgt)        { CanThrow = false },
 88			new OpCodeInfo(OpCodes.Cgt_Un)     { CanThrow = false },
 89			new OpCodeInfo(OpCodes.Ckfinite)   { CanThrow = true  },
 90			new OpCodeInfo(OpCodes.Clt)        { CanThrow = false },
 91			new OpCodeInfo(OpCodes.Clt_Un)     { CanThrow = false },
 92			// conv.<to type>
 93			new OpCodeInfo(OpCodes.Conv_I1)    { CanThrow = false },
 94			new OpCodeInfo(OpCodes.Conv_I2)    { CanThrow = false },
 95			new OpCodeInfo(OpCodes.Conv_I4)    { CanThrow = false },
 96			new OpCodeInfo(OpCodes.Conv_I8)    { CanThrow = false },
 97			new OpCodeInfo(OpCodes.Conv_R4)    { CanThrow = false },
 98			new OpCodeInfo(OpCodes.Conv_R8)    { CanThrow = false },
 99			new OpCodeInfo(OpCodes.Conv_U1)    { CanThrow = false },
100			new OpCodeInfo(OpCodes.Conv_U2)    { CanThrow = false },
101			new OpCodeInfo(OpCodes.Conv_U4)    { CanThrow = false },
102			new OpCodeInfo(OpCodes.Conv_U8)    { CanThrow = false },
103			new OpCodeInfo(OpCodes.Conv_I)     { CanThrow = false },
104			new OpCodeInfo(OpCodes.Conv_U)     { CanThrow = false },
105			new OpCodeInfo(OpCodes.Conv_R_Un)  { CanThrow = false },
106			// conv.ovf.<to type>
107			new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true},
108			new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true},
109			new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true},
110			new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true},
111			new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true},
112			new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true},
113			new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true},
114			new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true},
115			new OpCodeInfo(OpCodes.Conv_Ovf_I)  { CanThrow = true},
116			new OpCodeInfo(OpCodes.Conv_Ovf_U)  { CanThrow = true},
117			// conv.ovf.<to type>.un
118			new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true },
119			new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true },
120			new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true },
121			new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true },
122			new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true },
123			new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true },
124			new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true },
125			new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true },
126			new OpCodeInfo(OpCodes.Conv_Ovf_I_Un)  { CanThrow = true },
127			new OpCodeInfo(OpCodes.Conv_Ovf_U_Un)  { CanThrow = true },
128			
129			//new OpCodeInfo(OpCodes.Cpblk)      { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
130			new OpCodeInfo(OpCodes.Div)        { CanThrow = true },
131			new OpCodeInfo(OpCodes.Div_Un)     { CanThrow = true },
132			new OpCodeInfo(OpCodes.Dup)        { CanThrow = true, IsMoveInstruction = true },
133			new OpCodeInfo(OpCodes.Endfilter)  { CanThrow = false },
134			new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false },
135			//new OpCodeInfo(OpCodes.Initblk)    { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
136			//new OpCodeInfo(OpCodes.Jmp)        { CanThrow = true } - We don't support non-local control transfers.
137			new OpCodeInfo(OpCodes.Ldarg)   { CanThrow = false, IsMoveInstruction = true },
138			new OpCodeInfo(OpCodes.Ldarg_0) { CanThrow = false, IsMoveInstruction = true },
139			new OpCodeInfo(OpCodes.Ldarg_1) { CanThrow = false, IsMoveInstruction = true },
140			new OpCodeInfo(OpCodes.Ldarg_2) { CanThrow = false, IsMoveInstruction = true },
141			new OpCodeInfo(OpCodes.Ldarg_3) { CanThrow = false, IsMoveInstruction = true },
142			new OpCodeInfo(OpCodes.Ldarg_S) { CanThrow = false, IsMoveInstruction = true },
143			new OpCodeInfo(OpCodes.Ldarga)   { CanThrow = false },
144			new OpCodeInfo(OpCodes.Ldarga_S) { CanThrow = false },
145			new OpCodeInfo(OpCodes.Ldc_I4)    { CanThrow = false },
146			new OpCodeInfo(OpCodes.Ldc_I4_M1) { CanThrow = false },
147			new OpCodeInfo(OpCodes.Ldc_I4_0) { CanThrow = false },
148			new OpCodeInfo(OpCodes.Ldc_I4_1) { CanThrow = false },
149			new OpCodeInfo(OpCodes.Ldc_I4_2) { CanThrow = false },
150			new OpCodeInfo(OpCodes.Ldc_I4_3) { CanThrow = false },
151			new OpCodeInfo(OpCodes.Ldc_I4_4) { CanThrow = false },
152			new OpCodeInfo(OpCodes.Ldc_I4_5) { CanThrow = false },
153			new OpCodeInfo(OpCodes.Ldc_I4_6) { CanThrow = false },
154			new OpCodeInfo(OpCodes.Ldc_I4_7) { CanThrow = false },
155			new OpCodeInfo(OpCodes.Ldc_I4_8) { CanThrow = false },
156			new OpCodeInfo(OpCodes.Ldc_I4_S) { CanThrow = false },
157			new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false },
158			new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false },
159			new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false },
160			new OpCodeInfo(OpCodes.Ldftn)  { CanThrow = false },
161			// ldind.<type>
162			new OpCodeInfo(OpCodes.Ldind_I1)  { CanThrow = true },
163			new OpCodeInfo(OpCodes.Ldind_I2)  { CanThrow = true },
164			new OpCodeInfo(OpCodes.Ldind_I4)  { CanThrow = true },
165			new OpCodeInfo(OpCodes.Ldind_I8)  { CanThrow = true },
166			new OpCodeInfo(OpCodes.Ldind_U1)  { CanThrow = true },
167			new OpCodeInfo(OpCodes.Ldind_U2)  { CanThrow = true },
168			new OpCodeInfo(OpCodes.Ldind_U4)  { CanThrow = true },
169			new OpCodeInfo(OpCodes.Ldind_R4)  { CanThrow = true },
170			new OpCodeInfo(OpCodes.Ldind_R8)  { CanThrow = true },
171			new OpCodeInfo(OpCodes.Ldind_I)   { CanThrow = true },
172			new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true },
173			// the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
174			new OpCodeInfo(OpCodes.Ldloc)      { CanThrow = false, IsMoveInstruction = true },
175			new OpCodeInfo(OpCodes.Ldloc_0)    { CanThrow = false, IsMoveInstruction = true },
176			new OpCodeInfo(OpCodes.Ldloc_1)    { CanThrow = false, IsMoveInstruction = true },
177			new OpCodeInfo(OpCodes.Ldloc_2)    { CanThrow = false, IsMoveInstruction = true },
178			new OpCodeInfo(OpCodes.Ldloc_3)    { CanThrow = false, IsMoveInstruction = true },
179			new OpCodeInfo(OpCodes.Ldloc_S)    { CanThrow = false, IsMoveInstruction = true },
180			new OpCodeInfo(OpCodes.Ldloca)     { CanThrow = false },
181			new OpCodeInfo(OpCodes.Ldloca_S)   { CanThrow = false },
182			new OpCodeInfo(OpCodes.Ldnull)     { CanThrow = false },
183			new OpCodeInfo(OpCodes.Leave)      { CanThrow = false },
184			new OpCodeInfo(OpCodes.Leave_S)    { CanThrow = false },
185			new OpCodeInfo(OpCodes.Localloc)   { CanThrow = true  },
186			new OpCodeInfo(OpCodes.Mul)        { CanThrow = false },
187			new OpCodeInfo(OpCodes.Mul_Ovf)    { CanThrow = true  },
188			new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true  },
189			new OpCodeInfo(OpCodes.Neg)        { CanThrow = false },
190			new OpCodeInfo(OpCodes.Nop)        { CanThrow = false },
191			new OpCodeInfo(OpCodes.Not)        { CanThrow = false },
192			new OpCodeInfo(OpCodes.Or)         { CanThrow = false },
193			new OpCodeInfo(OpCodes.Pop)        { CanThrow = false },
194			new OpCodeInfo(OpCodes.Rem)        { CanThrow = true  },
195			new OpCodeInfo(OpCodes.Rem_Un)     { CanThrow = true  },
196			new OpCodeInfo(OpCodes.Ret)        { CanThrow = false },
197			new OpCodeInfo(OpCodes.Shl)        { CanThrow = false },
198			new OpCodeInfo(OpCodes.Shr)        { CanThrow = false },
199			new OpCodeInfo(OpCodes.Shr_Un)     { CanThrow = false },
200			new OpCodeInfo(OpCodes.Starg)      { CanThrow = false, IsMoveInstruction = true },
201			new OpCodeInfo(OpCodes.Starg_S)    { CanThrow = false, IsMoveInstruction = true },
202			new OpCodeInfo(OpCodes.Stind_I1)   { CanThrow = true },
203			new OpCodeInfo(OpCodes.Stind_I2)   { CanThrow = true },
204			new OpCodeInfo(OpCodes.Stind_I4)   { CanThrow = true },
205			new OpCodeInfo(OpCodes.Stind_I8)   { CanThrow = true },
206			new OpCodeInfo(OpCodes.Stind_R4)   { CanThrow = true },
207			new OpCodeInfo(OpCodes.Stind_R8)   { CanThrow = true },
208			new OpCodeInfo(OpCodes.Stind_I)    { CanThrow = true },
209			new OpCodeInfo(OpCodes.Stind_Ref)  { CanThrow = true },
210			new OpCodeInfo(OpCodes.Stloc)      { CanThrow = false, IsMoveInstruction = true },
211			new OpCodeInfo(OpCodes.Stloc_0)    { CanThrow = false, IsMoveInstruction = true },
212			new OpCodeInfo(OpCodes.Stloc_1)    { CanThrow = false, IsMoveInstruction = true },
213			new OpCodeInfo(OpCodes.Stloc_2)    { CanThrow = false, IsMoveInstruction = true },
214			new OpCodeInfo(OpCodes.Stloc_3)    { CanThrow = false, IsMoveInstruction = true },
215			new OpCodeInfo(OpCodes.Stloc_S)    { CanThrow = false, IsMoveInstruction = true },
216			new OpCodeInfo(OpCodes.Sub)        { CanThrow = false },
217			new OpCodeInfo(OpCodes.Sub_Ovf)    { CanThrow = true  },
218			new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true  },
219			new OpCodeInfo(OpCodes.Switch)     { CanThrow = false },
220			new OpCodeInfo(OpCodes.Xor)        { CanThrow = false },
221			#endregion
222			#region Object model instructions
223			// CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
224			new OpCodeInfo(OpCodes.Box),
225			new OpCodeInfo(OpCodes.Callvirt),
226			new OpCodeInfo(OpCodes.Castclass),
227			new OpCodeInfo(OpCodes.Cpobj),
228			new OpCodeInfo(OpCodes.Initobj) { CanThrow = false },
229			new OpCodeInfo(OpCodes.Isinst)  { CanThrow = false },
230			new OpCodeInfo(OpCodes.Ldelem_Any),
231			// ldelem.<type>
232			new OpCodeInfo(OpCodes.Ldelem_I) ,
233			new OpCodeInfo(OpCodes.Ldelem_I1),
234			new OpCodeInfo(OpCodes.Ldelem_I2),
235			new OpCodeInfo(OpCodes.Ldelem_I4),
236			new OpCodeInfo(OpCodes.Ldelem_I8),
237			new OpCodeInfo(OpCodes.Ldelem_R4),
238			new OpCodeInfo(OpCodes.Ldelem_R8),
239			new OpCodeInfo(OpCodes.Ldelem_Ref),
240			new OpCodeInfo(OpCodes.Ldelem_U1),
241			new OpCodeInfo(OpCodes.Ldelem_U2),
242			new OpCodeInfo(OpCodes.Ldelem_U4),
243			new OpCodeInfo(OpCodes.Ldelema)  ,
244			new OpCodeInfo(OpCodes.Ldfld) ,
245			new OpCodeInfo(OpCodes.Ldflda),
246			new OpCodeInfo(OpCodes.Ldlen) ,
247			new OpCodeInfo(OpCodes.Ldobj) ,
248			new OpCodeInfo(OpCodes.Ldsfld),
249			new OpCodeInfo(OpCodes.Ldsflda),
250			new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false },
251			new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false },
252			new OpCodeInfo(OpCodes.Ldvirtftn),
253			new OpCodeInfo(OpCodes.Mkrefany),
254			new OpCodeInfo(OpCodes.Newarr),
255			new OpCodeInfo(OpCodes.Newobj),
256			new OpCodeInfo(OpCodes.Refanytype) { CanThrow = false },
257			new OpCodeInfo(OpCodes.Refanyval),
258			new OpCodeInfo(OpCodes.Rethrow),
259			new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false },
260			new OpCodeInfo(OpCodes.Stelem_Any),
261			new OpCodeInfo(OpCodes.Stelem_I1),
262			new OpCodeInfo(OpCodes.Stelem_I2),
263			new OpCodeInfo(OpCodes.Stelem_I4),
264			new OpCodeInfo(OpCodes.Stelem_I8),
265			new OpCodeInfo(OpCodes.Stelem_R4),
266			new OpCodeInfo(OpCodes.Stelem_R8),
267			new OpCodeInfo(OpCodes.Stelem_Ref),
268			new OpCodeInfo(OpCodes.Stfld),
269			new OpCodeInfo(OpCodes.Stobj),
270			new OpCodeInfo(OpCodes.Stsfld),
271			new OpCodeInfo(OpCodes.Throw),
272			new OpCodeInfo(OpCodes.Unbox),
273			new OpCodeInfo(OpCodes.Unbox_Any),
274			#endregion
275		};
276		static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code);
277		
278		public static OpCodeInfo Get(OpCode opCode)
279		{
280			return Get(opCode.Code);
281		}
282		
283		public static OpCodeInfo Get(Code code)
284		{
285			OpCodeInfo info;
286			if (knownOpCodeDict.TryGetValue(code, out info))
287				return info;
288			else
289				throw new NotSupportedException(code.ToString());
290		}
291		
292		OpCode opcode;
293		
294		private OpCodeInfo(OpCode opcode)
295		{
296			this.opcode = opcode;
297			this.CanThrow = true;
298		}
299		
300		public OpCode OpCode { get { return opcode; } }
301		
302		/// <summary>
303		/// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
304		/// </summary>
305		public bool IsMoveInstruction { get; private set; }
306		
307		/// <summary>
308		/// Specifies whether this opcode is capable of throwing exceptions.
309		/// </summary>
310		public bool CanThrow { get; private set; }
311	}
312}