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",