Browse Source

Add JSON proxy mode to duk_debug.js

pull/195/head
Sami Vaarala 10 years ago
parent
commit
cd8433a630
  1. 4
      debugger/Makefile
  2. 20
      debugger/duk_classnames.yaml
  3. 561
      debugger/duk_debug.js
  4. 36
      debugger/duk_debugcommands.yaml
  5. 3
      debugger/package.json

4
debugger/Makefile

@ -16,6 +16,10 @@ all: run
run: node_modules static/socket.io-1.2.0.js static/jquery-1.11.1.min.js static/reset.css static/jquery-ui.min.js static/jquery-ui.min.css static/images
$(NODE) duk_debug.js --source-dirs=$(SOURCEDIRS)
.PHONY: runproxy
runproxy: node_modules static/socket.io-1.2.0.js static/jquery-1.11.1.min.js static/reset.css static/jquery-ui.min.js static/jquery-ui.min.css static/images
$(NODE) duk_debug.js --json-proxy
.PHONY: clean
clean:
@rm -f static/socket.io-1.2.0.js

20
debugger/duk_classnames.yaml

@ -0,0 +1,20 @@
# Must match C header
- unused
- Arguments
- Array
- Boolean
- Date
- Error
- Function
- JSON
- Math
- Number
- Object
- RegExp
- String
- global
- ObjEnv
- DecEnv
- Buffer
- Pointer
- Thread

561
debugger/duk_debug.js

@ -23,6 +23,7 @@ var stream = require('stream');
var path = require('path');
var fs = require('fs');
var net = require('net');
var byline = require('byline');
var util = require('util');
var readline = require('readline');
var sprintf = require('sprintf').sprintf;
@ -34,6 +35,8 @@ var yaml = require('yamljs');
var optTargetHost = '127.0.0.1';
var optTargetPort = 9091;
var optHttpPort = 9092;
var optJsonProxyPort = 9093;
var optJsonProxy = false;
var optSourceSearchDirs = [ '../ecmascript-testcases' ];
var optDumpDebugRead = null;
var optDumpDebugWrite = null;
@ -79,38 +82,22 @@ var ERR_TOOMANY = 0x02;
var ERR_NOTFOUND = 0x03;
// Marker objects for special protocol values
var DVAL_EOM = { EOM: true };
var DVAL_REQ = { REQ: true };
var DVAL_REP = { REP: true };
var DVAL_ERR = { ERR: true };
var DVAL_NFY = { NFY: true };
var DVAL_EOM = { type: 'eom' };
var DVAL_REQ = { type: 'req' };
var DVAL_REP = { type: 'rep' };
var DVAL_ERR = { type: 'err' };
var DVAL_NFY = { type: 'nfy' };
// String map for commands (debug dumping). A single map works (instead of
// separate maps for each direction) because command numbers don't currently
// overlap.
var debugCommandNames = {};
debugCommandNames[0x01] = 'Status';
debugCommandNames[0x02] = 'Print';
debugCommandNames[0x03] = 'Alert';
debugCommandNames[0x04] = 'Log';
debugCommandNames[0x05] = 'Gc';
debugCommandNames[0x10] = 'BasicInfo';
debugCommandNames[0x11] = 'TriggerStatus';
debugCommandNames[0x12] = 'Pause';
debugCommandNames[0x13] = 'Resume';
debugCommandNames[0x14] = 'StepInto';
debugCommandNames[0x15] = 'StepOver';
debugCommandNames[0x16] = 'StepOut';
debugCommandNames[0x17] = 'ListBreak';
debugCommandNames[0x18] = 'AddBreak';
debugCommandNames[0x19] = 'DelBreak';
debugCommandNames[0x1a] = 'GetVar';
debugCommandNames[0x1b] = 'PutVar';
debugCommandNames[0x1c] = 'GetCallStack';
debugCommandNames[0x1d] = 'GetLocals';
debugCommandNames[0x1e] = 'Eval';
debugCommandNames[0x1f] = 'Detach';
debugCommandNames[0x20] = 'DumpHeap';
var debugCommandNames = yaml.load('duk_debugcommands.yaml');
// Map debug command names to numbers.
var debugCommandNumbers = {};
debugCommandNames.forEach(function (k, i) {
debugCommandNumbers[k] = i;
});
// Duktape heaphdr type constants, must match C headers
var DUK_HTYPE_STRING = 1;
@ -118,27 +105,7 @@ var DUK_HTYPE_OBJECT = 2;
var DUK_HTYPE_BUFFER = 3;
// Duktape internal class numbers, must match C headers
var dukClassNames = [
'unused',
'Arguments',
'Array',
'Boolean',
'Date',
'Error',
'Function',
'JSON',
'Math',
'Number',
'Object',
'RegExp',
'String',
'global',
'ObjEnv',
'DecEnv',
'Buffer',
'Pointer',
'Thread'
];
var dukClassNames = yaml.load('duk_classnames.yaml');
// Bytecode opcode/extraop metadata
var dukOpcodes = yaml.load('duk_opcodes.yaml')
@ -206,11 +173,19 @@ function stringToDebugString(str) {
/* Pretty print a dvalue. Useful for dumping etc. */
function prettyDebugValue(x) {
if (x === DVAL_EOM) { return 'EOM'; }
if (x === DVAL_REQ) { return 'REQ'; }
if (x === DVAL_REP) { return 'REP'; }
if (x === DVAL_ERR) { return 'ERR'; }
if (x === DVAL_NFY) { return 'NFY'; }
if (typeof x === 'object' && x !== null) {
if (x.type === 'eom') {
return 'EOM';
} else if (x.type === 'req') {
return 'REQ';
} else if (x.type === 'rep') {
return 'REP';
} else if (x.type === 'err') {
return 'ERR';
} else if (x.type === 'nfy') {
return 'NFY';
}
}
return JSON.stringify(x);
}
@ -300,50 +275,49 @@ function prettyUiStringUnquoted(x, cliplen) {
* so that styling etc. could take typing into account.
*/
function prettyUiDebugValue(x, cliplen) {
var t;
if (x === DVAL_EOM) { return 'EOM'; }
if (x === DVAL_REQ) { return 'REQ'; }
if (x === DVAL_REP) { return 'REP'; }
if (x === DVAL_ERR) { return 'ERR'; }
if (x === DVAL_NFY) { return 'NFY'; }
if (x === undefined) { return 'undefined'; }
if (x === null) { return 'null'; }
if (typeof x === 'boolean') {
return x ? 'true' : 'false';
}
if (typeof x === 'string') {
return prettyUiString(x, cliplen);
}
if (typeof x === 'number') {
// Debug protocol integer
return prettyUiNumber(x);
}
if (typeof x === 'object' && x !== null) {
// Note: typeof null === 'object', so null special case explicitly
if (typeof x.UNUSED !== 'undefined') {
if (x.type === 'eom') {
return 'EOM';
} else if (x.type === 'req') {
return 'REQ';
} else if (x.type === 'rep') {
return 'REP';
} else if (x.type === 'err') {
return 'ERR';
} else if (x.type === 'nfy') {
return 'NFY';
} else if (x.type === 'unused') {
return 'unused';
}
if (typeof x.BUF !== 'undefined') {
return '|' + x.BUF + '|';
}
if (typeof x.OBJ !== 'undefined') {
return '[object ' + (dukClassNames[x.class] || ('class ' + x.class)) + ']';
}
if (typeof x.PTR !== 'undefined') {
return '<pointer ' + x.PTR + '>';
}
if (typeof x.LFUNC !== 'undefined') {
return '<lightfunc 0x' + x.flags.toString(16) + ' ' + x.LFUNC + '>';
}
if (typeof x.NUM !== 'undefined') {
} else if (x.type === 'undefined') {
return 'undefined';
} else if (x.type === 'buffer') {
return '|' + x.data + '|';
} else if (x.type === 'object') {
return '[object ' + (dukClassNames[x.class] || ('class ' + x.class)) + ' ' + x.pointer + ']';
} else if (x.type === 'pointer') {
return '<pointer ' + x.pointer + '>';
} else if (x.type === 'lightfunc') {
return '<lightfunc 0x' + x.flags.toString(16) + ' ' + x.pointer + '>';
} else if (x.type === 'number') {
// duk_tval number, any IEEE double
return prettyUiNumber(x.value);
var tmp = new Buffer(x.data, 'hex'); // decode into hex
var val = tmp.readDoubleBE(0); // big endian ieee double
return prettyUiNumber(val);
}
} else if (x === null) {
return 'null';
} else if (typeof x === 'boolean') {
return x ? 'true' : 'false';
} else if (typeof x === 'string') {
return prettyUiString(x, cliplen);
} else if (typeof x === 'number') {
// Debug protocol integer
return prettyUiNumber(x);
}
// We shouldn't come here, but if we do, JSON is a reasonable default.
return JSON.stringify(t);
return JSON.stringify(x);
}
/* Pretty print a debugger message given as an array of parsed dvalues.
@ -551,6 +525,7 @@ function DebugProtocolParser(inputStream,
this.prevBytes = 0;
this.bytesPerSec = 0;
this.statsTimer = null;
this.readableNumberValue = true;
events.EventEmitter.call(this);
@ -693,7 +668,7 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 5 + len) {
v = new Buffer(len);
buf.copy(v, 0, 5, 5 + len);
v = { BUF: v.toString('hex') };
v = { type: 'buffer', data: v.toString('hex') };
consume(5 + len);
// Value could be a Node.js buffer directly, but
// we prefer all dvalues to be JSON compatible
@ -706,7 +681,7 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 3 + len) {
v = new Buffer(len);
buf.copy(v, 0, 3, 3 + len);
v = { BUF: v.toString('hex') };
v = { type: 'buffer', data: v.toString('hex') };
consume(3 + len);
// Value could be a Node.js buffer directly, but
// we prefer all dvalues to be JSON compatible
@ -714,11 +689,11 @@ function DebugProtocolParser(inputStream,
}
break;
case 0x15: // unused/none
v = { UNUSED: true };
v = { type: 'unused' };
consume(1);
break;
case 0x16: // undefined
v = undefined;
v = { type: 'undefined' };
gotValue = true; // indicate 'v' is actually set
consume(1);
break;
@ -739,7 +714,13 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 9) {
v = new Buffer(8);
buf.copy(v, 0, 1, 9);
v = { NUM: v.toString('hex'), value: v.readDoubleBE(0) };
v = { type: 'number', data: v.toString('hex') }
if (_this.readableNumberValue) {
// The _value key should not be used programmatically,
// it is just there to make the dumps more readable.
v._value = buf.readDoubleBE(1);
}
consume(9);
}
break;
@ -749,7 +730,7 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 3 + len) {
v = new Buffer(len);
buf.copy(v, 0, 3, 3 + len);
v = { OBJ: v.toString('hex'), class: buf[1] };
v = { type: 'object', 'class': buf[1], pointer: v.toString('hex') };
consume(3 + len);
}
}
@ -760,7 +741,7 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 2 + len) {
v = new Buffer(len);
buf.copy(v, 0, 2, 2 + len);
v = { PTR: v.toString('hex') };
v = { type: 'pointer', pointer: v.toString('hex') };
consume(2 + len);
}
}
@ -771,7 +752,7 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 4 + len) {
v = new Buffer(len);
buf.copy(v, 0, 4, 4 + len);
v = { LFUNC: v.toString('hex'), flags: buf.readUInt16BE(1) };
v = { type: 'lightfunc', flags: buf.readUInt16BE(1), pointer: v.toString('hex') };
consume(4 + len);
}
}
@ -782,7 +763,7 @@ function DebugProtocolParser(inputStream,
if (buf.length >= 2 + len) {
v = new Buffer(len);
buf.copy(v, 0, 2, 2 + len);
v = { HEAPPTR: v.toString('hex') };
v = { type: 'heapptr', pointer: v.toString('hex') };
consume(2 + len);
}
}
@ -858,20 +839,94 @@ DebugProtocolParser.prototype.close = function () {
*/
function formatDebugValue(v) {
var buf;
var buf, dec, len;
// See doc/debugger.rst for format description.
if (v === DVAL_EOM) {
return new Buffer([ 0x00 ]);
} else if (v === DVAL_REQ) {
return new Buffer([ 0x01 ]);
} else if (v === DVAL_REP) {
return new Buffer([ 0x02 ]);
} else if (v === DVAL_ERR) {
return new Buffer([ 0x03 ]);
} else if (v === DVAL_NFY) {
return new Buffer([ 0x04 ]);
if (typeof v === 'object' && v !== null) {
// Note: typeof null === 'object', so null special case explicitly
if (v.type === 'eom') {
return new Buffer([ 0x00 ]);
} else if (v.type === 'req') {
return new Buffer([ 0x01 ]);
} else if (v.type === 'rep') {
return new Buffer([ 0x02 ]);
} else if (v.type === 'err') {
return new Buffer([ 0x03 ]);
} else if (v.type === 'nfy') {
return new Buffer([ 0x04 ]);
} else if (v.type === 'unused') {
return new Buffer([ 0x15 ]);
} else if (v.type === 'undefined') {
return new Buffer([ 0x16 ]);
} else if (v.type === 'number') {
dec = new Buffer(v.data, 'hex');
len = dec.length;
if (len !== 8) {
throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v));
}
buf = new Buffer(1 + len);
buf[0] = 0x1a;
dec.copy(buf, 1);
return buf;
} else if (v.type === 'buffer') {
dec = new Buffer(v.data, 'hex');
len = dec.length;
if (len <= 0xffff) {
buf = new Buffer(3 + len);
buf[0] = 0x14;
buf[1] = (len >> 8) & 0xff;
buf[2] = (len >> 0) & 0xff;
dec.copy(buf, 3);
return buf;
} else {
buf = new Buffer(5 + len);
buf[0] = 0x13;
buf[1] = (len >> 24) & 0xff;
buf[2] = (len >> 16) & 0xff;
buf[3] = (len >> 8) & 0xff;
buf[4] = (len >> 0) & 0xff;
dec.copy(buf, 5);
return buf;
}
} else if (v.type === 'object') {
dec = new Buffer(v.pointer, 'hex');
len = dec.length;
buf = new Buffer(3 + len);
buf[0] = 0x1b;
buf[1] = v.class;
buf[2] = len;
dec.copy(buf, 3);
return buf;
} else if (v.type === 'pointer') {
dec = new Buffer(v.pointer, 'hex');
len = dec.length;
buf = new Buffer(2 + len);
buf[0] = 0x1c;
buf[1] = len;
dec.copy(buf, 2);
return buf;
} else if (v.type === 'lightfunc') {
dec = new Buffer(v.pointer, 'hex');
len = dec.length;
buf = new Buffer(4 + len);
buf[0] = 0x1d;
buf[1] = (v.flags >> 8) & 0xff;
buf[2] = v.flags & 0xff;
buf[3] = len;
dec.copy(buf, 4);
return buf;
} else if (v.type === 'heapptr') {
dec = new Buffer(v.pointer, 'hex');
len = dec.length;
buf = new Buffer(2 + len);
buf[0] = 0x1e;
buf[1] = len;
dec.copy(buf, 2);
return buf;
}
} else if (v === null) {
return new Buffer([ 0x17 ]);
} else if (typeof v === 'boolean') {
return new Buffer([ v ? 0x18 : 0x19 ]);
} else if (typeof v === 'number') {
@ -930,10 +985,8 @@ function formatDebugValue(v) {
}
}
// XXX: missing support for various types (pointer, object, lightfunc, buffer)
// as they're not needed right now.
throw new TypeError('value cannot be converted to dvalue: ' + v);
// Shouldn't come here.
throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v));
}
/*
@ -1386,10 +1439,10 @@ Debugger.prototype.sendGetBytecodeRequest = function () {
preformatted = preformatted.join('\n') + '\n';
ret = {
"constants": consts,
"functions": funcs,
"bytecode": bcode,
"preformatted": preformatted
constants: consts,
functions: funcs,
bytecode: bcode,
preformatted: preformatted
};
return ret;
@ -2073,6 +2126,251 @@ DebugWebServer.prototype.emitLocals = function () {
this.socket.emit('locals', newMsg);
};
/*
* JSON debug proxy
*/
function DebugProxy(serverPort) {
this.serverPort = serverPort;
this.server = null;
this.socket = null;
this.targetStream = null;
this.inputParser = null;
// preformatted dvalues
this.dval_eom = formatDebugValue(DVAL_EOM);
this.dval_req = formatDebugValue(DVAL_REQ);
this.dval_rep = formatDebugValue(DVAL_REP);
this.dval_nfy = formatDebugValue(DVAL_NFY);
this.dval_err = formatDebugValue(DVAL_ERR);
}
DebugProxy.prototype.determineCommandNumber = function (cmdString, cmdNumber) {
var ret;
if (typeof cmdString === 'string') {
ret = debugCommandNumbers[cmdString];
}
ret = ret || cmdNumber;
if (typeof ret !== 'number') {
throw Error('cannot figure out command number for "' + cmdString + '" (' + cmdNumber + ')');
}
return ret;
};
DebugProxy.prototype.commandNumberToString = function (id) {
return debugCommandNames[id] || String(id);
};
DebugProxy.prototype.formatDvalues = function (args) {
if (!args) {
return [];
}
return args.map(function (v) {
return formatDebugValue(v);
});
};
DebugProxy.prototype.writeJson = function (val) {
this.socket.write(JSON.stringify(val) + '\n');
};
DebugProxy.prototype.writeJsonSafe = function (val) {
try {
this.writeJson(val);
} catch (e) {
console.log('Failed to write JSON in writeJsonSafe, ignoring: ' + e);
}
};
DebugProxy.prototype.disconnectJsonClient = function () {
if (this.socket) {
this.socket.destroy();
this.socket = null;
}
};
DebugProxy.prototype.disconnectTarget = function () {
if (this.inputParser) {
this.inputParser.close();
this.inputParser = null;
}
if (this.targetStream) {
this.targetStream.destroy();
this.targetStream = null;
}
};
DebugProxy.prototype.run = function () {
var _this = this;
console.log('Waiting for client connections on port ' + this.serverPort);
this.server = net.createServer(function (socket) {
console.log('JSON proxy client connected');
_this.disconnectJsonClient();
_this.disconnectTarget();
// A byline-parser is simple and good enough for now (assume
// compact JSON with no newlines).
var socketByline = byline(socket);
_this.socket = socket;
socketByline.on('data', function (line) {
try {
// console.log('Received json proxy input line: ' + line.toString('utf8'));
var msg = JSON.parse(line.toString('utf8'));
var first_dval;
var args_dvalues = _this.formatDvalues(msg.args);
var last_dval = _this.dval_eom;
var cmd;
if (msg.request) {
// "request" can be a string or "true"
first_dval = _this.dval_req;
cmd = _this.determineCommandNumber(msg.request, msg.command);
} else if (msg.reply) {
first_dval = _this.dval_rep;
} else if (msg.notify) {
// "notify" can be a string or "true"
first_dval = _this.dval_nfy;
cmd = _this.determineCommandNumber(msg.notify, msg.command);
} else if (msg.error) {
first_dval = _this.dval_err;
} else {
throw new Error('Invalid input JSON message: ' + JSON.stringify(msg));
}
_this.targetStream.write(first_dval);
if (cmd) {
_this.targetStream.write(formatDebugValue(cmd));
}
args_dvalues.forEach(function (v) {
_this.targetStream.write(v);
});
_this.targetStream.write(last_dval);
} catch (e) {
console.log(e);
_this.writeJsonSafe({
notify: '_Error',
args: [ 'Failed to handle input json message: ' + e ]
});
_this.disconnectJsonClient();
_this.disconnectTarget();
}
});
_this.connectToTarget();
}).listen(this.serverPort);
};
DebugProxy.prototype.connectToTarget = function () {
var _this = this;
console.log('Connecting to ' + optTargetHost + ':' + optTargetPort + '...');
this.targetStream = new net.Socket();
this.targetStream.connect(optTargetPort, optTargetHost, function () {
console.log('Debug transport connected');
});
this.inputParser = new DebugProtocolParser(
this.targetStream,
null,
optDumpDebugRead,
optDumpDebugPretty,
optDumpDebugPretty ? 'Recv: ' : null,
null,
null // console logging is done at a higher level to match request/response
);
// Don't add a '_value' key to numbers.
this.inputParser.readableNumberValue = false;
this.inputParser.on('transport-close', function () {
console.log('Debug transport closed');
_this.writeJsonSafe({
notify: '_Disconnecting'
});
_this.disconnectJsonClient();
_this.disconnectTarget();
});
this.inputParser.on('transport-error', function (err) {
console.log('Debug transport error', err);
_this.writeJsonSafe({
notify: '_Error',
args: [ String(err) ]
});
});
this.inputParser.on('protocol-version', function (msg) {
var ver = msg.protocolVersion;
console.log('Debug version identification:', msg.versionIdentification);
_this.writeJson({
notify: '_Connected',
args: [ msg.versionIdentification ] // raw identification string
});
if (ver !== 1) {
console.log('Protocol version ' + ver + ' unsupported, dropping connection');
}
});
this.inputParser.on('debug-message', function (msg) {
var t;
//console.log(msg);
if (typeof msg[0] !== 'object' || msg[0] === null) {
throw new Error('unexpected initial dvalue: ' + msg[0]);
} else if (msg.type === 'eom') {
throw new Error('unexpected initial dvalue: ' + msg[0]);
} else if (msg.type === 'req') {
if (typeof msg[1] !== 'number') {
throw new Error('unexpected request command number: ' + msg[1]);
}
t = {
request: _this.commandNumberToString(msg[1]),
command: msg[1],
args: msg.slice(2, msg.length - 1)
}
_this.writeJson(t);
} else if (msg[0].type === 'rep') {
t = {
reply: true,
args: msg.slice(1, msg.length - 1)
}
_this.writeJson(t);
} else if (msg[0].type === 'err') {
t = {
error: true,
args: msg.slice(1, msg.length - 1)
}
_this.writeJson(t);
} else if (msg[0].type === 'nfy') {
if (typeof msg[1] !== 'number') {
throw new Error('unexpected notify command number: ' + msg[1]);
}
t = {
notify: _this.commandNumberToString(msg[1]),
command: msg[1],
args: msg.slice(2, msg.length - 1)
}
_this.writeJson(t);
} else {
throw new Error('unexpected initial dvalue: ' + msg[0]);
}
});
this.inputParser.on('stats-update', function () {
});
};
/*
* Command line parsing and initialization
*/
@ -2093,6 +2391,12 @@ function main() {
if (argv['http-port']) {
optHttpPort = argv['http-port'];
}
if (argv['json-proxy-port']) {
optJsonProxyPort = argv['json-proxy-port'];
}
if (argv['json-proxy']) {
optJsonProxy = argv['json-proxy'];
}
if (argv['source-dirs']) {
optSourceSearchDirs = argv['source-dirs'].split(path.delimiter);
}
@ -2116,6 +2420,8 @@ function main() {
console.log(' --target-host: ' + optTargetHost);
console.log(' --target-port: ' + optTargetPort);
console.log(' --http-port: ' + optHttpPort);
console.log(' --json-proxy-port: ' + optJsonProxyPort);
console.log(' --json-proxy: ' + optJsonProxy);
console.log(' --source-dirs: ' + optSourceSearchDirs.join(' '));
console.log(' --dump-debug-read: ' + optDumpDebugRead);
console.log(' --dump-debug-write: ' + optDumpDebugWrite);
@ -2126,12 +2432,19 @@ function main() {
// Create debugger and web UI singletons, tie them together and
// start them.
var dbg = new Debugger();
var web = new DebugWebServer();
dbg.web = web;
web.dbg = dbg;
dbg.run();
web.run();
if (optJsonProxy) {
console.log('Starting in JSON proxy mode, JSON port: ' + optJsonProxyPort);
var prx = new DebugProxy(optJsonProxyPort);
prx.run();
} else {
var dbg = new Debugger();
var web = new DebugWebServer();
dbg.web = web;
web.dbg = dbg;
dbg.run();
web.run();
}
}
main();

36
debugger/duk_debugcommands.yaml

@ -0,0 +1,36 @@
# Debug command names. A single map works for now because command names
# provided by client/target don't overlap.
- Reserved_0
- Status
- Print
- Alert
- Log
- Gc
- Reserved_6
- Reserved_7
- Reserved_8
- Reserved_9
- Reserved_10
- Reserved_11
- Reserved_12
- Reserved_13
- Reserved_14
- Reserved_15
- BasicInfo
- TriggerStatus
- Pause
- Resume
- StepInto
- StepOver
- StepOut
- ListBreak
- AddBreak
- DelBreak
- GetVar
- PutVar
- GetCallStack
- GetLocals
- Eval
- Detach
- DumpHeap
- GetBytecode

3
debugger/package.json

@ -20,7 +20,8 @@
"readline": "0.0.5",
"util": "~0.10.3",
"http": "0.0.0",
"yamljs": "~0.2.1"
"yamljs": "~0.2.1",
"byline": "~4.2.1"
},
"main": "duk_debug.js"
}

Loading…
Cancel
Save