[debugger] setBreakpoint, clearBreakpoint, fix reqSource error handling, show breakpo...
authorFedor Indutny <fedor.indutny@gmail.com>
Wed, 14 Sep 2011 09:24:46 +0000 (16:24 +0700)
committerFedor Indutny <fedor.indutny@gmail.com>
Wed, 14 Sep 2011 16:05:04 +0000 (23:05 +0700)
lib/_debugger.js

index a15a176..55140db 100644 (file)
@@ -149,6 +149,7 @@ function Client() {
   this.currentSource = null;
   this.handles = {};
   this.scripts = {};
+  this.breakpoints = [];
 
   // Note that 'Protocol' requires strings instead of Buffers.
   socket.setEncoding('utf8');
@@ -432,6 +433,17 @@ Client.prototype.setBreakpoint = function(req, cb) {
   });
 };
 
+Client.prototype.clearBreakpoint = function(req, cb) {
+  var req = {
+    command: 'clearbreakpoint',
+    arguments: req
+  };
+
+  this.req(req, function(res) {
+    if (cb) cb(res);
+  });
+};
+
 Client.prototype.reqSource = function(from, to, cb) {
   var req = {
     command: 'source',
@@ -440,7 +452,7 @@ Client.prototype.reqSource = function(from, to, cb) {
   };
 
   this.req(req, function(res) {
-    if (cb) cb(res.body);
+    if (cb) cb(!res.success && (res.message || true), res.body);
   });
 };
 
@@ -695,7 +707,9 @@ function Interface() {
         'next': 'n',
         'step': 's',
         'out': 'o',
-        'backtrace': 'bt'
+        'backtrace': 'bt',
+        'setBreakpoint': 'sb',
+        'clearBreakpoint': 'cb'
       };
 
   function defineProperty(key, protoKey) {
@@ -860,12 +874,12 @@ function intChars(n) {
 }
 
 
-function leftPad(n) {
+function leftPad(n, prefix) {
   var s = n.toString();
   var nchars = intChars(n);
   var nspaces = nchars - s.length;
   for (var i = 0; i < nspaces; i++) {
-    s = ' ' + s;
+    s = (prefix || ' ') + s;
   }
   return s;
 }
@@ -926,8 +940,10 @@ Interface.prototype.list = function() {
       to = client.currentSourceLine + delta + 1;
 
   self.pause();
-  client.reqSource(from, to, function(res) {
-    if (!res.source) return self.error('You can\'t list source code right now');
+  client.reqSource(from, to, function(err, res) {
+    if (err || !res) {
+      return self.error('You can\'t list source code right now');
+    }
 
     var lines = res.source.split('\n');
     for (var i = 0; i < lines.length; i++) {
@@ -941,17 +957,22 @@ Interface.prototype.list = function() {
         lines[i] = lines[i].slice(wrapper.length);
       }
 
+      var breakpoint = client.breakpoints.some(function(bp) {
+        return bp.script === client.currentScript &&
+               bp.line == lineno;
+      });
+
       if (lineno == 1 + client.currentSourceLine) {
-        var nchars = intChars(lineno);
-        var pointer = '';
-        for (var j = 0; j < nchars - 1; j++) {
+        var nchars = intChars(lineno),
+            pointer = breakpoint ? '*' : '=';
+        for (var j = 1; j < nchars - 1; j++) {
           pointer += '=';
         }
         pointer += '>';
-        self.print(pointer + ' ' + SourceUnderline(lines[i],
-                                                   client.currentSourceColumn));
+        self.print(pointer + ' ' +
+                   SourceUnderline(lines[i], client.currentSourceColumn));
       } else {
-        self.print(leftPad(lineno) + ' ' + lines[i]);
+        self.print(leftPad(lineno, breakpoint && '*') + ' ' + lines[i]);
       }
     }
     self.resume();
@@ -1009,6 +1030,7 @@ Interface.prototype.scripts = function(displayNatives) {
           !script.isNative) {
         scripts.push(
           (script.name == client.currentScript ? '* ' : '  ') +
+          id + ': ' +
           require('path').basename(script.name)
         );
       }
@@ -1060,18 +1082,91 @@ Interface.prototype.out = Interface.stepGenerator('out', 1);
 
 // Add breakpoint
 Interface.prototype.setBreakpoint = function(script, line, condition) {
+  if (!this.requireConnection()) return;
+
+  var self = this,
+      scriptId,
+      ambiguous;
+
+  if (!this.client.scripts[script]) {
+    Object.keys(this.client.scripts).forEach(function(id) {
+      if (self.client.scripts[id].name.indexOf(script) !== -1) {
+        if (scriptId) {
+          ambiguous = true;
+        }
+        scriptId = id;
+      }
+    });
+  } else {
+    scriptId = script;
+  }
+
+  if (!scriptId) return this.error('Script : ' + script + ' not found');
+  if (ambiguous) return this.error('Script name is ambiguous');
+  if (line <= 0) return this.error('Line should be a positive value');
+
+  var req = {
+    type: 'scriptId',
+    target: scriptId,
+    line: line - 1,
+    condition: condition
+  };
+
+  self.pause();
+  self.client.setBreakpoint(req, function(res) {
+    if (res.success) {
+      self.list(5);
+      self.client.breakpoints.push({
+        id: res.body.breakpoint,
+        scriptId: scriptId,
+        script: self.client.scripts[scriptId].name,
+        line: line
+      });
+
+    } else {
+      self.print(req.message || 'error!');
+    }
+    self.resume();
+  });
+};
+
+// Clear breakpoint
+Interface.prototype.clearBreakpoint = function(script, line) {
+  if (!this.requireConnection()) return;
+
+  var ambiguous,
+      breakpoint,
+      index;
+
+  this.client.breakpoints.some(function(bp, i) {
+    if (bp.scriptId === script || bp.script.indexOf(script) !== -1) {
+      if (index !== undefined) {
+        ambiguous = true;
+      }
+      if (bp.line === line) {
+        index = i;
+        breakpoint = bp.id;
+        return true;
+      }
+    }
+  });
+
+  if (ambiguous) return this.error('Script name is ambiguous');
+
+  if (breakpoint === undefined) {
+    return this.error('Script : ' + script + ' not found');
+  }
+
   var self = this,
       req = {
-        type: 'script',
-        target: script,
-        line: line,
-        condition: condition
+        breakpoint: breakpoint
       };
 
   self.pause();
-  self.client.setBreakpoint(req, function(res) {
+  self.client.clearBreakpoint(req, function(res) {
     if (res.success) {
-      self.print('ok');
+      self.client.breakpoints = self.client.breakpoints.splice(index, -1);
+      self.list(5);
     } else {
       self.print(req.message || 'error!');
     }