Updated Windows/OSX agent.
Ylian Saint-Hilaire committed
Nov 26, 2018 at 17:12 UTC
d636b76da6fa51f3546b7a6735e3025f6834d190
14 files changed
+2236
-49
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshagent_osx-x86-64
Binary files a/agents/meshagent_osx-x86-64 and b/agents/meshagent_osx-x86-64 differ
agents/meshcore.js
+3
@@ -19,6 +19,7 @@ process.on('uncaughtException', function (ex) {
19
require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException1: " + ex });
20
});
21
22
+//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
23
24
function createMeshCore(agent) {
25
var obj = {};
@@ -1526,6 +1527,7 @@ function createMeshCore(agent) {
1527
obj.setupMeiOsAdmin = function (func, state) {
1528
if ((amtMei == null) || (amtMeiConnected != 2)) { return; } // If there is no MEI, don't bother with this.
1529
amtMei.getLocalSystemAccount(function (x) {
1530
+ if (x == null) return;
1531
var transport = require('amt-wsman-duk');
1532
var wsman = require('amt-wsman');
1533
var amt = require('amt');
@@ -1537,6 +1539,7 @@ function createMeshCore(agent) {
1539
//*************************************
1540
// Setup KVM data channel if this is Intel AMT 12 or above
1541
amtMei.getVersion(function (x) {
1542
+ if (x == null) return;
1543
var amtver = null;
1544
try { for (var i in x.Versions) { if (x.Versions[i].Description == 'AMT') amtver = parseInt(x.Versions[i].Version.split('.')[0]); } } catch (e) { }
1545
if ((amtver != null) && (amtver >= 12)) {
agents/modules_meshcmd/user-sessions.js
+1
-1
@@ -215,7 +215,7 @@ function UserSessions()
215
self.parent._user32.ACDC_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_ACDC_POWER_SOURCE, 0);
216
self.parent._user32.BATT_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_BATTERY_PERCENTAGE_REMAINING, 0);
217
self.parent._user32.DISP_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_CONSOLE_DISPLAY_STATE, 0);
218
- console.log(self.parent._user32.ACDC_H.Val, self.parent._user32.BATT_H.Val, self.parent._user32.DISP_H.Val);
218
+ //console.log(self.parent._user32.ACDC_H.Val, self.parent._user32.BATT_H.Val, self.parent._user32.DISP_H.Val);
219
}, this);
220
});
221
this._messagepump.on('message', function (msg)
agents/modules_meshcore/amt-lme.js
+8
-5
@@ -172,11 +172,14 @@ function lme_heci(options) {
172
{ // Bind a new server socket if not already present
173
this[name][port] = require('net').createServer();
174
this[name][port].HECI = this;
175
- if (lme_port_offset == 0) {
176
- this[name][port].listen({ port: port, host: '127.0.0.1' }); // Normal mode
177
- } else {
178
- this[name][port].listen({ port: (port + lme_port_offset) }); // Debug mode
179
- }
175
+
176
+ try {
177
+ if (lme_port_offset == 0) {
178
+ this[name][port].listen({ port: port, host: '127.0.0.1' }); // Normal mode
179
+ } else {
180
+ this[name][port].listen({ port: (port + lme_port_offset) }); // Debug mode
181
+ }
182
+ } catch (ex) { console.log('Binding error, LMS port ' + (port + lme_port_offset) + ': ' + ex) } // TODO: We can't bind
183
this[name][port].on('connection', function (socket) {
184
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
185
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort - lme_port_offset);
agents/modules_meshcore/duktape-debugger.jsx
new
+2163
@@ -0,0 +1,2163 @@
1
+/*
2
+ *
3
+ * ===============
4
+ * Duktape license
5
+ * ===============
6
+ *
7
+ * (http://opensource.org/licenses/MIT)
8
+ *
9
+ * Copyright (c) 2013-2017 by Duktape authors (see AUTHORS.rst)
10
+ *
11
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ * of this software and associated documentation files (the "Software"), to deal
13
+ * in the Software without restriction, including without limitation the rights
14
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ * copies of the Software, and to permit persons to whom the Software is
16
+ * furnished to do so, subject to the following conditions:
17
+ *
18
+ * The above copyright notice and this permission notice shall be included in
19
+ * all copies or substantial portions of the Software.
20
+ *
21
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
+ * THE SOFTWARE.
28
+ */
29
+
30
+/* AUTHORS.rst */
31
+/*
32
+ * ===============
33
+ * Duktape authors
34
+ * ===============
35
+ *
36
+ * Copyright
37
+ * =========
38
+ *
39
+ * Duktape copyrights are held by its authors. Each author has a copyright
40
+ * to their contribution, and agrees to irrevocably license the contribution
41
+ * under the Duktape ``LICENSE.txt``.
42
+ *
43
+ * Authors
44
+ * =======
45
+ *
46
+ * Please include an e-mail address, a link to your GitHub profile, or something
47
+ * similar to allow your contribution to be identified accurately.
48
+ *
49
+ * The following people have contributed code, website contents, or Wiki contents,
50
+ * and agreed to irrevocably license their contributions under the Duktape
51
+ * ``LICENSE.txt`` (in order of appearance):
52
+ *
53
+ * * Sami Vaarala <sami.vaarala@iki.fi>
54
+ * * Niki Dobrev
55
+ * * Andreas \u00d6man <andreas@lonelycoder.com>
56
+ * * L\u00e1szl\u00f3 Lang\u00f3 <llango.u-szeged@partner.samsung.com>
57
+ * * Legimet <legimet.calc@gmail.com>
58
+ * * Karl Skomski <karl@skomski.com>
59
+ * * Bruce Pascoe <fatcerberus1@gmail.com>
60
+ * * Ren\u00e9 Hollander <rene@rene8888.at>
61
+ * * Julien Hamaide (https://github.com/crazyjul)
62
+ * * Sebastian G\u00f6tte (https://github.com/jaseg)
63
+ * * Tomasz Magulski (https://github.com/magul)
64
+ * * \D. Bohdan (https://github.com/dbohdan)
65
+ * * Ond\u0159ej Jirman (https://github.com/megous)
66
+ * * Sa\u00fal Ibarra Corretg\u00e9 <saghul@gmail.com>
67
+ * * Jeremy HU <huxingyi@msn.com>
68
+ * * Ole Andr\u00e9 Vadla Ravn\u00e5s (https://github.com/oleavr)
69
+ * * Harold Brenes (https://github.com/harold-b)
70
+ * * Oliver Crow (https://github.com/ocrow)
71
+ * * Jakub Ch\u0142api\u0144ski (https://github.com/jchlapinski)
72
+ * * Brett Vickers (https://github.com/beevik)
73
+ * * Dominik Okwieka (https://github.com/okitec)
74
+ * * Remko Tron\u00e7on (https://el-tramo.be)
75
+ * * Romero Malaquias (rbsm@ic.ufal.br)
76
+ * * Michael Drake <michael.drake@codethink.co.uk>
77
+ * * Steven Don (https://github.com/shdon)
78
+ * * Simon Stone (https://github.com/sstone1)
79
+ * * \J. McC. (https://github.com/jmhmccr)
80
+ *
81
+ * Other contributions
82
+ * ===================
83
+ *
84
+ * The following people have contributed something other than code (e.g. reported
85
+ * bugs, provided ideas, etc; roughly in order of appearance):
86
+ *
87
+ * * Greg Burns
88
+ * * Anthony Rabine
89
+ * * Carlos Costa
90
+ * * Aur\u00e9lien Bouilland
91
+ * * Preet Desai (Pris Matic)
92
+ * * judofyr (http://www.reddit.com/user/judofyr)
93
+ * * Jason Woofenden
94
+ * * Micha\u0142 Przyby\u015b
95
+ * * Anthony Howe
96
+ * * Conrad Pankoff
97
+ * * Jim Schimpf
98
+ * * Rajaran Gaunker (https://github.com/zimbabao)
99
+ * * Andreas \u00d6man
100
+ * * Doug Sanden
101
+ * * Josh Engebretson (https://github.com/JoshEngebretson)
102
+ * * Remo Eichenberger (https://github.com/remoe)
103
+ * * Mamod Mehyar (https://github.com/mamod)
104
+ * * David Demelier (https://github.com/markand)
105
+ * * Tim Caswell (https://github.com/creationix)
106
+ * * Mitchell Blank Jr (https://github.com/mitchblank)
107
+ * * https://github.com/yushli
108
+ * * Seo Sanghyeon (https://github.com/sanxiyn)
109
+ * * Han ChoongWoo (https://github.com/tunz)
110
+ * * Joshua Peek (https://github.com/josh)
111
+ * * Bruce E. Pascoe (https://github.com/fatcerberus)
112
+ * * https://github.com/Kelledin
113
+ * * https://github.com/sstruchtrup
114
+ * * Michael Drake (https://github.com/tlsa)
115
+ * * https://github.com/chris-y
116
+ * * Laurent Zubiaur (https://github.com/lzubiaur)
117
+ * * Neil Kolban (https://github.com/nkolban)
118
+ *
119
+ * If you are accidentally missing from this list, send me an e-mail
120
+ * (``sami.vaarala@iki.fi``) and I'll fix the omission.
121
+ *
122
+ *
123
+ * =================================================================
124
+ * Modifications made by Bryan Roe, Copyright 2018 Intel Corporation
125
+ * =================================================================
126
+ *
127
+ *
128
+ */
129
+
130
+
131
+var events = require('events');
132
+var Promise = require('promise');
133
+var dbgHTML = '77u/PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBYSFRNTCAxLjAgVHJhbnNpdGlvbmFsLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL1RSL3hodG1sMS9EVEQveGh0bWwxLXRyYW5zaXRpb25hbC5kdGQiPg0KPGh0bWw+DQo8aGVhZD4NCjxtZXRhIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD11dGYtOCIgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiAvPg0KPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJJRT1FREdFIiAvPg0KPG1ldGEgbmFtZT0iZm9ybWF0LWRldGVjdGlvbiIgY29udGVudD0idGVsZXBob25lPW5vIiAvPg0KPHRpdGxlPldlYlJUQyBEZWJ1ZzwvdGl0bGU+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KYm9keSANCnsNCiAgICBtYXJnaW46IDA7DQoJcGFkZGluZzogMDsNCglib3JkZXI6IDA7DQoJY29sb3I6IGJsYWNrOw0KCWZvbnQtc2l6ZTogMTNweDsNCglmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsIEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQoJYmFja2dyb3VuZC1jb2xvcjogI2QzZDlkNjsNCn0NCiNjb250YWluZXIgew0KCWJhY2tncm91bmQtY29sb3I6ICNmZmY7DQoJd2lkdGg6IDk2MHB4Ow0KCW1hcmdpbjogMCBhdXRvOw0KCWJvcmRlci10b3A6IDA7DQoJYm9yZGVyLXJpZ2h0OiAxcHggc29saWQgI2I3YjdiNzsNCglib3JkZXItYm90dG9tOiAwOw0KCWJvcmRlci1sZWZ0OiAxcHggc29saWQgI2I3YjdiNzsNCglwYWRkaW5nOiAwOw0KfQ0KI21hc3RoZWFkIHsNCgl3aWR0aDogYXV0bzsNCgltYXJnaW46IDA7DQoJcGFkZGluZzogMDsNCglvdmVyZmxvdzogYXV0bzsNCgl0ZXh0LWFsaWduOiByaWdodDsNCgliYWNrZ3JvdW5kLWNvbG9yOiAjMDM2Ow0KCXdpZHRoOiA5NjBweDsNCg0KICAgIGJhY2tncm91bmQ6IHJnYig0NSw4NiwxMzcpOyAvKiBPbGQgYnJvd3NlcnMgKi8NCiAgICBiYWNrZ3JvdW5kOiAtbW96LWxpbmVhci1ncmFkaWVudChsZWZ0LCAgcmdiYSg0NSw4NiwxMzcsMSkgMCUsIHJnYmEoMCw1MSwxMDIsMSkgMjklKTsgLyogRkYzLjYrICovDQogICAgYmFja2dyb3VuZDogLXdlYmtpdC1ncmFkaWVudChsaW5lYXIsIGxlZnQgdG9wLCByaWdodCB0b3AsIGNvbG9yLXN0b3AoMCUscmdiYSg0NSw4NiwxMzcsMSkpLCBjb2xvci1zdG9wKDI5JSxyZ2JhKDAsNTEsMTAyLDEpKSk7IC8qIENocm9tZSxTYWZhcmk0KyAqLw0KICAgIGJhY2tncm91bmQ6IC13ZWJraXQtbGluZWFyLWdyYWRpZW50KGxlZnQsICByZ2JhKDQ1LDg2LDEzNywxKSAwJSxyZ2JhKDAsNTEsMTAyLDEpIDI5JSk7IC8qIENocm9tZTEwKyxTYWZhcmk1LjErICovDQogICAgYmFja2dyb3VuZDogLW8tbGluZWFyLWdyYWRpZW50KGxlZnQsICByZ2JhKDQ1LDg2LDEzNywxKSAwJSxyZ2JhKDAsNTEsMTAyLDEpIDI5JSk7IC8qIE9wZXJhIDExLjEwKyAqLw0KICAgIGJhY2tncm91bmQ6IC1tcy1saW5lYXItZ3JhZGllbnQobGVmdCwgIHJnYmEoNDUsODYsMTM3LDEpIDAlLHJnYmEoMCw1MSwxMDIsMSkgMjklKTsgLyogSUUxMCsgKi8NCiAgICBiYWNrZ3JvdW5kOiBsaW5lYXItZ3JhZGllbnQodG8gcmlnaHQsICByZ2JhKDQ1LDg2LDEzNywxKSAwJSxyZ2JhKDAsNTEsMTAyLDEpIDI5JSk7IC8qIFczQyAqLw0KICAgIGZpbHRlcjogcHJvZ2lkOkRYSW1hZ2VUcmFuc2Zvcm0uTWljcm9zb2Z0LmdyYWRpZW50KCBzdGFydENvbG9yc3RyPScjMmQ1Njg5JywgZW5kQ29sb3JzdHI9JyMwMDMzNjYnLEdyYWRpZW50VHlwZT0xICk7IC8qIElFNi05ICovDQp9DQojY29sdW1uX2wgew0KCXBvc2l0aW9uOiByZWxhdGl2ZTsNCglmbG9hdDogbGVmdDsNCgl3aWR0aDogOTMwcHg7DQoJbWFyZ2luOiAwOw0KCXBhZGRpbmc6IDAgMTVweDsNCgliYWNrZ3JvdW5kLWNvbG9yOiAjZmZmOw0KfQ0KI2Zvb3RlciB7DQoJY2xlYXI6IGJvdGg7DQoJb3ZlcmZsb3c6IGF1dG87DQoJd2lkdGg6IDk2MHB4Ow0KCXRleHQtYWxpZ246IGNlbnRlcjsNCgliYWNrZ3JvdW5kLWNvbG9yOiAjMTEzOTYyOw0KCXBhZGRpbmctdG9wOiA1cHg7DQoJcGFkZGluZy1ib3R0b206IDVweDsNCn0NCiNmb290ZXIgYSB7DQoJY29sb3I6ICNmZmY7DQoJdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7DQp9DQojZm9vdGVyIGE6aG92ZXIgew0KCWNvbG9yOiAjZmZmOw0KCXRleHQtZGVjb3JhdGlvbjogbm9uZTsNCn0NCmEgew0KCWNvbG9yOiAjMDM2Ow0KCXRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lOw0KfQ0KLnN0eWxlMyB7DQoJdGV4dC1hbGlnbjogY2VudGVyOw0KCWNvbG9yOiB3aGl0ZTsNCgliYWNrZ3JvdW5kLWNvbG9yOiAjODA4MDgwOw0KCWZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KLnN0eWxlNiB7DQogICAgcGFkZGluZy10b3A6IDJweDsNCiAgICBwYWRkaW5nLWJvdHRvbTogMnB4Ow0KCWJhY2tncm91bmQtY29sb3I6ICNDMEMwQzA7DQp9DQoueHNlY3Rpb24gew0KICAgIGJhY2tncm91bmQtY29sb3I6ICNFMEUwRTA7DQogICAgcGFkZGluZzogNXB4IDIwcHggMjVweCAyMHB4Ow0KICAgIGJvcmRlci1yYWRpdXM6IDEwcHg7DQp9DQouZml4ZWRmb250IHsNCiAgICBmb250LWZhbWlseTpjb3VyaWVyLCAiY291cmllciBuZXciLCBtb25vc3BhY2U7DQp9DQouaW5mb2VudHJ5IHsNCiAgICBwYWRkaW5nLXRvcDoxcHg7DQp9DQouR0VORVJJQ19NT0RVTEV7DQogICAgY29sb3I6YmxhY2s7DQp9DQouVkFSUVVFUll7DQogICAgY29sb3I6YmxhY2s7DQp9DQouVkFSUkVTVUxUew0KICAgIGNvbG9yOmRhcmtncmVlbjsNCn0NCi5CUkVBS1BPSU5Uew0KICAgIGNvbG9yOmRhcmt2aW9sZXQNCn0NCi5CUkVBS1BPSU5UOmhvdmVyIA0Kew0KCWJhY2tncm91bmQtY29sb3I6ICNFRUU7DQp9DQouU1RBVEVDSEFOR0V7DQogICAgY29sb3I6ZGFya3Zpb2xldA0KfQ0KLlNPVVJDRUNPREVQQVJFTlQgDQp7DQogICAgd2lkdGg6MTAwMDBweA0KfQ0KLlNPVVJDRUNPREVQQVJFTlQ6aG92ZXIgDQp7DQoJYmFja2dyb3VuZC1jb2xvcjogI0VFRTsNCn0NCi5TT1VSQ0VDT0RFew0KICAgIGNvbG9yOmRhcmtncmVlbjsNCiAgICBwYWRkaW5nLWxlZnQ6MTBweDsNCiAgICBkaXNwbGF5OmlubGluZS1ibG9jazsNCn0NCi5TT1VSQ0VDT0RFTElORQ0Kew0KICAgIHdpZHRoOjMwcHg7DQogICAgdGV4dC1hbGlnbjpyaWdodDsNCiAgICBjb2xvcjpncmF5Ow0KICAgIGRpc3BsYXk6aW5saW5lLWJsb2NrOw0KICAgIC13ZWJraXQtdXNlci1zZWxlY3Q6bm9uZTsNCiAgICAta2h0bWwtdXNlci1zZWxlY3Q6bm9uZTsNCiAgICAtbW96LXVzZXItc2VsZWN0Om5vbmU7DQogICAgLW1zLXVzZXItc2VsZWN0Om5vbmU7DQogICAgLW8tdXNlci1zZWxlY3Q6bm9uZTsNCiAgICAtdXNlci1zZWxlY3Q6bm9uZTsNCn0NCi5DQUxMU1RBQ0sNCnsNCiAgICBjb2xvcjpzYWRkbGVicm93bjsNCiAgICBmb250LXdlaWdodDpub3JtYWw7DQogICAgLXdlYmtpdC11c2VyLXNlbGVjdDpub25lOw0KICAgIC1raHRtbC11c2VyLXNlbGVjdDpub25lOw0KICAgIC1tb3otdXNlci1zZWxlY3Q6bm9uZTsNCiAgICAtbXMtdXNlci1zZWxlY3Q6bm9uZTsNCiAgICAtby11c2VyLXNlbGVjdDpub25lOw0KICAgIC11c2VyLXNlbGVjdDpub25lOw0KfQ0KLkNBTExTVEFDSzpob3Zlcg0Kew0KCWJhY2tncm91bmQtY29sb3I6ICNFRUU7DQp9DQouRVJST1J7DQogICAgY29sb3I6cmVkOw0KfQ0KLlVOS05PV05fTU9EVUxFew0KICAgIGNvbG9yOnJlZDsNCn0NCjwvc3R5bGU+DQo8L2hlYWQ+DQo8Ym9keSBvbmxvYWQ9ImlmICh0eXBlb2Yoc3RhcnR1cCkgIT09ICd1bmRlZmluZWQnKSBzdGFydHVwKCk7Ij4NCgk8ZGl2IGlkPSJjb250YWluZXIiPg0KCQk8ZGl2IGlkPSJtYXN0aGVhZCIgc3R5bGU9ImhlaWdodDogNjZweDsgd2lkdGg6IDEwMCU7IG92ZXJmbG93OmhpZGRlbiI+DQoJICAgICAgICA8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0OyBoZWlnaHQ6IDY2cHg7IGNvbG9yOiNjOGM4Yzg7IHBhZGRpbmctbGVmdDoyMHB4OyBwYWRkaW5nLXRvcDo4cHgiPjxzdHJvbmc+PGZvbnQgc3R5bGU9ImZvbnQtc2l6ZTo0NnB4OyBmb250LWZhbWlseTogQXJpYWwsIEhlbHZldGljYSwgc2Fucy1zZXJpZjsiPkJ1aWx0LWluIEpTIERlYnVnZ2VyPC9mb250Pjwvc3Ryb25nPjwvZGl2Pg0KCSAgICAgICAgPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDsgaGVpZ2h0OiA2NnB4OyBjb2xvcjojYzhjOGM4OyBwYWRkaW5nLWxlZnQ6NXB4OyBwYWRkaW5nLXRvcDoxNHB4Ij48c3Ryb25nPjxmb250IHN0eWxlPSJmb250LXNpemU6MTRweDsgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7Ij48c3BhbiBpZD0idGl0bGVob3N0Ij48L3NwYW4+PC9mb250Pjwvc3Ryb25nPjwvZGl2Pg0KCQk8L2Rpdj4NCgkJPGRpdiBpZD0idG9wYmFyIj4NCgkJCTx0YWJsZSBzdHlsZT0id2lkdGg6IDEwMCU7IGhlaWdodDogMjJweDsiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgY2xhc3M9InN0eWxlMSI+DQoJCQkJPHRyPg0KCQkJCQk8dGQgaWQ9IkF0dGFjaEJ1dHRvbiIgc3R5bGU9IndpZHRoOiAxMDBweDsgaGVpZ2h0OiAyNHB4OyBjdXJzb3I6ZGVmYXVsdDsiIG9uY2xpY2s9ImF0dGFjaERlYnVnZ2VyKCkiIGNsYXNzPSJzdHlsZTMiPkFUVEFDSDwvdGQ+DQoJCQkJCTx0ZCBpZD0iUGF1c2VSZXN1bWVCdXR0b24iIHN0eWxlPSJ3aWR0aDogMTAwcHg7IGhlaWdodDogMjRweDsgY3Vyc29yOmRlZmF1bHQ7IiBvbmNsaWNrPSJwYXVzZVJlc3VtZSgpIiBjbGFzcz0ic3R5bGUzIj5QQVVTRTwvdGQ+DQogICAgICAgICAgICAgICAgICAgIDx0ZCBpZD0iU3RlcE92ZXJCdXR0b24iIHN0eWxlPSJ3aWR0aDogMTAwcHg7IGhlaWdodDogMjRweDsgY3Vyc29yOmRlZmF1bHQ7IiBvbmNsaWNrPSJzdGVwT3ZlcigpIiBjbGFzcz0ic3R5bGUzIj5TdGVwLU92ZXI8L3RkPg0KICAgICAgICAgICAgICAgICAgICA8dGQgaWQ9IlN0ZXBJbnRvQnV0dG9uIiBzdHlsZT0id2lkdGg6IDEwMHB4OyBoZWlnaHQ6IDI0cHg7IGN1cnNvcjpkZWZhdWx0OyIgb25jbGljaz0ic3RlcEludG8oKSIgY2xhc3M9InN0eWxlMyI+U3RlcC1JbnRvPC90ZD4NCiAgICAgICAgICAgICAgICAgICAgPHRkIGlkPSJTdGVwT3V0QnV0dG9uIiBzdHlsZT0id2lkdGg6IDEwMHB4OyBoZWlnaHQ6IDI0cHg7IGN1cnNvcjpkZWZhdWx0OyIgb25jbGljaz0ic3RlcE91dCgpIiBjbGFzcz0ic3R5bGUzIj5TdGVwLU91dDwvdGQ+DQogICAgICAgICAgICAgICAgICAgIDwhLS08dGQgaWQ9IkhlYXBCdXR0b24iIHN0eWxlPSJ3aWR0aDogMTAwcHg7IGhlaWdodDogMjRweDsgY3Vyc29yOmRlZmF1bHQ7IiBvbmNsaWNrPSJvbkR1bXBIZWFwKCkiIGNsYXNzPSJzdHlsZTMiPlZpZXcgSGVhcDwvdGQ+IC0tPg0KCQkJCTwvdHI+DQoJCQk8L3RhYmxlPg0KICAgICAgICAgICAgPGRpdiBpZD0iY29sdW1uX2wiPg0KICAgICAgICAgICAgICAgIDxkaXYgaWQ9InN0YXR1c3RleHQiIGFsaWduPSJjZW50ZXIiIHN0eWxlPSJoZWlnaHQ6MjBweDsgZm9udC1zaXplOjE1cHgiPg0KICAgICAgICAgICAgICAgIDwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQoJCTwvZGl2Pg0KCQk8ZGl2IGlkPSJwYWdlX2NvbnRlbnQiPg0KICAgICAgICAgICAgPGRpdiBzdHlsZT0id2lkdGg6IDc1JTsgZmxvYXQ6bGVmdCI+DQogICAgICAgICAgICAgICAgPGRpdiBpZD0iQ2FsbHN0YWNrV2luZG93IiBzdHlsZT0id2lkdGg6IDEwMCU7aGVpZ2h0OjEwMHB4O292ZXJmbG93LXk6c2Nyb2xsIj4NCiAgICAgICAgICAgICAgICA8L2Rpdj4JDQogICAgICAgICAgICAgICAgPGRpdiBpZD0iTG9nV2luZG93IiBzdHlsZT0id2lkdGg6IDEwMCU7aGVpZ2h0OjUwMHB4O292ZXJmbG93LXk6c2Nyb2xsIj4NCiAgICAgICAgICAgICAgICA8L2Rpdj4JDQogICAgICAgICAgICA8L2Rpdj4NCiAgICAgICAgICAgIDxkaXYgc3R5bGU9IndpZHRoOiAyNSU7IGZsb2F0OnJpZ2h0Ij4NCiAgICAgICAgICAgICAgICA8ZGl2IGlkPSJMb2dXaW5kb3cyIiBzdHlsZT0id2lkdGg6IDEwMCU7aGVpZ2h0OjYwMHB4O292ZXJmbG93LXk6c2Nyb2xsIj4NCiAgICAgICAgICAgICAgICAgICAgPHRleHRhcmVhIGlkPSJsb2FkU291cmNlVGV4dCIgZGlzYWJsZWQgc3R5bGU9IndpZHRoOjk1JTtoZWlnaHQ6MzBweCI+PC90ZXh0YXJlYT4NCiAgICAgICAgICAgICAgICAgICAgPGlucHV0IGlkPSJsb2FkU291cmNlQnV0dG9uIiB0eXBlPSJidXR0b24iIHZhbHVlPSJMb2FkIFNvdXJjZSIgZGlzYWJsZWQgb25jbGljaz0ibWFudWFsbHlMb2FkU291cmNlKCkiIC8+DQogICAgICAgICAgICAgICAgICAgIDxwLz4NCiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0nR0VORVJJQ19NT0RVTEUnPkJyZWFrcG9pbnRzPC9kaXY+DQogICAgICAgICAgICAgICAgICAgIDxkaXYgaWQ9IkN1cnJlbnRCcmVha3BvaW50cyIgc3R5bGU9IndpZHRoOiA5NSU7aGVpZ2h0OjIwMHB4O292ZXJmbG93LXk6c2Nyb2xsIj4NCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICAgICAgICAgIDxpbnB1dCBpZD0iZGVsQnJlYWtwb2ludEJ1dHRvbiIgdHlwZT0iYnV0dG9uIiB2YWx1ZT0iRGVsIEJyZWFrcG9pbnQiIGRpc2FibGVkIG9uY2xpY2s9ImRlbEJyZWFrcG9pbnQoKSIgLz4NCiAgICAgICAgICAgICAgICAgICAgPHAgLz4NCiAgICAgICAgICAgICAgICAgICAgPHRleHRhcmVhIGlkPSJxdWVyeVRleHQiIGRpc2FibGVkIHN0eWxlPSJ3aWR0aDo5NSU7aGVpZ2h0OjE1cHgiPjwvdGV4dGFyZWE+DQogICAgICAgICAgICAgICAgICAgIDxpbnB1dCBpZD0icXVlcnlCdXR0b24iIHR5cGU9ImJ1dHRvbiIgdmFsdWU9IlF1ZXJ5IiBkaXNhYmxlZCBvbmNsaWNrPSJxdWVyeVZhbCgpIiAvPg0KICAgICAgICAgICAgICAgICAgICA8aW5wdXQgaWQ9ImV2YWxCdXR0b24iIHR5cGU9ImJ1dHRvbiIgdmFsdWU9IkV2YWwiIGRpc2FibGVkIG9uY2xpY2s9ImV2YWxTdHJpbmcoKSIgLz4NCiAgICAgICAgICAgICAgICAgICAgPGlucHV0IGlkPSJjbGVhckJ1dHRvbiIgdHlwZT0iYnV0dG9uIiB2YWx1ZT0iQ2xlYXIiIGRpc2FibGVkIG9uY2xpY2s9ImNsZWFyUXVlcnkoKSIgLz4NCiAgICAgICAgICAgICAgICAgICAgPGlucHV0IGlkPSJsb2NhbHNCdXR0b24iIHR5cGU9ImJ1dHRvbiIgdmFsdWU9IkxvY2FscyIgZGlzYWJsZWQgb25jbGljaz0ibG9jYWxzUXVlcnkoKSIgLz4NCiAgICAgICAgICAgICAgICAgICAgPHAgLz4NCiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0nR0VORVJJQ19NT0RVTEUnPlJlc3VsdHM8L2Rpdj4NCiAgICAgICAgICAgICAgICAgICAgPGRpdiBpZD0icXVlcnlSZXN1bHRzIiBzdHlsZT0id2lkdGg6IDk1JTtoZWlnaHQ6MjAwcHg7b3ZlcmZsb3cteTpzY3JvbGwiPg0KICAgICAgICAgICAgICAgICAgICA8L2Rpdj4JDQoNCiAgICAgICAgICAgICAgICA8L2Rpdj4JDQogICAgICAgICAgICA8L2Rpdj4NCg0KICAgICAgICAgICAgPGRpdiBpZD0iZm9vdGVyIj4NCiAgICAgICAgICAgICAgICA8dGFibGUgY2VsbHBhZGRpbmc9IjAiIGNlbGxzcGFjaW5nPSIxMCIgc3R5bGU9IndpZHRoOiAxMDAlIj4NCiAgICAgICAgICAgICAgICAgICAgPHRyPg0KICAgICAgICAgICAgICAgICAgICAgICAgPHRkIGlkPSd4Zm9vdGVyJyBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyBkaXNwbGF5Om5vbmUiPjwvdGQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8dGQgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyO2NvbG9yOmxpZ2h0Z3JheSI+VG90YWwgTWVtb3J5IEFsbG9jYXRpb246IDxiIGlkPSJtZW11c2FnZSIgc3R5bGU9ImNvbG9yOnllbGxvdyI+PC9iPjwvdGQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8dGQ+PGlucHV0IGlkPSJnY0J1dHRvbiIgdHlwZT0iYnV0dG9uIiB2YWx1ZT0iR0MiIG9uY2xpY2s9ImZvcmNlR0MoKSIgLz48L3RkPg0KICAgICAgICAgICAgICAgICAgICAgPC90cj4NCiAgICAgICAgICAgICAgICA8L3RhYmxlPg0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgPC9kaXY+DQoNCiAgICA8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+DSAgICAgICAgdmFyIGJyZWFrcG9pbnRJRCA9IDA7DSAgICAgICAgdmFyIHNlbGVjdGVkQnJlYWtwb2ludCA9ICIiOw0gICAgICAgIHZhciB3c29ja2V0Ow0gICAgICAgIHZhciBjb25uZWN0ZWQgPSBmYWxzZTsNICAgICAgICB2YXIgY3VycmVudExpbmUgPSAwOw0gICAgICAgIHZhciBjdXJyZW50TW9kdWxlID0gJyc7DSAgICAgICAgdmFyIGN1cnJlbnRNb2R1bGVUb2tlbnMgPSBudWxsOw0gICAgICAgIHZhciBzb3VyY2VIaWdobGlnaHQgPSAneWVsbG93JzsNICAgICAgICB2YXIgZXhjZXB0aW9uSGlnaGxpZ2h0ID0gJ2RlZXBwaW5rJzsNICAgICAgICB2YXIgYnBDb2xvciA9ICdibHVlJzsNICAgICAgICB2YXIgZXhjZXB0aW9uTWVzc2FnZSA9IG51bGw7DSAgICAgICAgdmFyIGhpZ2hsaWdodCA9IHNvdXJjZUhpZ2hsaWdodDsNICAgICAgICB2YXIgY2FsbHN0YWNrX3Njb3BlID0gJ3JveWFsYmx1ZSc7DSAgICAgICAgdmFyIGNhbGxzdGFja19kZWZhdWx0ID0gJ3NhZGRsZWJyb3duJzsNICAgICAgICB2YXIgY3VycmVudENhbGxzdGFjayA9IFtdOw0gICAgICAgIHZhciBpc05hdGl2ZSA9IGZhbHNlOw0NICAgICAgICBmdW5jdGlvbiBRKHgpIHsgcmV0dXJuIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHgpOyB9IC8vICJRIg0KICAgICAgICBmdW5jdGlvbiBRQyh4LCB5KSB7IFFTKHgpWyJjdXJzb3IiXSA9IHk7fQ0KICAgICAgICBmdW5jdGlvbiBRUyh4KSB7IHJldHVybiBRKHgpLnN0eWxlOyB9ICAgICAgICAgICAgICAgIC8vICJRIiBzdHlsZQ0KICAgICAgICBmdW5jdGlvbiBRRSh4LCB5KSB7IFEoeCkuZGlzYWJsZWQgPSAheTsgfSAgICAgICAgICAgIC8vICJRIiBlbmFibGUNCiAgICAgICAgZnVuY3Rpb24gUVYoeCwgeSkgeyBRUyh4KS5kaXNwbGF5ID0gKHkgPyAnJyA6ICdub25lJyk7IH0gLy8gIlEiIHZpc2libGUNCiAgICAgICAgZnVuY3Rpb24gUUEoeCwgeSkgeyBRKHgpLmlubmVySFRNTCArPSB5OyB9ICAgICAgICAgICAvLyAiUSIgYXBwZW5kDQogICAgICAgIGZ1bmN0aW9uIFFIKHgsIHkpIHsgUSh4KS5pbm5lckhUTUwgPSB5OyB9ICAgICAgICAgICAgLy8gIlEiIGh0bWwNCiAgICAgICAgZnVuY3Rpb24gUUhWQUwoeCkgeyByZXR1cm4gKFEoeCkuaW5uZXJIVE1MKTsgfQ0KICAgICAgICBmdW5jdGlvbiBRVkFMKHgsIHkpIHsgaWYgKHkgPT0gdW5kZWZpbmVkKSB7IHJldHVybiAoUSh4KS52YWx1ZSk7IH0gZWxzZSB7IFEoeCkudmFsdWUgPSB5OyB9IH0NCiAgICAgICAgZnVuY3Rpb24gUUJDKHgsIHkpIHsgUVMoeClbImJhY2tncm91bmQtY29sb3IiXSA9IHk7IH0NCiAgICAgICAgZnVuY3Rpb24gUUNISUxEUkVOKHgpIHsgcmV0dXJuIChRKHgpLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdkaXYnKSk7IH0NDSAgICAgICAgZnVuY3Rpb24gSW50MzJUb1N0cih2KSB7IHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKCh2ID4+IDI0KSAmIDB4RkYsICh2ID4+IDE2KSAmIDB4RkYsICh2ID4+IDgpICYgMHhGRiwgdiAmIDB4RkYpOyB9DQogICAgICAgIGZ1bmN0aW9uIEludDE2VG9TdHIodikgeyByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZSgodiA+PiA4KSAmIDB4RkYsIHYgJiAweEZGKTsgfQ0KICAgICAgICBmdW5jdGlvbiBSZWFkU2hvcnQoZGF0YSwgcHRyKSB7IHJldHVybiAoZGF0YS5jaGFyQ29kZUF0KHB0cikgPDwgOCkgKyBkYXRhLmNoYXJDb2RlQXQocHRyICsgMSk7IH0NCiAgICAgICAgZnVuY3Rpb24gUmVhZEludChkYXRhLCBwdHIpIHsgcmV0dXJuIChkYXRhLmNoYXJDb2RlQXQocHRyKSAqIDE2Nzc3MjE2KSArIChkYXRhLmNoYXJDb2RlQXQocHRyICsgMSkgKiA2NTUzNikgKyAoZGF0YS5jaGFyQ29kZUF0KHB0ciArIDIpICogMjU2KSArIGRhdGEuY2hhckNvZGVBdChwdHIgKyAzKTsgfQ0KICAgICAgICBmdW5jdGlvbiBSZWFkTG9uZyhkYXRhLCBwdHIpIHsgcmV0dXJuICgoZGF0YS5jaGFyQ29kZUF0KHB0cikgKiA3MjA1NzU5NDAzNzkyNzkzNikgKyAoZGF0YS5jaGFyQ29kZUF0KHB0ciArIDEpICogMjgxNDc0OTc2NzEwNjU2KSArIChkYXRhLmNoYXJDb2RlQXQocHRyICsgMikgKiAxMDk5NTExNjI3Nzc2KSArIChkYXRhLmNoYXJDb2RlQXQocHRyICsgMykgKiA0Mjk0OTY3Mjk2KSArIGRhdGEuY2hhckNvZGVBdChwdHIgKyA0KSAqIDE2Nzc3MjE2KSArIChkYXRhLmNoYXJDb2RlQXQocHRyICsgNSkgKiA2NTUzNikgKyAoZGF0YS5jaGFyQ29kZUF0KHB0ciArIDYpICogMjU2KSArIGRhdGEuY2hhckNvZGVBdChwdHIgKyA3KTsgfQ0KDSAgICAgICAgZnVuY3Rpb24gU2VuZENvbW1hbmQoY21kLCBkYXRhKQ0KICAgICAgICB7DQogICAgICAgICAgICB3c29ja2V0LnNlbmQoSW50MTZUb1N0cihjbWQpICsgZGF0YSk7DQogICAgICAgIH0NDSAgICAgICAgZnVuY3Rpb24gQWRkTG9nKGNvZGUsIG1zZykNCiAgICAgICAgew0KICAgICAgICAgICAgdmFyIGRjbGFzcyA9ICIiOw0KICAgICAgICAgICAgaWYgKG1zZyA9PSB1bmRlZmluZWQpIHsNCiAgICAgICAgICAgICAgICBRQSgiTG9nV2luZG93IiwgIjxkaXYgY2xhc3M9J0dFTkVSSUNfTU9EVUxFJyA+ICIgKyBjb2RlICsgIjwvYnI+PC9kaXY+Iik7DQogICAgICAgICAgICAgICAgcmV0dXJuOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBkY2xhc3MgPSBjb2RlOw0KDQogICAgICAgICAgICBRQSgiTG9nV2luZG93IiwgIjxkaXYgY2xhc3M9JyIgKyBkY2xhc3MgKyAiJyA+ICIgKyBtc2cgKyAiPC9icj48L2Rpdj4iKTsNCiAgICAgICAgfQ0gICAgICAgIGZ1bmN0aW9uIHN0YXJ0dXAoKQ0KICAgICAgICB7DQogICAgICAgICAgICBkaXNwbGF5U3RhdHVzKCJFc3RhYmxpc2hpbmcgY29ubmVjdGlvbi4uLiIpOyAgICAgICAgICANCiAgICAgICAgICAgIHdzb2NrZXQgPSBuZXcgV2ViU29ja2V0KCJ3czovLyIgKyB3aW5kb3cubG9jYXRpb24uaG9zdG5hbWUgKyAiOiIgKyB3aW5kb3cubG9jYXRpb24ucG9ydCArICIvIik7DQogICAgICAgICAgICB3c29ja2V0LmJpbmFyeVR5cGUgPSAiYXJyYXlidWZmZXIiOw0KICAgICAgICAgICAgd3NvY2tldC5vbm9wZW4gPSBmdW5jdGlvbiAoZXZ0KQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGNvbm5lY3RlZCA9IHRydWU7IGRpc3BsYXlTdGF0dXMoIkRlYnVnIENsaWVudCBDb25uZWN0ZWQuLi4iKTsNCiAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiQXR0YWNoQnV0dG9uIiksIDEpOw0KICAgICAgICAgICAgICAgIGF0dGFjaERlYnVnZ2VyKCk7DQogICAgICAgICAgICB9DQogICAgICAgICAgICB3c29ja2V0Lm9uY2xvc2UgPSBmdW5jdGlvbiAoZXZ0KQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGNvbm5lY3RlZCA9IGZhbHNlOyBkaXNwbGF5U3RhdHVzKCJEZWJ1ZyBDbGllbnQgQ29ubmVjdGlvbiBsb3N0Li4uIFJldHJ5aW5nLi4uIik7DQogICAgICAgICAgICAgICAgdG9nZ2xlQ29sb3IoUVMoIkF0dGFjaEJ1dHRvbiIpLCAwKTsNCiAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcE92ZXJCdXR0b24iKSwgMCk7DQogICAgICAgICAgICAgICAgdG9nZ2xlQ29sb3IoUVMoIlN0ZXBJbnRvQnV0dG9uIiksIDApOw0KICAgICAgICAgICAgICAgIHRvZ2dsZUNvbG9yKFFTKCJTdGVwT3V0QnV0dG9uIiksIDApOw0KICAgICAgICAgICAgICAgIHRvZ2dsZUNvbG9yKFFTKCJQYXVzZVJlc3VtZUJ1dHRvbiIpLCAwKTsNCiAgICAgICAgICAgICAgICBRRSgibG9hZFNvdXJjZUJ1dHRvbiIsIDApOw0KICAgICAgICAgICAgICAgIFFFKCJsb2FkU291cmNlVGV4dCIsIDApOw0KICAgICAgICAgICAgICAgIFFFKCdxdWVyeUJ1dHRvbicsIDApOw0KICAgICAgICAgICAgICAgIFFFKCdjbGVhckJ1dHRvbicsIDApOw0KICAgICAgICAgICAgICAgIFFFKCdsb2NhbHNCdXR0b24nLCAwKTsNCiAgICAgICAgICAgICAgICBRRSgnZXZhbEJ1dHRvbicsIDApOw0KICAgICAgICAgICAgICAgIFFWKCdwYWdlX2NvbnRlbnQnLCBmYWxzZSk7DQogICAgICAgICAgICAgICAgc2V0VGltZW91dChzZXJ2ZXJQb2xsLCAxMDAwKTsNCiAgICAgICAgICAgICAgICBjdXJyZW50TW9kdWxlID0gJyc7DQogICAgICAgICAgICB9DQogICAgICAgICAgICB3c29ja2V0Lm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChldnQpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgdmFyIG1zZyA9IEpTT04ucGFyc2UoZXZ0LmRhdGEpOw0KDQogICAgICAgICAgICAgICAgc3dpdGNoKG1zZy5jbWQpDQogICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICBjYXNlICdNRU1PUlknOg0KICAgICAgICAgICAgICAgICAgICAgICAgUUgoJ21lbXVzYWdlJywgTWF0aC5yb3VuZChtc2cudG90YWwvMTAyNCkgKyAnIGtiJyk7DQogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgY2FzZSAnQVRUQUNIJzoNCiAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlTdGF0dXMoJ0F0dGFjaGVkLi4uJyk7DQogICAgICAgICAgICAgICAgICAgICAgICBRSCgiQXR0YWNoQnV0dG9uIiwgIkRFVEFDSCIpOw0KICAgICAgICAgICAgICAgICAgICAgICAgdG9nZ2xlQ29sb3IoUVMoIlBhdXNlUmVzdW1lQnV0dG9uIiksIDEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgUUUoImxvYWRTb3VyY2VCdXR0b24iLCAxKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCJsb2FkU291cmNlVGV4dCIsIDEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgUVYoJ3BhZ2VfY29udGVudCcsIHRydWUpOw0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIGNhc2UgJ1BBVVNFJzoNCiAgICAgICAgICAgICAgICAgICAgICAgIGlzTmF0aXZlID0gbXNnLm5hdGl2ZTsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhpZ2hsaWdodCA9PSBleGNlcHRpb25IaWdobGlnaHQpDQogICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgUUgoJ3N0YXR1c3RleHQnLCAnPGRpdiBjbGFzcz0iU1RBVEVDSEFOR0UiPicgKyBleGNlcHRpb25NZXNzYWdlICsgJyBpbiAnICsgbXNnLmZpbGUgKyAnOicgKyBtc2cubGluZSArICc8L2Rpdj4nKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAobXNnLm5hdGl2ZSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFFIKCdzdGF0dXN0ZXh0JywgJzxkaXYgY2xhc3M9IlNUQVRFQ0hBTkdFIj5QQVVTRUQgaW4gW05BVElWRSBDT0RFXTwvZGl2PicpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRSCgnTG9nV2luZG93JywgJycpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRSCgnc3RhdHVzdGV4dCcsICc8ZGl2IGNsYXNzPSJTVEFURUNIQU5HRSI+UEFVU0VEIGluICcgKyBtc2cuZmlsZSArICc6JyArIG1zZy5saW5lICsgJzwvZGl2PicpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCg0KICAgICAgICAgICAgICAgICAgICAgICAgdG9nZ2xlQ29sb3IoUVMoIlN0ZXBPdmVyQnV0dG9uIiksIDEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgdG9nZ2xlQ29sb3IoUVMoIlN0ZXBJbnRvQnV0dG9uIiksIDEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgdG9nZ2xlQ29sb3IoUVMoIlN0ZXBPdXRCdXR0b24iKSwgMSk7DQogICAgICAgICAgICAgICAgICAgICAgICBRSCgiUGF1c2VSZXN1bWVCdXR0b24iLCAiUkVTVU1FIik7DQogICAgICAgICAgICAgICAgICAgICAgICBRRSgncXVlcnlUZXh0JywgMSk7DQogICAgICAgICAgICAgICAgICAgICAgICBRRSgncXVlcnlCdXR0b24nLCAxKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCdjbGVhckJ1dHRvbicsIDEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgUUUoJ2xvY2Fsc0J1dHRvbicsIDEpOw0KICAgICAgICAgICAgICAgICAgICAgICAgUUUoJ2V2YWxCdXR0b24nLCAxKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1zZy5maWxlID09IGN1cnJlbnRNb2R1bGUpDQogICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgUUJDKHNvdXJjZUxpbmVEaXYobXNnLmxpbmUpLCBoaWdobGlnaHQpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRMaW5lID0gbXNnLmxpbmU7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgUShzb3VyY2VMaW5lRGl2KGN1cnJlbnRMaW5lKSkuc2Nyb2xsSW50b1ZpZXcoe2Jsb2NrOiAnY2VudGVyJ30pOw0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRMaW5lID0gbXNnLmxpbmU7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGpqID0geyBjbWQ6ICdTT1VSQ0UnLCBuYW1lOiBtc2cuZmlsZSB9Ow0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdzb2NrZXQuc2VuZChKU09OLnN0cmluZ2lmeShqaikpOw0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIGNhc2UgJ1JVTk5JTkcnOg0KICAgICAgICAgICAgICAgICAgICAgICAgUUgoJ3N0YXR1c3RleHQnLCAnPGRpdiBjbGFzcz0iU1RBVEVDSEFOR0UiPlJVTk5JTkcgJyArIG1zZy5maWxlICsgJzwvZGl2PicpOw0KICAgICAgICAgICAgICAgICAgICAgICAgUUgoIlBhdXNlUmVzdW1lQnV0dG9uIiwgIlBBVVNFIik7DQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcE92ZXJCdXR0b24iKSwgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcEludG9CdXR0b24iKSwgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcE91dEJ1dHRvbiIpLCAwKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCdxdWVyeVRleHQnLCAwKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCdxdWVyeUJ1dHRvbicsIDApOw0KICAgICAgICAgICAgICAgICAgICAgICAgUUUoJ2NsZWFyQnV0dG9uJywgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICBRRSgnbG9jYWxzQnV0dG9uJywgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICBRRSgnZXZhbEJ1dHRvbicsIDApOw0KDQoNCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICBjYXNlICdUSFJPVyc6DQogICAgICAgICAgICAgICAgICAgICAgICBoaWdobGlnaHQgPSBleGNlcHRpb25IaWdobGlnaHQ7DQogICAgICAgICAgICAgICAgICAgICAgICBleGNlcHRpb25NZXNzYWdlID0gJyoqICcgKyBtc2cubXNnICsgJyAqKic7DQogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgY2FzZSAnREVUQUNIJzoNCiAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlTdGF0dXMoIkRlYnVnZ2VyIERldGFjaGVkLi4uIik7DQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiUGF1c2VSZXN1bWVCdXR0b24iKSwgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICBRSCgiQXR0YWNoQnV0dG9uIiwgIkFUVEFDSCIpOw0KDQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcE92ZXJCdXR0b24iKSwgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcEludG9CdXR0b24iKSwgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb2xvcihRUygiU3RlcE91dEJ1dHRvbiIpLCAwKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCJsb2FkU291cmNlQnV0dG9uIiwgMCk7DQogICAgICAgICAgICAgICAgICAgICAgICBRRSgibG9hZFNvdXJjZVRleHQiLCAwKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCdxdWVyeVRleHQnLCAwKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFFKCdxdWVyeUJ1dHRvbicsIDApOw0KICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudE1vZHVsZSA9ICcnOw0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIGNhc2UgJ1FVRVJZJzoNCiAgICAgICAgICAgICAgICAgICAgICAgIFFBKCJxdWVyeVJlc3VsdHMiLCAnPGRpdiBjbGFzcz0iVkFSUVVFUlkiPicgKyBtc2cudmFyICsgJz08L2Rpdj4nKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIFFBKCJxdWVyeVJlc3VsdHMiLCAnPGRpdiBjbGFzcz0iVkFSUkVTVUxUIj4gICAnICsgbXNnLnZhbCArICc8L2Rpdj4nKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICBjYXNlICdFVkFMJzoNCiAgICAgICAgICAgICAgICAgICAgICAgIFFBKCJxdWVyeVJlc3VsdHMiLCAnPGRpdiBjbGFzcz0iVkFSUVVFUlkiPicgKyBtc2cuZXZhbCArICc9PC9kaXY+Jyk7DQogICAgICAgICAgICAgICAgICAgICAgICBRQSgicXVlcnlSZXN1bHRzIiwgJzxkaXYgY2xhc3M9IlZBUlJFU1VMVCI+ICAgJyArIG1zZy52YWwgKyAnPC9kaXY+Jyk7DQogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICAgICAgY2FzZSAnU09VUkNFJzoNCiAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRNb2R1bGUgPSBtc2cubmFtZTsNCiAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRNb2R1bGVUb2tlbnMgPSBtc2cuc291cmNlLnNwbGl0KCdccicpLmpvaW4oJycpLnNwbGl0KCcgJykuam9pbignJm5ic3A7Jykuc3BsaXQoJ1x0Jykuam9pbignJm5ic3A7Jm5ic3A7Jm5ic3A7Jykuc3BsaXQoJ1xuJyk7DQogICAgICAgICAgICAgICAgICAgICAgICBRSCgnTG9nV2luZG93JywgJycpOw0KICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGxpbmVzID0gW107DQogICAgICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGN1cnJlbnRNb2R1bGVUb2tlbnMubGVuZ3RoOysraSkNCiAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lcy5wdXNoKCc8ZGl2IGNsYXNzPSJTT1VSQ0VDT0RFUEFSRU5UIiBvbmRibGNsaWNrPSJvblRvZ2dsZUJyZWFrcG9pbnQoJyArIChpKzEpICsgJykiPicpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVzLnB1c2goJzxkaXYgY2xhc3M9IlNPVVJDRUNPREVMSU5FIiBpZD0iTCcgKyBzb3VyY2VMaW5lRGl2KGkgKyAxKSArICciPicgKyAoaSArIDEpICsgJzwvZGl2PicpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVzLnB1c2goJzxkaXYgY2xhc3M9IlNPVVJDRUNPREUiIGlkPSInICsgc291cmNlTGluZURpdihpICsgMSkgKyAnIj48Y29kZSBzdHlsZT0id2hpdGUtc3BhY2U6bm9ybWFsIj4nICsgY3VycmVudE1vZHVsZVRva2Vuc1tpXSArICc8L2NvZGU+PC9kaXY+Jyk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXMucHVzaCgnPC9kaXY+Jyk7DQogICAgICAgICAgICAgICAgICAgICAgICB9DQoNCiAgICAgICAgICAgICAgICAgICAgICAgIHZhciBjbGluZSA9IGdldFNvdXJjZUxpbmUoKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgUUEoJ0xvZ1dpbmRvdycsIGxpbmVzLmpvaW4oJycpKTsNCg0KICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNsaW5lID49IDApDQogICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgUUJDKHNvdXJjZUxpbmVEaXYoY2xpbmUpLCBoaWdobGlnaHQpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFEoc291cmNlTGluZURpdihjbGluZSkpLnNjcm9sbEludG9WaWV3KHsgYmxvY2s6ICdjZW50ZXInIH0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIFEoJ0wnICsgc291cmNlTGluZURpdihjbGluZSkpLnNjcm9sbEludG9WaWV3KHsgaW5saW5lOiAnc3RhcnQnIH0pOw0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICAgICAgICAgICAgICB2YXIgZGl2cyA9IFFDSElMRFJFTigiQ3VycmVudEJyZWFrcG9pbnRzIik7DQogICAgICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBpIGluIGRpdnMpDQogICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoY3VycmVudE1vZHVsZS50b0xvd2VyQ2FzZSgpLmVuZHNXaXRoKCcuanMnKSB8fCBjdXJyZW50TW9kdWxlID09ICc8PE5BVElWRT4+JykNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKGRpdnNbaV0uaWQuc3RhcnRzV2l0aCgnX2JwX18nKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUUJDKCdMJyArIHNvdXJjZUxpbmVEaXYoZGl2c1tpXS5pZC5zbGljZSg1KSksIGJwQ29sb3IpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChkaXZzW2ldLmlkLnN0YXJ0c1dpdGgoJ19icF8nICsgY3VycmVudE1vZHVsZSArICdfJykpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFFCQygnTCcgKyBzb3VyY2VMaW5lRGl2KGRpdnNbaV0uaWQuc2xpY2UoNSArIGN1cnJlbnRNb2R1bGUubGVuZ3RoKSksIGJwQ29sb3IpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgICAgIGNhc2UgJ0JSRUFLUE9JTlQnOg0KICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGl0ZW1zID0gW107DQogICAgICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBpIGluIG1zZy5saXN0KQ0KICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBmbiA9IG1zZy5saXN0W2ldLmZpbGVOYW1lOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBpZDsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZihmbi50b0xvd2VyQ2FzZSgpLmVuZHNXaXRoKCcuanMnKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkID0gJ19icF9fJyArIG1zZy5saXN0W2ldLmxpbmVOdW1iZXI7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0ZW1zLnB1c2goJzxkaXYgaWQ9IicgKyBpZCArICciIGNsYXNzPSJCUkVBS1BPSU5UIiBvbmNsaWNrPSJvbkJyZWFrcG9pbnRDbGljayhcJycgKyBpZCArICdcJykiPjonICsgbXNnLmxpc3RbaV0ubGluZU51bWJlciArICc8L2Rpdj4nKTsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAnX2JwXycgKyBtc2cubGlzdFtpXS5maWxlTmFtZSArICdfJyArIG1zZy5saXN0W2ldLmxpbmVOdW1iZXI7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0ZW1zLnB1c2goJzxkaXYgaWQ9IicgKyBpZCArICciIGNsYXNzPSJCUkVBS1BPSU5UIiBvbmNsaWNrPSJvbkJyZWFrcG9pbnRDbGljayhcJycgKyBpZCArICdcJykiPicgKyBtc2cubGlzdFtpXS5maWxlTmFtZSArICc6JyArIG1zZy5saXN0W2ldLmxpbmVOdW1iZXIgKyAnPC9kaXY+Jyk7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgUUgoIkN1cnJlbnRCcmVha3BvaW50cyIsIGl0ZW1zLmpvaW4oJycpKTsNCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgICAgICBjYXNlICdDQUxMU1RBQ0snOg0KICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudENhbGxzdGFjayA9IG1zZy5jYWxsc3RhY2s7DQogICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5Q2FsbHN0YWNrKCk7DQogICAgICAgICAgICAgICAgICAgICAgICBicmVhaw0KICAgICAgICAgICAgICAgICAgICBjYXNlICdMT0NBTFMnOg0KICAgICAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgdiBpbiBtc2cudmFsKQ0KICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChtc2cudmFsW3ZdLnR5cGUgPT0gJ3VuZGVmaW5lZCcpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRQSgicXVlcnlSZXN1bHRzIiwgJzxkaXYgY2xhc3M9IlZBUlFVRVJZIj4nICsgbXNnLnZhbFt2XS5rZXkgKyAnPTwvZGl2PicpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3c29ja2V0LnNlbmQoJ3siY21kIjogIlFVRVJZIiwgInZhciI6ICInICsgbXNnLnZhbFt2XS5rZXkgKyAnIiwgImxldmVsIjogJyArIGdldENhbGxzdGFja0xldmVsKCkgKyAnfScpOw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0NICAgICAgICBmdW5jdGlvbiBnZXRTb3VyY2VMaW5lKCkNICAgICAgICB7DSAgICAgICAgICAgIGZvcih2YXIgaSA9IDA7IGkgPCBjdXJyZW50Q2FsbHN0YWNrLmxlbmd0aDsgKytpKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlmIChRUygnX3N0YWNrXycgKyBpKS5jb2xvciA9PSBjYWxsc3RhY2tfc2NvcGUgJiYgY3VycmVudENhbGxzdGFja1tpXS5maWxlTmFtZSA9PSBjdXJyZW50TW9kdWxlKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChjdXJyZW50Q2FsbHN0YWNrW2ldLmxpbmVOdW1iZXIpOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIHJldHVybiAoLTEpOw0KICAgICAgICB9DQ0gICAgICAgIGZ1bmN0aW9uIHRvZ2dsZUNvbG9yKGlzdHlsZSwgc3RhdGUpDQogICAgICAgIHsNCiAgICAgICAgICAgIGlzdHlsZVsiYmFja2dyb3VuZC1jb2xvciJdID0gc3RhdGUgPyAnYmx1ZScgOiAnZ3JleSc7DQogICAgICAgICAgICBpc3R5bGVbImN1cnNvciJdID0gc3RhdGUgPyAncG9pbnRlcicgOiAnZGVmYXVsdCc7DQogICAgICAgIH0NDSAgICAgICAgZnVuY3Rpb24gYXR0YWNoRGVidWdnZXIoKQ0KICAgICAgICB7DQogICAgICAgICAgICBzd2l0Y2goUSgiQXR0YWNoQnV0dG9uIikuaW5uZXJIVE1MKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGNhc2UgJ0FUVEFDSCc6DQogICAgICAgICAgICAgICAgICAgIGRpc3BsYXlTdGF0dXMoIkF0dGFjaGluZyBkZWJ1Z2dlci4uLiIpOw0KICAgICAgICAgICAgICAgICAgICB3c29ja2V0LnNlbmQoJ3siY21kIjogIkFUVEFDSCJ9Jyk7DQogICAgICAgICAgICAgICAgICAgIGJyZWFrOw0KICAgICAgICAgICAgICAgIGNhc2UgJ0RFVEFDSCc6DQogICAgICAgICAgICAgICAgICAgIGN1cnJlbnRDYWxsc3RhY2sgPSBbXTsNCiAgICAgICAgICAgICAgICAgICAgZGlzcGxheUNhbGxzdGFjaygpOw0KDQogICAgICAgICAgICAgICAgICAgIGRpc3BsYXlTdGF0dXMoIkRldGFjaGluZyBkZWJ1Z2dlci4uLiIpOw0KICAgICAgICAgICAgICAgICAgICBjdXJyZW50TW9kdWxlID0gJyc7DQogICAgICAgICAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiREVUQUNIIn0nKTsNCiAgICAgICAgICAgICAgICAgICAgUVYoJ3BhZ2VfY29udGVudCcsIGZhbHNlKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICB9DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBwYXVzZVJlc3VtZSgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHN3aXRjaChRKCJQYXVzZVJlc3VtZUJ1dHRvbiIpLmlubmVySFRNTCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBjYXNlICdQQVVTRSc6DQogICAgICAgICAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiUEFVU0UifScpOw0KICAgICAgICAgICAgICAgICAgICBicmVhazsNCiAgICAgICAgICAgICAgICBjYXNlICdSRVNVTUUnOg0KICAgICAgICAgICAgICAgICAgICBoaWdobGlnaHQgPSBzb3VyY2VIaWdobGlnaHQ7DQogICAgICAgICAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiUkVTVU1FIn0nKTsNCiAgICAgICAgICAgICAgICAgICAgUUJDKHNvdXJjZUxpbmVEaXYoY3VycmVudExpbmUpLCAnd2hpdGUnKTsNCg0KICAgICAgICAgICAgICAgICAgICBjdXJyZW50Q2FsbHN0YWNrID0gW107DQogICAgICAgICAgICAgICAgICAgIGRpc3BsYXlDYWxsc3RhY2soKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICB9DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBzdGVwT3ZlcigpDQogICAgICAgIHsNCiAgICAgICAgICAgIGhpZ2hsaWdodCA9IHNvdXJjZUhpZ2hsaWdodDsNCiAgICAgICAgICAgIFFCQyhzb3VyY2VMaW5lRGl2KGN1cnJlbnRMaW5lKSwgJ3doaXRlJyk7DQogICAgICAgICAgICB3c29ja2V0LnNlbmQoJ3siY21kIjogIlNURVBPVkVSIn0nKTsNCiAgICAgICAgfQ0gICAgICAgIGZ1bmN0aW9uIHN0ZXBJbnRvKCkNICAgICAgICB7DQogICAgICAgICAgICBoaWdobGlnaHQgPSBzb3VyY2VIaWdobGlnaHQ7DQogICAgICAgICAgICBRQkMoc291cmNlTGluZURpdihjdXJyZW50TGluZSksICd3aGl0ZScpOw0KICAgICAgICAgICAgd3NvY2tldC5zZW5kKCd7ImNtZCI6ICJTVEVQSU5UTyJ9Jyk7DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBzdGVwT3V0KCkNCiAgICAgICAgew0KICAgICAgICAgICAgaGlnaGxpZ2h0ID0gc291cmNlSGlnaGxpZ2h0Ow0KICAgICAgICAgICAgUUJDKHNvdXJjZUxpbmVEaXYoY3VycmVudExpbmUpLCAnd2hpdGUnKTsNCiAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiU1RFUE9VVCJ9Jyk7DQogICAgICAgIH0NICAgICAgICANICAgICAgICBmdW5jdGlvbiBkZWxCcmVha3BvaW50KCkNICAgICAgICB7DQogICAgICAgICAgICBRVihzZWxlY3RlZEJyZWFrcG9pbnQsIDApOw0KICAgICAgICAgICAgc2VsZWN0ZWRCcmVha3BvaW50ID0gIiI7DQogICAgICAgICAgICBRRSgiZGVsQnJlYWtwb2ludEJ1dHRvbiIsIDApOw0KICAgICAgICB9DSAgICAgICAgZnVuY3Rpb24gb25CcmVha3BvaW50Q2xpY2soaWQpDSAgICAgICAgew0KICAgICAgICAgICAgaWYgKHNlbGVjdGVkQnJlYWtwb2ludCA9PSBpZCkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBRUyhpZClbImJhY2tncm91bmQtY29sb3IiXSA9ICd3aGl0ZSc7DQogICAgICAgICAgICAgICAgc2VsZWN0ZWRCcmVha3BvaW50ID0gIiI7DQogICAgICAgICAgICAgICAgUUUoImRlbEJyZWFrcG9pbnRCdXR0b24iLCAwKTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIGVsc2UNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiAoc2VsZWN0ZWRCcmVha3BvaW50ICE9ICIiKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgUVMoc2VsZWN0ZWRCcmVha3BvaW50KVsiYmFja2dyb3VuZC1jb2xvciJdID0gJ3doaXRlJzsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgUVMoaWQpWyJiYWNrZ3JvdW5kLWNvbG9yIl0gPSAneWVsbG93JzsNCiAgICAgICAgICAgICAgICBzZWxlY3RlZEJyZWFrcG9pbnQgPSBpZDsNCiAgICAgICAgICAgICAgICBRRSgiZGVsQnJlYWtwb2ludEJ1dHRvbiIsIDEpOw0KICAgICAgICAgICAgfQ0KDQogICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGN1cnJlbnRDYWxsc3RhY2subGVuZ3RoOyArK2kpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgUVMoJ19zdGFja18nICsgaSkuY29sb3IgPSBjYWxsc3RhY2tfZGVmYXVsdDsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIHZhciBicCA9IFFIVkFMKGlkKS5zcGxpdCgnOicpWzBdOw0KICAgICAgICAgICAgdmFyIGpqID0geyBjbWQ6ICdTT1VSQ0UnLCBuYW1lOiBicCA9PSAnJyA/ICc8PE5BVElWRT4+JyA6IGJwIH07DQogICAgICAgICAgICB3c29ja2V0LnNlbmQoSlNPTi5zdHJpbmdpZnkoamopKTsgICAgICAgIA0KICAgICAgICB9DSAgICAgICAgZnVuY3Rpb24gZ2V0Q2FsbHN0YWNrTGV2ZWwoKQ0gICAgICAgIHsNCiAgICAgICAgICAgIC8vIERldGVybWluZSB0aGUgQ2FsbHN0YWNrIExldmVsDQogICAgICAgICAgICB2YXIgbGV2ZWwgPSAtMTsNCiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY3VycmVudENhbGxzdGFjay5sZW5ndGg7ICsraSkgew0KICAgICAgICAgICAgICAgIGlmIChRUygnX3N0YWNrXycgKyBpKS5jb2xvciA9PSBjYWxsc3RhY2tfc2NvcGUpIHsNCiAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSAwIC0gKGkgKyAxKTsNCiAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQogICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgcmV0dXJuIChsZXZlbCk7DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBxdWVyeVZhbCgpDSAgICAgICAgew0KICAgICAgICAgICAgLy9RQSgicXVlcnlSZXN1bHRzIiwgJzxkaXYgY2xhc3M9IlZBUlFVRVJZIj4nICsgUVZBTCgncXVlcnlUZXh0JykgKyAnPTwvZGl2PicpOw0KICAgICAgICAgICAgd3NvY2tldC5zZW5kKCd7ImNtZCI6ICJRVUVSWSIsICJ2YXIiOiAiJyArIFFWQUwoJ3F1ZXJ5VGV4dCcpICsgJyIsICJsZXZlbCI6ICcgKyBnZXRDYWxsc3RhY2tMZXZlbCgpICsgJ30nKTsNCiAgICAgICAgICAgIFFWQUwoJ3F1ZXJ5VGV4dCcsICIiKTsNCiAgICAgICAgfQ0gICAgICAgIGZ1bmN0aW9uIGV2YWxTdHJpbmcoKQ0gICAgICAgIHsNCiAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiRVZBTCIsICJldmFsIjogIicgKyBRVkFMKCdxdWVyeVRleHQnKSArICciLCAibGV2ZWwiOiAnICsgZ2V0Q2FsbHN0YWNrTGV2ZWwoKSArICd9Jyk7DQogICAgICAgICAgICBRVkFMKCdxdWVyeVRleHQnLCAiIik7DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBsb2NhbHNRdWVyeSgpDSAgICAgICAgew0KICAgICAgICAgICAgUUEoInF1ZXJ5UmVzdWx0cyIsICc8ZGl2IGNsYXNzPSJWQVJRVUVSWSI+TG9jYWxzOiZuYnNwOzwvZGl2PicpOw0KICAgICAgICAgICAgd3NvY2tldC5zZW5kKCd7ImNtZCI6ICJMT0NBTFMiLCAibGV2ZWwiOiAnICsgZ2V0Q2FsbHN0YWNrTGV2ZWwoKSArICd9Jyk7DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBjbGVhclF1ZXJ5KCkNICAgICAgICB7DQogICAgICAgICAgICBRSCgncXVlcnlSZXN1bHRzJywgJycpOw0KICAgICAgICB9DSAgICAgICAgZnVuY3Rpb24gZGlzcGxheVN0YXR1cyhtc2csIG1zZ0NsYXNzKQ0gICAgICAgIHsNCiAgICAgICAgICAgIFFIKCdzdGF0dXN0ZXh0JywgJzxkaXYgY2xhc3M9IicgKyAobXNnQ2xhc3M/bXNnQ2xhc3M6J0dFTkVSSUNfTU9EVUxFJykgKyAnIj4nICsgbXNnICsgJzwvZGl2PicpOw0KICAgICAgICB9DSAgICAgICAgZnVuY3Rpb24gc291cmNlTGluZURpdihsaW5lKQ0gICAgICAgIHsNCiAgICAgICAgICAgIHJldHVybiAoJ19fc3JjJyArIGxpbmUpOw0KICAgICAgICB9DQ0gICAgICAgIGZ1bmN0aW9uIG9uVG9nZ2xlQnJlYWtwb2ludChsaW5lKQ0gICAgICAgIHsNCiAgICAgICAgICAgIFFCQygnTCcgKyBzb3VyY2VMaW5lRGl2KGxpbmUpLCBicENvbG9yKTsNCiAgICAgICAgICAgIHZhciBmbiA9IGN1cnJlbnRNb2R1bGU7DQoNCiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY3VycmVudENhbGxzdGFjay5sZW5ndGg7ICsraSkNCiAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICBpZiAoUVMoJ19zdGFja18nICsgaSkuY29sb3IgPT0gY2FsbHN0YWNrX3Njb3BlKQ0KICAgICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgICAgZm4gPSBjdXJyZW50Q2FsbHN0YWNrW2ldLmZpbGVOYW1lLnNwbGl0KCdcXCcpLmpvaW4oJ1xcXFwnKTsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQoNCiAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiQlJFQUtQT0lOVCIsICJmaWxlIjoiJyArIGZuICsgJyIsICJsaW5lIjonICsgbGluZSArICcsICJtb2RlIjoiYWRkIn0nKTsNCiAgICAgICAgfQ0gICAgICAgIGZ1bmN0aW9uIG9uRHVtcEhlYXAoKQ0gICAgICAgIHsNCiAgICAgICAgICAgIHdzb2NrZXQuc2VuZCgneyJjbWQiOiAiSEVBUCJ9Jyk7DQogICAgICAgIH0NICAgICAgICBmdW5jdGlvbiBmb3JjZUdDKCkNICAgICAgICB7DQogICAgICAgICAgICB3c29ja2V0LnNlbmQoJ3siY21kIjogIkdDIn0nKTsNCiAgICAgICAgfQ0gICAgICAgIGZ1bmN0aW9uIGRpc3BsYXlDYWxsc3RhY2soKQ0gICAgICAgIHsNCiAgICAgICAgICAgIHZhciBkaXNwbGF5ID0gW107DQogICAgICAgICAgICB2YXIgaW5kZW50ID0gJy0tPiZuYnNwOyc7DQogICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGN1cnJlbnRDYWxsc3RhY2subGVuZ3RoOyArK2kpDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWYgKGN1cnJlbnRDYWxsc3RhY2tbaV0uZnVuY05hbWUgPT0gJycpIHsgY3VycmVudENhbGxzdGFja1tpXS5mdW5jTmFtZSA9ICcoYW5vbnltb3VzKSc7IH0NCiAgICAgICAgICAgICAgICB2YXIgZm5hbWUgPSBjdXJyZW50Q2FsbHN0YWNrW2ldLmZpbGVOYW1lLnNwbGl0KCdcXCcpLmpvaW4oJ1xcXFwnKTsNCg0KICAgICAgICAgICAgICAgIGRpc3BsYXkucHVzaCgnPGRpdiBkZXB0aD0iJyArIChpKzEpICsgJyIgaWQ9Il9zdGFja18nICsgaSArICciIGNsYXNzPSJDQUxMU1RBQ0siIG9uY2xpY2s9Im9uQ2FsbHN0YWNrQ2xpY2soXCcnICsgY3VycmVudENhbGxzdGFjay5sZW5ndGggKyAnXCcsIFwnX3N0YWNrXycgKyBpICsgJ1wnLCBcJycgKyBmbmFtZSArICdcJykiPicgKyBpbmRlbnQgKyBjdXJyZW50Q2FsbHN0YWNrW2ldLmZ1bmNOYW1lICsgJzonICsgY3VycmVudENhbGxzdGFja1tpXS5saW5lTnVtYmVyICsgJzwvZGl2PicpOw0KICAgICAgICAgICAgICAgIGluZGVudCA9ICcmbmJzcDsmbmJzcDsmbmJzcDsnICsgaW5kZW50Ow0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgUUgoJ0NhbGxzdGFja1dpbmRvdycsIGRpc3BsYXkuam9pbignJykpOw0KDQogICAgICAgICAgICBpZiAoY3VycmVudENhbGxzdGFjay5sZW5ndGggPiAwKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIFFTKCdfc3RhY2tfMCcpLmNvbG9yID0gY2FsbHN0YWNrX3Njb3BlOw0KICAgICAgICAgICAgfQ0KICAgICAgICB9DSAgICAgICAgZnVuY3Rpb24gb25DYWxsc3RhY2tDbGljayhsZW4sIGlkLCBmaWxlbmFtZSkNICAgICAgICB7DQogICAgICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgKytpKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIFFTKCdfc3RhY2tfJyArIGkpLmNvbG9yID0gY2FsbHN0YWNrX2RlZmF1bHQ7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBRUyhpZCkuY29sb3IgPSBjYWxsc3RhY2tfc2NvcGU7DQogICAgICAgICAgICB2YXIgamogPSB7IGNtZDogJ1NPVVJDRScsIG5hbWU6IGZpbGVuYW1lIH07DQogICAgICAgICAgICB3c29ja2V0LnNlbmQoSlNPTi5zdHJpbmdpZnkoamopKTsNCiAgICAgICAgfQ0gICAgICAgIGZ1bmN0aW9uIG1hbnVhbGx5TG9hZFNvdXJjZSgpDSAgICAgICAgew0KICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBjdXJyZW50Q2FsbHN0YWNrLmxlbmd0aDsgKytpKQ0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIFFTKCdfc3RhY2tfJyArIGkpLmNvbG9yID0gY2FsbHN0YWNrX2RlZmF1bHQ7DQogICAgICAgICAgICB9DQogICAgICAgICAgICBRSCgnTG9nV2luZG93JywgJycpOw0KDQogICAgICAgICAgICB2YXIgamogPSB7IGNtZDogJ1NPVVJDRScsIG5hbWU6IFFWQUwoJ2xvYWRTb3VyY2VUZXh0JykgPT0gJyc/Jzw8TkFUSVZFPj4nOlFWQUwoJ2xvYWRTb3VyY2VUZXh0JykgfTsNCiAgICAgICAgICAgIHdzb2NrZXQuc2VuZChKU09OLnN0cmluZ2lmeShqaikpOw0KICAgICAgICB9DSAgICAgICAgLy8gUG9sbCB0aGUgc2VydmVyLCBpZiBpdCByZXNwb25kcywgcmVmcmVzaCB0aGUgcGFnZS4NCiAgICAgICAgZnVuY3Rpb24gc2VydmVyUG9sbCgpDQogICAgICAgIHsNCiAgICAgICAgICAgIHhkciA9IG51bGw7DQogICAgICAgICAgICB0cnkgeyB4ZHIgPSBuZXcgWERvbWFpblJlcXVlc3QoKTsgfSBjYXRjaCAoZSkgeyB9DQogICAgICAgICAgICBpZiAoIXhkcikgeGRyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7DQogICAgICAgICAgICB4ZHIub3BlbigiSEVBRCIsIHdpbmRvdy5sb2NhdGlvbi5ocmVmKTsNCiAgICAgICAgICAgIHhkci50aW1lb3V0ID0gMTUwMDA7DQogICAgICAgICAgICB4ZHIub25sb2FkID0gZnVuY3Rpb24gKCkgeyByZWxvYWQoKTsgfTsNCiAgICAgICAgICAgIHhkci5vbmVycm9yID0geGRyLm9udGltZW91dCA9IGZ1bmN0aW9uICgpIHsgc2V0VGltZW91dChzZXJ2ZXJQb2xsLCAyMDAwKTsgfTsNCiAgICAgICAgICAgIHhkci5zZW5kKCk7DQogICAgICAgIH0NCg0KICAgICAgICBmdW5jdGlvbiByZWxvYWQoKSB7IHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gd2luZG93LmxvY2F0aW9uLmhyZWY7IH0NCg0gICAgPC9zY3JpcHQ+DQo8L2JvZHk+DQo8L2h0bWw+DQo=';
134
+
135
+var optTargetHost = '127.0.0.1';
136
+var optTargetPort = 9091;
137
+var optHttpPort = 9092;
138
+var optJsonProxyPort = 9093;
139
+var optJsonProxy = false;
140
+var optSourceSearchDirs = ['../tests/ecmascript'];
141
+var optDumpDebugRead = null;
142
+var optDumpDebugWrite = null;
143
+var optDumpDebugPretty = null;
144
+var optLogMessages = true;
145
+
146
+// Marker objects for special protocol values
147
+var DVAL_EOM = { type: 'eom' };
148
+var DVAL_REQ = { type: 'req' };
149
+var DVAL_REP = { type: 'rep' };
150
+var DVAL_ERR = { type: 'err' };
151
+var DVAL_NFY = { type: 'nfy' };
152
+
153
+
154
+// Commands initiated by Duktape
155
+var CMD_STATUS = 0x01;
156
+var CMD_UNUSED_2 = 0x02; // Duktape 1.x: print notify
157
+var CMD_UNUSED_3 = 0x03; // Duktape 1.x: alert notify
158
+var CMD_UNUSED_4 = 0x04; // Duktape 1.x: log notify
159
+var CMD_THROW = 0x05;
160
+var CMD_DETACHING = 0x06;
161
+var CMD_APPNOTIFY = 0x07;
162
+
163
+// Commands initiated by the debug client (= us)
164
+var CMD_BASICINFO = 0x10;
165
+var CMD_TRIGGERSTATUS = 0x11;
166
+var CMD_PAUSE = 0x12;
167
+var CMD_RESUME = 0x13;
168
+var CMD_STEPINTO = 0x14;
169
+var CMD_STEPOVER = 0x15;
170
+var CMD_STEPOUT = 0x16;
171
+var CMD_LISTBREAK = 0x17;
172
+var CMD_ADDBREAK = 0x18;
173
+var CMD_DELBREAK = 0x19;
174
+var CMD_GETVAR = 0x1a;
175
+var CMD_PUTVAR = 0x1b;
176
+var CMD_GETCALLSTACK = 0x1c;
177
+var CMD_GETLOCALS = 0x1d;
178
+var CMD_EVAL = 0x1e;
179
+var CMD_DETACH = 0x1f;
180
+var CMD_DUMPHEAP = 0x20;
181
+var CMD_GETBYTECODE = 0x21;
182
+
183
+// Constants
184
+var UI_MESSAGE_CLIPLEN = 128;
185
+var LOCALS_CLIPLEN = 64;
186
+var EVAL_CLIPLEN = 4096;
187
+var GETVAR_CLIPLEN = 4096;
188
+var SUPPORTED_DEBUG_PROTOCOL_VERSION = 2;
189
+
190
+
191
+var debugCommandNames = {};
192
+debugCommandNames['0'] = 'Reserved_0';
193
+debugCommandNames['1'] = 'Status';
194
+debugCommandNames['2'] = 'Reserved_2';
195
+debugCommandNames['3'] = 'Reserved_3';
196
+debugCommandNames['4'] = 'Reserved_4';
197
+debugCommandNames['5'] = 'Throw';
198
+debugCommandNames['6'] = 'Detaching';
199
+debugCommandNames['7'] = 'AppNotify';
200
+debugCommandNames['8'] = 'Reserved_8';
201
+debugCommandNames['9'] = 'Reserved_9';
202
+debugCommandNames['10'] = 'Reserved_10';
203
+debugCommandNames['11'] = 'Reserved_11';
204
+debugCommandNames['12'] = 'Reserved_12';
205
+debugCommandNames['13'] = 'Reserved_13';
206
+debugCommandNames['14'] = 'Reserved_14';
207
+debugCommandNames['15'] = 'Reserved_15';
208
+debugCommandNames['16'] = 'BasicInfo';
209
+debugCommandNames['17'] = 'TriggerStatus';
210
+debugCommandNames['18'] = 'Pause';
211
+debugCommandNames['19'] = 'Resume';
212
+debugCommandNames['20'] = 'StepInto';
213
+debugCommandNames['21'] = 'StepOver';
214
+debugCommandNames['22'] = 'StepOut';
215
+debugCommandNames['23'] = 'ListBreak';
216
+debugCommandNames['24'] = 'AddBreak';
217
+debugCommandNames['25'] = 'DelBreak';
218
+debugCommandNames['26'] = 'GetVar';
219
+debugCommandNames['27'] = 'PutVar';
220
+debugCommandNames['28'] = 'GetCallStack';
221
+debugCommandNames['29'] = 'GetLocals';
222
+debugCommandNames['30'] = 'Eval';
223
+debugCommandNames['31'] = 'Detach';
224
+debugCommandNames['32'] = 'DumpHeap';
225
+debugCommandNames['33'] = 'GetBytecode';
226
+debugCommandNames['34'] = 'AppRequest';
227
+debugCommandNames['35'] = 'GetHeapObjInfo';
228
+debugCommandNames['36'] = 'GetObjPropDesc';
229
+debugCommandNames['37'] = 'GetObjPropDescRange';
230
+
231
+// Duktape heaphdr type constants, must match C headers
232
+var DUK_HTYPE_STRING = 0;
233
+var DUK_HTYPE_OBJECT = 1;
234
+var DUK_HTYPE_BUFFER = 2;
235
+
236
+/*
237
+ * Debugger output formatting
238
+ */
239
+
240
+function formatDebugValue(v) {
241
+ var buf, dec, len;
242
+
243
+ // See doc/debugger.rst for format description.
244
+
245
+ if (typeof v === 'object' && v !== null) {
246
+ // Note: typeof null === 'object', so null special case explicitly
247
+ if (v.type === 'eom') {
248
+ return new Buffer([0x00]);
249
+ } else if (v.type === 'req') {
250
+ return new Buffer([0x01]);
251
+ } else if (v.type === 'rep') {
252
+ return new Buffer([0x02]);
253
+ } else if (v.type === 'err') {
254
+ return new Buffer([0x03]);
255
+ } else if (v.type === 'nfy') {
256
+ return new Buffer([0x04]);
257
+ } else if (v.type === 'unused') {
258
+ return new Buffer([0x15]);
259
+ } else if (v.type === 'undefined') {
260
+ return new Buffer([0x16]);
261
+ } else if (v.type === 'number') {
262
+ dec = new Buffer(v.data, 'hex');
263
+ len = dec.length;
264
+ if (len !== 8) {
265
+ throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v));
266
+ }
267
+ buf = Buffer.alloc(1 + len);
268
+ buf[0] = 0x1a;
269
+ dec.copy(buf, 1);
270
+ return buf;
271
+ } else if (v.type === 'buffer') {
272
+ dec = new Buffer(v.data, 'hex');
273
+ len = dec.length;
274
+ if (len <= 0xffff) {
275
+ buf = Buffer.alloc(3 + len);
276
+ buf[0] = 0x14;
277
+ buf[1] = (len >> 8) & 0xff;
278
+ buf[2] = (len >> 0) & 0xff;
279
+ dec.copy(buf, 3);
280
+ return buf;
281
+ } else {
282
+ buf = Buffer.alloc(5 + len);
283
+ buf[0] = 0x13;
284
+ buf[1] = (len >> 24) & 0xff;
285
+ buf[2] = (len >> 16) & 0xff;
286
+ buf[3] = (len >> 8) & 0xff;
287
+ buf[4] = (len >> 0) & 0xff;
288
+ dec.copy(buf, 5);
289
+ return buf;
290
+ }
291
+ } else if (v.type === 'object') {
292
+ dec = new Buffer(v.pointer, 'hex');
293
+ len = dec.length;
294
+ buf = Buffer.alloc(3 + len);
295
+ buf[0] = 0x1b;
296
+ buf[1] = v.class;
297
+ buf[2] = len;
298
+ dec.copy(buf, 3);
299
+ return buf;
300
+ } else if (v.type === 'pointer') {
301
+ dec = new Buffer(v.pointer, 'hex');
302
+ len = dec.length;
303
+ buf = Buffer.alloc(2 + len);
304
+ buf[0] = 0x1c;
305
+ buf[1] = len;
306
+ dec.copy(buf, 2);
307
+ return buf;
308
+ } else if (v.type === 'lightfunc') {
309
+ dec = new Buffer(v.pointer, 'hex');
310
+ len = dec.length;
311
+ buf = Buffer.alloc(4 + len);
312
+ buf[0] = 0x1d;
313
+ buf[1] = (v.flags >> 8) & 0xff;
314
+ buf[2] = v.flags & 0xff;
315
+ buf[3] = len;
316
+ dec.copy(buf, 4);
317
+ return buf;
318
+ } else if (v.type === 'heapptr') {
319
+ dec = new Buffer(v.pointer, 'hex');
320
+ len = dec.length;
321
+ buf = Buffer.alloc(2 + len);
322
+ buf[0] = 0x1e;
323
+ buf[1] = len;
324
+ dec.copy(buf, 2);
325
+ return buf;
326
+ }
327
+ } else if (v === null) {
328
+ return new Buffer([0x17]);
329
+ } else if (typeof v === 'boolean') {
330
+ return new Buffer([v ? 0x18 : 0x19]);
331
+ } else if (typeof v === 'number') {
332
+ if (Math.floor(v) === v && /* whole */
333
+ (v !== 0 || 1 / v > 0) && /* not negative zero */
334
+ v >= -0x80000000 && v <= 0x7fffffff) {
335
+ // Represented signed 32-bit integers as plain integers.
336
+ // Debugger code expects this for all fields that are not
337
+ // duk_tval representations (e.g. command numbers and such).
338
+ if (v >= 0x00 && v <= 0x3f) {
339
+ return new Buffer([0x80 + v]);
340
+ } else if (v >= 0x0000 && v <= 0x3fff) {
341
+ return new Buffer([0xc0 + (v >> 8), v & 0xff]);
342
+ } else if (v >= -0x80000000 && v <= 0x7fffffff) {
343
+ return new Buffer([0x10,
344
+ (v >> 24) & 0xff,
345
+ (v >> 16) & 0xff,
346
+ (v >> 8) & 0xff,
347
+ (v >> 0) & 0xff]);
348
+ } else {
349
+ throw new Error('internal error when encoding integer to dvalue: ' + v);
350
+ }
351
+ } else {
352
+ // Represent non-integers as IEEE double dvalues
353
+ buf = Buffer.alloc(1 + 8);
354
+ buf[0] = 0x1a;
355
+ buf.writeDoubleBE(v, 1);
356
+ return buf;
357
+ }
358
+ } else if (typeof v === 'string') {
359
+ if (v.length < 0 || v.length > 0xffffffff) {
360
+ // Not possible in practice.
361
+ throw new TypeError('cannot convert to dvalue, invalid string length: ' + v.length);
362
+ }
363
+ if (v.length <= 0x1f) {
364
+ buf = Buffer.alloc(1 + v.length);
365
+ buf[0] = 0x60 + v.length;
366
+ writeDebugStringToBuffer(v, buf, 1);
367
+ return buf;
368
+ } else if (v.length <= 0xffff) {
369
+ buf = Buffer.alloc(3 + v.length);
370
+ buf[0] = 0x12;
371
+ buf[1] = (v.length >> 8) & 0xff;
372
+ buf[2] = (v.length >> 0) & 0xff;
373
+ writeDebugStringToBuffer(v, buf, 3);
374
+ return buf;
375
+ } else {
376
+ buf = Buffer.alloc(5 + v.length);
377
+ buf[0] = 0x11;
378
+ buf[1] = (v.length >> 24) & 0xff;
379
+ buf[2] = (v.length >> 16) & 0xff;
380
+ buf[3] = (v.length >> 8) & 0xff;
381
+ buf[4] = (v.length >> 0) & 0xff;
382
+ writeDebugStringToBuffer(v, buf, 5);
383
+ return buf;
384
+ }
385
+ }
386
+
387
+ // Shouldn't come here.
388
+ throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v));
389
+}
390
+
391
+
392
+function DebugProtocolParser(inputStream,
393
+ protocolVersion,
394
+ rawDumpFileName,
395
+ textDumpFileName,
396
+ textDumpFilePrefix,
397
+ hexDumpConsolePrefix,
398
+ textDumpConsolePrefix)
399
+{
400
+ var _this = this;
401
+ this._ObjectID = 'DebugProtocolParser';
402
+ this.inputStream = inputStream;
403
+ this.closed = false; // stream is closed/broken, don't parse anymore
404
+ this.bytes = 0;
405
+ this.dvalues = 0;
406
+ this.messages = 0;
407
+ this.requests = 0;
408
+ this.prevBytes = 0;
409
+ this.bytesPerSec = 0;
410
+ this.statsTimer = null;
411
+ this.readableNumberValue = true;
412
+
413
+ events.EventEmitter.call(this);
414
+
415
+ var buf = Buffer.alloc(0); // accumulate data
416
+ var msg = []; // accumulated message until EOM
417
+ var versionIdentification;
418
+
419
+ var statsInterval = 2000;
420
+ var statsIntervalSec = statsInterval / 1000;
421
+ this.statsTimer = setInterval(function () {
422
+ _this.bytesPerSec = (_this.bytes - _this.prevBytes) / statsIntervalSec;
423
+ _this.prevBytes = _this.bytes;
424
+ _this.emit('stats-update');
425
+ }, statsInterval);
426
+
427
+ function consume(n) {
428
+ var tmp = Buffer.alloc(buf.length - n);
429
+ buf.copy(tmp, 0, n);
430
+ buf = tmp;
431
+ }
432
+
433
+ inputStream.on('data', function (data) {
434
+ var i, n, x, v, gotValue, len, t, tmpbuf, verstr;
435
+ var prettyMsg;
436
+
437
+ if (_this.closed || !_this.inputStream)
438
+ {
439
+ console.error('Ignoring incoming data from closed input stream, len ' + data.length);
440
+ return;
441
+ }
442
+
443
+ _this.bytes += data.length;
444
+ if (rawDumpFileName) {
445
+ fs.appendFileSync(rawDumpFileName, data);
446
+ }
447
+ if (hexDumpConsolePrefix) {
448
+ console.info1(hexDumpConsolePrefix + data.toString('hex'));
449
+ }
450
+
451
+ buf = Buffer.concat([buf, data]);
452
+
453
+ // Protocol version handling. When dumping an output stream, the
454
+ // caller gives a non-null protocolVersion so we don't read one here.
455
+
456
+ if (protocolVersion == null) {
457
+ if (buf.length > 1024) {
458
+ _this.emit('transport-error', 'Parse error (version identification too long), dropping connection');
459
+ _this.close();
460
+ return;
461
+ }
462
+
463
+
464
+ for (i = 0, n = buf.length; i < n; i++)
465
+ {
466
+ if (buf[i] == 0x0a)
467
+ {
468
+ tmpbuf = Buffer.alloc(i);
469
+ buf.copy(tmpbuf, 0, 0, i);
470
+ consume(i + 1);
471
+ verstr = tmpbuf.toString();
472
+ t = verstr.split(' ');
473
+ protocolVersion = Number(t[0]);
474
+ versionIdentification = verstr;
475
+
476
+ _this.emit('protocol-version', {
477
+ protocolVersion: protocolVersion,
478
+ versionIdentification: versionIdentification
479
+ });
480
+ break;
481
+ }
482
+ }
483
+
484
+ if (protocolVersion == null)
485
+ {
486
+ // Still waiting for version identification to complete.
487
+ return;
488
+ }
489
+ }
490
+
491
+ // Parse complete dvalues (quite inefficient now) by trial parsing.
492
+ // Consume a value only when it's fully present in 'buf'.
493
+ // See doc/debugger.rst for format description.
494
+
495
+ while (buf.length > 0) {
496
+ x = buf[0];
497
+ v = undefined;
498
+ gotValue = false; // used to flag special values like undefined
499
+
500
+ if (x >= 0xc0) {
501
+ // 0xc0...0xff: integers 0-16383
502
+ if (buf.length >= 2) {
503
+ v = ((x - 0xc0) << 8) + buf[1];
504
+ consume(2);
505
+ }
506
+ } else if (x >= 0x80) {
507
+ // 0x80...0xbf: integers 0-63
508
+ v = x - 0x80;
509
+ consume(1);
510
+ } else if (x >= 0x60) {
511
+ // 0x60...0x7f: strings with length 0-31
512
+ len = x - 0x60;
513
+ if (buf.length >= 1 + len) {
514
+ v = Buffer.alloc(len);
515
+ buf.copy(v, 0, 1, 1 + len);
516
+ v = bufferToDebugString(v);
517
+ consume(1 + len);
518
+ }
519
+ } else {
520
+ switch (x) {
521
+ case 0x00: v = DVAL_EOM; consume(1); break;
522
+ case 0x01: v = DVAL_REQ; consume(1); break;
523
+ case 0x02: v = DVAL_REP; consume(1); break;
524
+ case 0x03: v = DVAL_ERR; consume(1); break;
525
+ case 0x04: v = DVAL_NFY; consume(1); break;
526
+ case 0x10: // 4-byte signed integer
527
+ if (buf.length >= 5) {
528
+ v = buf.readInt32BE(1);
529
+ consume(5);
530
+ }
531
+ break;
532
+ case 0x11: // 4-byte string
533
+ if (buf.length >= 5) {
534
+ len = buf.readUInt32BE(1);
535
+ if (buf.length >= 5 + len) {
536
+ v = Buffer.alloc(len);
537
+ buf.copy(v, 0, 5, 5 + len);
538
+ v = bufferToDebugString(v);
539
+ consume(5 + len);
540
+ }
541
+ }
542
+ break;
543
+ case 0x12: // 2-byte string
544
+ if (buf.length >= 3) {
545
+ len = buf.readUInt16BE(1);
546
+ if (buf.length >= 3 + len) {
547
+ v = Buffer.alloc(len);
548
+ buf.copy(v, 0, 3, 3 + len);
549
+ v = bufferToDebugString(v);
550
+ consume(3 + len);
551
+ }
552
+ }
553
+ break;
554
+ case 0x13: // 4-byte buffer
555
+ if (buf.length >= 5) {
556
+ len = buf.readUInt32BE(1);
557
+ if (buf.length >= 5 + len) {
558
+ v = Buffer.alloc(len);
559
+ buf.copy(v, 0, 5, 5 + len);
560
+ v = { type: 'buffer', data: v.toString('hex') };
561
+ consume(5 + len);
562
+ // Value could be a Node.js buffer directly, but
563
+ // we prefer all dvalues to be JSON compatible
564
+ }
565
+ }
566
+ break;
567
+ case 0x14: // 2-byte buffer
568
+ if (buf.length >= 3) {
569
+ len = buf.readUInt16BE(1);
570
+ if (buf.length >= 3 + len) {
571
+ v = Buffer.alloc(len);
572
+ buf.copy(v, 0, 3, 3 + len);
573
+ v = { type: 'buffer', data: v.toString('hex') };
574
+ consume(3 + len);
575
+ // Value could be a Node.js buffer directly, but
576
+ // we prefer all dvalues to be JSON compatible
577
+ }
578
+ }
579
+ break;
580
+ case 0x15: // unused/none
581
+ v = { type: 'unused' };
582
+ consume(1);
583
+ break;
584
+ case 0x16: // undefined
585
+ v = { type: 'undefined' };
586
+ gotValue = true; // indicate 'v' is actually set
587
+ consume(1);
588
+ break;
589
+ case 0x17: // null
590
+ v = null;
591
+ gotValue = true; // indicate 'v' is actually set
592
+ consume(1);
593
+ break;
594
+ case 0x18: // true
595
+ v = true;
596
+ consume(1);
597
+ break;
598
+ case 0x19: // false
599
+ v = false;
600
+ consume(1);
601
+ break;
602
+ case 0x1a: // number (IEEE double), big endian
603
+ if (buf.length >= 9) {
604
+ v = Buffer.alloc(8);
605
+ buf.copy(v, 0, 1, 9);
606
+ v = { type: 'number', data: v.toString('hex') };
607
+
608
+ if (_this.readableNumberValue) {
609
+ // The value key should not be used programmatically,
610
+ // it is just there to make the dumps more readable.
611
+ v.value = buf.readDoubleBE(1);
612
+ }
613
+ consume(9);
614
+ }
615
+ break;
616
+ case 0x1b: // object
617
+ if (buf.length >= 3) {
618
+ len = buf[2];
619
+ if (buf.length >= 3 + len) {
620
+ v = Buffer.alloc(len);
621
+ buf.copy(v, 0, 3, 3 + len);
622
+ v = { type: 'object', 'class': buf[1], pointer: v.toString('hex') };
623
+ consume(3 + len);
624
+ }
625
+ }
626
+ break;
627
+ case 0x1c: // pointer
628
+ if (buf.length >= 2) {
629
+ len = buf[1];
630
+ if (buf.length >= 2 + len) {
631
+ v = Buffer.alloc(len);
632
+ buf.copy(v, 0, 2, 2 + len);
633
+ v = { type: 'pointer', pointer: v.toString('hex') };
634
+ consume(2 + len);
635
+ }
636
+ }
637
+ break;
638
+ case 0x1d: // lightfunc
639
+ if (buf.length >= 4) {
640
+ len = buf[3];
641
+ if (buf.length >= 4 + len) {
642
+ v = Buffer.alloc(len);
643
+ buf.copy(v, 0, 4, 4 + len);
644
+ v = { type: 'lightfunc', flags: buf.readUInt16BE(1), pointer: v.toString('hex') };
645
+ consume(4 + len);
646
+ }
647
+ }
648
+ break;
649
+ case 0x1e: // heapptr
650
+ if (buf.length >= 2) {
651
+ len = buf[1];
652
+ if (buf.length >= 2 + len) {
653
+ v = Buffer.alloc(len);
654
+ buf.copy(v, 0, 2, 2 + len);
655
+ v = { type: 'heapptr', pointer: v.toString('hex') };
656
+ consume(2 + len);
657
+ }
658
+ }
659
+ break;
660
+ default:
661
+ _this.emit('transport-error', 'Parse error, dropping connection');
662
+ _this.close();
663
+ }
664
+ }
665
+
666
+ if (typeof v === 'undefined' && !gotValue) {
667
+ break;
668
+ }
669
+ msg.push(v);
670
+ _this.dvalues++;
671
+
672
+ // Could emit a 'debug-value' event here, but that's not necessary
673
+ // because the receiver will just collect statistics which can also
674
+ // be done using the finished message.
675
+
676
+ if (v === DVAL_EOM)
677
+ {
678
+ _this.messages++;
679
+
680
+ if (textDumpFileName || textDumpConsolePrefix) {
681
+ prettyMsg = prettyDebugMessage(msg);
682
+ if (textDumpFileName) {
683
+ fs.appendFileSync(textDumpFileName, (textDumpFilePrefix || '') + prettyMsg + '\n');
684
+ }
685
+ if (textDumpConsolePrefix) {
686
+ console.info1(textDumpConsolePrefix + prettyMsg);
687
+ }
688
+ }
689
+
690
+ _this.emit('debug-message', msg);
691
+ msg = []; // new object, old may be in circulation for a while
692
+ }
693
+ }
694
+ });
695
+
696
+ // Not all streams will emit this.
697
+ inputStream.on('error', function (err) {
698
+ _this.emit('transport-error', err);
699
+ _this.close();
700
+ });
701
+
702
+ // Not all streams will emit this.
703
+ inputStream.on('close', function () {
704
+ _this.close();
705
+ });
706
+}
707
+
708
+DebugProtocolParser.prototype.close = function () {
709
+ // Although the underlying transport may not have a close() or destroy()
710
+ // method or even a 'close' event, this method is always available and
711
+ // will generate a 'transport-close'.
712
+ //
713
+ // The caller is responsible for closing the underlying stream if that
714
+ // is necessary.
715
+
716
+ if (this.closed) { return; }
717
+
718
+ this.closed = true;
719
+ if (this.statsTimer) {
720
+ clearInterval(this.statsTimer);
721
+ this.statsTimer = null;
722
+ }
723
+ this.emit('transport-close');
724
+};
725
+
726
+
727
+/*
728
+ * Debugger implementation
729
+ *
730
+ * A debugger instance communicates with the debug target and maintains
731
+ * persistent debug state so that the current state can be resent to the
732
+ * socket.io client (web UI) if it reconnects. Whenever the debugger state
733
+ * is changed an event is generated. The socket.io handler will listen to
734
+ * state change events and push the necessary updates to the web UI, often
735
+ * in a rate limited fashion or using a client pull to ensure the web UI
736
+ * is not overloaded.
737
+ *
738
+ * The debugger instance assumes that if the debug protocol connection is
739
+ * re-established, it is always to the same target. There is no separate
740
+ * abstraction for a debugger session.
741
+ */
742
+
743
+function Debugger()
744
+{
745
+ events.EventEmitter.call(this);
746
+ this._ObjectID = 'Debugger';
747
+ this.web = null; // web UI singleton
748
+ this.targetStream = null; // transport connection to target
749
+ this.outputPassThroughStream = null; // dummy passthrough for message dumping
750
+ this.inputParser = null; // parser for incoming debug messages
751
+ this.outputParser = null; // parser for outgoing debug messages (stats, dumping)
752
+ this.protocolVersion = null;
753
+ this.dukVersion = null;
754
+ this.dukGitDescribe = null;
755
+ this.targetInfo = null;
756
+ this.debugger_attached = false;
757
+ this.handshook = false;
758
+ this.reqQueue = null;
759
+ this.stats = { // stats for current debug connection
760
+ rxBytes: 0, rxDvalues: 0, rxMessages: 0, rxBytesPerSec: 0,
761
+ txBytes: 0, txDvalues: 0, txMessages: 0, txBytesPerSec: 0
762
+ };
763
+ this.execStatus = {
764
+ attached: false,
765
+ state: 'detached',
766
+ fileName: '',
767
+ funcName: '',
768
+ line: 0,
769
+ pc: 0
770
+ };
771
+ this.breakpoints = [];
772
+ this.callstack = [];
773
+ this.locals = [];
774
+ this.messageLines = [];
775
+ this.messageScrollBack = 100;
776
+
777
+ this.on('attached', function OnAttached()
778
+ {
779
+ this.websocket.write({ cmd: 'ATTACH' });
780
+ console.info1('Debug transport connected');
781
+
782
+ this.debugger_attached = true;
783
+ this.reqQueue = [];
784
+ this.uiMessage('debugger-info', 'Debug transport connected');
785
+
786
+ this.sendStatusRequest().then(function () { statusPending = false; console.info1('finally'); }, console.info1);
787
+ hostCooperate();
788
+ });
789
+}
790
+
791
+
792
+Debugger.prototype.decodeBytecodeFromBuffer = function (buf, consts, funcs)
793
+{
794
+ var i, j, n, m, ins, pc;
795
+ var res = [];
796
+ var op, str, args, comments;
797
+
798
+ // XXX: add constants inline to preformatted output (e.g. for strings,
799
+ // add a short escaped snippet as a comment on the line after the
800
+ // compact argument list).
801
+
802
+ for (i = 0, n = buf.length; i < n; i += 4) {
803
+ pc = i / 4;
804
+
805
+ // shift forces unsigned
806
+ if (this.endianness === 'little') {
807
+ ins = buf.readInt32LE(i) >>> 0;
808
+ } else {
809
+ ins = buf.readInt32BE(i) >>> 0;
810
+ }
811
+
812
+ op = dukOpcodes.opcodes[ins & 0xff];
813
+
814
+ args = [];
815
+ comments = [];
816
+ if (op.args) {
817
+ for (j = 0, m = op.args.length; j < m; j++) {
818
+ var A = (ins >>> 8) & 0xff;
819
+ var B = (ins >>> 16) & 0xff;
820
+ var C = (ins >>> 24) & 0xff;
821
+ var BC = (ins >>> 16) & 0xffff;
822
+ var ABC = (ins >>> 8) & 0xffffff;
823
+ var Bconst = op & 0x01;
824
+ var Cconst = op & 0x02;
825
+
826
+ switch (op.args[j]) {
827
+ case 'A_R': args.push('r' + A); break;
828
+ case 'A_RI': args.push('r' + A + '(indirect)'); break;
829
+ case 'A_C': args.push('c' + A); break;
830
+ case 'A_H': args.push('0x' + A.toString(16)); break;
831
+ case 'A_I': args.push(A.toString(10)); break;
832
+ case 'A_B': args.push(A ? 'true' : 'false'); break;
833
+ case 'B_RC': args.push((Bconst ? 'c' : 'r') + B); break;
834
+ case 'B_R': args.push('r' + B); break;
835
+ case 'B_RI': args.push('r' + B + '(indirect)'); break;
836
+ case 'B_C': args.push('c' + B); break;
837
+ case 'B_H': args.push('0x' + B.toString(16)); break;
838
+ case 'B_I': args.push(B.toString(10)); break;
839
+ case 'C_RC': args.push((Cconst ? 'c' : 'r') + C); break;
840
+ case 'C_R': args.push('r' + C); break;
841
+ case 'C_RI': args.push('r' + C + '(indirect)'); break;
842
+ case 'C_C': args.push('c' + C); break;
843
+ case 'C_H': args.push('0x' + C.toString(16)); break;
844
+ case 'C_I': args.push(C.toString(10)); break;
845
+ case 'BC_R': args.push('r' + BC); break;
846
+ case 'BC_C': args.push('c' + BC); break;
847
+ case 'BC_H': args.push('0x' + BC.toString(16)); break;
848
+ case 'BC_I': args.push(BC.toString(10)); break;
849
+ case 'ABC_H': args.push(ABC.toString(16)); break;
850
+ case 'ABC_I': args.push(ABC.toString(10)); break;
851
+ case 'BC_LDINT': args.push(BC - (1 << 15)); break;
852
+ case 'BC_LDINTX': args.push(BC - 0); break; // no bias in LDINTX
853
+ case 'ABC_JUMP': {
854
+ var pc_add = ABC - (1 << 23) + 1; // pc is preincremented before adding
855
+ var pc_dst = pc + pc_add;
856
+ args.push(pc_dst + ' (' + (pc_add >= 0 ? '+' : '') + pc_add + ')');
857
+ break;
858
+ }
859
+ default: args.push('?'); break;
860
+ }
861
+ }
862
+ }
863
+ if (op.flags) {
864
+ for (j = 0, m = op.flags.length; j < m; j++) {
865
+ if (ins & op.flags[j].mask) {
866
+ comments.push(op.flags[j].name);
867
+ }
868
+ }
869
+ }
870
+
871
+ if (args.length > 0) {
872
+ str = sprintf('%05d %08x %-10s %s', pc, ins, op.name, args.join(', '));
873
+ } else {
874
+ str = sprintf('%05d %08x %-10s', pc, ins, op.name);
875
+ }
876
+ if (comments.length > 0) {
877
+ str = sprintf('%-44s ; %s', str, comments.join(', '));
878
+ }
879
+
880
+ res.push({
881
+ str: str,
882
+ ins: ins
883
+ });
884
+ }
885
+
886
+ return res;
887
+};
888
+
889
+Debugger.prototype.uiMessage = function (type, val)
890
+{
891
+ var msg;
892
+ if (typeof type === 'object') {
893
+ msg = type;
894
+ } else if (typeof type === 'string') {
895
+ msg = { type: type, message: val };
896
+ } else {
897
+ throw new TypeError('invalid ui message: ' + type);
898
+ }
899
+ this.messageLines.push(msg);
900
+ while (this.messageLines.length > this.messageScrollBack) {
901
+ this.messageLines.shift();
902
+ }
903
+ this.emit('ui-message-update'); // just trigger a sync, gets rate limited
904
+};
905
+
906
+Debugger.prototype.sendRequest = function (msg)
907
+{
908
+ var _this = this;
909
+ return new Promise(function (resolve, reject) {
910
+ var dvals = [];
911
+ var dval;
912
+ var data;
913
+ var i;
914
+
915
+ if (!_this.debugger_attached || !_this.handshook || !_this.reqQueue || !_this.targetStream)
916
+ {
917
+ console.error(_this.debugger_attached, _this.handshook, _this.reqQueue);
918
+ throw new Error('invalid state for sendRequest');
919
+ }
920
+
921
+ for (i = 0; i < msg.length; i++) {
922
+ try {
923
+ dval = formatDebugValue(msg[i]);
924
+ } catch (e) {
925
+ console.error('Failed to format dvalue, dropping connection: ' + e);
926
+ console.error(e.stack || e);
927
+ _this.targetStream.destroy();
928
+ throw new Error('failed to format dvalue');
929
+ }
930
+ dvals.push(dval);
931
+ }
932
+
933
+ data = Buffer.concat(dvals);
934
+
935
+ //console.log('Writing: ' + data.toString('hex'));
936
+
937
+ _this.targetStream.write(data);
938
+
939
+ if (_this.outputPassThroughStream) { _this.outputPassThroughStream.write(data); } // stats and dumping
940
+ if (optLogMessages) {
941
+ console.info1('Request ' + prettyDebugCommand(msg[1]) + ': ' + prettyDebugMessage(msg));
942
+ }
943
+
944
+ if (!_this.reqQueue) {
945
+ throw new Error('no reqQueue');
946
+ }
947
+ _this.reqQueue.push({
948
+ reqMsg: msg,
949
+ reqCmd: msg[1],
950
+ resolveCb: resolve,
951
+ rejectCb: reject
952
+ });
953
+ });
954
+};
955
+
956
+Debugger.prototype.sendBasicInfoRequest = function () {
957
+ var _this = this;
958
+ return this.sendRequest([DVAL_REQ, CMD_BASICINFO, DVAL_EOM]).then(function (msg) {
959
+ _this.dukVersion = msg[1];
960
+ _this.dukGitDescribe = msg[2];
961
+ _this.targetInfo = msg[3];
962
+ _this.endianness = { 1: 'little', 2: 'mixed', 3: 'big' }[msg[4]] || 'unknown';
963
+ _this.emit('basic-info-update');
964
+ return msg;
965
+ });
966
+};
967
+
968
+Debugger.prototype.sendGetVarRequest = function (varname, level) {
969
+ var _this = this;
970
+ return this.sendRequest([DVAL_REQ, CMD_GETVAR, (typeof level === 'number' ? level : -1), varname, DVAL_EOM]).then(function (msg) {
971
+ return { found: msg[1] === 1, value: msg[2] };
972
+ });
973
+};
974
+
975
+Debugger.prototype.sendPutVarRequest = function (varname, varvalue, level) {
976
+ var _this = this;
977
+ return this.sendRequest([DVAL_REQ, CMD_PUTVAR, (typeof level === 'number' ? level : -1), varname, varvalue, DVAL_EOM]);
978
+};
979
+
980
+Debugger.prototype.sendInvalidCommandTestRequest = function () {
981
+ // Intentional invalid command
982
+ var _this = this;
983
+ return this.sendRequest([DVAL_REQ, 0xdeadbeef, DVAL_EOM]);
984
+};
985
+
986
+Debugger.prototype.sendStatusRequest = function () {
987
+ // Send a status request to trigger a status notify, result is ignored:
988
+ // target sends a status notify instead of a meaningful reply
989
+ //console.log('this', this.sendRequest);
990
+ return (this.sendRequest([DVAL_REQ, CMD_TRIGGERSTATUS, DVAL_EOM]));
991
+};
992
+
993
+Debugger.prototype.sendBreakpointListRequest = function () {
994
+ var _this = this;
995
+ return this.sendRequest([DVAL_REQ, CMD_LISTBREAK, DVAL_EOM]).then(function (msg) {
996
+ var i, n;
997
+ var breakpts = [];
998
+
999
+ for (i = 1, n = msg.length - 1; i < n; i += 2) {
1000
+ breakpts.push({ fileName: msg[i], lineNumber: msg[i + 1] });
1001
+ }
1002
+
1003
+ _this.breakpoints = breakpts;
1004
+ _this.emit('breakpoints-update', breakpts);
1005
+ return msg;
1006
+ });
1007
+};
1008
+
1009
+Debugger.prototype.sendGetLocalsRequest = function (level) {
1010
+ var _this = this;
1011
+ return this.sendRequest([DVAL_REQ, CMD_GETLOCALS, (typeof level === 'number' ? level : -1), DVAL_EOM]).then(function (msg) {
1012
+ var i;
1013
+ var locals = [];
1014
+
1015
+ for (i = 1; i <= msg.length - 2; i += 2)
1016
+ {
1017
+ // XXX: do pretty printing in debug client for now
1018
+ locals.push({ key: msg[i], value: prettyUiDebugValue(msg[i + 1], LOCALS_CLIPLEN), type: (msg[i+1]?msg[i+1].type:'undefined') });
1019
+ }
1020
+
1021
+ _this.locals = locals;
1022
+ _this.emit('locals-update');
1023
+ return locals;
1024
+ });
1025
+};
1026
+
1027
+Debugger.prototype.sendGetCallStackRequest = function () {
1028
+ var _this = this;
1029
+ return this.sendRequest([DVAL_REQ, CMD_GETCALLSTACK, DVAL_EOM]).then(function (msg) {
1030
+ var i;
1031
+ var stack = [];
1032
+
1033
+ for (i = 1; i + 3 <= msg.length - 1; i += 4)
1034
+ {
1035
+ stack.push({
1036
+ fileName: msg[i],
1037
+ funcName: msg[i + 1],
1038
+ lineNumber: msg[i + 2],
1039
+ pc: msg[i + 3]
1040
+ });
1041
+ }
1042
+
1043
+ _this.callstack = stack;
1044
+ _this.emit('callstack-update');
1045
+ return msg;
1046
+ });
1047
+};
1048
+
1049
+Debugger.prototype.sendStepIntoRequest = function () {
1050
+ var _this = this;
1051
+ return this.sendRequest([DVAL_REQ, CMD_STEPINTO, DVAL_EOM]);
1052
+};
1053
+
1054
+Debugger.prototype.sendStepOverRequest = function () {
1055
+ var _this = this;
1056
+ return this.sendRequest([DVAL_REQ, CMD_STEPOVER, DVAL_EOM]);
1057
+};
1058
+
1059
+Debugger.prototype.sendStepOutRequest = function () {
1060
+ var _this = this;
1061
+ return this.sendRequest([DVAL_REQ, CMD_STEPOUT, DVAL_EOM]);
1062
+};
1063
+
1064
+Debugger.prototype.sendPauseRequest = function () {
1065
+ var _this = this;
1066
+ return this.sendRequest([DVAL_REQ, CMD_PAUSE, DVAL_EOM]);
1067
+};
1068
+
1069
+Debugger.prototype.sendResumeRequest = function () {
1070
+ var _this = this;
1071
+ return this.sendRequest([DVAL_REQ, CMD_RESUME, DVAL_EOM]);
1072
+};
1073
+
1074
+Debugger.prototype.sendEvalRequest = function (evalInput, level) {
1075
+ var _this = this;
1076
+ // Use explicit level if given. If no level is given, use null if the call
1077
+ // stack is empty, -1 otherwise. This works well when we're paused and the
1078
+ // callstack information is not liable to change before we do an Eval.
1079
+ console.info1('levelType = ' + typeof (level));
1080
+ if (typeof level !== 'number') {
1081
+ level = this.callstack && this.callstack.length > 0 ? -1 : null;
1082
+ }
1083
+ console.info1('level=' + level);
1084
+ return this.sendRequest([DVAL_REQ, CMD_EVAL, level, evalInput, DVAL_EOM]).then(function (msg) {
1085
+ return { error: msg[1] === 1 /*error*/, value: msg[2] };
1086
+ });
1087
+};
1088
+
1089
+Debugger.prototype.sendDetachRequest = function () {
1090
+ var _this = this;
1091
+ return this.sendRequest([DVAL_REQ, CMD_DETACH, DVAL_EOM]);
1092
+};
1093
+
1094
+Debugger.prototype.sendDumpHeapRequest = function () {
1095
+ var _this = this;
1096
+
1097
+ return this.sendRequest([DVAL_REQ, CMD_DUMPHEAP, DVAL_EOM]).then(function (msg) {
1098
+ var res = {};
1099
+ var objs = [];
1100
+ var i, j, n, m, o, prop;
1101
+
1102
+ res.type = 'heapDump';
1103
+ res.heapObjects = objs;
1104
+
1105
+ for (i = 1, n = msg.length - 1; i < n; /*nop*/) {
1106
+ o = {};
1107
+ o.ptr = msg[i++];
1108
+ o.type = msg[i++];
1109
+ o.flags = msg[i++] >>> 0; /* unsigned */
1110
+ o.refc = msg[i++];
1111
+
1112
+ if (o.type === DUK_HTYPE_STRING) {
1113
+ o.blen = msg[i++];
1114
+ o.clen = msg[i++];
1115
+ o.hash = msg[i++] >>> 0; /* unsigned */
1116
+ o.data = msg[i++];
1117
+ } else if (o.type === DUK_HTYPE_BUFFER) {
1118
+ o.len = msg[i++];
1119
+ o.data = msg[i++];
1120
+ } else if (o.type === DUK_HTYPE_OBJECT) {
1121
+ o['class'] = msg[i++];
1122
+ o.proto = msg[i++];
1123
+ o.esize = msg[i++];
1124
+ o.enext = msg[i++];
1125
+ o.asize = msg[i++];
1126
+ o.hsize = msg[i++];
1127
+ o.props = [];
1128
+ for (j = 0, m = o.enext; j < m; j++) {
1129
+ prop = {};
1130
+ prop.flags = msg[i++];
1131
+ prop.key = msg[i++];
1132
+ prop.accessor = (msg[i++] == 1);
1133
+ if (prop.accessor) {
1134
+ prop.getter = msg[i++];
1135
+ prop.setter = msg[i++];
1136
+ } else {
1137
+ prop.value = msg[i++];
1138
+ }
1139
+ o.props.push(prop);
1140
+ }
1141
+ o.array = [];
1142
+ for (j = 0, m = o.asize; j < m; j++) {
1143
+ prop = {};
1144
+ prop.value = msg[i++];
1145
+ o.array.push(prop);
1146
+ }
1147
+ } else {
1148
+ console.error('invalid htype: ' + o.type + ', disconnect');
1149
+ _this.disconnectDebugger();
1150
+ throw new Error('invalid htype');
1151
+ return;
1152
+ }
1153
+
1154
+ objs.push(o);
1155
+ }
1156
+
1157
+ return res;
1158
+ });
1159
+};
1160
+
1161
+Debugger.prototype.sendGetBytecodeRequest = function () {
1162
+ var _this = this;
1163
+
1164
+ return this.sendRequest([DVAL_REQ, CMD_GETBYTECODE, -1 /* level; could be other than -1 too */, DVAL_EOM]).then(function (msg) {
1165
+ var idx = 1;
1166
+ var nconst;
1167
+ var nfunc;
1168
+ var val;
1169
+ var buf;
1170
+ var i, n;
1171
+ var consts = [];
1172
+ var funcs = [];
1173
+ var bcode;
1174
+ var preformatted;
1175
+ var ret;
1176
+ var idxPreformattedInstructions;
1177
+
1178
+ //console.log(JSON.stringify(msg));
1179
+
1180
+ nconst = msg[idx++];
1181
+ for (i = 0; i < nconst; i++) {
1182
+ val = msg[idx++];
1183
+ consts.push(val);
1184
+ }
1185
+
1186
+ nfunc = msg[idx++];
1187
+ for (i = 0; i < nfunc; i++) {
1188
+ val = msg[idx++];
1189
+ funcs.push(val);
1190
+ }
1191
+ val = msg[idx++];
1192
+
1193
+ // Right now bytecode is a string containing a direct dump of the
1194
+ // bytecode in target endianness. Decode here so that the web UI
1195
+ // doesn't need to.
1196
+
1197
+ buf = Buffer.alloc(val.length);
1198
+ writeDebugStringToBuffer(val, buf, 0);
1199
+ bcode = _this.decodeBytecodeFromBuffer(buf, consts, funcs);
1200
+
1201
+ preformatted = [];
1202
+ consts.forEach(function (v, i) {
1203
+ preformatted.push('; c' + i + ' ' + JSON.stringify(v));
1204
+ });
1205
+ preformatted.push('');
1206
+ idxPreformattedInstructions = preformatted.length;
1207
+ bcode.forEach(function (v) {
1208
+ preformatted.push(v.str);
1209
+ });
1210
+ preformatted = preformatted.join('\n') + '\n';
1211
+
1212
+ ret = {
1213
+ constants: consts,
1214
+ functions: funcs,
1215
+ bytecode: bcode,
1216
+ preformatted: preformatted,
1217
+ idxPreformattedInstructions: idxPreformattedInstructions
1218
+ };
1219
+
1220
+ return ret;
1221
+ });
1222
+};
1223
+
1224
+Debugger.prototype.changeBreakpoint = function (fileName, lineNumber, mode) {
1225
+ var _this = this;
1226
+
1227
+ return this.sendRequest([DVAL_REQ, CMD_LISTBREAK, DVAL_EOM]).then(function (msg) {
1228
+ var i, n;
1229
+ var breakpts = [];
1230
+ var deleted = false;
1231
+
1232
+ // Up-to-date list of breakpoints on target
1233
+ for (i = 1, n = msg.length - 1; i < n; i += 2) {
1234
+ breakpts.push({ fileName: msg[i], lineNumber: msg[i + 1] });
1235
+ }
1236
+
1237
+ // Delete matching breakpoints in reverse order so that indices
1238
+ // remain valid. We do this for all operations so that duplicates
1239
+ // are eliminated if present.
1240
+ for (i = breakpts.length - 1; i >= 0; i--) {
1241
+ var bp = breakpts[i];
1242
+ if (mode === 'deleteall' || (bp.fileName === fileName && bp.lineNumber === lineNumber))
1243
+ {
1244
+ deleted = true;
1245
+ _this.sendRequest([DVAL_REQ, CMD_DELBREAK, i, DVAL_EOM], function (msg)
1246
+ {
1247
+ // nop
1248
+ }, function (err) {
1249
+ // nop
1250
+ });
1251
+ }
1252
+ }
1253
+
1254
+ // Technically we should wait for each delbreak reply but because
1255
+ // target processes the requests in order, it doesn't matter.
1256
+ if ((mode === 'add') || (mode === 'toggle' && !deleted)) {
1257
+ _this.sendRequest([DVAL_REQ, CMD_ADDBREAK, fileName, lineNumber, DVAL_EOM], function (msg)
1258
+ {
1259
+ // nop
1260
+ }, function (err) {
1261
+ _this.uiMessage('debugger-info', 'Failed to add breakpoint: ' + err);
1262
+ });
1263
+ }
1264
+
1265
+ // Read final, effective breakpoints from the target
1266
+ _this.sendBreakpointListRequest();
1267
+ hostCooperate();
1268
+ });
1269
+};
1270
+
1271
+Debugger.prototype.disconnectDebugger = function ()
1272
+{
1273
+ console.info1('Debugger.disconnectDebugger();');
1274
+ if (this.targetStream)
1275
+ {
1276
+ // We require a destroy() method from the actual target stream
1277
+ this.targetStream.end();
1278
+ this.targetStream = null;
1279
+ }
1280
+ if (this.inputParser) {
1281
+ this.inputParser.close();
1282
+ this.inputParser = null;
1283
+ }
1284
+ if (this.outputPassThroughStream) {
1285
+ // There is no close() or destroy() for a passthrough stream, so just
1286
+ // close the outputParser which will cancel timers etc.
1287
+ }
1288
+ if (this.outputParser) {
1289
+ this.outputParser.close();
1290
+ this.outputParser = null;
1291
+ }
1292
+ console.info1('disconnecting');
1293
+ this.debugger_attached = false;
1294
+ this.handshook = false;
1295
+ this.reqQueue = null;
1296
+ this.execStatus = {
1297
+ attached: false,
1298
+ state: 'detached',
1299
+ fileName: '',
1300
+ funcName: '',
1301
+ line: 0,
1302
+ pc: 0
1303
+ };
1304
+};
1305
+
1306
+Debugger.prototype.connectDebugger = function connectDebugger()
1307
+{
1308
+ var _this = this;
1309
+ this.disconnectDebugger(); // close previous target connection
1310
+
1311
+ // CUSTOMTRANSPORT: to use a custom transport, change this.targetStream to
1312
+ // use your custom transport.
1313
+
1314
+ console.info1('Connecting to ' + optTargetHost + ':' + optTargetPort + '...');
1315
+ this.targetStream = require('net').createConnection(optTargetPort, optTargetHost);
1316
+ this.targetStream.Debugger = this;
1317
+
1318
+ this.targetStream.on('connect', function OnDebugConnect()
1319
+ {
1320
+ console.info1('Debugger Transport Connection Attached...');
1321
+ hostCooperate();
1322
+ });
1323
+ //this.targetStream.on('data', function (msg) { console.log('recv');});
1324
+
1325
+ try
1326
+ {
1327
+ this.inputParser = new DebugProtocolParser(
1328
+ this.targetStream,
1329
+ null,
1330
+ optDumpDebugRead,
1331
+ optDumpDebugPretty,
1332
+ optDumpDebugPretty ? 'Recv: ' : null,
1333
+ null,
1334
+ null // console logging is done at a higher level to match request/response
1335
+ );
1336
+ }
1337
+ catch(ee)
1338
+ {
1339
+ console.error(ee);
1340
+ }
1341
+
1342
+ //// Use a PassThrough stream to debug dump and get stats for output messages.
1343
+ //// Simply write outgoing data to both the targetStream and this passthrough
1344
+ //// separately.
1345
+ //this.outputPassThroughStream = stream.PassThrough();
1346
+ //this.outputParser = new DebugProtocolParser(
1347
+ // this.outputPassThroughStream,
1348
+ // 1,
1349
+ // optDumpDebugWrite,
1350
+ // optDumpDebugPretty,
1351
+ // optDumpDebugPretty ? 'Send: ' : null,
1352
+ // null,
1353
+ // null // console logging is done at a higher level to match request/response
1354
+ //);
1355
+
1356
+ this.inputParser.on('transport-close', function ()
1357
+ {
1358
+ console.info1('TRANSPORT-CLOSE');
1359
+ _this.uiMessage('debugger-info', 'Debug transport closed');
1360
+ _this.disconnectDebugger();
1361
+ _this.emit('exec-status-update');
1362
+ _this.emit('detached');
1363
+ });
1364
+
1365
+ this.inputParser.on('transport-error', function (err)
1366
+ {
1367
+ console.info1('TRANSPORT-ERROR', err);
1368
+ _this.uiMessage('debugger-info', 'Debug transport error: ' + err);
1369
+ _this.disconnectDebugger();
1370
+ });
1371
+
1372
+ this.inputParser.on('protocol-version', function (msg) {
1373
+ var ver = msg.protocolVersion;
1374
+ console.info1('Debug version identification:', msg.versionIdentification);
1375
+ _this.protocolVersion = ver;
1376
+ _this.uiMessage('debugger-info', 'Debug version identification: ' + msg.versionIdentification);
1377
+ if (ver !== SUPPORTED_DEBUG_PROTOCOL_VERSION) {
1378
+ console.error('Unsupported debug protocol version (got ' + ver + ', support ' + SUPPORTED_DEBUG_PROTOCOL_VERSION + ')');
1379
+ _this.uiMessage('debugger-info', 'Protocol version ' + ver + ' unsupported, dropping connection');
1380
+ _this.targetStream.destroy();
1381
+ } else {
1382
+ _this.uiMessage('debugger-info', 'Debug protocol version: ' + ver);
1383
+ _this.handshook = true;
1384
+ _this.execStatus = {
1385
+ attached: true,
1386
+ state: 'attached',
1387
+ fileName: '',
1388
+ funcName: '',
1389
+ line: 0,
1390
+ pc: 0
1391
+ };
1392
+ _this.debugger_attached = true;
1393
+ _this.emit('exec-status-update');
1394
+ _this.emit('attached'); // inform web UI
1395
+
1396
+ // Fetch basic info right away
1397
+ _this.sendBasicInfoRequest();
1398
+ }
1399
+ });
1400
+
1401
+ this.inputParser.on('debug-message', function (msg)
1402
+ {
1403
+ _this.processDebugMessage(msg);
1404
+ });
1405
+
1406
+ this.inputParser.on('stats-update', function () {
1407
+ _this.stats.rxBytes = this.bytes;
1408
+ _this.stats.rxDvalues = this.dvalues;
1409
+ _this.stats.rxMessages = this.messages;
1410
+ _this.stats.rxBytesPerSec = this.bytesPerSec;
1411
+ _this.emit('debug-stats-update');
1412
+ });
1413
+
1414
+ if (this.outputParser)
1415
+ {
1416
+ this.outputParser.on('stats-update', function () {
1417
+ _this.stats.txBytes = this.bytes;
1418
+ _this.stats.txDvalues = this.dvalues;
1419
+ _this.stats.txMessages = this.messages;
1420
+ _this.stats.txBytesPerSec = this.bytesPerSec;
1421
+ _this.emit('debug-stats-update');
1422
+ });
1423
+ }
1424
+};
1425
+
1426
+Debugger.prototype.processDebugMessage = function (msg) {
1427
+ var req;
1428
+ var prevState, newState;
1429
+ var err;
1430
+
1431
+ if (msg[0] === DVAL_REQ) {
1432
+ // No actual requests sent by the target right now (just notifys).
1433
+ console.error('Unsolicited reply message, dropping connection: ' + prettyDebugMessage(msg));
1434
+ } else if (msg[0] === DVAL_REP) {
1435
+ if (this.reqQueue.length <= 0) {
1436
+ console.error('Unsolicited reply message, dropping connection: [' + this.reqQueue.length + '] ' + prettyDebugMessage(msg));
1437
+ this.targetStream.destroy();
1438
+ }
1439
+ req = this.reqQueue.shift();
1440
+
1441
+ if (optLogMessages) {
1442
+ console.info1('Reply for ' + prettyDebugCommand(req.reqCmd) + ': ' + prettyDebugMessage(msg));
1443
+ }
1444
+
1445
+ if (req.resolveCb) {
1446
+ req.resolveCb(msg);
1447
+ } else {
1448
+ // nop: no callback
1449
+ }
1450
+ } else if (msg[0] === DVAL_ERR) {
1451
+ if (this.reqQueue.length <= 0) {
1452
+ console.error('Unsolicited error message, dropping connection: ' + prettyDebugMessage(msg));
1453
+ this.targetStream.destroy();
1454
+ }
1455
+ err = new Error(String(msg[2]) + ' (code ' + String(msg[1]) + ')');
1456
+ err.errorCode = msg[1] || 0;
1457
+ req = this.reqQueue.shift();
1458
+
1459
+ if (optLogMessages) {
1460
+ console.info1('Error for ' + prettyDebugCommand(req.reqCmd) + ': ' + prettyDebugMessage(msg));
1461
+ }
1462
+
1463
+ console.info1(req.rejectCb, err);
1464
+
1465
+ if (req.rejectCb)
1466
+ {
1467
+ req.rejectCb(err);
1468
+ } else
1469
+ {
1470
+ // nop: no callback
1471
+ }
1472
+ } else if (msg[0] === DVAL_NFY) {
1473
+ if (optLogMessages) {
1474
+ console.info1('Notify ' + prettyDebugCommand(msg[1]) + ': ' + prettyDebugMessage(msg));
1475
+ }
1476
+
1477
+ if (msg[1] === CMD_STATUS)
1478
+ {
1479
+ prevState = this.execStatus.state;
1480
+ newState = msg[2] === 0 ? 'running' : 'paused';
1481
+ this.execStatus = {
1482
+ attached: true,
1483
+ state: newState,
1484
+ fileName: (typeof(msg[3])=='string'?msg[3]:''),
1485
+ funcName: (typeof (msg[4]) == 'string' ? msg[4] : ''),
1486
+ line: msg[5],
1487
+ pc: msg[6]
1488
+ };
1489
+ console.info1('MSG=>', msg);
1490
+ for (var ii in msg)
1491
+ {
1492
+ console.info1('[' + ii + ']', msg[ii]);
1493
+ }
1494
+ if (prevState !== newState && newState === 'paused') {
1495
+ // update run state now that we're paused
1496
+ if(typeof(msg[3])=='string')
1497
+ {
1498
+ this.sendBreakpointListRequest();
1499
+ this.sendGetLocalsRequest();
1500
+ this.sendGetCallStackRequest();
1501
+ }
1502
+ }
1503
+
1504
+ this.emit('exec-status-update');
1505
+ } else if (msg[1] === CMD_THROW)
1506
+ {
1507
+ console.info1(msg);
1508
+ this.emit('throw', { fatal: msg[2], message: (msg[2] ? 'UNCAUGHT: ' : 'THROW: ') + prettyUiStringUnquoted(msg[3], UI_MESSAGE_CLIPLEN), fileName: msg[4], lineNumber: msg[5] });
1509
+ this.uiMessage({ type: 'throw', fatal: msg[2], message: (msg[2] ? 'UNCAUGHT: ' : 'THROW: ') + prettyUiStringUnquoted(msg[3], UI_MESSAGE_CLIPLEN), fileName: msg[4], lineNumber: msg[5] });
1510
+ } else if (msg[1] === CMD_DETACHING) {
1511
+ this.uiMessage({ type: 'detaching', reason: msg[2], message: 'DETACH: ' + (msg.length >= 5 ? prettyUiStringUnquoted(msg[3]) : 'detaching') });
1512
+ } else if(msg[1] === CMD_APPNOTIFY)
1513
+ {
1514
+ switch(msg[2])
1515
+ {
1516
+ case 'MemoryAllocations':
1517
+ console.info1('MemoryAllocation: ' + msg[3]);
1518
+ this.emit('memory-allocation', { total: parseInt(msg[3]) });
1519
+ break;
1520
+ default:
1521
+ console.info1('Unknown AppNotify: ' + msg[2]);
1522
+ break;
1523
+ }
1524
+ }
1525
+ else
1526
+ {
1527
+ // Ignore unknown notify messages
1528
+ console.info1('Unknown notify, ignoring: ' + prettyDebugMessage(msg));
1529
+ }
1530
+ } else {
1531
+ console.error('Invalid initial dvalue, dropping connection: ' + prettyDebugMessage(msg));
1532
+ this.targetStream.destroy();
1533
+ }
1534
+};
1535
+
1536
+Debugger.prototype.run = function ()
1537
+{
1538
+ var _this = this;
1539
+ optTargetPort = transport;
1540
+
1541
+ this.WebServer = require('http').createServer();
1542
+ this.WebServer.Debugger = this;
1543
+ this.WebServer.listen({ port: webport });
1544
+ updateWebPort(this.WebServer.address().port);
1545
+ this.WebServer.on('request', function OnDebugWebsiteRequest(imsg, rsp)
1546
+ {
1547
+ console.info1('Received inbound request: ' + imsg.method + ' ' + imsg.url);
1548
+ if (imsg.url == '/')
1549
+ {
1550
+ rsp.statusCode = 200;
1551
+ rsp.statusText = 'OK';
1552
+ rsp.setHeader('Content-Type', 'text/html');
1553
+
1554
+ if (require('fs').existsSync('debugger.html'))
1555
+ {
1556
+ rsp.fs = require('fs').createReadStream('debugger.html');
1557
+ rsp.fs.pipe(rsp);
1558
+ }
1559
+ else
1560
+ {
1561
+ rsp.end(Buffer.from(dbgHTML, 'base64'));
1562
+ }
1563
+ }
1564
+ else
1565
+ {
1566
+ rsp.statusCode = 404;
1567
+ rsp.end();
1568
+ }
1569
+ });
1570
+ this.WebServer.on('upgrade', function OnDebugWebSocket(req, soc, head)
1571
+ {
1572
+ console.info1('WebSocket request on: ', req.url);
1573
+ this.Debugger.websocket = soc.upgradeWebSocket();
1574
+ this.Debugger.websocket.Debugger = this.Debugger;
1575
+ this.Debugger.websocket.on('end', function ()
1576
+ {
1577
+ this.Debugger.disconnectDebugger();
1578
+ console.info1('web socket closed');
1579
+ });
1580
+ this.Debugger.websocket.on('data', function OnDebugCmd(buf)
1581
+ {
1582
+ var j = null;
1583
+
1584
+ try
1585
+ {
1586
+ j = JSON.parse(buf);
1587
+ }
1588
+ catch(e)
1589
+ {
1590
+ console.error('Parse Error: ' + e);
1591
+ return;
1592
+ }
1593
+
1594
+ switch(j.cmd)
1595
+ {
1596
+ case 'ATTACH':
1597
+ console.info1('Attaching Debugger');
1598
+ this.Debugger.connectDebugger();
1599
+ break;
1600
+ case 'GC':
1601
+ _debugGC(); // Debugger Heap
1602
+ hostGC(); // Host Heap
1603
+ console.info1('Force GC');
1604
+ break;
1605
+ case 'STEPOVER':
1606
+ this.Debugger.sendStepOverRequest();
1607
+ break;
1608
+ case 'STEPINTO':
1609
+ this.Debugger.sendStepIntoRequest();
1610
+ break;
1611
+ case 'STEPOUT':
1612
+ this.Debugger.sendStepOutRequest();
1613
+ break;
1614
+ case 'CALLSTACK':
1615
+ this.Debugger.sendGetCallStackRequest();
1616
+ break;
1617
+ case 'HEAP':
1618
+ var p = this.Debugger.sendDumpHeapRequest();
1619
+ p.Debugger = this.Debugger;
1620
+ p.then(function onSendDumpHeap(obj)
1621
+ {
1622
+ console.info1('\n\n\n\n');
1623
+ var heapObjects = obj.heapObjects;
1624
+ for(var i in heapObjects)
1625
+ {
1626
+ console.info1(heapObjects[i].ptr, heapObjects[i].type);
1627
+ }
1628
+ //console.info1('this', this);
1629
+ //process.stdout.write('THEN\n');
1630
+ //process.stdout.write(JSON.stringify(this) + '\n');
1631
+ //process.stdout.write(JSON.stringify(obj) + '\n');
1632
+ }, function OnError(err) { console.info1('ERROR=>', err); });
1633
+ break;
1634
+ case 'PAUSE':
1635
+ this.Debugger.sendPauseRequest();
1636
+ break;
1637
+ case 'RESUME':
1638
+ this.Debugger.sendResumeRequest();
1639
+ break;
1640
+ case 'DETACH':
1641
+ console.info1('Detach Debugger');
1642
+ this.Debugger.disconnectDebugger();
1643
+ break;
1644
+ case 'BREAKPOINT':
1645
+ console.info1(j);
1646
+ if (j.file == '')
1647
+ {
1648
+ if ((j.file = this.Debugger.execStatus.fileName) == '')
1649
+ {
1650
+ j.file = _scriptPath;
1651
+ }
1652
+ console.info1('using filePath: ' + j.file);
1653
+ }
1654
+
1655
+ console.info1('ChangeBreakpoint => ' + j.file + ':' + j.line + ' (' + j.mode + ')');
1656
+ this.Debugger.changeBreakpoint(j.file, j.line, j.mode);
1657
+ break;
1658
+ case 'QUERY':
1659
+ console.info1(j);
1660
+ var p = this.Debugger.sendEvalRequest('JSON.stringify(' + j.var + ')', j.level);
1661
+ p.var = j.var;
1662
+ p.then(function (msg)
1663
+ {
1664
+ _this.websocket.write({ cmd: 'QUERY', var: this.promise.var, val: msg.value });
1665
+ });
1666
+ break;
1667
+ case 'EVAL':
1668
+ console.info1(j);
1669
+ var p = this.Debugger.sendEvalRequest(j.eval, j.level);
1670
+ p.eval = j.eval;
1671
+ p.then(function (msg)
1672
+ {
1673
+ _this.websocket.write({ cmd: 'EVAL', eval: this.promise.eval, val: msg.value });
1674
+ });
1675
+ break;
1676
+ break;
1677
+ case 'LOCALS':
1678
+ console.info1(j);
1679
+ this.Debugger.sendGetLocalsRequest(j.level).then(function (locals)
1680
+ {
1681
+ var vals = [];
1682
+ for (var v in locals)
1683
+ {
1684
+ console.info1('Pushing: [' + locals[v].key + '] = ' + locals[v].type);
1685
+ vals.push({ key: locals[v].key, type: locals[v].type });
1686
+ }
1687
+ _this.websocket.write({ cmd: 'LOCALS', val: vals });
1688
+ });
1689
+
1690
+
1691
+ break
1692
+ case 'SOURCE':
1693
+ console.info1('SOURCE', j);
1694
+ if (j.name != '')
1695
+ {
1696
+ if (j.name.toLowerCase().endsWith('.js') || j.name == '<<NATIVE>>')
1697
+ {
1698
+ console.info1(JSON.stringify({ cmd: 'SOURCE', name: j.name, source: _scriptTokens.join('\n') }));
1699
+ _this.websocket.write({ cmd: 'SOURCE', name: j.name, source: _scriptTokens.join('\n') });
1700
+ }
1701
+ else
1702
+ {
1703
+ var p = _this.sendEvalRequest("getJSModule('" + j.name + "');");
1704
+ p.websocket = _this.websocket;
1705
+ p.name = j.name;
1706
+
1707
+ p.then(function (result) {
1708
+ this.promise.websocket.write({ cmd: 'SOURCE', name: this.promise.name, source: result.value });
1709
+ });
1710
+ }
1711
+ }
1712
+ break;
1713
+ }
1714
+
1715
+ hostCooperate();
1716
+ });
1717
+
1718
+ this.Debugger.on('memory-allocation', function (mem)
1719
+ {
1720
+ var cmd = { cmd: 'MEMORY', total: mem.total };
1721
+ this.websocket.write(cmd);
1722
+ });
1723
+ this.Debugger.on('detached', function ()
1724
+ {
1725
+ detachCleanup();
1726
+ });
1727
+
1728
+ this.Debugger.on('debug-stats-update', function ()
1729
+ {
1730
+ // _this.debugStatsLimiter.trigger();
1731
+ });
1732
+
1733
+ this.Debugger.on('ui-message-update', function ()
1734
+ {
1735
+ // Explicit rate limiter because this is a source of a lot of traffic.
1736
+ //_this.uiMessageLimiter.trigger();
1737
+ });
1738
+
1739
+ this.Debugger.on('basic-info-update', function ()
1740
+ {
1741
+ // _this.emitBasicInfo();
1742
+ });
1743
+
1744
+ this.Debugger.on('breakpoints-update', function (bpts)
1745
+ {
1746
+ console.info1('breakpoints updated');
1747
+ var cmd = { cmd: 'BREAKPOINT', list: bpts };
1748
+ this.websocket.write(cmd);
1749
+ //_this.emitBreakpoints();
1750
+ });
1751
+
1752
+ this.Debugger.on('exec-status-update', function ()
1753
+ {
1754
+ if (!this.previousState || this.previousState != this.execStatus.state)
1755
+ {
1756
+ this.previousState = this.execStatus.state;
1757
+ console.info1('new state', this.execStatus.state);
1758
+ if (this.execStatus.state == 'attached') { console.info1('forcing cooperate'); hostCooperate(); }
1759
+ if (this.execStatus.attached)
1760
+ {
1761
+ this.websocket.pausedNative = false;
1762
+ switch(this.execStatus.state)
1763
+ {
1764
+ case 'paused':
1765
+ console.info1('PAUSED', this.execStatus);
1766
+ var src = _scriptPath == this.execStatus.fileName ? _scriptTokens[this.execStatus.line - 1] : null;
1767
+ if (src == null)
1768
+ {
1769
+ if (this.execStatus.fileName != '')
1770
+ {
1771
+ var p = this.sendEvalRequest("getJSModule('" + this.execStatus.fileName + "');");
1772
+ p.websocket = this.websocket;
1773
+ p.execStatus = this.execStatus;
1774
+
1775
+ p.then(function (result) {
1776
+ var tk = result.value.split('\n');
1777
+ this.promise.websocket.write({ cmd: 'PAUSE', native: false, file: this.promise.execStatus.fileName, line: this.promise.execStatus.line, source: tk[this.promise.execStatus.line - 1] });
1778
+ });
1779
+ }
1780
+ else
1781
+ {
1782
+ this.websocket.pausedNative = true;
1783
+ this.websocket.write({ cmd: 'PAUSE', native: true, file: this.execStatus.fileName, line: this.execStatus.line, source: '(Native)' });
1784
+ }
1785
+ }
1786
+ else
1787
+ {
1788
+ this.websocket.write({ cmd: 'PAUSE', native: false, file: this.execStatus.fileName, line: this.execStatus.line, source: src });
1789
+ }
1790
+ break;
1791
+ case 'running':
1792
+ this.websocket.write({ cmd: 'RUNNING', file: _scriptPath });
1793
+ break;
1794
+ }
1795
+ }
1796
+ else if (this.execStatus.state == 'detached')
1797
+ {
1798
+ this.websocket.write({ cmd: 'DETACH'});
1799
+ }
1800
+ }
1801
+ // Explicit rate limiter because this is a source of a lot of traffic.
1802
+ //_this.execStatusLimiter.trigger();
1803
+ });
1804
+ this.Debugger.on('throw', function OnThrown(err)
1805
+ {
1806
+ var source = err.fileName == _scriptPath ? (_scriptTokens[err.lineNumber - 1]) : null;
1807
+ if (source == null) {
1808
+ var p = this.sendEvalRequest("getJSModule('" + err.fileName + "');");
1809
+ p.ws = this.websocket;
1810
+ p.err = err;
1811
+
1812
+ p.then(function (result)
1813
+ {
1814
+ var tk = result.value.split('\n');
1815
+ this.promise.ws.write({ cmd: 'THROW', line: this.promise.err.lineNumber, msg: this.promise.err.message, file: this.promise.err.fileName, source: tk[this.promise.err.lineNumber - 1] });
1816
+ });
1817
+ }
1818
+ else
1819
+ {
1820
+ this.websocket.write({ cmd: 'THROW', line: err.lineNumber, msg: err.message, file: err.fileName, source: source });
1821
+ }
1822
+ });
1823
+
1824
+ this.Debugger.on('locals-update', function ()
1825
+ {
1826
+ //_this.emitLocals();
1827
+ });
1828
+
1829
+ this.Debugger.on('callstack-update', function ()
1830
+ {
1831
+ this.websocket.write({ cmd: 'CALLSTACK', callstack: this.callstack });
1832
+ });
1833
+
1834
+
1835
+
1836
+ });
1837
+
1838
+
1839
+
1840
+
1841
+
1842
+
1843
+ //// Initial debugger connection
1844
+
1845
+ //this.connectDebugger();
1846
+
1847
+ //// Poll various state items when running
1848
+
1849
+ //var sendRound = 0;
1850
+ //var statusPending = false;
1851
+ //var bplistPending = false;
1852
+ //var localsPending = false;
1853
+ //var callStackPending = false;
1854
+
1855
+ //setInterval(function () {
1856
+ // if (_this.execStatus.state !== 'running') {
1857
+ // return;
1858
+ // }
1859
+
1860
+ // // Could also check for an empty request queue, but that's probably
1861
+ // // too strict?
1862
+
1863
+ // // Pending flags are used to avoid requesting the same thing twice
1864
+ // // while a previous request is pending. The flag-based approach is
1865
+ // // quite awkward. Rework to use promises.
1866
+
1867
+ // switch (sendRound) {
1868
+ // case 0:
1869
+ // if (!statusPending) {
1870
+ // statusPending = true;
1871
+ // _this.sendStatusRequest().finally(function () { statusPending = false; });
1872
+ // }
1873
+ // break;
1874
+ // case 1:
1875
+ // if (!bplistPending) {
1876
+ // bplistPending = true;
1877
+ // _this.sendBreakpointListRequest().finally(function () { bplistPending = false; });
1878
+ // }
1879
+ // break;
1880
+ // case 2:
1881
+ // if (!localsPending) {
1882
+ // localsPending = true;
1883
+ // _this.sendGetLocalsRequest().finally(function () { localsPending = false; });
1884
+ // }
1885
+ // break;
1886
+ // case 3:
1887
+ // if (!callStackPending) {
1888
+ // callStackPending = true;
1889
+ // _this.sendGetCallStackRequest().finally(function () { callStackPending = false; });
1890
+ // }
1891
+ // break;
1892
+ // }
1893
+ // sendRound = (sendRound + 1) % 4;
1894
+ //}, 500);
1895
+};
1896
+
1897
+
1898
+
1899
+
1900
+
1901
+/*
1902
+ * Miscellaneous helpers
1903
+ */
1904
+
1905
+var nybbles = '0123456789abcdef';
1906
+
1907
+/* Convert a buffer into a string using Unicode codepoints U+0000...U+00FF.
1908
+ * This is the NodeJS 'binary' encoding, but since it's being deprecated,
1909
+ * reimplement it here. We need to avoid parsing strings as e.g. UTF-8:
1910
+ * although Duktape strings are usually UTF-8/CESU-8 that's not always the
1911
+ * case, e.g. for internal strings. Buffer values are also represented as
1912
+ * strings in the debug protocol, so we must deal accurately with arbitrary
1913
+ * byte arrays.
1914
+ */
1915
+function bufferToDebugString(buf) {
1916
+ var cp = [];
1917
+ var i, n;
1918
+
1919
+ /*
1920
+ // This fails with "RangeError: Maximum call stack size exceeded" for some
1921
+ // reason, so use a much slower variant.
1922
+
1923
+ for (i = 0, n = buf.length; i < n; i++) {
1924
+ cp[i] = buf[i];
1925
+ }
1926
+
1927
+ return String.fromCharCode.apply(String, cp);
1928
+ */
1929
+
1930
+ for (i = 0, n = buf.length; i < n; i++) {
1931
+ cp[i] = String.fromCharCode(buf[i]);
1932
+ }
1933
+
1934
+ return cp.join('');
1935
+}
1936
+
1937
+/* Write a string into a buffer interpreting codepoints U+0000...U+00FF
1938
+ * as bytes. Drop higher bits.
1939
+ */
1940
+function writeDebugStringToBuffer(str, buf, off) {
1941
+ var i, n;
1942
+
1943
+ for (i = 0, n = str.length; i < n; i++) {
1944
+ buf[off + i] = str.charCodeAt(i) & 0xff; // truncate higher bits
1945
+ }
1946
+}
1947
+
1948
+/* Encode an ordinary Unicode string into a dvalue compatible format, i.e.
1949
+ * into a byte array represented as codepoints U+0000...U+00FF. Concretely,
1950
+ * encode with UTF-8 and then represent the bytes with U+0000...U+00FF.
1951
+ */
1952
+function stringToDebugString(str) {
1953
+ return utf8.encode(str);
1954
+}
1955
+
1956
+/* Pretty print a dvalue. Useful for dumping etc. */
1957
+function prettyDebugValue(x) {
1958
+ if (typeof x === 'object' && x !== null) {
1959
+ if (x.type === 'eom') {
1960
+ return 'EOM';
1961
+ } else if (x.type === 'req') {
1962
+ return 'REQ';
1963
+ } else if (x.type === 'rep') {
1964
+ return 'REP';
1965
+ } else if (x.type === 'err') {
1966
+ return 'ERR';
1967
+ } else if (x.type === 'nfy') {
1968
+ return 'NFY';
1969
+ }
1970
+ }
1971
+ return JSON.stringify(x);
1972
+}
1973
+
1974
+/* Pretty print a number for UI usage. Types and values should be easy to
1975
+ * read and typing should be obvious. For numbers, support Infinity, NaN,
1976
+ * and signed zeroes properly.
1977
+ */
1978
+function prettyUiNumber(x) {
1979
+ if (x === 1 / 0) { return 'Infinity'; }
1980
+ if (x === -1 / 0) { return '-Infinity'; }
1981
+ if (isNaN(x)) { return 'NaN'; }
1982
+ if (x === 0 && 1 / x > 0) { return '0'; }
1983
+ if (x === 0 && 1 / x < 0) { return '-0'; }
1984
+ return x.toString();
1985
+}
1986
+
1987
+/* Pretty print a dvalue string (bytes represented as U+0000...U+00FF)
1988
+ * for UI usage. Try UTF-8 decoding to get a nice Unicode string (JSON
1989
+ * encoded) but if that fails, ensure that bytes are encoded transparently.
1990
+ * The result is a quoted string with a special quote marker for a "raw"
1991
+ * string when UTF-8 decoding fails. Very long strings are optionally
1992
+ * clipped.
1993
+ */
1994
+function prettyUiString(x, cliplen) {
1995
+ var ret;
1996
+
1997
+ if (typeof x !== 'string') {
1998
+ throw new Error('invalid input to prettyUiString: ' + typeof x);
1999
+ }
2000
+ try {
2001
+ // Here utf8.decode() is better than decoding using NodeJS buffer
2002
+ // operations because we want strict UTF-8 interpretation.
2003
+ ret = JSON.stringify(utf8.decode(x));
2004
+ } catch (e) {
2005
+ // When we fall back to representing bytes, indicate that the string
2006
+ // is "raw" with a 'r"' prefix (a somewhat arbitrary convention).
2007
+ // U+0022 = ", U+0027 = '
2008
+ ret = 'r"' + x.replace(/[\u0022\u0027\u0000-\u001f\u0080-\uffff]/g, function (match) {
2009
+ var cp = match.charCodeAt(0);
2010
+ return '\\x' + nybbles[(cp >> 4) & 0x0f] + nybbles[cp & 0x0f];
2011
+ }) + '"';
2012
+ }
2013
+
2014
+ if (cliplen && ret.length > cliplen) {
2015
+ ret = ret.substring(0, cliplen) + '...'; // trailing '"' intentionally missing
2016
+ }
2017
+ return ret;
2018
+}
2019
+
2020
+/* Pretty print a dvalue string (bytes represented as U+0000...U+00FF)
2021
+ * for UI usage without quotes.
2022
+ */
2023
+function prettyUiStringUnquoted(x, cliplen) {
2024
+ var ret;
2025
+
2026
+ if (typeof x !== 'string') {
2027
+ throw new Error('invalid input to prettyUiStringUnquoted: ' + typeof x);
2028
+ }
2029
+
2030
+ try {
2031
+ // Here utf8.decode() is better than decoding using NodeJS buffer
2032
+ // operations because we want strict UTF-8 interpretation.
2033
+
2034
+ // XXX: unprintable characters etc? In some UI cases we'd want to
2035
+ // e.g. escape newlines and in others not.
2036
+ ret = utf8.decode(x);
2037
+ } catch (e) {
2038
+ // For the unquoted version we don't need to escape single or double
2039
+ // quotes.
2040
+ ret = x.replace(/[\u0000-\u001f\u0080-\uffff]/g, function (match) {
2041
+ var cp = match.charCodeAt(0);
2042
+ return '\\x' + nybbles[(cp >> 4) & 0x0f] + nybbles[cp & 0x0f];
2043
+ });
2044
+ }
2045
+
2046
+ if (cliplen && ret.length > cliplen) {
2047
+ ret = ret.substring(0, cliplen) + '...';
2048
+ }
2049
+ return ret;
2050
+}
2051
+
2052
+/* Pretty print a dvalue for UI usage. Everything comes out as a ready-to-use
2053
+ * string.
2054
+ *
2055
+ * XXX: Currently the debug client formats all values for UI use. A better
2056
+ * solution would be to pass values in typed form and let the UI format them,
2057
+ * so that styling etc. could take typing into account.
2058
+ */
2059
+function prettyUiDebugValue(x, cliplen) {
2060
+ if (typeof x === 'object' && x !== null) {
2061
+ // Note: typeof null === 'object', so null special case explicitly
2062
+ if (x.type === 'eom') {
2063
+ return 'EOM';
2064
+ } else if (x.type === 'req') {
2065
+ return 'REQ';
2066
+ } else if (x.type === 'rep') {
2067
+ return 'REP';
2068
+ } else if (x.type === 'err') {
2069
+ return 'ERR';
2070
+ } else if (x.type === 'nfy') {
2071
+ return 'NFY';
2072
+ } else if (x.type === 'unused') {
2073
+ return 'unused';
2074
+ } else if (x.type === 'undefined') {
2075
+ return 'undefined';
2076
+ } else if (x.type === 'buffer') {
2077
+ return '|' + x.data + '|';
2078
+ } else if (x.type === 'object') {
2079
+ console.info1('OBJECT => ', JSON.stringify(x));
2080
+ // return '[object ' + (dukClassNames[x.class] || ('class ' + x.class)) + ' ' + x.pointer + ']';
2081
+ return '[object ' + ('class ' + x.class) + ' ' + x.pointer + ']';
2082
+ } else if (x.type === 'pointer') {
2083
+ return '<pointer ' + x.pointer + '>';
2084
+ } else if (x.type === 'lightfunc') {
2085
+ return '<lightfunc 0x' + x.flags.toString(16) + ' ' + x.pointer + '>';
2086
+ } else if (x.type === 'number') {
2087
+ // duk_tval number, any IEEE double
2088
+ var tmp = new Buffer(x.data, 'hex'); // decode into hex
2089
+ var val = tmp.readDoubleBE(0); // big endian ieee double
2090
+ return prettyUiNumber(val);
2091
+ }
2092
+ } else if (x === null) {
2093
+ return 'null';
2094
+ } else if (typeof x === 'boolean') {
2095
+ return x ? 'true' : 'false';
2096
+ } else if (typeof x === 'string') {
2097
+ return prettyUiString(x, cliplen);
2098
+ } else if (typeof x === 'number') {
2099
+ // Debug protocol integer
2100
+ return prettyUiNumber(x);
2101
+ }
2102
+
2103
+ // We shouldn't come here, but if we do, JSON is a reasonable default.
2104
+ return JSON.stringify(x);
2105
+}
2106
+
2107
+/* Pretty print a debugger message given as an array of parsed dvalues.
2108
+ * Result should be a pure ASCII one-liner.
2109
+ */
2110
+function prettyDebugMessage(msg) {
2111
+ return msg.map(prettyDebugValue).join(' ');
2112
+}
2113
+
2114
+/* Pretty print a debugger command. */
2115
+function prettyDebugCommand(cmd)
2116
+{
2117
+ return debugCommandNames[cmd] || String(cmd);
2118
+}
2119
+
2120
+/* Decode and normalize source file contents: UTF-8, tabs to 8,
2121
+ * CR LF to LF.
2122
+ */
2123
+function decodeAndNormalizeSource(data) {
2124
+ var tmp;
2125
+ var lines, line, repl;
2126
+ var i, n;
2127
+ var j, m;
2128
+
2129
+ try {
2130
+ tmp = data.toString('utf8');
2131
+ } catch (e) {
2132
+ console.error('Failed to UTF-8 decode source file, ignoring: ' + e);
2133
+ tmp = String(data);
2134
+ }
2135
+
2136
+ lines = tmp.split(/\r?\n/);
2137
+ for (i = 0, n = lines.length; i < n; i++) {
2138
+ line = lines[i];
2139
+ if (/\t/.test(line)) {
2140
+ repl = '';
2141
+ for (j = 0, m = line.length; j < m; j++) {
2142
+ if (line.charAt(j) === '\t') {
2143
+ repl += ' ';
2144
+ while ((repl.length % 8) != 0) {
2145
+ repl += ' ';
2146
+ }
2147
+ } else {
2148
+ repl += line.charAt(j);
2149
+ }
2150
+ }
2151
+ lines[i] = repl;
2152
+ }
2153
+ }
2154
+
2155
+ // XXX: normalize last newline (i.e. force a newline if contents don't
2156
+ // end with a newline)?
2157
+
2158
+ return lines.join('\n');
2159
+}
2160
+
2161
+
2162
+
2163
+module.exports = Debugger;
\ No newline at end of file
agents/modules_meshcore/monitor-info.js
+59
-41
@@ -55,56 +55,74 @@ function monitorinfo()
55
else if(process.platform == 'linux')
56
{
57
// First thing we need to do, is determine where the X11 libraries are
58
- var fs = require('fs');
59
- var files = fs.readdirSync('/usr/lib');
60
- var files2;
61
-
62
- /*
63
- for (j in files)
58
+ var askOS = false;
59
+ try
60
{
65
- if (files[j].split('libX11.so.').length > 1 && files[j].split('.').length == 3)
66
- {
67
- Object.defineProperty(this, 'Location_X11LIB', { value: '/usr/lib/' + files[j] });
68
- }
69
- if (files[j].split('libXtst.so.').length > 1 && files[j].split('.').length == 3)
70
- {
71
- Object.defineProperty(this, 'Location_X11TST', { value: '/usr/lib/' + files[j] });
72
- }
73
- if (files[j].split('libXext.so.').length > 1 && files[j].split('.').length == 3)
74
- {
75
- Object.defineProperty(this, 'Location_X11EXT', { value: '/usr/lib/' + files[j] });
76
- }
61
+ if (require('user-sessions').isRoot()) { askOS = true; }
62
}
78
- */
63
+ catch (e)
64
+ { }
65
80
- for (var i in files)
66
+ if (askOS)
67
{
82
- try {
83
- if (files[i].split('libX11.so.').length > 1 && files[i].split('.').length == 3) {
84
- Object.defineProperty(this, 'Location_X11LIB', { value: '/usr/lib/' + files[i] });
85
- }
86
- if (files[i].split('libXtst.so.').length > 1 && files[i].split('.').length == 3) {
87
- Object.defineProperty(this, 'Location_X11TST', { value: '/usr/lib/' + files[i] });
68
+ // Sufficient access rights to use ldconfig
69
+ var p = require('child_process').execFile('/bin/sh', ['sh']);
70
+ p.stdout._lines = '';
71
+ p.stdout.on('data', function (chunk) { this._lines += chunk.toString(); });
72
+ p.stdin.write('ldconfig -v\nexit\n');
73
+ p.waitExit();
74
+
75
+ var paths = p.stdout._lines.split('\n');
76
+ var searchPath = '';
77
+ for (var i in paths)
78
+ {
79
+ if (paths[i].endsWith(':'))
80
+ {
81
+ searchPath = paths[i].substring(0, paths[i].length - 1);
82
}
89
- if (files[i].split('libXext.so.').length > 1 && files[i].split('.').length == 3) {
90
- Object.defineProperty(this, 'Location_X11EXT', { value: '/usr/lib/' + files[i] });
83
+ else
84
+ {
85
+ if (paths[i].split('libX11.').length > 1) { Object.defineProperty(this, 'Location_X11LIB', { value: searchPath + '/' + paths[i].split('->')[1].trim() }); }
86
+ if (paths[i].split('libXtst.').length > 1) { Object.defineProperty(this, 'Location_X11TST', { value: searchPath + '/' + paths[i].split('->')[1].trim() }); }
87
+ if (paths[i].split('libXext.').length > 1) { Object.defineProperty(this, 'Location_X11EXT', { value: searchPath + '/' + paths[i].split('->')[1].trim() }); }
88
}
89
+ }
90
+ }
91
+ else
92
+ {
93
+ // Not enough access rights to use ldconfig, so manually search
94
+ var fs = require('fs');
95
+ var files = fs.readdirSync('/usr/lib');
96
+ var files2;
97
+
98
+ for (var i in files) {
99
+ try {
100
+ if (files[i].split('libX11.so.').length > 1 && files[i].split('.').length == 3) {
101
+ Object.defineProperty(this, 'Location_X11LIB', { value: '/usr/lib/' + files[i] });
102
+ }
103
+ if (files[i].split('libXtst.so.').length > 1 && files[i].split('.').length == 3) {
104
+ Object.defineProperty(this, 'Location_X11TST', { value: '/usr/lib/' + files[i] });
105
+ }
106
+ if (files[i].split('libXext.so.').length > 1 && files[i].split('.').length == 3) {
107
+ Object.defineProperty(this, 'Location_X11EXT', { value: '/usr/lib/' + files[i] });
108
+ }
109
93
- if (files[i].split('-linux-').length > 1) {
94
- files2 = fs.readdirSync('/usr/lib/' + files[i]);
95
- for (j in files2) {
96
- if (files2[j].split('libX11.so.').length > 1 && files2[j].split('.').length == 3) {
97
- Object.defineProperty(this, 'Location_X11LIB', { value: '/usr/lib/' + files[i] + '/' + files2[j] });
98
- }
99
- if (files2[j].split('libXtst.so.').length > 1 && files2[j].split('.').length == 3) {
100
- Object.defineProperty(this, 'Location_X11TST', { value: '/usr/lib/' + files[i] + '/' + files2[j] });
101
- }
102
- if (files2[j].split('libXext.so.').length > 1 && files2[j].split('.').length == 3) {
103
- Object.defineProperty(this, 'Location_X11EXT', { value: '/usr/lib/' + files[i] + '/' + files2[j] });
110
+ if (files[i].split('-linux-').length > 1) {
111
+ files2 = fs.readdirSync('/usr/lib/' + files[i]);
112
+ for (j in files2) {
113
+ if (files2[j].split('libX11.so.').length > 1 && files2[j].split('.').length == 3) {
114
+ Object.defineProperty(this, 'Location_X11LIB', { value: '/usr/lib/' + files[i] + '/' + files2[j] });
115
+ }
116
+ if (files2[j].split('libXtst.so.').length > 1 && files2[j].split('.').length == 3) {
117
+ Object.defineProperty(this, 'Location_X11TST', { value: '/usr/lib/' + files[i] + '/' + files2[j] });
118
+ }
119
+ if (files2[j].split('libXext.so.').length > 1 && files2[j].split('.').length == 3) {
120
+ Object.defineProperty(this, 'Location_X11EXT', { value: '/usr/lib/' + files[i] + '/' + files2[j] });
121
+ }
122
}
123
}
106
- }
107
- } catch (ex) { }
124
+ } catch (ex) { }
125
+ }
126
}
127
Object.defineProperty(this, 'kvm_x11_support', { value: (this.Location_X11LIB && this.Location_X11TST && this.Location_X11EXT)?true:false });
128
agents/modules_meshcore/user-sessions.js
+1
-1
@@ -215,7 +215,7 @@ function UserSessions()
215
self.parent._user32.ACDC_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_ACDC_POWER_SOURCE, 0);
216
self.parent._user32.BATT_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_BATTERY_PERCENTAGE_REMAINING, 0);
217
self.parent._user32.DISP_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_CONSOLE_DISPLAY_STATE, 0);
218
- console.log(self.parent._user32.ACDC_H.Val, self.parent._user32.BATT_H.Val, self.parent._user32.DISP_H.Val);
218
+ //console.log(self.parent._user32.ACDC_H.Val, self.parent._user32.BATT_H.Val, self.parent._user32.DISP_H.Val);
219
}, this);
220
});
221
this._messagepump.on('message', function (msg)
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.2.3-f",
3
+ "version": "0.2.3-g",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",