You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ph...@apache.org on 2018/01/02 18:28:41 UTC

[46/51] [partial] nifi-minifi-cpp git commit: MINIFICPP-351: Remove Civetweb third party directory

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/ede68a10/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/debugger/duk_debug.js
----------------------------------------------------------------------
diff --git a/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/debugger/duk_debug.js b/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/debugger/duk_debug.js
deleted file mode 100644
index ab0623f..0000000
--- a/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/debugger/duk_debug.js
+++ /dev/null
@@ -1,2473 +0,0 @@
-/*
- *  Minimal debug web console for Duktape command line tool
- *
- *  See debugger/README.rst.
- *
- *  The web UI socket.io communication can easily become a bottleneck and
- *  it's important to ensure that the web UI remains responsive.  Basic rate
- *  limiting mechanisms (token buckets, suppressing identical messages, etc)
- *  are used here now.  Ideally the web UI would pull data on its own terms
- *  which would provide natural rate limiting.
- *
- *  Promises are used to structure callback chains.
- *
- *  https://github.com/petkaantonov/bluebird
- *  https://github.com/petkaantonov/bluebird/blob/master/API.md
- *  https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns
- */
-
-var Promise = require('bluebird');
-var events = require('events');
-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;
-var utf8 = require('utf8');
-var wrench = require('wrench');  // https://github.com/ryanmcgrath/wrench-js
-var yaml = require('yamljs');
-
-// Command line options (defaults here, overwritten if necessary)
-var optTargetHost = '127.0.0.1';
-var optTargetPort = 9091;
-var optHttpPort = 9092;
-var optJsonProxyPort = 9093;
-var optJsonProxy = false;
-var optSourceSearchDirs = [ '../tests/ecmascript' ];
-var optDumpDebugRead = null;
-var optDumpDebugWrite = null;
-var optDumpDebugPretty = null;
-var optLogMessages = false;
-
-// Constants
-var UI_MESSAGE_CLIPLEN = 128;
-var LOCALS_CLIPLEN = 64;
-var EVAL_CLIPLEN = 4096;
-var GETVAR_CLIPLEN = 4096;
-
-// Commands initiated by Duktape
-var CMD_STATUS = 0x01;
-var CMD_PRINT = 0x02;
-var CMD_ALERT = 0x03;
-var CMD_LOG = 0x04;
-var CMD_THROW = 0x05;
-var CMD_DETACHING = 0x06;
-
-// Commands initiated by the debug client (= us)
-var CMD_BASICINFO = 0x10;
-var CMD_TRIGGERSTATUS = 0x11;
-var CMD_PAUSE = 0x12;
-var CMD_RESUME = 0x13;
-var CMD_STEPINTO = 0x14;
-var CMD_STEPOVER = 0x15;
-var CMD_STEPOUT = 0x16;
-var CMD_LISTBREAK = 0x17;
-var CMD_ADDBREAK = 0x18;
-var CMD_DELBREAK = 0x19;
-var CMD_GETVAR = 0x1a;
-var CMD_PUTVAR = 0x1b;
-var CMD_GETCALLSTACK = 0x1c;
-var CMD_GETLOCALS = 0x1d;
-var CMD_EVAL = 0x1e;
-var CMD_DETACH = 0x1f;
-var CMD_DUMPHEAP = 0x20;
-var CMD_GETBYTECODE = 0x21;
-
-// Errors
-var ERR_UNKNOWN = 0x00;
-var ERR_UNSUPPORTED = 0x01;
-var ERR_TOOMANY = 0x02;
-var ERR_NOTFOUND = 0x03;
-
-// Marker objects for special protocol values
-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.  So merge the YAML metadata.
-var debugCommandMeta = yaml.load('duk_debugcommands.yaml');
-var debugCommandNames = [];  // list of command names, merged client/target
-debugCommandMeta.target_commands.forEach(function (k, i) {
-    debugCommandNames[i] = k;
-});
-debugCommandMeta.client_commands.forEach(function (k, i) {  // override
-    debugCommandNames[i] = k;
-});
-var debugCommandNumbers = {};  // map from (merged) command name to number
-debugCommandNames.forEach(function (k, i) {
-    debugCommandNumbers[k] = i;
-});
-
-// Duktape heaphdr type constants, must match C headers
-var DUK_HTYPE_STRING = 1;
-var DUK_HTYPE_OBJECT = 2;
-var DUK_HTYPE_BUFFER = 3;
-
-// Duktape internal class numbers, must match C headers
-var dukClassNameMeta = yaml.load('duk_classnames.yaml');
-var dukClassNames = dukClassNameMeta.class_names;
-
-// Bytecode opcode/extraop metadata
-var dukOpcodes = yaml.load('duk_opcodes.yaml');
-if (dukOpcodes.opcodes.length != 64) {
-    throw new Error('opcode metadata length incorrect');
-}
-if (dukOpcodes.extra.length != 256) {
-    throw new Error('extraop metadata length incorrect');
-}
-
-/*
- *  Miscellaneous helpers
- */
-
-var nybbles = '0123456789abcdef';
-
-/* Convert a buffer into a string using Unicode codepoints U+0000...U+00FF.
- * This is the NodeJS 'binary' encoding, but since it's being deprecated,
- * reimplement it here.  We need to avoid parsing strings as e.g. UTF-8:
- * although Duktape strings are usually UTF-8/CESU-8 that's not always the
- * case, e.g. for internal strings.  Buffer values are also represented as
- * strings in the debug protocol, so we must deal accurately with arbitrary
- * byte arrays.
- */
-function bufferToDebugString(buf) {
-    var cp = [];
-    var i, n;
-
-/*
-    // This fails with "RangeError: Maximum call stack size exceeded" for some
-    // reason, so use a much slower variant.
-
-    for (i = 0, n = buf.length; i < n; i++) {
-        cp[i] = buf[i];
-    }
-
-    return String.fromCharCode.apply(String, cp);
-*/
-
-    for (i = 0, n = buf.length; i < n; i++) {
-        cp[i] = String.fromCharCode(buf[i]);
-    }
-
-    return cp.join('');
-}
-
-/* Write a string into a buffer interpreting codepoints U+0000...U+00FF
- * as bytes.  Drop higher bits.
- */
-function writeDebugStringToBuffer(str, buf, off) {
-    var i, n;
-
-    for (i = 0, n = str.length; i < n; i++) {
-        buf[off + i] = str.charCodeAt(i) & 0xff;  // truncate higher bits
-    }
-}
-
-/* Encode an ordinary Unicode string into a dvalue compatible format, i.e.
- * into a byte array represented as codepoints U+0000...U+00FF.  Concretely,
- * encode with UTF-8 and then represent the bytes with U+0000...U+00FF.
- */
-function stringToDebugString(str) {
-    return utf8.encode(str);
-}
-
-/* Pretty print a dvalue.  Useful for dumping etc. */
-function prettyDebugValue(x) {
-    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);
-}
-
-/* Pretty print a number for UI usage.  Types and values should be easy to
- * read and typing should be obvious.  For numbers, support Infinity, NaN,
- * and signed zeroes properly.
- */
-function prettyUiNumber(x) {
-    if (x === 1 / 0) { return 'Infinity'; }
-    if (x === -1 / 0) { return '-Infinity'; }
-    if (Number.isNaN(x)) { return 'NaN'; }
-    if (x === 0 && 1 / x > 0) { return '0'; }
-    if (x === 0 && 1 / x < 0) { return '-0'; }
-    return x.toString();
-}
-
-/* Pretty print a dvalue string (bytes represented as U+0000...U+00FF)
- * for UI usage.  Try UTF-8 decoding to get a nice Unicode string (JSON
- * encoded) but if that fails, ensure that bytes are encoded transparently.
- * The result is a quoted string with a special quote marker for a "raw"
- * string when UTF-8 decoding fails.  Very long strings are optionally
- * clipped.
- */
-function prettyUiString(x, cliplen) {
-    var ret;
-
-    if (typeof x !== 'string') {
-        throw new Error('invalid input to prettyUiString: ' + typeof x);
-    }
-    try {
-        // Here utf8.decode() is better than decoding using NodeJS buffer
-        // operations because we want strict UTF-8 interpretation.
-        ret = JSON.stringify(utf8.decode(x));
-    } catch (e) {
-        // When we fall back to representing bytes, indicate that the string
-        // is "raw" with a 'r"' prefix (a somewhat arbitrary convention).
-        // U+0022 = ", U+0027 = '
-        ret = 'r"' + x.replace(/[\u0022\u0027\u0000-\u001f\u0080-\uffff]/g, function (match) {
-            var cp = match.charCodeAt(0);
-            return '\\x' + nybbles[(cp >> 4) & 0x0f] + nybbles[cp & 0x0f];
-        }) + '"';
-    }
-
-    if (cliplen && ret.length > cliplen) {
-        ret = ret.substring(0, cliplen) + '...';  // trailing '"' intentionally missing
-    }
-    return ret;
-}
-
-/* Pretty print a dvalue string (bytes represented as U+0000...U+00FF)
- * for UI usage without quotes.
- */
-function prettyUiStringUnquoted(x, cliplen) {
-    var ret;
-
-    if (typeof x !== 'string') {
-        throw new Error('invalid input to prettyUiStringUnquoted: ' + typeof x);
-    }
-
-    try {
-        // Here utf8.decode() is better than decoding using NodeJS buffer
-        // operations because we want strict UTF-8 interpretation.
-
-        // XXX: unprintable characters etc?  In some UI cases we'd want to
-        // e.g. escape newlines and in others not.
-        ret = utf8.decode(x);
-    } catch (e) {
-        // For the unquoted version we don't need to escape single or double
-        // quotes.
-        ret = x.replace(/[\u0000-\u001f\u0080-\uffff]/g, function (match) {
-            var cp = match.charCodeAt(0);
-            return '\\x' + nybbles[(cp >> 4) & 0x0f] + nybbles[cp & 0x0f];
-        });
-    }
-
-    if (cliplen && ret.length > cliplen) {
-        ret = ret.substring(0, cliplen) + '...';
-    }
-    return ret;
-}
-
-/* Pretty print a dvalue for UI usage.  Everything comes out as a ready-to-use
- * string.
- *
- * XXX: Currently the debug client formats all values for UI use.  A better
- * solution would be to pass values in typed form and let the UI format them,
- * so that styling etc. could take typing into account.
- */
-function prettyUiDebugValue(x, cliplen) {
-    if (typeof x === 'object' && x !== null) {
-        // Note: typeof null === 'object', so null special case explicitly
-        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';
-        } 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
-            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(x);
-}
-
-/* Pretty print a debugger message given as an array of parsed dvalues.
- * Result should be a pure ASCII one-liner.
- */
-function prettyDebugMessage(msg) {
-    return msg.map(prettyDebugValue).join(' ');
-}
-
-/* Pretty print a debugger command. */
-function prettyDebugCommand(cmd) {
-    return debugCommandNames[cmd] || String(cmd);
-}
-
-/* Decode and normalize source file contents: UTF-8, tabs to 8,
- * CR LF to LF.
- */
-function decodeAndNormalizeSource(data) {
-    var tmp;
-    var lines, line, repl;
-    var i, n;
-    var j, m;
-
-    try {
-        tmp = data.toString('utf8');
-    } catch (e) {
-        console.log('Failed to UTF-8 decode source file, ignoring: ' + e);
-        tmp = String(data);
-    }
-
-    lines = tmp.split(/\r?\n/);
-    for (i = 0, n = lines.length; i < n; i++) {
-        line = lines[i];
-        if (/\t/.test(line)) {
-            repl = '';
-            for (j = 0, m = line.length; j < m; j++) {
-                if (line.charAt(j) === '\t') {
-                    repl += ' ';
-                    while ((repl.length % 8) != 0) {
-                        repl += ' ';
-                    }
-                } else {
-                    repl += line.charAt(j);
-                }
-            }
-            lines[i] = repl;
-        }
-    }
-
-    // XXX: normalize last newline (i.e. force a newline if contents don't
-    // end with a newline)?
-
-    return lines.join('\n');
-}
-
-/* Token bucket rate limiter for a given callback.  Calling code calls
- * trigger() to request 'cb' to be called, and the rate limiter ensures
- * that 'cb' is not called too often.
- */
-function RateLimited(tokens, rate, cb) {
-    var _this = this;
-    this.maxTokens = tokens;
-    this.tokens = this.maxTokens;
-    this.rate = rate;
-    this.cb = cb;
-    this.delayedCb = false;
-
-    // Right now the implementation is setInterval-based, but could also be
-    // made timerless.  There are so few rate limited resources that this
-    // doesn't matter in practice.
-
-    this.tokenAdder = setInterval(function () {
-        if (_this.tokens < _this.maxTokens) {
-            _this.tokens++;
-        }
-        if (_this.delayedCb) {
-            _this.delayedCb = false;
-            _this.tokens--;
-            _this.cb();
-        }
-    }, this.rate);
-}
-RateLimited.prototype.trigger = function () {
-    if (this.tokens > 0) {
-        this.tokens--;
-        this.cb();
-    } else {
-        this.delayedCb = true;
-    }
-};
-
-/*
- *  Source file manager
- *
- *  Scan the list of search directories for Ecmascript source files and
- *  build an index of them.  Provides a mechanism to find a source file
- *  based on a raw 'fileName' property provided by the debug target, and
- *  to provide a file list for the web UI.
- *
- *  NOTE: it's tempting to do loose matching for filenames, but this does
- *  not work in practice.  Filenames must match 1:1 with the debug target
- *  so that e.g. breakpoints assigned based on filenames found from the
- *  search paths will match 1:1 on the debug target.  If this is not the
- *  case, breakpoints won't work as expected.
- */
-
-function SourceFileManager(directories) {
-    this.directories = directories;
-    this.extensions = { '.js': true, '.jsm': true };
-    this.files;
-}
-
-SourceFileManager.prototype.scan = function () {
-    var _this = this;
-    var fileMap = {};   // absFn -> true
-    var files;
-
-    this.directories.forEach(function (dir) {
-        console.log('Scanning source files: ' + dir);
-        try {
-            wrench.readdirSyncRecursive(dir).forEach(function (fn) {
-                var absFn = path.normalize(path.join(dir, fn));   // './foo/bar.js' -> 'foo/bar.js'
-                var ent;
-
-                if (fs.existsSync(absFn) &&
-                    fs.lstatSync(absFn).isFile() &&
-                    _this.extensions[path.extname(fn)]) {
-                    // We want the fileMap to contain the filename relative to
-                    // the search dir root.
-                    fileMap[fn] = true;
-                }
-            });
-        } catch (e) {
-            console.log('Failed to scan ' + dir + ': ' + e);
-        }
-    });
-
-    files = Object.keys(fileMap);
-    files.sort();
-    this.files = files;
-
-    console.log('Found ' + files.length + ' source files in ' + this.directories.length + ' search directories');
-};
-
-SourceFileManager.prototype.getFiles = function () {
-    return this.files;
-};
-
-SourceFileManager.prototype.search = function (fileName) {
-    var _this = this;
-
-    // Loose matching is tempting but counterproductive: filenames must
-    // match 1:1 between the debug client and the debug target for e.g.
-    // breakpoints to work as expected.  Note that a breakpoint may be
-    // assigned by selecting a file from a dropdown populated by scanning
-    // the filesystem for available sources and there's no way of knowing
-    // if the debug target uses the exact same name.
-
-    function tryLookup() {
-        var i, fn, data;
-
-        for (i = 0; i < _this.directories.length; i++) {
-            fn = path.join(_this.directories[i], fileName);
-            if (fs.existsSync(fn) && fs.lstatSync(fn).isFile()) {
-                data = fs.readFileSync(fn);             // Raw bytes
-                return decodeAndNormalizeSource(data);  // Unicode string
-            }
-        }
-        return null;
-    }
-
-    return tryLookup(fileName);
-};
-
-/*
- *  Debug protocol parser
- *
- *  The debug protocol parser is an EventEmitter which parses debug messages
- *  from an input stream and emits 'debug-message' events for completed
- *  messages ending in an EOM.  The parser also provides debug dumping, stream
- *  logging functionality, and statistics gathering functionality.
- *
- *  This parser is used to parse both incoming and outgoing messages.  For
- *  outgoing messages the only function is to validate and debug dump the
- *  messages we're about to send.  The downside of dumping at this low level
- *  is that we can't match request and reply/error messages here.
- *
- *  http://www.sitepoint.com/nodejs-events-and-eventemitter/
- */
-
-function DebugProtocolParser(inputStream,
-                             protocolVersion,
-                             rawDumpFileName,
-                             textDumpFileName,
-                             textDumpFilePrefix,
-                             hexDumpConsolePrefix,
-                             textDumpConsolePrefix) {
-    var _this = this;
-    this.inputStream = inputStream;
-    this.closed = false;       // stream is closed/broken, don't parse anymore
-    this.bytes = 0;
-    this.dvalues = 0;
-    this.messages = 0;
-    this.requests = 0;
-    this.prevBytes = 0;
-    this.bytesPerSec = 0;
-    this.statsTimer = null;
-    this.readableNumberValue = true;
-
-    events.EventEmitter.call(this);
-
-    var buf = new Buffer(0);    // accumulate data
-    var msg = [];               // accumulated message until EOM
-    var versionIdentification;
-
-    var statsInterval = 2000;
-    var statsIntervalSec = statsInterval / 1000;
-    this.statsTimer = setInterval(function () {
-        _this.bytesPerSec = (_this.bytes - _this.prevBytes) / statsIntervalSec;
-        _this.prevBytes = _this.bytes;
-        _this.emit('stats-update');
-    }, statsInterval);
-
-    function consume(n) {
-        var tmp = new Buffer(buf.length - n);
-        buf.copy(tmp, 0, n);
-        buf = tmp;
-    }
-
-    inputStream.on('data', function (data) {
-        var i, n, x, v, gotValue, len, t, tmpbuf, verstr;
-        var prettyMsg;
-
-        if (_this.closed || !_this.inputStream) {
-            console.log('Ignoring incoming data from closed input stream, len ' + data.length);
-            return;
-        }
-
-        _this.bytes += data.length;
-        if (rawDumpFileName) {
-            fs.appendFileSync(rawDumpFileName, data);
-        }
-        if (hexDumpConsolePrefix) {
-            console.log(hexDumpConsolePrefix + data.toString('hex'));
-        }
-
-        buf = Buffer.concat([ buf, data ]);
-
-        // Protocol version handling.  When dumping an output stream, the
-        // caller gives a non-null protocolVersion so we don't read one here.
-        if (protocolVersion == null) {
-            if (buf.length > 1024) {
-                _this.emit('transport-error', 'Parse error (version identification too long), dropping connection');
-                _this.close();
-                return;
-            }
-
-            for (i = 0, n = buf.length; i < n; i++) {
-                if (buf[i] == 0x0a) {
-                    tmpbuf = new Buffer(i);
-                    buf.copy(tmpbuf, 0, 0, i);
-                    consume(i + 1);
-                    verstr = tmpbuf.toString('utf-8');
-                    t = verstr.split(' ');
-                    protocolVersion = Number(t[0]);
-                    versionIdentification = verstr;
-
-                    _this.emit('protocol-version', {
-                        protocolVersion: protocolVersion,
-                        versionIdentification: versionIdentification
-                    });
-                    break;
-                }
-            }
-
-            if (protocolVersion == null) {
-                // Still waiting for version identification to complete.
-                return;
-            }
-        }
-
-        // Parse complete dvalues (quite inefficient now) by trial parsing.
-        // Consume a value only when it's fully present in 'buf'.
-        // See doc/debugger.rst for format description.
-
-        while (buf.length > 0) {
-            x = buf[0];
-            v = undefined;
-            gotValue = false;  // used to flag special values like undefined
-
-            if (x >= 0xc0) {
-                // 0xc0...0xff: integers 0-16383
-                if (buf.length >= 2) {
-                    v = ((x - 0xc0) << 8) + buf[1];
-                    consume(2);
-                }
-            } else if (x >= 0x80) {
-                // 0x80...0xbf: integers 0-63
-                v = x - 0x80;
-                consume(1);
-            } else if (x >= 0x60) {
-                // 0x60...0x7f: strings with length 0-31
-                len = x - 0x60;
-                if (buf.length >= 1 + len) {
-                    v = new Buffer(len);
-                    buf.copy(v, 0, 1, 1 + len);
-                    v = bufferToDebugString(v);
-                    consume(1 + len);
-                }
-            } else {
-                switch (x) {
-                case 0x00: v = DVAL_EOM; consume(1); break;
-                case 0x01: v = DVAL_REQ; consume(1); break;
-                case 0x02: v = DVAL_REP; consume(1); break;
-                case 0x03: v = DVAL_ERR; consume(1); break;
-                case 0x04: v = DVAL_NFY; consume(1); break;
-                case 0x10:  // 4-byte signed integer
-                    if (buf.length >= 5) {
-                        v = buf.readInt32BE(1);
-                        consume(5);
-                    }
-                    break;
-                case 0x11:  // 4-byte string
-                    if (buf.length >= 5) {
-                        len = buf.readUInt32BE(1);
-                        if (buf.length >= 5 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 5, 5 + len);
-                            v = bufferToDebugString(v);
-                            consume(5 + len);
-                        }
-                    }
-                    break;
-                case 0x12:  // 2-byte string
-                    if (buf.length >= 3) {
-                        len = buf.readUInt16BE(1);
-                        if (buf.length >= 3 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 3, 3 + len);
-                            v = bufferToDebugString(v);
-                            consume(3 + len);
-                        }
-                    }
-                    break;
-                case 0x13:  // 4-byte buffer
-                    if (buf.length >= 5) {
-                        len = buf.readUInt32BE(1);
-                        if (buf.length >= 5 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 5, 5 + len);
-                            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
-                        }
-                    }
-                    break;
-                case 0x14:  // 2-byte buffer
-                    if (buf.length >= 3) {
-                        len = buf.readUInt16BE(1);
-                        if (buf.length >= 3 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 3, 3 + len);
-                            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
-                        }
-                    }
-                    break;
-                case 0x15:  // unused/none
-                    v = { type: 'unused' };
-                    consume(1);
-                    break;
-                case 0x16:  // undefined
-                    v = { type: 'undefined' };
-                    gotValue = true;  // indicate 'v' is actually set
-                    consume(1);
-                    break;
-                case 0x17:  // null
-                    v = null;
-                    gotValue = true;  // indicate 'v' is actually set
-                    consume(1);
-                    break;
-                case 0x18:  // true
-                    v = true;
-                    consume(1);
-                    break;
-                case 0x19:  // false
-                    v = false;
-                    consume(1);
-                    break;
-                case 0x1a:  // number (IEEE double), big endian
-                    if (buf.length >= 9) {
-                        v = new Buffer(8);
-                        buf.copy(v, 0, 1, 9);
-                        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;
-                case 0x1b:  // object
-                    if (buf.length >= 3) {
-                        len = buf[2];
-                        if (buf.length >= 3 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 3, 3 + len);
-                            v = { type: 'object', 'class': buf[1], pointer: v.toString('hex') };
-                            consume(3 + len);
-                        }
-                    }
-                    break;
-                case 0x1c:  // pointer
-                    if (buf.length >= 2) {
-                        len = buf[1];
-                        if (buf.length >= 2 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 2, 2 + len);
-                            v = { type: 'pointer', pointer: v.toString('hex') };
-                            consume(2 + len);
-                        }
-                    }
-                    break;
-                case 0x1d:  // lightfunc
-                    if (buf.length >= 4) {
-                        len = buf[3];
-                        if (buf.length >= 4 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 4, 4 + len);
-                            v = { type: 'lightfunc', flags: buf.readUInt16BE(1), pointer: v.toString('hex') };
-                            consume(4 + len);
-                        }
-                    }
-                    break;
-                case 0x1e:  // heapptr
-                    if (buf.length >= 2) {
-                        len = buf[1];
-                        if (buf.length >= 2 + len) {
-                            v = new Buffer(len);
-                            buf.copy(v, 0, 2, 2 + len);
-                            v = { type: 'heapptr', pointer: v.toString('hex') };
-                            consume(2 + len);
-                        }
-                    }
-                    break;
-                default:
-                    _this.emit('transport-error', 'Parse error, dropping connection');
-                    _this.close();
-                }
-            }
-
-            if (typeof v === 'undefined' && !gotValue) {
-                break;
-            }
-            msg.push(v);
-            _this.dvalues++;
-
-            // Could emit a 'debug-value' event here, but that's not necessary
-            // because the receiver will just collect statistics which can also
-            // be done using the finished message.
-
-            if (v === DVAL_EOM) {
-                _this.messages++;
-
-                if (textDumpFileName || textDumpConsolePrefix) {
-                    prettyMsg = prettyDebugMessage(msg);
-                    if (textDumpFileName) {
-                        fs.appendFileSync(textDumpFileName, (textDumpFilePrefix || '') + prettyMsg + '\n');
-                    }
-                    if (textDumpConsolePrefix) {
-                        console.log(textDumpConsolePrefix + prettyMsg);
-                    }
-                }
-
-                _this.emit('debug-message', msg);
-                msg = [];  // new object, old may be in circulation for a while
-            }
-        }
-    });
-
-    // Not all streams will emit this.
-    inputStream.on('error', function (err) {
-        _this.emit('transport-error', err);
-        _this.close();
-    });
-
-    // Not all streams will emit this.
-    inputStream.on('close', function () {
-        _this.close();
-    });
-}
-DebugProtocolParser.prototype = Object.create(events.EventEmitter.prototype);
-
-DebugProtocolParser.prototype.close = function () {
-    // Although the underlying transport may not have a close() or destroy()
-    // method or even a 'close' event, this method is always available and
-    // will generate a 'transport-close'.
-    //
-    // The caller is responsible for closing the underlying stream if that
-    // is necessary.
-
-    if (this.closed) { return; }
-
-    this.closed = true;
-    if (this.statsTimer) {
-        clearInterval(this.statsTimer);
-        this.statsTimer = null;
-    }
-    this.emit('transport-close');
-};
-
-/*
- *  Debugger output formatting
- */
-
-function formatDebugValue(v) {
-    var buf, dec, len;
-
-    // See doc/debugger.rst for format description.
-
-    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') {
-        if (Math.floor(v) === v &&     /* whole */
-            (v !== 0 || 1 / v > 0) &&  /* not negative zero */
-            v >= -0x80000000 && v <= 0x7fffffff) {
-            // Represented signed 32-bit integers as plain integers.
-            // Debugger code expects this for all fields that are not
-            // duk_tval representations (e.g. command numbers and such).
-            if (v >= 0x00 && v <= 0x3f) {
-                return new Buffer([ 0x80 + v ]);
-            } else if (v >= 0x0000 && v <= 0x3fff) {
-                return new Buffer([ 0xc0 + (v >> 8), v & 0xff ]);
-            } else if (v >= -0x80000000 && v <= 0x7fffffff) {
-                return new Buffer([ 0x10,
-                                    (v >> 24) & 0xff,
-                                    (v >> 16) & 0xff,
-                                    (v >> 8) & 0xff,
-                                    (v >> 0) & 0xff ]);
-            } else {
-                throw new Error('internal error when encoding integer to dvalue: ' + v);
-            }
-        } else {
-            // Represent non-integers as IEEE double dvalues
-            buf = new Buffer(1 + 8);
-            buf[0] = 0x1a;
-            buf.writeDoubleBE(v, 1);
-            return buf;
-        }
-    } else if (typeof v === 'string') {
-        if (v.length < 0 || v.length > 0xffffffff) {
-            // Not possible in practice.
-            throw new TypeError('cannot convert to dvalue, invalid string length: ' + v.length);
-        }
-        if (v.length <= 0x1f) {
-            buf = new Buffer(1 + v.length);
-            buf[0] = 0x60 + v.length;
-            writeDebugStringToBuffer(v, buf, 1);
-            return buf;
-        } else if (v.length <= 0xffff) {
-            buf = new Buffer(3 + v.length);
-            buf[0] = 0x12;
-            buf[1] = (v.length >> 8) & 0xff;
-            buf[2] = (v.length >> 0) & 0xff;
-            writeDebugStringToBuffer(v, buf, 3);
-            return buf;
-        } else {
-            buf = new Buffer(5 + v.length);
-            buf[0] = 0x11;
-            buf[1] = (v.length >> 24) & 0xff;
-            buf[2] = (v.length >> 16) & 0xff;
-            buf[3] = (v.length >> 8) & 0xff;
-            buf[4] = (v.length >> 0) & 0xff;
-            writeDebugStringToBuffer(v, buf, 5);
-            return buf;
-        }
-    }
-
-    // Shouldn't come here.
-    throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v));
-}
-
-/*
- *  Debugger implementation
- *
- *  A debugger instance communicates with the debug target and maintains
- *  persistent debug state so that the current state can be resent to the
- *  socket.io client (web UI) if it reconnects.  Whenever the debugger state
- *  is changed an event is generated.  The socket.io handler will listen to
- *  state change events and push the necessary updates to the web UI, often
- *  in a rate limited fashion or using a client pull to ensure the web UI
- *  is not overloaded.
- *
- *  The debugger instance assumes that if the debug protocol connection is
- *  re-established, it is always to the same target.  There is no separate
- *  abstraction for a debugger session.
- */
-
-function Debugger() {
-    events.EventEmitter.call(this);
-
-    this.web = null;                      // web UI singleton
-    this.targetStream = null;             // transport connection to target
-    this.outputPassThroughStream = null;  // dummy passthrough for message dumping
-    this.inputParser = null;              // parser for incoming debug messages
-    this.outputParser = null;             // parser for outgoing debug messages (stats, dumping)
-    this.protocolVersion = null;
-    this.dukVersion = null;
-    this.dukGitDescribe = null;
-    this.targetInfo = null;
-    this.attached = false;
-    this.handshook = false;
-    this.reqQueue = null;
-    this.stats = {                        // stats for current debug connection
-        rxBytes: 0, rxDvalues: 0, rxMessages: 0, rxBytesPerSec: 0,
-        txBytes: 0, txDvalues: 0, txMessages: 0, txBytesPerSec: 0
-    };
-    this.execStatus = {
-        attached: false,
-        state: 'detached',
-        fileName: '',
-        funcName: '',
-        line: 0,
-        pc: 0
-    };
-    this.breakpoints = [];
-    this.callstack = [];
-    this.locals = [];
-    this.messageLines = [];
-    this.messageScrollBack = 100;
-}
-Debugger.prototype = events.EventEmitter.prototype;
-
-Debugger.prototype.decodeBytecodeFromBuffer = function (buf, consts, funcs) {
-    var i, j, n, m, ins, pc;
-    var res = [];
-    var op, str, args, comments;
-
-    // XXX: add constants inline to preformatted output (e.g. for strings,
-    // add a short escaped snippet as a comment on the line after the
-    // compact argument list).
-
-    for (i = 0, n = buf.length; i < n; i += 4) {
-        pc = i / 4;
-
-        // shift forces unsigned
-        if (this.endianness === 'little') {
-            ins = buf.readInt32LE(i) >>> 0;
-        } else {
-            ins = buf.readInt32BE(i) >>> 0;
-        }
-
-        op = dukOpcodes.opcodes[ins & 0x3f];
-        if (op.extra) {
-            op = dukOpcodes.extra[(ins >> 6) & 0xff];
-        }
-
-        args = [];
-        comments = [];
-        if (op.args) {
-            for (j = 0, m = op.args.length; j < m; j++) {
-                switch (op.args[j]) {
-                case 'A_R':   args.push('r' + ((ins >>> 6) & 0xff)); break;
-                case 'A_RI':  args.push('r' + ((ins >>> 6) & 0xff) + '(indirect)'); break;
-                case 'A_C':   args.push('c' + ((ins >>> 6) & 0xff)); break;
-                case 'A_H':   args.push('0x' + ((ins >>> 6) & 0xff).toString(16)); break;
-                case 'A_I':   args.push(((ins >>> 6) & 0xff).toString(10)); break;
-                case 'A_B':   args.push(((ins >>> 6) & 0xff) ? 'true' : 'false'); break;
-                case 'B_RC':  args.push((ins & (1 << 22) ? 'c' : 'r') + ((ins >>> 14) & 0x0ff)); break;
-                case 'B_R':   args.push('r' + ((ins >>> 14) & 0x1ff)); break;
-                case 'B_RI':  args.push('r' + ((ins >>> 14) & 0x1ff) + '(indirect)'); break;
-                case 'B_C':   args.push('c' + ((ins >>> 14) & 0x1ff)); break;
-                case 'B_H':   args.push('0x' + ((ins >>> 14) & 0x1ff).toString(16)); break;
-                case 'B_I':   args.push(((ins >>> 14) & 0x1ff).toString(10)); break;
-                case 'C_RC':  args.push((ins & (1 << 31) ? 'c' : 'r') + ((ins >>> 23) & 0x0ff)); break;
-                case 'C_R':   args.push('r' + ((ins >>> 23) & 0x1ff)); break;
-                case 'C_RI':  args.push('r' + ((ins >>> 23) & 0x1ff) + '(indirect)'); break;
-                case 'C_C':   args.push('c' + ((ins >>> 23) & 0x1ff)); break;
-                case 'C_H':   args.push('0x' + ((ins >>> 23) & 0x1ff).toString(16)); break;
-                case 'C_I':   args.push(((ins >>> 23) & 0x1ff).toString(10)); break;
-                case 'BC_R':  args.push('r' + ((ins >>> 14) & 0x3ffff)); break;
-                case 'BC_C':  args.push('c' + ((ins >>> 14) & 0x3ffff)); break;
-                case 'BC_H':  args.push('0x' + ((ins >>> 14) & 0x3ffff).toString(16)); break;
-                case 'BC_I':  args.push(((ins >>> 14) & 0x3ffff).toString(10)); break;
-                case 'ABC_H': args.push(((ins >>> 6) & 0x03ffffff).toString(16)); break;
-                case 'ABC_I': args.push(((ins >>> 6) & 0x03ffffff).toString(10)); break;
-                case 'BC_LDINT': args.push(((ins >>> 14) & 0x3ffff) - (1 << 17)); break;
-                case 'BC_LDINTX': args.push(((ins >>> 14) & 0x3ffff) - 0); break;  // no bias in LDINTX
-                case 'ABC_JUMP': {
-                    var pc_add = ((ins >>> 6) & 0x03ffffff) - (1 << 25) + 1;  // pc is preincremented before adding
-                    var pc_dst = pc + pc_add;
-                    args.push(pc_dst + ' (' + (pc_add >= 0 ? '+' : '') + pc_add + ')');
-                    break;
-                }
-                default:      args.push('?'); break;
-                }
-            }
-        }
-        if (op.flags) {
-            for (j = 0, m = op.flags.length; j < m; j++) {
-                if (ins & op.flags[j].mask) {
-                    comments.push(op.flags[j].name);
-                }
-            }
-        }
-
-        if (args.length > 0) {
-            str = sprintf('%05d %08x   %-10s %s', pc, ins, op.name, args.join(', '));
-        } else {
-            str = sprintf('%05d %08x   %-10s', pc, ins, op.name);
-        }
-        if (comments.length > 0) {
-            str = sprintf('%-44s ; %s', str, comments.join(', '));
-        }
-
-        res.push({
-            str: str,
-            ins: ins
-        });
-    }
-
-    return res;
-};
-
-Debugger.prototype.uiMessage = function (type, val) {
-    var msg;
-    if (typeof type === 'object') {
-        msg = type;
-    } else if (typeof type === 'string') {
-        msg = { type: type, message: val };
-    } else {
-        throw new TypeError('invalid ui message: ' + type);
-    }
-    this.messageLines.push(msg);
-    while (this.messageLines.length > this.messageScrollBack) {
-        this.messageLines.shift();
-    }
-    this.emit('ui-message-update');  // just trigger a sync, gets rate limited
-};
-
-Debugger.prototype.sendRequest = function (msg) {
-    var _this = this;
-    return new Promise(function (resolve, reject) {
-        var dvals = [];
-        var dval;
-        var data;
-        var i;
-
-        if (!_this.attached || !_this.handshook || !_this.reqQueue || !_this.targetStream) {
-            throw new Error('invalid state for sendRequest');
-        }
-
-        for (i = 0; i < msg.length; i++) {
-            try {
-                dval = formatDebugValue(msg[i]);
-            } catch (e) {
-                console.log('Failed to format dvalue, dropping connection: ' + e);
-                console.log(e.stack || e);
-                _this.targetStream.destroy();
-                throw new Error('failed to format dvalue');
-            }
-            dvals.push(dval);
-        }
-
-        data = Buffer.concat(dvals);
-
-        _this.targetStream.write(data);
-        _this.outputPassThroughStream.write(data);  // stats and dumping
-
-        if (optLogMessages) {
-            console.log('Request ' + prettyDebugCommand(msg[1]) + ': ' + prettyDebugMessage(msg));
-        }
-
-        if (!_this.reqQueue) {
-            throw new Error('no reqQueue');
-        }
-
-        _this.reqQueue.push({
-            reqMsg: msg,
-            reqCmd: msg[1],
-            resolveCb: resolve,
-            rejectCb: reject
-        });
-    });
-};
-
-Debugger.prototype.sendBasicInfoRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_BASICINFO, DVAL_EOM ]).then(function (msg) {
-        _this.dukVersion = msg[1];
-        _this.dukGitDescribe = msg[2];
-        _this.targetInfo = msg[3];
-        _this.endianness = { 1: 'little', 2: 'mixed', 3: 'big' }[msg[4]] || 'unknown';
-        _this.emit('basic-info-update');
-        return msg;
-    });
-};
-
-Debugger.prototype.sendGetVarRequest = function (varname, level) {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_GETVAR, varname, (typeof level === 'number' ? level : -1), DVAL_EOM ]).then(function (msg) {
-        return { found: msg[1] === 1, value: msg[2] };
-    });
-};
-
-Debugger.prototype.sendPutVarRequest = function (varname, varvalue, level) {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_PUTVAR, varname, varvalue, (typeof level === 'number' ? level : -1), DVAL_EOM ]);
-};
-
-Debugger.prototype.sendInvalidCommandTestRequest = function () {
-    // Intentional invalid command
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, 0xdeadbeef, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendStatusRequest = function () {
-    // Send a status request to trigger a status notify, result is ignored:
-    // target sends a status notify instead of a meaningful reply
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_TRIGGERSTATUS, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendBreakpointListRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_LISTBREAK, DVAL_EOM ]).then(function (msg) {
-        var i, n;
-        var breakpts = [];
-
-        for (i = 1, n = msg.length - 1; i < n; i += 2) {
-            breakpts.push({ fileName: msg[i], lineNumber: msg[i + 1] });
-        }
-
-        _this.breakpoints = breakpts;
-        _this.emit('breakpoints-update');
-        return msg;
-    });
-};
-
-Debugger.prototype.sendGetLocalsRequest = function (level) {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_GETLOCALS, (typeof level === 'number' ? level : -1), DVAL_EOM ]).then(function (msg) {
-        var i;
-        var locals = [];
-
-        for (i = 1; i <= msg.length - 2; i += 2) {
-            // XXX: do pretty printing in debug client for now
-            locals.push({ key: msg[i], value: prettyUiDebugValue(msg[i + 1], LOCALS_CLIPLEN) });
-        }
-
-        _this.locals = locals;
-        _this.emit('locals-update');
-        return msg;
-    });
-};
-
-Debugger.prototype.sendGetCallStackRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_GETCALLSTACK, DVAL_EOM ]).then(function (msg) {
-        var i;
-        var stack = [];
-
-        for (i = 1; i + 3 <= msg.length - 1; i += 4) {
-            stack.push({
-                fileName: msg[i],
-                funcName: msg[i + 1],
-                lineNumber: msg[i + 2],
-                pc: msg[i + 3]
-            });
-        }
-
-        _this.callstack = stack;
-        _this.emit('callstack-update');
-        return msg;
-    });
-};
-
-Debugger.prototype.sendStepIntoRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_STEPINTO, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendStepOverRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_STEPOVER, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendStepOutRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_STEPOUT, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendPauseRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_PAUSE, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendResumeRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_RESUME, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendEvalRequest = function (evalInput, level) {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_EVAL, evalInput, (typeof level === 'number' ? level : -1), DVAL_EOM ]).then(function (msg) {
-        return { error: msg[1] === 1 /*error*/, value: msg[2] };
-    });
-};
-
-Debugger.prototype.sendDetachRequest = function () {
-    var _this = this;
-    return this.sendRequest([ DVAL_REQ, CMD_DETACH, DVAL_EOM ]);
-};
-
-Debugger.prototype.sendDumpHeapRequest = function () {
-    var _this = this;
-
-    return this.sendRequest([ DVAL_REQ, CMD_DUMPHEAP, DVAL_EOM ]).then(function (msg) {
-        var res = {};
-        var objs = [];
-        var i, j, n, m, o, prop;
-
-        res.type = 'heapDump';
-        res.heapObjects = objs;
-
-        for (i = 1, n = msg.length - 1; i < n; /*nop*/) {
-            o = {};
-            o.ptr = msg[i++];
-            o.type = msg[i++];
-            o.flags = msg[i++] >>> 0;  /* unsigned */
-            o.refc = msg[i++];
-
-            if (o.type === DUK_HTYPE_STRING) {
-                o.blen = msg[i++];
-                o.clen = msg[i++];
-                o.hash = msg[i++] >>> 0;  /* unsigned */
-                o.data = msg[i++];
-            } else if (o.type === DUK_HTYPE_BUFFER) {
-                o.len = msg[i++];
-                o.data = msg[i++];
-            } else if (o.type === DUK_HTYPE_OBJECT) {
-                o['class'] = msg[i++];
-                o.proto = msg[i++];
-                o.esize = msg[i++];
-                o.enext = msg[i++];
-                o.asize = msg[i++];
-                o.hsize = msg[i++];
-                o.props = [];
-                for (j = 0, m = o.enext; j < m; j++) {
-                    prop = {};
-                    prop.flags = msg[i++];
-                    prop.key = msg[i++];
-                    prop.accessor = (msg[i++] == 1);
-                    if (prop.accessor) {
-                        prop.getter = msg[i++];
-                        prop.setter = msg[i++];
-                    } else {
-                        prop.value = msg[i++];
-                    }
-                    o.props.push(prop);
-                }
-                o.array = [];
-                for (j = 0, m = o.asize; j < m; j++) {
-                    prop = {};
-                    prop.value = msg[i++];
-                    o.array.push(prop);
-                }
-            } else {
-                console.log('invalid htype: ' + o.type + ', disconnect');
-                _this.disconnectDebugger();
-                throw new Error('invalid htype');
-                return;
-            }
-
-            objs.push(o);
-        }
-
-        return res;
-    });
-};
-
-Debugger.prototype.sendGetBytecodeRequest = function () {
-    var _this = this;
-
-    return this.sendRequest([ DVAL_REQ, CMD_GETBYTECODE, DVAL_EOM ]).then(function (msg) {
-        var idx = 1;
-        var nconst;
-        var nfunc;
-        var val;
-        var buf;
-        var i, n;
-        var consts = [];
-        var funcs = [];
-        var bcode;
-        var preformatted;
-        var ret;
-        var idxPreformattedInstructions;
-
-        //console.log(JSON.stringify(msg));
-
-        nconst = msg[idx++];
-        for (i = 0; i < nconst; i++) {
-            val = msg[idx++];
-            consts.push(val);
-        }
-
-        nfunc = msg[idx++];
-        for (i = 0; i < nfunc; i++) {
-            val = msg[idx++];
-            funcs.push(val);
-        }
-        val = msg[idx++];
-
-        // Right now bytecode is a string containing a direct dump of the
-        // bytecode in target endianness.  Decode here so that the web UI
-        // doesn't need to.
-
-        buf = new Buffer(val.length);
-        writeDebugStringToBuffer(val, buf, 0);
-        bcode = _this.decodeBytecodeFromBuffer(buf, consts, funcs);
-
-        preformatted = [];
-        consts.forEach(function (v, i) {
-            preformatted.push('; c' + i + ' ' + JSON.stringify(v));
-        });
-        preformatted.push('');
-        idxPreformattedInstructions = preformatted.length;
-        bcode.forEach(function (v) {
-            preformatted.push(v.str);
-        });
-        preformatted = preformatted.join('\n') + '\n';
-
-        ret = {
-            constants: consts,
-            functions: funcs,
-            bytecode: bcode,
-            preformatted: preformatted,
-            idxPreformattedInstructions: idxPreformattedInstructions
-        };
-
-        return ret;
-    });
-};
-
-Debugger.prototype.changeBreakpoint = function (fileName, lineNumber, mode) {
-    var _this = this;
-
-    return this.sendRequest([ DVAL_REQ, CMD_LISTBREAK, DVAL_EOM ]).then(function (msg) {
-        var i, n;
-        var breakpts = [];
-        var deleted = false;
-
-        // Up-to-date list of breakpoints on target
-        for (i = 1, n = msg.length - 1; i < n; i += 2) {
-            breakpts.push({ fileName: msg[i], lineNumber: msg[i + 1] });
-        }
-
-        // Delete matching breakpoints in reverse order so that indices
-        // remain valid.  We do this for all operations so that duplicates
-        // are eliminated if present.
-        for (i = breakpts.length - 1; i >= 0; i--) {
-            var bp = breakpts[i];
-            if (mode === 'deleteall' || (bp.fileName === fileName && bp.lineNumber === lineNumber)) {
-                deleted = true;
-                _this.sendRequest([ DVAL_REQ, CMD_DELBREAK, i, DVAL_EOM ], function (msg) {
-                    // nop
-                }, function (err) {
-                    // nop
-                });
-            }
-        }
-
-        // Technically we should wait for each delbreak reply but because
-        // target processes the requests in order, it doesn't matter.
-        if ((mode === 'add') || (mode === 'toggle' && !deleted)) {
-            _this.sendRequest([ DVAL_REQ, CMD_ADDBREAK, fileName, lineNumber, DVAL_EOM ], function (msg) {
-                // nop
-            }, function (err) {
-                _this.uiMessage('debugger-info', 'Failed to add breakpoint: ' + err);
-            });
-        }
-
-        // Read final, effective breakpoints from the target
-        _this.sendBreakpointListRequest();
-    });
-};
-
-Debugger.prototype.disconnectDebugger = function () {
-    if (this.targetStream) {
-        // We require a destroy() method from the actual target stream
-        this.targetStream.destroy();
-        this.targetStream = null;
-    }
-    if (this.inputParser) {
-        this.inputParser.close();
-        this.inputParser = null;
-    }
-    if (this.outputPassThroughStream) {
-        // There is no close() or destroy() for a passthrough stream, so just
-        // close the outputParser which will cancel timers etc.
-    }
-    if (this.outputParser) {
-        this.outputParser.close();
-        this.outputParser = null;
-    }
-
-    this.attached = false;
-    this.handshook = false;
-    this.reqQueue = null;
-    this.execStatus = {
-        attached: false,
-        state: 'detached',
-        fileName: '',
-        funcName: '',
-        line: 0,
-        pc: 0
-    };
-};
-
-Debugger.prototype.connectDebugger = function () {
-    var _this = this;
-
-    this.disconnectDebugger();  // close previous target connection
-
-    // CUSTOMTRANSPORT: to use a custom transport, change this.targetStream to
-    // use your custom transport.
-
-    console.log('Connecting to ' + optTargetHost + ':' + optTargetPort + '...');
-    this.targetStream = new net.Socket();
-    this.targetStream.connect(optTargetPort, optTargetHost, function () {
-        console.log('Debug transport connected');
-        _this.attached = true;
-        _this.reqQueue = [];
-        _this.uiMessage('debugger-info', '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
-    );
-
-    // Use a PassThrough stream to debug dump and get stats for output messages.
-    // Simply write outgoing data to both the targetStream and this passthrough
-    // separately.
-    this.outputPassThroughStream = stream.PassThrough();
-    this.outputParser = new DebugProtocolParser(
-        this.outputPassThroughStream,
-        1,
-        optDumpDebugWrite,
-        optDumpDebugPretty,
-        optDumpDebugPretty ? 'Send: ' : null,
-        null,
-        null   // console logging is done at a higher level to match request/response
-    );
-
-    this.inputParser.on('transport-close', function () {
-        _this.uiMessage('debugger-info', 'Debug transport closed');
-        _this.disconnectDebugger();
-        _this.emit('exec-status-update');
-        _this.emit('detached');
-    });
-
-    this.inputParser.on('transport-error', function (err) {
-        _this.uiMessage('debugger-info', 'Debug transport error: ' + err);
-        _this.disconnectDebugger();
-    });
-
-    this.inputParser.on('protocol-version', function (msg) {
-        var ver = msg.protocolVersion;
-        console.log('Debug version identification:', msg.versionIdentification);
-        _this.protocolVersion = ver;
-        _this.uiMessage('debugger-info', 'Debug version identification: ' + msg.versionIdentification);
-        if (ver !== 1) {
-            _this.uiMessage('debugger-info', 'Protocol version ' + ver + ' unsupported, dropping connection');
-            _this.targetStream.destroy();
-        } else {
-            _this.uiMessage('debugger-info', 'Debug protocol version: ' + ver);
-            _this.handshook = true;
-            _this.execStatus = {
-                attached: true,
-                state: 'attached',
-                fileName: '',
-                funcName: '',
-                line: 0,
-                pc: 0
-            };
-            _this.emit('exec-status-update');
-            _this.emit('attached');  // inform web UI
-
-            // Fetch basic info right away
-            _this.sendBasicInfoRequest();
-        }
-    });
-
-    this.inputParser.on('debug-message', function (msg) {
-        _this.processDebugMessage(msg);
-    });
-
-    this.inputParser.on('stats-update', function () {
-        _this.stats.rxBytes = this.bytes;
-        _this.stats.rxDvalues = this.dvalues;
-        _this.stats.rxMessages = this.messages;
-        _this.stats.rxBytesPerSec = this.bytesPerSec;
-        _this.emit('debug-stats-update');
-    });
-
-    this.outputParser.on('stats-update', function () {
-        _this.stats.txBytes = this.bytes;
-        _this.stats.txDvalues = this.dvalues;
-        _this.stats.txMessages = this.messages;
-        _this.stats.txBytesPerSec = this.bytesPerSec;
-        _this.emit('debug-stats-update');
-    });
-};
-
-Debugger.prototype.processDebugMessage = function (msg) {
-    var req;
-    var prevState, newState;
-    var err;
-
-    if (msg[0] === DVAL_REQ) {
-        // No actual requests sent by the target right now (just notifys).
-        console.log('Unsolicited reply message, dropping connection: ' + prettyDebugMessage(msg));
-    } else if (msg[0] === DVAL_REP) {
-        if (this.reqQueue.length <= 0) {
-            console.log('Unsolicited reply message, dropping connection: ' + prettyDebugMessage(msg));
-            this.targetStream.destroy();
-        }
-        req = this.reqQueue.shift();
-
-        if (optLogMessages) {
-            console.log('Reply for ' + prettyDebugCommand(req.reqCmd) + ': ' + prettyDebugMessage(msg));
-        }
-
-        if (req.resolveCb) {
-            req.resolveCb(msg);
-        } else {
-            // nop: no callback
-        }
-    } else if (msg[0] === DVAL_ERR) {
-        if (this.reqQueue.length <= 0) {
-            console.log('Unsolicited error message, dropping connection: ' + prettyDebugMessage(msg));
-            this.targetStream.destroy();
-        }
-        err = new Error(String(msg[2]) + ' (code ' + String(msg[1]) + ')');
-        err.errorCode = msg[1] || 0;
-        req = this.reqQueue.shift();
-
-        if (optLogMessages) {
-            console.log('Error for ' + prettyDebugCommand(req.reqCmd) + ': ' + prettyDebugMessage(msg));
-        }
-
-        if (req.rejectCb) {
-            req.rejectCb(err);
-        } else {
-            // nop: no callback
-        }
-    } else if (msg[0] === DVAL_NFY) {
-        if (optLogMessages) {
-            console.log('Notify ' + prettyDebugCommand(msg[1]) + ': ' + prettyDebugMessage(msg));
-        }
-
-        if (msg[1] === CMD_STATUS) {
-            prevState = this.execStatus.state;
-            newState = msg[2] === 0 ? 'running' : 'paused';
-            this.execStatus = {
-                attached: true,
-                state: newState,
-                fileName: msg[3],
-                funcName: msg[4],
-                line: msg[5],
-                pc: msg[6]
-            };
-
-            if (prevState !== newState && newState === 'paused') {
-                // update run state now that we're paused
-                this.sendBreakpointListRequest();
-                this.sendGetLocalsRequest();
-                this.sendGetCallStackRequest();
-            }
-
-            this.emit('exec-status-update');
-        } else if (msg[1] === CMD_PRINT) {
-            this.uiMessage('print', prettyUiStringUnquoted(msg[2], UI_MESSAGE_CLIPLEN));
-        } else if (msg[1] === CMD_ALERT) {
-            this.uiMessage('alert', prettyUiStringUnquoted(msg[2], UI_MESSAGE_CLIPLEN));
-        } else if (msg[1] === CMD_LOG) {
-            this.uiMessage({ type: 'log', level: msg[2], message: prettyUiStringUnquoted(msg[3], UI_MESSAGE_CLIPLEN) });
-        } else if (msg[1] === CMD_THROW) {
-            this.uiMessage({ type: 'throw', fatal: msg[2], message: (msg[2] ? 'UNCAUGHT: ' : 'THROW: ') + prettyUiStringUnquoted(msg[3], UI_MESSAGE_CLIPLEN), fileName: msg[4], lineNumber: msg[5] });
-        } else if (msg[1] === CMD_DETACHING) {
-            this.uiMessage({ type: 'detaching', reason: msg[2], message: 'DETACH: ' + (msg.length >= 5 ? prettyUiStringUnquoted(msg[3]) : 'detaching') });
-        } else {
-            // Ignore unknown notify messages
-            console.log('Unknown notify, ignoring: ' + prettyDebugMessage(msg));
-
-            //this.targetStream.destroy();
-        }
-    } else {
-        console.log('Invalid initial dvalue, dropping connection: ' + prettyDebugMessage(msg));
-        this.targetStream.destroy();
-    }
-};
-
-Debugger.prototype.run = function () {
-    var _this = this;
-
-    // Initial debugger connection
-
-    this.connectDebugger();
-
-    // Poll various state items when running
-
-    var sendRound = 0;
-    var statusPending = false;
-    var bplistPending = false;
-    var localsPending = false;
-    var callStackPending = false;
-
-    setInterval(function () {
-        if (_this.execStatus.state !== 'running') {
-            return;
-        }
-
-        // Could also check for an empty request queue, but that's probably
-        // too strict?
-
-        // Pending flags are used to avoid requesting the same thing twice
-        // while a previous request is pending.  The flag-based approach is
-        // quite awkward.  Rework to use promises.
-
-        switch (sendRound) {
-        case 0:
-            if (!statusPending) {
-                statusPending = true;
-                _this.sendStatusRequest().finally(function () { statusPending = false; });
-            }
-            break;
-        case 1:
-            if (!bplistPending) {
-                bplistPending = true;
-                _this.sendBreakpointListRequest().finally(function () { bplistPending = false; });
-            }
-            break;
-        case 2:
-            if (!localsPending) {
-                localsPending = true;
-                _this.sendGetLocalsRequest().finally(function () { localsPending = false; });
-            }
-            break;
-        case 3:
-            if (!callStackPending) {
-                callStackPending = true;
-                _this.sendGetCallStackRequest().finally(function () { callStackPending = false; });
-            }
-            break;
-        }
-        sendRound = (sendRound + 1) % 4;
-    }, 500);
-};
-
-/*
- *  Express setup and socket.io
- */
-
-function DebugWebServer() {
-    this.dbg = null;       // debugger singleton
-    this.socket = null;    // current socket (or null)
-    this.keepaliveTimer = null;
-    this.uiMessageLimiter = null;
-    this.cachedJson = {};  // cache to avoid resending identical data
-    this.sourceFileManager = new SourceFileManager(optSourceSearchDirs);
-    this.sourceFileManager.scan();
-}
-
-DebugWebServer.prototype.handleSourcePost = function (req, res) {
-    var fileName = req.body && req.body.fileName;
-    var fileData;
-
-    console.log('Source request: ' + fileName);
-
-    if (typeof fileName !== 'string') {
-        res.status(500).send('invalid request');
-        return;
-    }
-    fileData = this.sourceFileManager.search(fileName, optSourceSearchDirs);
-    if (typeof fileData !== 'string') {
-        res.status(404).send('not found');
-        return;
-    }
-    res.status(200).send(fileData);  // UTF-8
-};
-
-DebugWebServer.prototype.handleSourceListPost = function (req, res) {
-    console.log('Source list request');
-
-    var files = this.sourceFileManager.getFiles();
-    res.header('Content-Type', 'application/json');
-    res.status(200).json(files);
-};
-
-DebugWebServer.prototype.handleHeapDumpGet = function (req, res) {
-    console.log('Heap dump get');
-
-    this.dbg.sendDumpHeapRequest().then(function (val) {
-        res.header('Content-Type', 'application/json');
-        //res.status(200).json(val);
-        res.status(200).send(JSON.stringify(val, null, 4));
-    }).catch(function (err) {
-        res.status(500).send('Failed to get heap dump: ' + (err.stack || err));
-    });
-};
-
-DebugWebServer.prototype.run = function () {
-    var _this = this;
-
-    var express = require('express');
-    var bodyParser = require('body-parser');
-    var app = express();
-    var http = require('http').Server(app);
-    var io = require('socket.io')(http);
-
-    app.use(bodyParser.json());
-    app.post('/source', this.handleSourcePost.bind(this));
-    app.post('/sourceList', this.handleSourceListPost.bind(this));
-    app.get('/heapDump.json', this.handleHeapDumpGet.bind(this));
-    app.use('/', express.static(__dirname + '/static'));
-
-    http.listen(optHttpPort, function () {
-        console.log('Listening on *:' + optHttpPort);
-    });
-
-    io.on('connection', this.handleNewSocketIoConnection.bind(this));
-
-    this.dbg.on('attached', function () {
-        console.log('Debugger attached');
-    });
-
-    this.dbg.on('detached', function () {
-        console.log('Debugger detached');
-    });
-
-    this.dbg.on('debug-stats-update', function () {
-        _this.debugStatsLimiter.trigger();
-    });
-
-    this.dbg.on('ui-message-update', function () {
-        // Explicit rate limiter because this is a source of a lot of traffic.
-        _this.uiMessageLimiter.trigger();
-    });
-
-    this.dbg.on('basic-info-update', function () {
-        _this.emitBasicInfo();
-    });
-
-    this.dbg.on('breakpoints-update', function () {
-        _this.emitBreakpoints();
-    });
-
-    this.dbg.on('exec-status-update', function () {
-        // Explicit rate limiter because this is a source of a lot of traffic.
-        _this.execStatusLimiter.trigger();
-    });
-
-    this.dbg.on('locals-update', function () {
-        _this.emitLocals();
-    });
-
-    this.dbg.on('callstack-update', function () {
-        _this.emitCallStack();
-    });
-
-    this.uiMessageLimiter = new RateLimited(10, 1000, this.uiMessageLimiterCallback.bind(this));
-    this.execStatusLimiter = new RateLimited(50, 500, this.execStatusLimiterCallback.bind(this));
-    this.debugStatsLimiter = new RateLimited(1, 2000, this.debugStatsLimiterCallback.bind(this));
-
-    this.keepaliveTimer = setInterval(this.emitKeepalive.bind(this), 30000);
-};
-
-DebugWebServer.prototype.handleNewSocketIoConnection = function (socket) {
-    var _this = this;
-
-    console.log('Socket.io connected');
-    if (this.socket) {
-        console.log('Closing previous socket.io socket');
-        this.socket.emit('replaced');
-    }
-    this.socket = socket;
-
-    this.emitKeepalive();
-
-    socket.on('disconnect', function () {
-        console.log('Socket.io disconnected');
-        if (_this.socket === socket) {
-             _this.socket = null;
-        }
-    });
-
-    socket.on('keepalive', function (msg) {
-        // nop
-    });
-
-    socket.on('attach', function (msg) {
-        if (_this.dbg.targetStream) {
-            console.log('Attach request when debugger already has a connection, ignoring');
-        } else {
-            _this.dbg.connectDebugger();
-        }
-    });
-
-    socket.on('detach', function (msg) {
-        // Try to detach cleanly, timeout if no response
-        Promise.any([
-            _this.dbg.sendDetachRequest(),
-            Promise.delay(3000)
-        ]).finally(function () {
-            _this.dbg.disconnectDebugger();
-        });
-    });
-
-    socket.on('stepinto', function (msg) {
-        _this.dbg.sendStepIntoRequest();
-    });
-
-    socket.on('stepover', function (msg) {
-        _this.dbg.sendStepOverRequest();
-    });
-
-    socket.on('stepout', function (msg) {
-        _this.dbg.sendStepOutRequest();
-    });
-
-    socket.on('pause', function (msg) {
-        _this.dbg.sendPauseRequest();
-    });
-
-    socket.on('resume', function (msg) {
-        _this.dbg.sendResumeRequest();
-    });
-
-    socket.on('eval', function (msg) {
-        // msg.input is a proper Unicode strings here, and needs to be
-        // converted into a protocol string (U+0000...U+00FF).
-        var input = stringToDebugString(msg.input);
-        _this.dbg.sendEvalRequest(input, msg.level).then(function (v) {
-            socket.emit('eval-result', { error: v.error, result: prettyUiDebugValue(v.value, EVAL_CLIPLEN) });
-        });
-
-        // An eval call quite possibly changes the local variables so always
-        // re-read locals afterwards.  We don't need to wait for Eval to
-        // complete here; the requests will pipeline automatically and be
-        // executed in order.
-
-        // XXX: move this to the web UI so that the UI can control what
-        // locals are listed (or perhaps show locals for all levels with
-        // an expandable tree view).
-        _this.dbg.sendGetLocalsRequest();
-    });
-
-    socket.on('getvar', function (msg) {
-        // msg.varname is a proper Unicode strings here, and needs to be
-        // converted into a protocol string (U+0000...U+00FF).
-        var varname = stringToDebugString(msg.varname);
-        _this.dbg.sendGetVarRequest(varname, msg.level)
-        .then(function (v) {
-            socket.emit('getvar-result', { found: v.found, result: prettyUiDebugValue(v.value, GETVAR_CLIPLEN) });
-        });
-    });
-
-    socket.on('putvar', function (msg) {
-        // msg.varname and msg.varvalue are proper Unicode strings here, they
-        // need to be converted into protocol strings (U+0000...U+00FF).
-        var varname = stringToDebugString(msg.varname);
-        var varvalue = msg.varvalue;
-
-        // varvalue is JSON parsed by the web UI for now, need special string
-        // encoding here.
-        if (typeof varvalue === 'string') {
-            varvalue = stringToDebugString(msg.varvalue);
-        }
-
-        _this.dbg.sendPutVarRequest(varname, varvalue, msg.level)
-        .then(function (v) {
-            console.log('putvar done');  // XXX: signal success to UI?
-        });
-
-        // A PutVar call quite possibly changes the local variables so always
-        // re-read locals afterwards.  We don't need to wait for PutVar to
-        // complete here; the requests will pipeline automatically and be
-        // executed in order.
-
-        // XXX: make the client do this?
-        _this.dbg.sendGetLocalsRequest();
-    });
-
-    socket.on('add-breakpoint', function (msg) {
-        _this.dbg.changeBreakpoint(msg.fileName, msg.lineNumber, 'add');
-    });
-
-    socket.on('delete-breakpoint', function (msg) {
-        _this.dbg.changeBreakpoint(msg.fileName, msg.lineNumber, 'delete');
-    });
-
-    socket.on('toggle-breakpoint', function (msg) {
-        _this.dbg.changeBreakpoint(msg.fileName, msg.lineNumber, 'toggle');
-    });
-
-    socket.on('delete-all-breakpoints', function (msg) {
-        _this.dbg.changeBreakpoint(null, null, 'deleteall');
-    });
-
-    socket.on('get-bytecode', function (msg) {
-        _this.dbg.sendGetBytecodeRequest().then(function (res) {
-            socket.emit('bytecode', res);
-        });
-    });
-
-    // Resend all debugger state for new client
-    this.cachedJson = {};  // clear client state cache
-    this.emitBasicInfo();
-    this.emitStats();
-    this.emitExecStatus();
-    this.emitUiMessages();
-    this.emitBreakpoints();
-    this.emitCallStack();
-    this.emitLocals();
-};
-
-// Check if 'msg' would encode to the same JSON which was previously sent
-// to the web client.  The caller then avoid resending unnecessary stuff.
-DebugWebServer.prototype.cachedJsonCheck = function (cacheKey, msg) {
-    var newJson = JSON.stringify(msg);
-    if (this.cachedJson[cacheKey] === newJson) {
-        return true;  // cached
-    }
-    this.cachedJson[cacheKey] = newJson;
-    return false;  // not cached, send (cache already updated)
-};
-
-DebugWebServer.prototype.uiMessageLimiterCallback = function () {
-    this.emitUiMessages();
-};
-
-DebugWebServer.prototype.execStatusLimiterCallback = function () {
-    this.emitExecStatus();
-};
-
-DebugWebServer.prototype.debugStatsLimiterCallback = function () {
-    this.emitStats();
-};
-
-DebugWebServer.prototype.emitKeepalive = function () {
-    if (!this.socket) { return; }
-
-    this.socket.emit('keepalive', { nodeVersion: process.version });
-};
-
-DebugWebServer.prototype.emitBasicInfo = function () {
-    if (!this.socket) { return; }
-
-    var newMsg = {
-        duk_version: this.dbg.dukVersion,
-        duk_git_describe: this.dbg.dukGitDescribe,
-        target_info: this.dbg.targetInfo,
-        endianness: this.dbg.endianness
-    };
-    if (this.cachedJsonCheck('basic-info', newMsg)) {
-        return;
-    }
-    this.socket.emit('basic-info', newMsg);
-};
-
-DebugWebServer.prototype.emitStats = function () {
-    if (!this.socket) { return; }
-
-    this.socket.emit('debug-stats', this.dbg.stats);
-};
-
-DebugWebServer.prototype.emitExecStatus = function () {
-    if (!this.socket) { return; }
-
-    var newMsg = this.dbg.execStatus;
-    if (this.cachedJsonCheck('exec-status', newMsg)) {
-        return;
-    }
-    this.socket.emit('exec-status', newMsg);
-};
-
-DebugWebServer.prototype.emitUiMessages = function () {
-    if (!this.socket) { return; }
-
-    var newMsg = this.dbg.messageLines;
-    if (this.cachedJsonCheck('output-lines', newMsg)) {
-        return;
-    }
-    this.socket.emit('output-lines', newMsg);
-};
-
-DebugWebServer.prototype.emitBreakpoints = function () {
-    if (!this.socket) { return; }
-
-    var newMsg = { breakpoints: this.dbg.breakpoints };
-    if (this.cachedJsonCheck('breakpoints', newMsg)) {
-        return;
-    }
-    this.socket.emit('breakpoints', newMsg);
-};
-
-DebugWebServer.prototype.emitCallStack = function () {
-    if (!this.socket) { return; }
-
-    var newMsg = { callstack: this.dbg.callstack };
-    if (this.cachedJsonCheck('callstack', newMsg)) {
-        return;
-    }
-    this.socket.emit('callstack', newMsg);
-};
-
-DebugWebServer.prototype.emitLocals = function () {
-    if (!this.socket) { return; }
-
-    var newMsg = { locals: this.dbg.locals };
-    if (this.cachedJsonCheck('locals', newMsg)) {
-        return;
-    }
-    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 (cmdName, cmdNumber) {
-    var ret;
-    if (typeof cmdName === 'string') {
-        ret = debugCommandNumbers[cmdName];
-    } else if (typeof cmdName === 'number') {
-        ret = cmdName;
-    }
-    ret = ret || cmdNumber;
-    if (typeof ret !== 'number') {
-        throw Error('cannot figure out command number for "' + cmdName + '" (' + 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: '_TargetConnected',
-            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[0].type === 'eom') {
-            throw new Error('unexpected initial dvalue: ' + msg[0]);
-        } else if (msg[0].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
- */
-
-function main() {
-    console.log('((o) Duktape debugger');
-
-    // Parse arguments.
-
-    var argv = require('minimist')(process.argv.slice(2));
-    //console.dir(argv);
-    if (argv['target-host']) {
-        optTargetHost = argv['target-host'];
-    }
-    if (argv['target-port']) {
-        optTargetPort = argv['target-port'];
-    }
-    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);
-    }
-    if (argv['dump-debug-read']) {
-        optDumpDebugRead = argv['dump-debug-read'];
-    }
-    if (argv['dump-debug-write']) {
-        optDumpDebugWrite = argv['dump-debug-write'];
-    }
-    if (argv['dump-debug-pretty']) {
-        optDumpDebugPretty = argv['dump-debug-pretty'];
-    }
-    if (argv['log-messages']) {
-        optLogMessages = true;
-    }
-
-    // Dump effective options.  Also provides a list of option names.
-
-    console.log('');
-    console.log('Effective options:');
-    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);
-    console.log('  --dump-debug-pretty: ' + optDumpDebugPretty);
-    console.log('  --log-messages:      ' + optLogMessages);
-    console.log('');
-
-    // Create debugger and web UI singletons, tie them together and
-    // start them.
-
-    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();