Subversion Repositories wimsdev

Rev

Rev 14283 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /**
  2.  * Debugger support for skulpt module
  3.  */
  4.  
  5. var Sk = Sk || {}; //jshint ignore:line
  6.  
  7. function hasOwnProperty(obj, prop) {
  8.     var proto = obj.constructor.prototype;
  9.     return (prop in obj) &&
  10.         (!(prop in proto) || proto[prop] !== obj[prop]);
  11. }
  12.  
  13. Sk.Breakpoint = function(filename, lineno, colno) {
  14.     this.filename = filename;
  15.     this.lineno = lineno;
  16.     this.colno = colno;
  17.     this.enabled = true;
  18.     this.ignore_count = 0;
  19. };
  20.  
  21. Sk.Debugger = function(filename, output_callback) {
  22.     this.dbg_breakpoints = {};
  23.     this.tmp_breakpoints = {};
  24.     this.suspension_stack = [];
  25.     this.current_suspension = -1;
  26.     this.eval_callback = null;
  27.     this.suspension = null;
  28.     this.output_callback = output_callback;
  29.     this.step_mode = false;
  30.     this.filename = filename;
  31. };
  32.  
  33. Sk.Debugger.prototype.print = function(txt) {
  34.     if (this.output_callback != null) {
  35.         this.output_callback.print(txt);
  36.     }
  37. };
  38.  
  39. Sk.Debugger.prototype.get_source_line = function(lineno) {
  40.     if (this.output_callback != null) {
  41.         return this.output_callback.get_source_line(lineno);
  42.     }
  43.    
  44.     return "";
  45. };
  46.  
  47. Sk.Debugger.prototype.move_up_the_stack = function() {
  48.     this.current_suspension = Math.min(this.current_suspension + 1, this.suspension_stack.length - 1);
  49. };
  50.  
  51. Sk.Debugger.prototype.move_down_the_stack = function() {
  52.     this.current_suspension = Math.max(this.current_suspension - 1, 0);
  53. };
  54.  
  55. Sk.Debugger.prototype.enable_step_mode = function() {
  56.     this.step_mode = true;
  57. };
  58.  
  59. Sk.Debugger.prototype.disable_step_mode = function() {
  60.     this.step_mode = false;
  61. };
  62.  
  63. Sk.Debugger.prototype.get_suspension_stack = function() {
  64.     return this.suspension_stack;
  65. };
  66.  
  67. Sk.Debugger.prototype.get_active_suspension = function() {
  68.     if (this.suspension_stack.length === 0) {
  69.         return null;
  70.     }
  71.  
  72.     return this.suspension_stack[this.current_suspension];
  73. };
  74.  
  75. Sk.Debugger.prototype.generate_breakpoint_key = function(filename, lineno, colno) {
  76.     var key = filename + "-" + lineno;
  77.     return key;
  78. };
  79.  
  80. Sk.Debugger.prototype.check_breakpoints = function(filename, lineno, colno, globals, locals) {
  81.     // If Step mode is enabled then ignore breakpoints since we will just break
  82.     // at every line.
  83.     if (this.step_mode === true) {
  84.         return true;
  85.     }
  86.    
  87.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  88.     if (hasOwnProperty(this.dbg_breakpoints, key) &&
  89.         this.dbg_breakpoints[key].enabled === true) {
  90.         var bp = null;
  91.         if (hasOwnProperty(this.tmp_breakpoints, key)) {
  92.             delete this.dbg_breakpoints[key];
  93.             delete this.tmp_breakpoints[key];
  94.             return true;
  95.         }
  96.        
  97.         this.dbg_breakpoints[key].ignore_count -= 1;
  98.         this.dbg_breakpoints[key].ignore_count = Math.max(0, this.dbg_breakpoints[key].ignore_count);
  99.        
  100.         bp = this.dbg_breakpoints[key];
  101.         if (bp.ignore_count === 0) {
  102.             return true;
  103.         } else {
  104.             return false;
  105.         }
  106.     }
  107.     return false;
  108. };
  109.  
  110. Sk.Debugger.prototype.get_breakpoints_list = function() {
  111.     return this.dbg_breakpoints;
  112. };
  113.  
  114. Sk.Debugger.prototype.disable_breakpoint = function(filename, lineno, colno) {
  115.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  116.    
  117.     if (hasOwnProperty(this.dbg_breakpoints, key)) {
  118.         this.dbg_breakpoints[key].enabled = false;
  119.     }
  120. };
  121.  
  122. Sk.Debugger.prototype.enable_breakpoint = function(filename, lineno, colno) {
  123.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  124.    
  125.     if (hasOwnProperty(this.dbg_breakpoints, key)) {
  126.         this.dbg_breakpoints[key].enabled = true;
  127.     }
  128. };
  129.  
  130. Sk.Debugger.prototype.clear_breakpoint = function(filename, lineno, colno) {
  131.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  132.     if (hasOwnProperty(this.dbg_breakpoints, key)) {
  133.         delete this.dbg_breakpoints[key];
  134.         return null;
  135.     } else {
  136.         return "Invalid breakpoint specified: " + filename + " line: " + lineno;
  137.     }
  138. };
  139.  
  140. Sk.Debugger.prototype.clear_all_breakpoints = function() {
  141.     this.dbg_breakpoints = {};
  142.     this.tmp_breakpoints = {};
  143. };
  144.  
  145. Sk.Debugger.prototype.set_ignore_count = function(filename, lineno, colno, count) {
  146.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  147.     if (hasOwnProperty(this.dbg_breakpoints, key)) {
  148.         var bp = this.dbg_breakpoints[key];
  149.         bp.ignore_count = count;
  150.     }
  151. };
  152.  
  153. Sk.Debugger.prototype.set_condition = function(filename, lineno, colno, lhs, cond, rhs) {
  154.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  155.     var bp;
  156.     if (hasOwnProperty(this.dbg_breakpoints, key)) {
  157.         // Set a new condition
  158.         bp = this.dbg_breakpoints[key];
  159.     } else {
  160.         bp = new Sk.Breakpoint(filename, lineno, colno);
  161.     }
  162.    
  163.     bp.condition = new Sk.Condition(lhs, cond, rhs);
  164.     this.dbg_breakpoints[key] = bp;
  165. };
  166.  
  167. Sk.Debugger.prototype.print_suspension_info = function(suspension) {
  168.     var filename = suspension.filename;
  169.     var lineno = suspension.lineno;
  170.     var colno = suspension.colno;
  171.     this.print("Hit Breakpoint at <" + filename + "> at line: " + lineno + " column: " + colno + "\n");
  172.     this.print("----------------------------------------------------------------------------------\n");
  173.     this.print(" ==> " + this.get_source_line(lineno - 1) + "\n");
  174.     this.print("----------------------------------------------------------------------------------\n");
  175. };
  176.  
  177. Sk.Debugger.prototype.set_suspension = function(suspension) {
  178.     var parent = null;
  179.     if (!hasOwnProperty(suspension, "filename") && suspension.child instanceof Sk.misceval.Suspension) {
  180.         suspension = suspension.child;
  181.     }
  182.        
  183.     // Pop the last suspension of the stack if there is more than 0
  184.     if (this.suspension_stack.length > 0) {
  185.         this.suspension_stack.pop();
  186.         this.current_suspension -= 1;
  187.     }
  188.    
  189.     // Unroll the stack to get each suspension.
  190.     while (suspension instanceof Sk.misceval.Suspension) {
  191.         parent = suspension;
  192.         this.suspension_stack.push(parent);
  193.         this.current_suspension += 1;
  194.         suspension = suspension.child;
  195.     }
  196.  
  197.     suspension = parent;
  198.    
  199.     this.print_suspension_info(suspension);
  200. };
  201.  
  202. Sk.Debugger.prototype.add_breakpoint = function(filename, lineno, colno, temporary) {
  203.     var key = this.generate_breakpoint_key(filename, lineno, colno);
  204.     this.dbg_breakpoints[key] = new Sk.Breakpoint(filename, lineno, colno);
  205.     if (temporary) {
  206.         this.tmp_breakpoints[key] = true;
  207.     }
  208. };
  209.  
  210. Sk.Debugger.prototype.suspension_handler = function(susp) {
  211.     return new Promise(function(resolve, reject) {
  212.         try {
  213.             resolve(susp.resume());
  214.         } catch(e) {
  215.             reject(e);
  216.         }
  217.     });
  218. };
  219.  
  220. Sk.Debugger.prototype.resume = function() {
  221.     // Reset the suspension stack to the topmost
  222.     this.current_suspension = this.suspension_stack.length - 1;
  223.    
  224.     if (this.suspension_stack.length === 0) {
  225.         this.print("No running program");
  226.     } else {
  227.         var promise = this.suspension_handler(this.get_active_suspension());
  228.         promise.then(this.success.bind(this), this.error.bind(this));
  229.     }
  230. };
  231.  
  232. Sk.Debugger.prototype.pop_suspension_stack = function() {
  233.     this.suspension_stack.pop();
  234.     this.current_suspension -= 1;
  235. };
  236.  
  237. Sk.Debugger.prototype.success = function(r) {
  238.     if (r instanceof Sk.misceval.Suspension) {
  239.         this.set_suspension(r);
  240.     } else {
  241.         if (this.suspension_stack.length > 0) {
  242.             // Current suspension needs to be popped of the stack
  243.             this.pop_suspension_stack();
  244.            
  245.             if (this.suspension_stack.length === 0) {
  246.                 this.print("Program execution complete");
  247.                 return;
  248.             }
  249.            
  250.             var parent_suspension = this.get_active_suspension();
  251.             // The child has completed the execution. So override the child's resume
  252.             // so we can continue the execution.
  253.             parent_suspension.child.resume = function() {
  254.                 return r;
  255.             };
  256.             this.resume();
  257.         } else {
  258.             this.print("Program execution complete");
  259.         }
  260.     }
  261. };
  262.  
  263. Sk.Debugger.prototype.error = function(e) {
  264.     this.print("Traceback (most recent call last):");
  265.     for (var idx = 0; idx < e.traceback.length; ++idx) {
  266.         this.print("  File \"" + e.traceback[idx].filename + "\", line " + e.traceback[idx].lineno + ", in <module>");
  267.         var code = this.get_source_line(e.traceback[idx].lineno - 1);
  268.         code = code.trim();
  269.         code = "    " + code;
  270.         this.print(code);
  271.     }
  272.    
  273.     var err_ty = e.constructor.tp$name;
  274.     for (idx = 0; idx < e.args.v.length; ++idx) {
  275.         this.print(err_ty + ": " + e.args.v[idx].v);
  276.     }
  277. };
  278.  
  279. Sk.Debugger.prototype.asyncToPromise = function(suspendablefn, suspHandlers, debugger_obj) {
  280.     return new Promise(function(resolve, reject) {
  281.         try {
  282.             var r = suspendablefn();
  283.  
  284.             (function handleResponse (r) {
  285.                 try {
  286.                     while (r instanceof Sk.misceval.Suspension) {
  287.                         debugger_obj.set_suspension(r);
  288.                         return;
  289.                     }
  290.  
  291.                     resolve(r);
  292.                 } catch(e) {
  293.                     reject(e);
  294.                 }
  295.             })(r);
  296.  
  297.         } catch (e) {
  298.             reject(e);
  299.         }
  300.     });
  301. };
  302.  
  303. Sk.Debugger.prototype.execute = function(suspendablefn, suspHandlers) {
  304.     var r = suspendablefn();
  305.    
  306.     if (r instanceof Sk.misceval.Suspension) {
  307.         this.suspensions.concat(r);
  308.         this.eval_callback(r);
  309.     }
  310. };
  311.  
  312. goog.exportSymbol("Sk.Debugger", Sk.Debugger);
  313.