using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Scripting.Hosting; using IronPython.Hosting; using System.Threading; using System.Windows.Forms; using System.Reflection; using System.ComponentModel; namespace IronPythonDebugger { public class IronPythonDebugger : IIronPythonDebugger { private ScriptEngine _engine; private ScriptScope _scope; private string _source; private string _debugSource; private ScriptSource _debugScriptSource; private Dictionary<int, bool> _breakpoints; private Thread _debugThread; private int _currentLine = 0;//从1开始计算 private int _logicStartLine;//从1开始计算 private ScriptBackgroundExecute _backgroundExecute; private bool _debugging = false; private int _sourceLineCount; private const string BACKGROUND_BREAK_CODE = "_backgroundExecute.Break()"; private Action<int> _breakCallback; private int _nextBreakLine; private bool _debugThreadSleep = false; private AsyncOperation _asyncOp; private Action _exec; private SendOrPostCallback _onBreakCallback; private void Execute() { try { _debugScriptSource.Execute(_scope); } catch (DebugStopException) { } catch (ThreadAbortException) { } catch (Exception e) { throw new Exception(e.Message); } finally { Stop(); } } private void BackgroundBreakCallback() { if (!_debugging) { throw new DebugStopException(); } _currentLine++; if (_currentLine == _nextBreakLine) { _debugThreadSleep = true; //if (_breakCallback != null) //{ // _breakCallback(_currentLine); //} _asyncOp.Post(_onBreakCallback, _currentLine); WaitDebugThreadContinue(); } } private void OnBreakCallback(object lineNumber) { if (Break != null) { Break(this, new BreakEventArgs((int)lineNumber)); } else if (_breakCallback != null) { _breakCallback((int)lineNumber); } } private void WaitDebugThreadContinue() { while (_debugThreadSleep) { Thread.Sleep(100); } } private void AddBackgroundBreakCode() { string[] lines = _source.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); _sourceLineCount = lines.Length; _logicStartLine = GetLogicStartLine(lines);//获取第一行逻辑代码的行号 _debugSource = string.Empty; for (int i = 0; i < _sourceLineCount; i++) { if (!string.IsNullOrEmpty(_debugSource)) { _debugSource += Environment.NewLine; } if (i >= _logicStartLine - 1) { _debugSource += BACKGROUND_BREAK_CODE + Environment.NewLine; } _debugSource += lines[i]; } } ////import clr,sys ////clr.AddReference('TestClass') ////clr.AddReference('System.Windows.Forms') ////from TestClass import * ////from System.Windows.Forms import * ////c1 = Class1() ////c1.Name = "c1" ////MessageBox.Show(c1.Name) ////child = Class1() ////child.Name = "child1" ////c1.Child = child ////MessageBox.Show(c1.Child.Name) private int GetLogicStartLine(string[] lines) { int startLine = 1; for (int i = 0; i < lines.Length;i++ ) { string line = lines[i].ToLower(); if (line.IndexOf("import") < 0 && line.IndexOf("clr") < 0 && line.IndexOf("from") < 0) { startLine = i + 1; break; } } return startLine; } private int GetNextBreakLine(int currentLine) { int next = -1; _breakpoints.OrderBy(breakpoint => breakpoint.Key); foreach (KeyValuePair<int, bool> breakpoint in _breakpoints) { if (breakpoint.Value && breakpoint.Key > currentLine) { next = breakpoint.Key; break; } } return next; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public event Action<object, BreakEventArgs> Break; public ScriptEngine Engine { get { return _engine; } } public ScriptScope Scope { get { return _scope; } } public string Source { get { return _source; } } public Action<int> BreakCallback { get { return _breakCallback; } set { _breakCallback = value; } } public IronPythonDebugger() { _engine = Python.CreateEngine(); _scope = _engine.CreateScope(); _scope.SetVariable("_backgroundExecute", _backgroundExecute); _breakpoints = new Dictionary<int, bool>(); _backgroundExecute = new ScriptBackgroundExecute(BackgroundBreakCallback); _exec = new Action(Execute); _onBreakCallback = new SendOrPostCallback(OnBreakCallback); } public void InitialDebugger() { if (_debugging) { throw new Exception("调试器正在调试中!"); } _scope = _engine.CreateScope(); _scope.SetVariable("_backgroundExecute", _backgroundExecute); _breakpoints.Clear(); } public void InitialDebugger(List<int> breakpoints) { InitialDebugger(); foreach (int breakpoint in breakpoints) { _breakpoints[breakpoint] = true; } } public void ClearBreakpoints() { _breakpoints.Clear(); } public void Start(string source) { if (_debugging) { throw new Exception("调试器正在调试中!"); } _source = source; AddBackgroundBreakCode(); _debugScriptSource = _engine.CreateScriptSourceFromString(_debugSource); _currentLine = _logicStartLine - 1; _debugThreadSleep = false; _nextBreakLine = GetNextBreakLine(0); //_debugThread = new Thread(Execute); //_debugThread.Start(); _asyncOp = AsyncOperationManager.CreateOperation(1); _exec.BeginInvoke(null, null); _debugging = true; } public void Stop() { if (_debugging) { _debugging = false; _debugThreadSleep = false; //if (_debugThread != null && _debugThread.IsAlive) //{ // _debugThread.Abort(); //} } } public void AddBreakpoint(int line) { _breakpoints[line] = true; } public void AddBreakpoints(List<int> lines) { foreach (int line in lines) { AddBreakpoint(line); } } public void DeleteBreakpoint(int line) { _breakpoints[line] = false; } public void DeleteBreakpoints(List<int> lines) { foreach (int line in lines) { DeleteBreakpoint(line); } } public void StepOver() { if (!_debugging) { throw new Exception("调试器未开始调试!"); } _nextBreakLine++; _debugThreadSleep = false; } public void StepInto() { MessageBox.Show("暂不支持逐语句调试!"); } public void StepOut() { MessageBox.Show("暂不支持跳出调试!"); } public void Continue() { if (!_debugging) { throw new Exception("调试器未开始调试!"); } _nextBreakLine = GetNextBreakLine(_currentLine); _debugThreadSleep = false; } public string GetValueAsString(string variable) { string value = string.Empty; try { string[] names = variable.Split('.'); object obj = _scope.GetVariable(names[0]); for (int i = 1; i < names.Length; i++) { Type type = obj.GetType(); PropertyInfo pi = type.GetProperty(names[i]); obj = pi.GetValue(obj, null); } value = obj.ToString(); } catch (Exception e) { throw new Exception(e.Message); } return value; } public bool IsDebugging() { return _debugging; } } public class BreakEventArgs : EventArgs { public int LineNumber; public BreakEventArgs(int lineNumber) { this.LineNumber = lineNumber; } } public class DebugStopException : Exception { } }