var util = require('util'), path = require('path'), net = require('net'), vm = require('vm'), repl = require('repl'), inherits = util.inherits, spawn = require('child_process').spawn; exports.start = function (argv, stdin, stdout){ argv || (argv = process.argv.slice(2)); if (_AN_Read_length('length', argv) < 1) { console.error('Usage: node debug script.js'); process.exit(1); } stdin = stdin || process.openStdin(); stdout = stdout || process.stdout; var args = ['--debug-brk'] .concat(argv), interface_ = new Interface(stdin, stdout, args); stdin.resume(); process.on('uncaughtException', function (e){ console.error("There was an internal error in Node's debugger. " + 'Please report this bug.'); console.error(e.message); console.error(e.stack); if (interface_.child) interface_.child.kill(); process.exit(1); } ); } ; _AN_Write_port('port', exports, false , 5858); function Protocol(){ this._newRes(); } _AN_Write_protocol('Protocol', exports, false , Protocol); Protocol.prototype._newRes = function (raw){ this.res = { raw: raw || '', headers: { } } ; this.state = 'headers'; this.reqSeq = 1; this.execute(''); } ; Protocol.prototype.execute = function (d){ var res = this.res; res.raw += d; switch (this.state){ case 'headers': var endHeaderIndex = res.raw.indexOf('\r\n\r\n'); if (endHeaderIndex < 0) break ; var rawHeader = res.raw.slice(0, endHeaderIndex); var endHeaderByteIndex = Buffer.byteLength(rawHeader, 'utf8'); var lines = rawHeader.split('\r\n'); for (var i = 0; i < _AN_Read_length('length', lines); i++ ){ var kv = lines[i].split(/: +/); res.headers[kv[0]] = kv[1]; } this.contentLength = + res.headers["Content-Length"] ; this.bodyStartByteIndex = endHeaderByteIndex + 4; this.state = 'body'; var len = Buffer.byteLength(res.raw, 'utf8'); if (len - this.bodyStartByteIndex < this.contentLength) { break ; } case 'body': var resRawByteLength = Buffer.byteLength(res.raw, 'utf8'); if (resRawByteLength - this.bodyStartByteIndex >= this.contentLength) { var buf = new Buffer(resRawByteLength); _AN_Call_write('write', buf, res.raw, 0, resRawByteLength, 'utf8'); res.body = buf.slice(this.bodyStartByteIndex, this.bodyStartByteIndex + this.contentLength).toString('utf8'); res.body = _AN_Read_length('length', res.body)? JSON.parse(res.body): { } ; this.onResponse(res); this._newRes(buf.slice(this.bodyStartByteIndex + this.contentLength).toString('utf8')); } break ; default : { throw new Error('Unknown state') break ; } } } ; Protocol.prototype.serialize = function (req){ req.type = 'request'; req.seq = this.reqSeq++ ; var json = JSON.stringify(req); return 'Content-Length: ' + Buffer.byteLength(json, 'utf8') + '\r\n\r\n' + json; } ; var NO_FRAME = -1; function Client(){ net.Stream.call(this); var protocol = _AN_Write_protocol('protocol', this, false , new Protocol(this)); this._reqCallbacks = [] ; var socket = this; this.currentFrame = NO_FRAME; this.currentSourceLine = -1; this.currentSource = null ; this.handles = { } ; this.scripts = { } ; this.breakpoints = [] ; socket.setEncoding('utf8'); socket.on('data', function (d){ protocol.execute(d); } ); protocol.onResponse = this._onResponse.bind(this); } inherits(Client, net.Stream); exports.Client = Client; Client.prototype._addHandle = function (desc){ if (typeof desc != 'object' || typeof desc.handle != 'number') { return ; } this.handles[desc.handle] = desc; if (desc.type == 'script') { this._addScript(desc); } } ; var natives = process.binding('natives'); Client.prototype._addScript = function (desc){ this.scripts[desc.id] = desc; if (desc.name) { desc.isNative = (_AN_Call_replace('replace', desc.name, '.js', '') in natives) || desc.name == 'node.js'; } } ; Client.prototype._removeScript = function (desc){ this.scripts[desc.id] = undefined; } ; Client.prototype._onResponse = function (res){ var cb, index = -1; this._reqCallbacks.some(function (fn, i){ if (fn.request_seq == res.body.request_seq) { cb = fn; index = i; return true ; } } ); var self = this; var handled = false ; if (res.headers.Type == 'connect') { self.reqScripts(); self.emit('ready'); handled = true ; } else if (res.body && res.body.event == 'break') { this.emit('break', res.body); handled = true ; } else if (res.body && res.body.event == 'exception') { this.emit('exception', res.body); handled = true ; } else if (res.body && res.body.event == 'afterCompile') { this._addHandle(res.body.body.script); handled = true ; } else if (res.body && res.body.event == 'scriptCollected') { this._removeScript(res.body.body.script); handled = true ; } if (cb) { this._reqCallbacks.splice(index, 1); handled = true ; var err = res.success === false && (res.message || true ) || res.body.success === false && (res.body.message || true ); cb(err, res.body && res.body.body || res.body, res); } if (!handled) this.emit('unhandledResponse', res.body); } ; Client.prototype.req = function (req, cb){ _AN_Call_write('write', this, _AN_Read_protocol('protocol', this).serialize(req)); cb.request_seq = req.seq; this._reqCallbacks.push(cb); } ; Client.prototype.reqVersion = function (cb){ cb = cb || function (){ } ; this.req({ command: 'version'} , function (err, body, res){ if (err) return cb(err); cb(null , res.body.body.V8Version, res.body.running); } ); } ; Client.prototype.reqLookup = function (refs, cb){ var self = this; var req = { command: 'lookup', arguments: { handles: refs} } ; cb = cb || function (){ } ; this.req(req, function (err, res){ if (err) return cb(err); for (var ref in res){ if (typeof res[ref] == 'object') { self._addHandle(res[ref]); } } cb(null , res); } ); } ; Client.prototype.reqScopes = function (cb){ var self = this, req = { command: 'scopes', arguments: { } } ; cb = cb || function (){ } ; this.req(req, function (err, res){ if (err) return cb(err); var refs = res.scopes.map(function (scope){ return scope.object.ref; } ); self.reqLookup(refs, function (err, res){ if (err) return cb(err); var globals = Object.keys(res).map(function (key){ return res[key].properties.map(function (prop){ return prop.name; } ); } ); cb(null , globals.reverse()); } ); } ); } ; Client.prototype.reqEval = function (expression, cb){ var self = this; if (this.currentFrame == NO_FRAME) { this.reqFrameEval(expression, NO_FRAME, cb); return ; } cb = cb || function (){ } ; this.reqBacktrace(function (err, bt){ if (err || !bt.frames) { return cb(null , { } ); } var frame = bt.frames[self.currentFrame]; var evalFrames = frame.scopes.map(function (s){ if (!s) return ; var x = bt.frames[s.index]; if (!x) return ; return x.index; } ); self._reqFramesEval(expression, evalFrames, cb); } ); } ; Client.prototype._reqFramesEval = function (expression, evalFrames, cb){ if (_AN_Read_length('length', evalFrames) == 0) { this.reqFrameEval(expression, NO_FRAME, cb); return ; } var self = this; var i = evalFrames.shift(); cb = cb || function (){ } ; this.reqFrameEval(expression, i, function (err, res){ if (!err) return cb(null , res); self._reqFramesEval(expression, evalFrames, cb); } ); } ; Client.prototype.reqFrameEval = function (expression, frame, cb){ var self = this; var req = { command: 'evaluate', arguments: { expression: expression} } ; if (frame == NO_FRAME) { req.arguments.global = true ; } else { req.arguments.frame = frame; } cb = cb || function (){ } ; this.req(req, function (err, res){ if (!err) self._addHandle(res); cb(err, res); } ); } ; Client.prototype.reqBacktrace = function (cb){ this.req({ command: 'backtrace', arguments: { inlineRefs: true } } , cb); } ; Client.prototype.reqSetExceptionBreak = function (type, cb){ this.req({ command: 'setexceptionbreak', arguments: { type: type, enabled: true } } , cb); } ; Client.prototype.reqScripts = function (cb){ var self = this; cb = cb || function (){ } ; this.req({ command: 'scripts'} , function (err, res){ if (err) return cb(err); for (var i = 0; i < _AN_Read_length('length', res); i++ ){ self._addHandle(res[i]); } cb(null ); } ); } ; Client.prototype.reqContinue = function (cb){ this.currentFrame = NO_FRAME; this.req({ command: 'continue'} , cb); } ; Client.prototype.listbreakpoints = function (cb){ this.req({ command: 'listbreakpoints'} , cb); } ; Client.prototype.setBreakpoint = function (req, cb){ req = { command: 'setbreakpoint', arguments: req} ; this.req(req, cb); } ; Client.prototype.clearBreakpoint = function (req, cb){ var req = { command: 'clearbreakpoint', arguments: req} ; this.req(req, cb); } ; Client.prototype.reqSource = function (from, to, cb){ var req = { command: 'source', fromLine: from, toLine: to} ; this.req(req, cb); } ; Client.prototype.step = function (action, count, cb){ var req = { command: 'continue', arguments: { stepaction: action, stepcount: count} } ; this.currentFrame = NO_FRAME; this.req(req, cb); } ; Client.prototype.mirrorObject = function (handle, depth, cb){ var self = this; var val; if (handle.type === 'object') { var propertyRefs = handle.properties.map(function (p){ return p.ref; } ); cb = cb || function (){ } ; this.reqLookup(propertyRefs, function (err, res){ if (err) { console.error('problem with reqLookup'); cb(null , handle); return ; } var mirror, waiting = 1; if (handle.className == 'Array') { mirror = [] ; } else if (handle.className == 'Date') { mirror = new Date(handle.value); } else { mirror = { } ; } var keyValues = [] ; handle.properties.forEach(function (prop, i){ var value = res[prop.ref]; var mirrorValue; if (value) { mirrorValue = value.value? value.value: value.text; } else { mirrorValue = '[?]'; } if (Array.isArray(mirror) && typeof prop.name != 'number') { return ; } keyValues[i] = { name: prop.name, value: mirrorValue} ; if (value && value.handle && depth > 0) { waiting++ ; self.mirrorObject(value, depth - 1, function (err, result){ if (!err) keyValues[i].value = result; waitForOthers(); } ); } } ); waitForOthers(); function waitForOthers(){ if (--waiting === 0 && cb) { keyValues.forEach(function (kv){ mirror[kv.name] = kv.value; } ); cb(null , mirror); } } ; } ); return ; } else if (handle.type === 'function') { val = function (){ } ; } else if (handle.type === 'null') { val = null ; } else if (handle.value !== undefined) { val = handle.value; } else if (handle.type === 'undefined') { val = undefined; } else { val = handle; } process.nextTick(function (){ cb(null , val); } ); } ; Client.prototype.fullTrace = function (cb){ var self = this; cb = cb || function (){ } ; this.reqBacktrace(function (err, trace){ if (err) return cb(err); if (trace.totalFrames <= 0) return cb(Error('No frames')); var refs = [] ; for (var i = 0; i < _AN_Read_length('length', trace.frames); i++ ){ var frame = trace.frames[i]; refs.push(frame.script.ref); refs.push(frame.func.ref); refs.push(frame.receiver.ref); } self.reqLookup(refs, function (err, res){ if (err) return cb(err); for (var i = 0; i < _AN_Read_length('length', trace.frames); i++ ){ var frame = trace.frames[i]; frame.script = res[frame.script.ref]; frame.func = res[frame.func.ref]; frame.receiver = res[frame.receiver.ref]; } cb(null , trace); } ); } ); } ; var commands = [['run (r)', 'cont (c)', 'next (n)', 'step (s)', 'out (o)', 'backtrace (bt)', 'setBreakpoint (sb)', 'clearBreakpoint (cb)'] , ['watch', 'unwatch', 'watchers', 'repl', 'restart', 'kill', 'list', 'scripts', 'breakOnException', 'breakpoints', 'version'] ] ; var helpMessage = 'Commands: ' + commands.map(function (group){ return group.join(', '); } ).join(',\n'); function SourceUnderline(sourceText, position, repl){ if (!sourceText) return ''; var head = sourceText.slice(0, position), tail = sourceText.slice(position); if (repl.useColors) { tail = _AN_Call_replace('replace', tail, /(.+?)([^\w]|$)/, '\u001b[32m$1\u001b[39m$2'); } return [head, tail] .join(''); } function SourceInfo(body){ var result = body.exception? 'exception in ': 'break in '; if (body.script) { if (body.script.name) { var name = body.script.name, dir = path.resolve() + '/'; if (name.indexOf(dir) === 0) { name = name.slice(_AN_Read_length('length', dir)); } result += name; } else { result += '[unnamed]'; } } result += ':'; result += body.sourceLine + 1; if (body.exception) result += '\n' + body.exception.text; return result; } function Interface(stdin, stdout, args){ var self = this, child; this.stdin = stdin; this.stdout = stdout; this.args = args; var opts = { prompt: 'debug> ', input: this.stdin, output: this.stdout, eval: this.controlEval.bind(this), useGlobal: false , ignoreUndefined: true } ; if (parseInt(process.env.NODE_NO_READLINE, 10)) { opts.terminal = false ; } if (parseInt(process.env.NODE_DISABLE_COLORS, 10)) { opts.useColors = false ; } this.repl = repl.start(opts); repl._builtinLibs.splice(repl._builtinLibs.indexOf('repl'), 1); this.repl.on('exit', function (){ process.exit(0); } ); process.on('exit', function (){ self.killChild(); } ); var proto = Interface.prototype, ignored = ['pause', 'resume', 'exitRepl', 'handleBreak', 'requireConnection', 'killChild', 'trySpawn', 'controlEval', 'debugEval', 'print', 'childPrint', 'clearline'] , shortcut = { 'run': 'r', 'cont': 'c', 'next': 'n', 'step': 's', 'out': 'o', 'backtrace': 'bt', 'setBreakpoint': 'sb', 'clearBreakpoint': 'cb', 'pause_': 'pause'} ; function defineProperty(key, protoKey){ var fn = proto[protoKey].bind(self); if (_AN_Read_length('length', proto[protoKey]) === 0) { Object.defineProperty(self.repl.context, key, { get: fn, enumerable: true , configurable: false } ); } else { self.repl.context[key] = fn; } } ; for (var i in proto){ if (Object.prototype.hasOwnProperty.call(proto, i) && ignored.indexOf(i) === -1) { defineProperty(i, i); if (shortcut[i]) defineProperty(shortcut[i], i); } } this.killed = false ; this.waiting = null ; this.paused = 0; this.context = this.repl.context; this.history = { debug: [] , control: [] } ; this.breakpoints = [] ; this._watchers = [] ; this.pause(); _AN_Call_settimeout('setTimeout', window, function (){ self.run(function (){ self.resume(); } ); } , 10); } Interface.prototype.pause = function (){ if (this.killed || this.paused++ > 0) return false ; this.repl.rli.pause(); this.stdin.pause(); } ; Interface.prototype.resume = function (silent){ if (this.killed || this.paused === 0 || --this.paused !== 0) return false ; this.repl.rli.resume(); if (silent !== true ) { this.repl.displayPrompt(); } this.stdin.resume(); if (this.waiting) { this.waiting(); this.waiting = null ; } } ; Interface.prototype.clearline = function (){ if (this.stdout.isTTY) { this.stdout.cursorTo(0); this.stdout.clearLine(1); } else { _AN_Call_write('write', this.stdout, '\b'); } } ; Interface.prototype.print = function (text, oneline){ if (this.killed) return ; this.clearline(); _AN_Call_write('write', this.stdout, typeof text === 'string'? text: util.inspect(text)); if (oneline !== true ) { _AN_Call_write('write', this.stdout, '\n'); } } ; Interface.prototype.childPrint = function (text){ this.print(text.toString().split(/\r\n|\r|\n/g).filter(function (chunk){ return chunk; } ).map(function (chunk){ return '< ' + chunk; } ).join('\n')); this.repl.displayPrompt(true ); } ; Interface.prototype.error = function (text){ this.print(text); this.resume(); } ; Interface.prototype.handleBreak = function (r){ var self = this; this.pause(); this.client.currentSourceLine = r.sourceLine; this.client.currentSourceLineText = r.sourceLineText; this.client.currentSourceColumn = r.sourceColumn; this.client.currentFrame = 0; this.client.currentScript = r.script && r.script.name; this.print(SourceInfo(r)); this.watchers(true , function (err){ if (err) return self.error(err); self.list(2); self.resume(true ); } ); } ; Interface.prototype.requireConnection = function (){ if (!this.client) { this.error('App isn\'t running... Try `run` instead'); return false ; } return true ; } ; Interface.prototype.controlEval = function (code, context, filename, callback){ try { if (this.repl.rli.history && _AN_Read_length('length', this.repl.rli.history) > 0) { if (code === '(undefined\n)') { code = '(' + this.repl.rli.history[0] + '\n)'; } } var result = vm.runInContext(code, context, filename); if (this.paused === 0) return callback(null , result); this.waiting = function (){ callback(null , result); } ; } catch (e) { callback(e); } } ; Interface.prototype.debugEval = function (code, context, filename, callback){ if (!this.requireConnection()) return ; var self = this, client = this.client; if (code === '.scope') { client.reqScopes(callback); return ; } var frame = client.currentFrame === NO_FRAME? frame: undefined; self.pause(); client.reqFrameEval(code, frame, function (err, res){ if (err) { callback(err); self.resume(true ); return ; } client.mirrorObject(res, 3, function (err, mirror){ callback(null , mirror); self.resume(true ); } ); } ); } ; function intChars(n){ if (n < 50) { return 3; } else if (n < 950) { return 4; } else if (n < 9950) { return 5; } else { return 6; } } function leftPad(n, prefix){ var s = n.toString(), nchars = intChars(n), nspaces = nchars - _AN_Read_length('length', s) - 1; prefix || (prefix = ' '); for (var i = 0; i < nspaces; i++ ){ prefix += ' '; } return prefix + s; } Interface.prototype.help = function (){ this.print(helpMessage); } ; Interface.prototype.run = function (){ var callback = arguments[0]; if (this.child) { this.error('App is already running... Try `restart` instead'); callback && callback(true ); } else { this.trySpawn(callback); } } ; Interface.prototype.restart = function (){ if (!this.requireConnection()) return ; var self = this; self.pause(); self.killChild(); _AN_Call_settimeout('setTimeout', window, function (){ self.trySpawn(); self.resume(); } , 1000); } ; Interface.prototype.version = function (){ if (!this.requireConnection()) return ; var self = this; this.pause(); this.client.reqVersion(function (err, v){ if (err) { self.error(err); } else { self.print(v); } self.resume(); } ); } ; Interface.prototype.list = function (delta){ if (!this.requireConnection()) return ; delta || (delta = 5); var self = this, client = this.client, from = client.currentSourceLine - delta + 1, to = client.currentSourceLine + delta + 1; self.pause(); client.reqSource(from, to, function (err, res){ if (err || !res) { self.error('You can\'t list source code right now'); self.resume(); return ; } var lines = res.source.split('\n'); for (var i = 0; i < _AN_Read_length('length', lines); i++ ){ var lineno = res.fromLine + i + 1; if (lineno < from || lineno > to) continue ; var current = lineno == 1 + client.currentSourceLine, breakpoint = client.breakpoints.some(function (bp){ return bp.script === client.currentScript && bp.line == lineno; } ); if (lineno == 1) { var wrapper = require('module').wrapper[0]; lines[i] = lines[i].slice(_AN_Read_length('length', wrapper)); client.currentSourceColumn -= _AN_Read_length('length', wrapper); } var line; if (current) { line = SourceUnderline(lines[i], client.currentSourceColumn, self.repl); } else { line = lines[i]; } self.print(leftPad(lineno, breakpoint && '*') + ' ' + line); } self.resume(); } ); } ; Interface.prototype.backtrace = function (){ if (!this.requireConnection()) return ; var self = this, client = this.client; self.pause(); client.fullTrace(function (err, bt){ if (err) { self.error('Can\'t request backtrace now'); self.resume(); return ; } if (bt.totalFrames == 0) { self.print('(empty stack)'); } else { var trace = [] , firstFrameNative = bt.frames[0].script.isNative; for (var i = 0; i < _AN_Read_length('length', bt.frames); i++ ){ var frame = bt.frames[i]; if (!firstFrameNative && frame.script.isNative) break ; var text = '#' + i + ' '; if (frame.func.inferredName && _AN_Read_length('length', frame.func.inferredName) > 0) { text += frame.func.inferredName + ' '; } text += path.basename(frame.script.name) + ':'; text += (frame.line + 1) + ':' + (frame.column + 1); trace.push(text); } self.print(trace.join('\n')); } self.resume(); } ); } ; Interface.prototype.scripts = function (){ if (!this.requireConnection()) return ; var client = this.client, displayNatives = arguments[0] || false , scripts = [] ; 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) { scripts.push((script.name == client.currentScript? '* ': ' ') + id + ': ' + path.basename(script.name)); } } } this.print(scripts.join('\n')); this.resume(); } ; Interface.prototype.cont = function (){ if (!this.requireConnection()) return ; this.pause(); var self = this; this.client.reqContinue(function (err){ if (err) self.error(err); self.resume(); } ); } ; Interface.stepGenerator = function (type, count){ return function (){ if (!this.requireConnection()) return ; var self = this; self.pause(); self.client.step(type, count, function (err, res){ if (err) self.error(err); self.resume(); } ); } ; } ; Interface.prototype.next = Interface.stepGenerator('next', 1); Interface.prototype.step = Interface.stepGenerator('in', 1); Interface.prototype.out = Interface.stepGenerator('out', 1); Interface.prototype.watch = function (expr){ this._watchers.push(expr); } ; Interface.prototype.unwatch = function (expr){ var index = this._watchers.indexOf(expr); this._watchers.splice(index !== -1? index: + expr, 1); } ; Interface.prototype.watchers = function (){ var self = this, verbose = arguments[0] || false , callback = arguments[1] || function (){ } , waiting = _AN_Read_length('length', this._watchers), values = [] ; this.pause(); if (!waiting) { this.resume(); return callback(); } this._watchers.forEach(function (watcher, i){ self.debugEval(watcher, null , null , function (err, value){ values[i] = err? '': value; wait(); } ); } ); function wait(){ if (--waiting === 0) { if (verbose) self.print('Watchers:'); self._watchers.forEach(function (watcher, i){ self.print(leftPad(i, ' ') + ': ' + watcher + ' = ' + JSON.stringify(values[i])); } ); if (verbose) self.print(''); self.resume(); callback(null ); } } } ; Interface.prototype.breakOnException = function breakOnException(){ if (!this.requireConnection()) return ; var self = this; this.pause(); this.client.reqSetExceptionBreak('all', function (err, res){ self.resume(); } ); } ; Interface.prototype.setBreakpoint = function (script, line, condition, silent){ if (!this.requireConnection()) return ; var self = this, scriptId, ambiguous; if (script === undefined) { script = this.client.currentScript; line = this.client.currentSourceLine + 1; } if (line === undefined && typeof script === 'number') { line = script; script = this.client.currentScript; } if (/\(\)$/.test(script)) { var req = { type: 'function', target: _AN_Call_replace('replace', script, /\(\)$/, ''), condition: condition} ; } else { if (script != + script && !this.client.scripts[script]) { var scripts = this.client.scripts; Object.keys(scripts).forEach(function (id){ if (scripts[id] && 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 (err, res){ if (err) { if (!silent) { self.error(err); } } else { if (!silent) { self.list(5); } if (!scriptId) { scriptId = res.script_id; line = res.line; } if (scriptId) { self.client.breakpoints.push({ id: res.breakpoint, scriptId: scriptId, script: (self.client.scripts[scriptId] || { } ).name, line: line, condition: condition} ); } } self.resume(); } ); } ; 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 = { breakpoint: breakpoint} ; self.pause(); self.client.clearBreakpoint(req, function (err, res){ if (err) { self.error(err); } else { self.client.breakpoints.splice(index, 1); self.list(5); } self.resume(); } ); } ; Interface.prototype.breakpoints = function (){ if (!this.requireConnection()) return ; this.pause(); var self = this; this.client.listbreakpoints(function (err, res){ if (err) { self.error(err); } else { self.print(res); self.resume(); } } ); } ; Interface.prototype.pause_ = function (){ if (!this.requireConnection()) return ; var self = this, cmd = 'process._debugPause();'; this.pause(); this.client.reqFrameEval(cmd, NO_FRAME, function (err, res){ if (err) { self.error(err); } else { self.resume(); } } ); } ; Interface.prototype.kill = function (){ if (!this.child) return ; this.killChild(); } ; Interface.prototype.repl = function (){ if (!this.requireConnection()) return ; var self = this; self.print('Press Ctrl + C to leave debug repl'); var listeners = this.repl.rli.listeners('SIGINT').slice(0); this.repl.rli.removeAllListeners('SIGINT'); this.repl.rli.once('SIGINT', function (){ process.nextTick(function (){ listeners.forEach(function (listener){ self.repl.rli.on('SIGINT', listener); } ); } ); self.exitRepl(); } ); this.repl.eval = this.debugEval.bind(this); this.repl.context = { } ; this.history.control = this.repl.rli.history; this.repl.rli.history = this.history.debug; this.repl.prompt = '> '; this.repl.rli.setPrompt('> '); this.repl.displayPrompt(); } ; Interface.prototype.exitRepl = function (){ this.repl.eval = this.controlEval.bind(this); this.history.debug = this.repl.rli.history; this.repl.rli.history = this.history.control; this.repl.context = this.context; this.repl.prompt = 'debug> '; this.repl.rli.setPrompt('debug> '); this.repl.displayPrompt(); } ; Interface.prototype.quit = function (){ this.killChild(); process.exit(0); } ; Interface.prototype.killChild = function (){ if (this.child) { this.child.kill(); this.child = null ; } if (this.client) { this.breakpoints = this.client.breakpoints; this.client.destroy(); this.client = null ; } } ; Interface.prototype.trySpawn = function (cb){ var self = this, breakpoints = this.breakpoints || [] , port = _AN_Read_port('port', exports), host = 'localhost'; this.killChild(); if (_AN_Read_length('length', this.args) === 2) { var match = this.args[1].match(/^([^:]+):(\d+)$/); if (match) { host = match[1]; port = parseInt(match[2], 10); this.child = { kill: function (){ } } ; } } else if (_AN_Read_length('length', this.args) === 3) { if (this.args[1] === '-p' && /^\d+$/.test(this.args[2])) { this.child = { kill: function (){ } } ; process._debugProcess(parseInt(this.args[2], 10)); } else { var match = this.args[1].match(/^--port=(\d+)$/); if (match) { port = parseInt(match[1], 10); this.args.splice(0, 2, '--debug-brk=' + port); } } } if (!this.child) { this.child = spawn(process.execPath, this.args); this.child.stdout.on('data', this.childPrint.bind(this)); this.child.stderr.on('data', this.childPrint.bind(this)); } this.pause(); var client = self.client = new Client(), connectionAttempts = 0; client.once('ready', function (){ _AN_Call_write('write', self.stdout, ' ok\n'); breakpoints.forEach(function (bp){ self.setBreakpoint(bp.scriptId, bp.line, bp.condition, true ); } ); client.on('close', function (){ self.pause(); self.print('program terminated'); self.resume(); self.client = null ; self.killChild(); } ); if (cb) cb(); self.resume(); } ); client.on('unhandledResponse', function (res){ self.pause(); self.print('\nunhandled res:' + JSON.stringify(res)); self.resume(); } ); client.on('break', function (res){ self.handleBreak(res.body); } ); client.on('exception', function (res){ self.handleBreak(res.body); } ); client.on('error', connectError); function connectError(){ if (connectionAttempts >= 10) { client.removeListener('error', connectError); } _AN_Call_settimeout('setTimeout', window, attemptConnect, 500); } function attemptConnect(){ ++connectionAttempts; _AN_Call_write('write', self.stdout, '.'); client.connect(port, host); } _AN_Call_settimeout('setTimeout', window, function (){ self.print('connecting..', true ); attemptConnect(); } , 50); } ;