[debugger] port all commands
authorFedor Indutny <fedor.indutny@gmail.com>
Wed, 7 Sep 2011 10:31:43 +0000 (17:31 +0700)
committerFedor Indutny <fedor.indutny@gmail.com>
Thu, 8 Sep 2011 19:06:06 +0000 (02:06 +0700)
lib/_debugger.js

index 1e942dd..f79f42b 100644 (file)
@@ -270,6 +270,29 @@ Client.prototype.reqLookup = function(refs, cb) {
   });
 };
 
+Client.prototype.reqScopes = function(cb) {
+  var self = this,
+      req = {
+        command: 'scopes',
+        arguments: {}
+      };
+  this.req(req, function(res) {
+    if (!res.success) return cb(Error(res.message) || true);
+    var refs = res.body.scopes.map(function(scope) {
+      return scope.object.ref;
+    });
+
+    self.reqLookup(refs, function(res) {
+      var globals = Object.keys(res.body).map(function(key) {
+        return res.body[key].properties.map(function(prop) {
+          return prop.name;
+        });
+      });
+      cb(null, globals.reverse());
+    });
+  });
+};
+
 // This is like reqEval, except it will look up the expression in each of the
 // scopes associated with the current frame.
 Client.prototype.reqEval = function(expression, cb) {
@@ -617,7 +640,8 @@ function Interface() {
 
   // Lift all instance methods to repl context
   var proto = Interface.prototype,
-      ignored = ['pause', 'resume', 'exitRepl'];
+      ignored = ['pause', 'resume', 'exitRepl', 'handleBreak',
+                 'requireConnection', 'killChild', 'trySpawn'];
 
   for (var i in proto) {
     if (proto.hasOwnProperty(i) && ignored.indexOf(i) === -1) {
@@ -730,9 +754,13 @@ Interface.prototype.restart = function() {
   }, 1000);
 };
 
-Interface.prototype.version = function() {
-  if (!this.child) throw Error('App isn\'t running... Try `run()` instead');
+Interface.prototype.requireConnection = function() {
+  if (!this.client) throw Error('App isn\'t running... Try `run()` instead');
+};
+
 
+Interface.prototype.version = function() {
+  this.requireConnection();
   var self = this;
 
   this.pause();
@@ -742,6 +770,161 @@ Interface.prototype.version = function() {
   });
 };
 
+Interface.prototype.list = function() {
+  this.requireConnection();
+
+  var self = this,
+      client = this.client,
+      from = client.currentSourceLine - 5,
+      to = client.currentSourceLine + 5;
+
+  self.pause();
+  client.reqSource(from, to, function(res) {
+    var lines = res.source.split('\n');
+    for (var i = 0; i < lines.length; i++) {
+      var lineno = res.fromLine + i + 1;
+      if (lineno < from || lineno > to) continue;
+
+      if (lineno == 1) {
+        // The first line needs to have the module wrapper filtered out of
+        // it.
+        var wrapper = require('module').wrapper[0];
+        lines[i] = lines[i].slice(wrapper.length);
+      }
+
+      if (lineno == 1 + client.currentSourceLine) {
+        var nchars = intChars(lineno);
+        var pointer = '';
+        for (var j = 0; j < nchars - 1; j++) {
+          pointer += '=';
+        }
+        pointer += '>';
+        console.log(pointer + ' ' + lines[i]);
+      } else {
+        console.log(leftPad(lineno) + ' ' + lines[i]);
+      }
+    }
+    self.resume();
+  });
+};
+
+Interface.prototype.backtrace = function() {
+  this.requireConnection();
+
+  var self = this,
+      client = this.client;
+
+  self.pause();
+  client.fullTrace(function(bt) {
+    if (bt.totalFrames == 0) {
+      console.log('(empty stack)');
+    } else {
+      var text = '';
+      var firstFrameNative = bt.frames[0].script.isNative;
+      for (var i = 0; i < bt.frames.length; i++) {
+        var frame = bt.frames[i];
+        if (!firstFrameNative && frame.script.isNative) break;
+
+        text += '#' + i + ' ';
+        if (frame.func.inferredName && frame.func.inferredName.length > 0) {
+          text += frame.func.inferredName + ' ';
+        }
+        text += require('path').basename(frame.script.name) + ':';
+        text += (frame.line + 1) + ':' + (frame.column + 1);
+        text += '\n';
+      }
+
+      process.stdout.write(text);
+    }
+    self.resume();
+  });
+};
+
+// argument full tells if it should display internal node scripts or not
+Interface.prototype.scripts = function(displayNatives) {
+  this.requireConnection();
+
+  var client = this.client;
+  var text = '';
+
+  this.pause();
+  for (var id in client.scripts) {
+    var script = client.scripts[id];
+    if (typeof script == 'object' && script.name) {
+      if (displayNatives ||
+          script.name == client.currentScript ||
+          !script.isNative) {
+        text += script.name == client.currentScript ? '* ' : '  ';
+        text += require('path').basename(script.name) + '\n';
+      }
+    }
+  }
+  process.stdout.write(text);
+  this.resume();
+};
+
+Interface.prototype.cont = function() {
+  this.requireConnection();
+  this.pause();
+
+  var self = this;
+  this.client.reqContinue(function() {
+    self.resume();
+  });
+};
+
+Interface.prototype.next = function() {
+  this.requireConnection();
+
+  this.pause();
+
+  var self = this;
+  this.client.step('next', 1, function(res) {
+    self.resume();
+  });
+};
+
+Interface.prototype.step = function() {
+  this.requireConnection();
+
+  this.pause();
+
+  var self = this;
+  this.client.step('in', 1, function(res) {
+    self.resume();
+  });
+};
+
+Interface.prototype.out = function() {
+  this.requireConnection();
+
+  this.pause();
+
+  var self = this;
+  this.client.step('out', 1, function(res) {
+    self.resume();
+  });
+};
+
+Interface.prototype.breakpoints = function() {
+  this.requireConnection();
+  this.pause();
+  var self = this;
+  this.client.listbreakpoints(function(res) {
+    if (res.success) {
+      console.log(res.body);
+    } else {
+      throw Error(res.message || 'Some error happened');
+    }
+    self.resume();
+  });
+};
+
+Interface.prototype.kill = function() {
+  if (!this.child) return;
+  this.killChild();
+};
+
 Interface.prototype.repl = function() {
   if (!this.child) throw Error('App isn\'t running... Try `run()` instead');
 
@@ -755,12 +938,24 @@ Interface.prototype.repl = function() {
 
   var client = this.client;
 
+  // Save old eval
   this.repl._eval = this.repl.eval;
+
+  // Set new
   this.repl.eval = function(code, context, filename, callback) {
+    if (code === '.scope') {
+      client.reqScopes(callback);
+      return;
+    }
+
     client.reqEval(code, function(res) {
       if (!res.success) {
         if (res.message) {
-          callback(new Error(res.message));
+          if (/SyntaxError/.test(res.message)) {
+            callback(new SyntaxError(res.message));
+          } else {
+            callback(new Error(res.message));
+          }
         } else {
           callback(null);
         }
@@ -772,6 +967,7 @@ Interface.prototype.repl = function() {
       });
     });
   };
+  this.repl.context = {};
 
   this.repl.prompt = '> ';
   this.repl.rli.setPrompt('> ');
@@ -782,193 +978,11 @@ Interface.prototype.exitRepl = function() {
   this.debugRepl = false;
   this.repl.eval = this.repl._eval;
   this.repl.context = this.context;
-  this.repl.prompt = 'debug>';
-  this.repl.rli.setPrompt('debug>');
+  this.repl.prompt = 'debug> ';
+  this.repl.rli.setPrompt('debug> ');
   this.repl.displayPrompt();
 };
 
-Interface.prototype.handleCommand = function(cmd) {
-  var self = this;
-
-  var client = this.client;
-  var term = this.term;
-
-  if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') {
-    self._lastCommand = null;
-    self.tryQuit();
-
-  } else if (/^r(un)?/.test(cmd)) {
-    // DONE
-  } else if (/^help/.test(cmd)) {
-    // DONE
-  } else if ('version' == cmd) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    client.reqVersion(function(v) {
-      console.log(v);
-    });
-
-  } else if (/info +breakpoints/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    client.listbreakpoints(function(res) {
-      console.log(res);
-    });
-
-
-  } else if ('l' == cmd || 'list' == cmd) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-
-    var from = client.currentSourceLine - 5;
-    var to = client.currentSourceLine + 5;
-
-    client.reqSource(from, to, function(res) {
-      var lines = res.source.split('\n');
-      for (var i = 0; i < lines.length; i++) {
-        var lineno = res.fromLine + i + 1;
-        if (lineno < from || lineno > to) continue;
-
-        if (lineno == 1) {
-          // The first line needs to have the module wrapper filtered out of
-          // it.
-          var wrapper = require('module').wrapper[0];
-          lines[i] = lines[i].slice(wrapper.length);
-        }
-
-        if (lineno == 1 + client.currentSourceLine) {
-          var nchars = intChars(lineno);
-          var pointer = '';
-          for (var j = 0; j < nchars - 1; j++) {
-            pointer += '=';
-          }
-          pointer += '>';
-          console.log(pointer + ' ' + lines[i]);
-        } else {
-          console.log(leftPad(lineno) + ' ' + lines[i]);
-        }
-      }
-    });
-
-  } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-
-    client.fullTrace(function(bt) {
-      if (bt.totalFrames == 0) {
-        console.log('(empty stack)');
-      } else {
-        var text = '';
-        var firstFrameNative = bt.frames[0].script.isNative;
-        for (var i = 0; i < bt.frames.length; i++) {
-          var frame = bt.frames[i];
-          if (!firstFrameNative && frame.script.isNative) break;
-
-          text += '#' + i + ' ';
-          if (frame.func.inferredName && frame.func.inferredName.length > 0) {
-            text += frame.func.inferredName + ' ';
-          }
-          text += require('path').basename(frame.script.name) + ':';
-          text += (frame.line + 1) + ':' + (frame.column + 1);
-          text += '\n';
-        }
-
-        console.log(text);
-      }
-    });
-
-  } else if (cmd == 'scripts' || cmd == 'scripts full') {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    self.printScripts(cmd.indexOf('full') > 0);
-
-  } else if (/^c(ontinue)?/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-
-    self.pause();
-    client.reqContinue(function() {
-      self.resume();
-    });
-
-  } else if (/^k(ill)?/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    // kill
-    if (self.child) {
-      self.killQuestion(function(yes) {
-        if (yes) {
-          self.killChild();
-        } else {
-          self._lastCommand = null;
-        }
-      });
-    } else {
-    }
-
-  } else if (/^next/.test(cmd) || /^n/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    client.step('next', 1, function(res) {
-      // Wait for break point. (disable raw mode?)
-    });
-
-  } else if (/^step/.test(cmd) || /^s/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    client.step('in', 1, function(res) {
-      // Wait for break point. (disable raw mode?)
-    });
-
-  } else if (/^print/.test(cmd) || /^p/.test(cmd)) {
-    if (!client) {
-      self.printNotConnected();
-      return;
-    }
-    var i = cmd.indexOf(' ');
-    if (i < 0) {
-      console.log('print [expression]');
-    } else {
-      cmd = cmd.slice(i);
-      client.reqEval(cmd, function(res) {
-        if (!res.success) {
-          console.log(res.message);
-          return;
-        }
-
-        client.mirrorObject(res.body, function(mirror) {
-          console.log(mirror);
-        });
-      });
-    }
-
-  } else {
-    if (!/^\s*$/.test(cmd)) {
-      // If it's not all white-space print this error message.
-      console.log('Unknown command "%s". Try "help"', cmd);
-    }
-  }
-};
-
-
 
 Interface.prototype.killChild = function() {
   if (this.child) {
@@ -1009,15 +1023,19 @@ Interface.prototype.trySpawn = function(cb) {
     });
 
     client.on('close', function() {
+      self.pause()
       console.log('program terminated');
+      self.resume();
       self.client = null;
       self.killChild();
     });
   });
 
   client.on('unhandledResponse', function(res) {
+    self.pause();
     console.log('\r\nunhandled res:');
     console.log(res);
+    self.resume();
   });
 
   client.on('break', function(res) {
@@ -1046,23 +1064,6 @@ Interface.prototype.trySpawn = function(cb) {
 };
 
 
-// argument full tells if it should display internal node scripts or not
-Interface.prototype.printScripts = function(displayNatives) {
-  var client = this.client;
-  var text = '';
-  for (var id in client.scripts) {
-    var script = client.scripts[id];
-    if (typeof script == 'object' && script.name) {
-      if (displayNatives ||
-          script.name == client.currentScript ||
-          !script.isNative) {
-        text += script.name == client.currentScript ? '* ' : '  ';
-        text += require('path').basename(script.name) + '\n';
-      }
-    }
-  }
-  process.stdout.write(text);
-};