PageRenderTime 31ms CodeModel.GetById 15ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Debugger/Debugger.Core/Eval.cs

http://github.com/icsharpcode/ILSpy
C# | 416 lines | 320 code | 48 blank | 48 comment | 72 complexity | b4259bf572d296eb116bcdd172538d0f MD5 | raw file
  1// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3
  4using System;
  5using System.Collections.Generic;
  6using System.Runtime.InteropServices;
  7
  8using Debugger.MetaData;
  9using Debugger.Interop.CorDebug;
 10
 11namespace Debugger
 12{
 13	public enum EvalState {
 14		Evaluating,
 15		EvaluatedSuccessfully,
 16		EvaluatedException,
 17		EvaluatedNoResult,
 18		EvaluatedTimeOut,
 19	};
 20	
 21	/// <summary>
 22	/// This class holds information about function evaluation.
 23	/// </summary>
 24	public class Eval: DebuggerObject
 25	{
 26		delegate void EvalStarter(Eval eval);
 27		
 28		AppDomain     appDomain;
 29		Process       process;
 30		
 31		string        description;
 32		ICorDebugEval corEval;
 33		Thread        thread;
 34		Value         result;
 35		EvalState     state;
 36		
 37		public AppDomain AppDomain {
 38			get { return appDomain; }
 39		}
 40		
 41		public Process Process {
 42			get { return process; }
 43		}
 44		
 45		public string Description {
 46			get { return description; }
 47		}
 48		
 49		public ICorDebugEval CorEval {
 50			get { return corEval; }
 51		}
 52		
 53		public ICorDebugEval2 CorEval2 {
 54			get { return (ICorDebugEval2)corEval; }
 55		}
 56
 57	    /// <exception cref="GetValueException">Evaluating...</exception>
 58	    public Value Result {
 59			get {
 60				switch(this.State) {
 61					case EvalState.Evaluating:            throw new GetValueException("Evaluating...");
 62					case EvalState.EvaluatedSuccessfully: return result;
 63					case EvalState.EvaluatedException:    return result;
 64					case EvalState.EvaluatedNoResult:     return null;
 65					case EvalState.EvaluatedTimeOut:      throw new GetValueException("Timeout");
 66					default: throw new DebuggerException("Unknown state");
 67				}
 68			}
 69		}
 70		
 71		public EvalState State {
 72			get { return state; }
 73		}
 74		
 75		public bool Evaluated {
 76			get {
 77				return state == EvalState.EvaluatedSuccessfully ||
 78				       state == EvalState.EvaluatedException ||
 79				       state == EvalState.EvaluatedNoResult ||
 80				       state == EvalState.EvaluatedTimeOut;
 81			}
 82		}
 83		
 84		Eval(AppDomain appDomain, string description, EvalStarter evalStarter)
 85		{
 86			this.appDomain = appDomain;
 87			this.process = appDomain.Process;
 88			this.description = description;
 89			this.state = EvalState.Evaluating;
 90			this.thread = GetEvaluationThread(appDomain);
 91			this.corEval = thread.CorThread.CreateEval();
 92			
 93			try {
 94				evalStarter(this);
 95			} catch (COMException e) {
 96				if ((uint)e.ErrorCode == 0x80131C26) {
 97					throw new GetValueException("Can not evaluate in optimized code");
 98				} else if ((uint)e.ErrorCode == 0x80131C28) {
 99					throw new GetValueException("Object is in wrong AppDomain");
100				} else if ((uint)e.ErrorCode == 0x8013130A) {
101					// Happens on getting of Sytem.Threading.Thread.ManagedThreadId; See SD2-1116
102					throw new GetValueException("Function does not have IL code");
103				} else if ((uint)e.ErrorCode == 0x80131C23) {
104					// The operation failed because it is a GC unsafe point. (Exception from HRESULT: 0x80131C23)
105					// This can probably happen when we break and the thread is in native code
106					throw new GetValueException("Thread is in GC unsafe point");
107				} else if ((uint)e.ErrorCode == 0x80131C22) {
108					// The operation is illegal because of a stack overflow.
109					throw new GetValueException("Can not evaluate after stack overflow");
110				} else if ((uint)e.ErrorCode == 0x80131313) {
111					// Func eval cannot work. Bad starting point.
112					// Reproduction circumstancess are unknown
113					throw new GetValueException("Func eval cannot work. Bad starting point.");
114				} else {
115					#if DEBUG
116						throw; // Expose for more diagnostics
117					#else
118						throw new GetValueException(e.Message);
119					#endif
120				}
121			}
122			
123			appDomain.Process.ActiveEvals.Add(this);
124			
125			if (appDomain.Process.Options.SuspendOtherThreads) {
126				appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] { thread }, CorDebugThreadState.THREAD_SUSPEND);
127			} else {
128				appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep, this.Process.UnsuspendedThreads, CorDebugThreadState.THREAD_RUN);
129			}
130		}
131		
132		static Thread GetEvaluationThread(AppDomain appDomain)
133		{
134			appDomain.Process.AssertPaused();
135			
136			Thread st = appDomain.Process.SelectedThread;
137			if (st != null && !st.Suspended && !st.IsInNativeCode && st.IsAtSafePoint && st.CorThread.GetAppDomain().GetID() == appDomain.ID) {
138				return st;
139			}
140			
141			foreach(Thread t in appDomain.Process.Threads) {
142				if (!t.Suspended && !t.IsInNativeCode && t.IsAtSafePoint && t.CorThread.GetAppDomain().GetID() == appDomain.ID) {
143					return t;
144				}
145			}
146			
147			throw new GetValueException("No suitable thread for evaluation");
148		}
149
150		internal bool IsCorEval(ICorDebugEval corEval)
151		{
152			return this.corEval == corEval;
153		}
154
155	    /// <exception cref="DebuggerException">Evaluation can not be stopped</exception>
156	    /// <exception cref="GetValueException">Process exited</exception>
157	    Value WaitForResult()
158		{
159			// Note that aborting is not supported for suspended threads
160			try {
161				process.WaitForPause(TimeSpan.FromMilliseconds(500));
162				if (!Evaluated) {
163					process.TraceMessage("Aborting eval: " + Description);
164					this.CorEval.Abort();
165					process.WaitForPause(TimeSpan.FromMilliseconds(2500));
166					if (!Evaluated) {
167						process.TraceMessage("Rude aborting eval: " + Description);
168						this.CorEval2.RudeAbort();
169						process.WaitForPause(TimeSpan.FromMilliseconds(5000));
170						if (!Evaluated) {
171							throw new DebuggerException("Evaluation can not be stopped");
172						}
173					}
174					// Note that this sets Evaluated to true
175					state = EvalState.EvaluatedTimeOut;
176				}
177				process.AssertPaused();
178				return this.Result;
179			} catch (ProcessExitedException) {
180				throw new GetValueException("Process exited");
181			}
182		}
183		
184		internal void NotifyEvaluationComplete(bool successful) 
185		{
186			// Eval result should be ICorDebugHandleValue so it should survive Continue()
187			if (state == EvalState.EvaluatedTimeOut) {
188				return;
189			}
190			if (corEval.GetResult() == null) {
191				state = EvalState.EvaluatedNoResult;
192			} else {
193				if (successful) {
194					state = EvalState.EvaluatedSuccessfully;
195				} else {
196					state = EvalState.EvaluatedException;
197				}
198				result = new Value(AppDomain, corEval.GetResult());
199			}
200		}
201		
202		/// <summary> Synchronously calls a function and returns its return value </summary>
203		public static Value InvokeMethod(DebugMethodInfo method, Value thisValue, Value[] args)
204		{
205			if (method.BackingField != null) {
206				method.Process.TraceMessage("Using backing field for " + method.FullName);
207				return Value.GetMemberValue(thisValue, method.BackingField, args);
208			}
209			return AsyncInvokeMethod(method, thisValue, args).WaitForResult();
210		}
211		
212		public static Eval AsyncInvokeMethod(DebugMethodInfo method, Value thisValue, Value[] args)
213		{
214			return new Eval(
215				method.AppDomain,
216				"Function call: " + method.FullName,
217				delegate(Eval eval) {
218					MethodInvokeStarter(eval, method, thisValue, args);
219				}
220			);
221		}
222
223		/// <exception cref="GetValueException"><c>GetValueException</c>.</exception>
224		static void MethodInvokeStarter(Eval eval, DebugMethodInfo method, Value thisValue, Value[] args)
225		{
226			List<ICorDebugValue> corArgs = new List<ICorDebugValue>();
227			args = args ?? new Value[0];
228			if (args.Length != method.ParameterCount) {
229				throw new GetValueException("Invalid parameter count");
230			}
231			if (!method.IsStatic) {
232				if (thisValue == null)
233					throw new GetValueException("'this' is null");
234				if (thisValue.IsNull)
235					throw new GetValueException("Null reference");
236				// if (!(thisValue.IsObject)) // eg Can evaluate on array
237				if (!method.DeclaringType.IsInstanceOfType(thisValue)) {
238					throw new GetValueException(
239						"Can not evaluate because the object is not of proper type.  " + 
240						"Expected: " + method.DeclaringType.FullName + "  Seen: " + thisValue.Type.FullName
241					);
242				}
243				corArgs.Add(thisValue.CorValue);
244			}
245			for(int i = 0; i < args.Length; i++) {
246				Value arg = args[i];
247				DebugType paramType = (DebugType)method.GetParameters()[i].ParameterType;
248				if (!arg.Type.CanImplicitelyConvertTo(paramType))
249					throw new GetValueException("Inncorrect parameter type");
250				// Implicitely convert to correct primitve type
251				if (paramType.IsPrimitive && args[i].Type != paramType) {
252					object oldPrimVal = arg.PrimitiveValue;
253					object newPrimVal = Convert.ChangeType(oldPrimVal, paramType.PrimitiveType);
254					arg = CreateValue(method.AppDomain, newPrimVal);
255				}
256				// It is importatnt to pass the parameted in the correct form (boxed/unboxed)
257				if (paramType.IsValueType) {
258					corArgs.Add(arg.CorGenericValue);
259				} else {
260					if (args[i].Type.IsValueType) {
261						corArgs.Add(arg.Box().CorValue);
262					} else {
263						corArgs.Add(arg.CorValue);
264					}
265				}
266			}
267			
268			ICorDebugType[] genericArgs = ((DebugType)method.DeclaringType).GenericArgumentsAsCorDebugType;
269			eval.CorEval2.CallParameterizedFunction(
270				method.CorFunction,
271				(uint)genericArgs.Length, genericArgs,
272				(uint)corArgs.Count, corArgs.ToArray()
273			);
274		}
275	    
276	    public static Value CreateValue(AppDomain appDomain, object value)
277	    {
278	    	if (value == null) {
279				ICorDebugClass corClass = appDomain.ObjectType.CorType.GetClass();
280				Thread thread = GetEvaluationThread(appDomain);
281				ICorDebugEval corEval = thread.CorThread.CreateEval();
282				ICorDebugValue corValue = corEval.CreateValue((uint)CorElementType.CLASS, corClass);
283				return new Value(appDomain, corValue);
284			} else if (value is string) {
285	    		return Eval.NewString(appDomain, (string)value);
286			} else {
287	    		if (!value.GetType().IsPrimitive)
288	    			throw new DebuggerException("Value must be primitve type.  Seen " + value.GetType());
289				Value val = Eval.NewObjectNoConstructor(DebugType.CreateFromType(appDomain.Mscorlib, value.GetType()));
290				val.PrimitiveValue = value;
291				return val;
292			}
293	    }
294		
295	    /*
296		// The following function create values only for the purpuse of evalutaion
297		// They actually do not allocate memory on the managed heap
298		// The advantage is that it does not continue the process
299	    /// <exception cref="DebuggerException">Can not create string this way</exception>
300	    public static Value CreateValue(Process process, object value)
301		{
302			if (value is string) throw new DebuggerException("Can not create string this way");
303			CorElementType corElemType;
304			ICorDebugClass corClass = null;
305			if (value != null) {
306				corElemType = DebugType.TypeNameToCorElementType(value.GetType().FullName);
307			} else {
308				corElemType = CorElementType.CLASS;
309				corClass = DebugType.Create(process, null, typeof(object).FullName).CorType.Class;
310			}
311			ICorDebugEval corEval = CreateCorEval(process);
312			ICorDebugValue corValue = corEval.CreateValue((uint)corElemType, corClass);
313			Value v = new Value(process, new Expressions.PrimitiveExpression(value), corValue);
314			if (value != null) {
315				v.PrimitiveValue = value;
316			}
317			return v;
318		}
319		*/
320		
321		#region Convenience methods
322		
323		public static Value NewString(AppDomain appDomain, string textToCreate)
324		{
325			return AsyncNewString(appDomain, textToCreate).WaitForResult();
326		}
327		
328		#endregion
329		
330		public static Eval AsyncNewString(AppDomain appDomain, string textToCreate)
331		{
332			return new Eval(
333				appDomain,
334				"New string: " + textToCreate,
335				delegate(Eval eval) {
336					eval.CorEval2.NewStringWithLength(textToCreate, (uint)textToCreate.Length);
337				}
338			);
339		}
340		
341		#region Convenience methods
342		
343		public static Value NewArray(DebugType type, uint length, uint? lowerBound)
344		{
345			return AsyncNewArray(type, length, lowerBound).WaitForResult();
346		}
347		
348		#endregion
349		
350		public static Eval AsyncNewArray(DebugType type, uint length, uint? lowerBound)
351		{
352			lowerBound = lowerBound ?? 0;
353			return new Eval(
354				type.AppDomain,
355				"New array: " + type + "[" + length + "]",
356				delegate(Eval eval) {
357					// Multi-dimensional arrays not supported in .NET 2.0
358					eval.CorEval2.NewParameterizedArray(type.CorType, 1, new uint[] { length }, new uint[] { lowerBound.Value });
359				}
360			);
361		}
362		
363		#region Convenience methods
364		
365		public static Value NewObject(DebugMethodInfo constructor, Value[] constructorArguments)
366		{
367			return AsyncNewObject(constructor, constructorArguments).WaitForResult();
368		}
369		
370		#endregion
371		
372		public static Eval AsyncNewObject(DebugMethodInfo constructor, Value[] constructorArguments)
373		{
374			ICorDebugValue[] constructorArgsCorDebug = ValuesAsCorDebug(constructorArguments);
375			return new Eval(
376				constructor.AppDomain,
377				"New object: " + constructor.FullName,
378				delegate(Eval eval) {
379					eval.CorEval2.NewParameterizedObject(
380						constructor.CorFunction,
381						(uint)constructor.DeclaringType.GetGenericArguments().Length, ((DebugType)constructor.DeclaringType).GenericArgumentsAsCorDebugType,
382						(uint)constructorArgsCorDebug.Length, constructorArgsCorDebug);
383				}
384			);
385		}
386		
387		#region Convenience methods
388		
389		public static Value NewObjectNoConstructor(DebugType debugType)
390		{
391			return AsyncNewObjectNoConstructor(debugType).WaitForResult();
392		}
393		
394		#endregion
395		
396		public static Eval AsyncNewObjectNoConstructor(DebugType debugType)
397		{
398			return new Eval(
399				debugType.AppDomain,
400				"New object: " + debugType.FullName,
401				delegate(Eval eval) {
402					eval.CorEval2.NewParameterizedObjectNoConstructor(debugType.CorType.GetClass(), (uint)debugType.GetGenericArguments().Length, debugType.GenericArgumentsAsCorDebugType);
403				}
404			);
405		}
406		
407		static ICorDebugValue[] ValuesAsCorDebug(Value[] values)
408		{
409			ICorDebugValue[] valuesAsCorDebug = new ICorDebugValue[values.Length];
410			for(int i = 0; i < values.Length; i++) {
411				valuesAsCorDebug[i] = values[i].CorValue;
412			}
413			return valuesAsCorDebug;
414		}
415	}
416}