roku-debug 0.8.6 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -2
- package/dist/CompileErrorProcessor.d.ts +1 -0
- package/dist/CompileErrorProcessor.js +5 -3
- package/dist/CompileErrorProcessor.js.map +1 -1
- package/dist/LaunchConfiguration.d.ts +5 -0
- package/dist/SceneGraphDebugCommandController.d.ts +1 -0
- package/dist/SceneGraphDebugCommandController.js +11 -5
- package/dist/SceneGraphDebugCommandController.js.map +1 -1
- package/dist/adapters/DebugProtocolAdapter.d.ts +3 -7
- package/dist/adapters/DebugProtocolAdapter.js +21 -16
- package/dist/adapters/DebugProtocolAdapter.js.map +1 -1
- package/dist/adapters/TelnetAdapter.d.ts +10 -51
- package/dist/adapters/TelnetAdapter.js +218 -364
- package/dist/adapters/TelnetAdapter.js.map +1 -1
- package/dist/adapters/TelnetRequestPipeline.d.ts +55 -0
- package/dist/adapters/TelnetRequestPipeline.js +250 -0
- package/dist/adapters/TelnetRequestPipeline.js.map +1 -0
- package/dist/debugProtocol/Debugger.d.ts +1 -0
- package/dist/debugProtocol/Debugger.js +20 -19
- package/dist/debugProtocol/Debugger.js.map +1 -1
- package/dist/debugSession/BrightScriptDebugSession.d.ts +6 -0
- package/dist/debugSession/BrightScriptDebugSession.js +83 -44
- package/dist/debugSession/BrightScriptDebugSession.js.map +1 -1
- package/dist/interfaces.d.ts +11 -0
- package/dist/interfaces.js +16 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/logging.d.ts +11 -0
- package/dist/logging.js +14 -0
- package/dist/logging.js.map +1 -0
- package/dist/managers/FileManager.d.ts +1 -0
- package/dist/managers/FileManager.js +3 -1
- package/dist/managers/FileManager.js.map +1 -1
- package/dist/managers/ProjectManager.d.ts +2 -0
- package/dist/managers/ProjectManager.js +5 -2
- package/dist/managers/ProjectManager.js.map +1 -1
- package/dist/managers/SourceMapManager.d.ts +1 -0
- package/dist/managers/SourceMapManager.js +5 -5
- package/dist/managers/SourceMapManager.js.map +1 -1
- package/dist/util.d.ts +19 -6
- package/dist/util.js +41 -30
- package/dist/util.js.map +1 -1
- package/package.json +4 -1
- package/roku-debug-0.9.2.tgz +0 -0
- package/roku-debug-0.8.6.tgz +0 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const EventEmitter = require("events");
|
|
3
|
+
exports.PrimativeType = exports.KeyType = exports.EventName = exports.TelnetAdapter = void 0;
|
|
5
4
|
const natural_orderby_1 = require("natural-orderby");
|
|
6
|
-
const
|
|
5
|
+
const EventEmitter = require("eventemitter3");
|
|
6
|
+
const net_1 = require("net");
|
|
7
7
|
const rokuDeploy = require("roku-deploy");
|
|
8
8
|
const PrintedObjectParser_1 = require("../PrintedObjectParser");
|
|
9
9
|
const CompileErrorProcessor_1 = require("../CompileErrorProcessor");
|
|
10
10
|
const RendezvousTracker_1 = require("../RendezvousTracker");
|
|
11
11
|
const ChanperfTracker_1 = require("../ChanperfTracker");
|
|
12
12
|
const util_1 = require("../util");
|
|
13
|
+
const logging_1 = require("../logging");
|
|
14
|
+
const interfaces_1 = require("../interfaces");
|
|
15
|
+
const TelnetRequestPipeline_1 = require("./TelnetRequestPipeline");
|
|
13
16
|
/**
|
|
14
17
|
* A class that connects to a Roku device over telnet debugger port and provides a standardized way of interacting with it.
|
|
15
18
|
*/
|
|
@@ -17,6 +20,7 @@ class TelnetAdapter {
|
|
|
17
20
|
constructor(host, enableDebuggerAutoRecovery = false) {
|
|
18
21
|
this.host = host;
|
|
19
22
|
this.enableDebuggerAutoRecovery = enableDebuggerAutoRecovery;
|
|
23
|
+
this.logger = logging_1.logger.createLogger(`[${TelnetAdapter.name}]`);
|
|
20
24
|
this.isNextBreakpointSkipped = false;
|
|
21
25
|
this.cache = {};
|
|
22
26
|
this.supportsMultipleRuns = true;
|
|
@@ -71,6 +75,7 @@ class TelnetAdapter {
|
|
|
71
75
|
}, 0);
|
|
72
76
|
}
|
|
73
77
|
async activate() {
|
|
78
|
+
this.logger.log('Activate TelnetAdapter');
|
|
74
79
|
this.isActivated = true;
|
|
75
80
|
await this.handleStartupIfReady();
|
|
76
81
|
}
|
|
@@ -80,11 +85,13 @@ class TelnetAdapter {
|
|
|
80
85
|
async handleStartupIfReady() {
|
|
81
86
|
var _a;
|
|
82
87
|
if (this.isActivated && this.isAppRunning) {
|
|
88
|
+
this.logger.log('Handling startup');
|
|
83
89
|
this.emit('start');
|
|
84
90
|
//if we are already sitting at a debugger prompt, we need to emit the first suspend event.
|
|
85
91
|
//If not, then there are probably still messages being received, so let the normal handler
|
|
86
92
|
//emit the suspend event when it's ready
|
|
87
93
|
if (this.isAtDebuggerPrompt === true) {
|
|
94
|
+
this.logger.log(`At debug prompt, so trigger the 'suspend' event`);
|
|
88
95
|
let threads = await this.getThreads();
|
|
89
96
|
this.emit('suspend', (_a = threads[0]) === null || _a === void 0 ? void 0 : _a.threadId);
|
|
90
97
|
}
|
|
@@ -97,19 +104,22 @@ class TelnetAdapter {
|
|
|
97
104
|
* @param maxWaitMilliseconds
|
|
98
105
|
*/
|
|
99
106
|
settle(client, name, maxWaitMilliseconds = 400) {
|
|
107
|
+
const startTime = new Date();
|
|
108
|
+
this.logger.log('Waiting for telnet client to settle');
|
|
100
109
|
return new Promise((resolve) => {
|
|
101
110
|
let callCount = -1;
|
|
102
|
-
|
|
111
|
+
const handler = () => {
|
|
103
112
|
callCount++;
|
|
104
113
|
let myCallCount = callCount;
|
|
105
114
|
setTimeout(() => {
|
|
106
115
|
//if no other calls have been made since the timeout started, then the listener has settled
|
|
107
116
|
if (myCallCount === callCount) {
|
|
108
117
|
client.removeListener(name, handler);
|
|
118
|
+
this.logger.log(`Telnet client has settled after ${new Date().getTime() - startTime.getTime()} milliseconds`);
|
|
109
119
|
resolve(callCount);
|
|
110
120
|
}
|
|
111
121
|
}, maxWaitMilliseconds);
|
|
112
|
-
}
|
|
122
|
+
};
|
|
113
123
|
client.addListener(name, handler);
|
|
114
124
|
//call the handler immediately so we have a timeout
|
|
115
125
|
handler();
|
|
@@ -135,36 +145,38 @@ class TelnetAdapter {
|
|
|
135
145
|
}
|
|
136
146
|
}
|
|
137
147
|
}
|
|
138
|
-
return text;
|
|
139
148
|
}
|
|
140
149
|
/**
|
|
141
150
|
* Connect to the telnet session. This should be called before the channel is launched.
|
|
142
151
|
*/
|
|
143
152
|
async connect() {
|
|
153
|
+
this.logger.log('Establishing telnet connection');
|
|
144
154
|
let deferred = (0, util_1.defer)();
|
|
145
155
|
this.isInMicroDebugger = false;
|
|
146
156
|
this.isNextBreakpointSkipped = false;
|
|
147
157
|
try {
|
|
158
|
+
this.logger.log('Pressing home button');
|
|
148
159
|
//force roku to return to home screen. This gives the roku adapter some security in knowing new messages won't be appearing during initialization
|
|
149
160
|
await rokuDeploy.pressHomeButton(this.host);
|
|
150
|
-
let client = new
|
|
161
|
+
let client = new net_1.Socket();
|
|
151
162
|
//listen for the close event
|
|
152
163
|
client.addListener('close', (err, data) => {
|
|
153
164
|
this.emit('close');
|
|
154
165
|
});
|
|
155
166
|
//if the connection fails, reject the connect promise
|
|
156
167
|
client.addListener('error', (err) => {
|
|
157
|
-
deferred.reject(new Error(`Error with connection to: ${this.host} \n\n ${err.message}`));
|
|
168
|
+
deferred.reject(new Error(`Error with connection to: ${this.host} \n\n ${err.message} `));
|
|
158
169
|
});
|
|
159
170
|
const settlePromise = this.settle(client, 'data');
|
|
160
171
|
client.connect(8085, this.host, () => {
|
|
161
|
-
|
|
172
|
+
this.logger.log(`Telnet connection established to ${this.host}`);
|
|
162
173
|
this.connected = true;
|
|
163
174
|
this.emit('connected', this.connected);
|
|
164
175
|
});
|
|
165
176
|
await settlePromise;
|
|
166
177
|
//hook up the pipeline to the socket
|
|
167
|
-
this.requestPipeline = new
|
|
178
|
+
this.requestPipeline = new TelnetRequestPipeline_1.TelnetRequestPipeline(client);
|
|
179
|
+
this.requestPipeline.connect();
|
|
168
180
|
//forward all raw console output
|
|
169
181
|
this.requestPipeline.on('console-output', (output) => {
|
|
170
182
|
this.processBreakpoints(output);
|
|
@@ -190,13 +202,14 @@ class TelnetAdapter {
|
|
|
190
202
|
}
|
|
191
203
|
// short circuit after the output has been sent as console output
|
|
192
204
|
if (hasRuntimeError) {
|
|
193
|
-
|
|
205
|
+
this.logger.log('Detected runtime error in output', { responseText });
|
|
194
206
|
this.isAtDebuggerPrompt = true;
|
|
195
207
|
return;
|
|
196
208
|
}
|
|
197
|
-
this.processUnhandledLines(responseText);
|
|
209
|
+
this.compileErrorProcessor.processUnhandledLines(responseText);
|
|
198
210
|
let match;
|
|
199
211
|
if (this.isAtCannotContinue(responseText)) {
|
|
212
|
+
this.logger.log('is at cannot continue');
|
|
200
213
|
this.isAtDebuggerPrompt = true;
|
|
201
214
|
return;
|
|
202
215
|
}
|
|
@@ -205,6 +218,7 @@ class TelnetAdapter {
|
|
|
205
218
|
// eslint-disable-next-line no-cond-assign
|
|
206
219
|
if (match = /\[scrpt.ctx.run.enter\]/i.exec(responseText.trim())) {
|
|
207
220
|
this.isAppRunning = true;
|
|
221
|
+
this.logger.log('Running beacon detected', { responseText });
|
|
208
222
|
void this.handleStartupIfReady();
|
|
209
223
|
}
|
|
210
224
|
//watch for the end of the program
|
|
@@ -213,25 +227,29 @@ class TelnetAdapter {
|
|
|
213
227
|
this.beginAppExit();
|
|
214
228
|
}
|
|
215
229
|
//watch for debugger prompt output
|
|
216
|
-
if (util_1.util.
|
|
230
|
+
if (util_1.util.endsWithDebuggerPrompt(responseText)) {
|
|
231
|
+
this.logger.log('Debugger prompt detected in', { responseText });
|
|
217
232
|
//if we are activated AND this is the first time seeing the debugger prompt since a continue/step action
|
|
218
233
|
if (this.isNextBreakpointSkipped) {
|
|
219
|
-
|
|
234
|
+
this.logger.log('This debugger is flagged to be skipped');
|
|
220
235
|
this.isInMicroDebugger = false;
|
|
221
236
|
this.isNextBreakpointSkipped = false;
|
|
222
|
-
void this.requestPipeline.executeCommand('c',
|
|
237
|
+
void this.requestPipeline.executeCommand('c', { waitForPrompt: false, insertAtFront: true });
|
|
223
238
|
}
|
|
224
239
|
else {
|
|
225
240
|
if (this.isActivated && this.isAtDebuggerPrompt === false) {
|
|
226
241
|
this.isAtDebuggerPrompt = true;
|
|
242
|
+
this.logger.log('Sending the "suspend" event to the client');
|
|
227
243
|
this.emit('suspend');
|
|
228
244
|
}
|
|
229
245
|
else {
|
|
246
|
+
this.logger.log('Skipping "suspend" event because we are already suspended');
|
|
230
247
|
this.isAtDebuggerPrompt = true;
|
|
231
248
|
}
|
|
232
249
|
}
|
|
233
250
|
}
|
|
234
251
|
else {
|
|
252
|
+
this.logger.debug('responseText does not end with debugger prompt. isAtDebuggerPrompt = false', { responseText });
|
|
235
253
|
this.isAtDebuggerPrompt = false;
|
|
236
254
|
}
|
|
237
255
|
}
|
|
@@ -245,7 +263,9 @@ class TelnetAdapter {
|
|
|
245
263
|
return deferred.promise;
|
|
246
264
|
}
|
|
247
265
|
beginAppExit() {
|
|
266
|
+
this.logger.log('Beginning app exit');
|
|
248
267
|
this.compileErrorProcessor.compileErrorTimer = setTimeout(() => {
|
|
268
|
+
this.logger.info('emitting app-exit');
|
|
249
269
|
this.isAppRunning = false;
|
|
250
270
|
this.emit('app-exit');
|
|
251
271
|
}, 200);
|
|
@@ -286,43 +306,46 @@ class TelnetAdapter {
|
|
|
286
306
|
return false;
|
|
287
307
|
}
|
|
288
308
|
}
|
|
289
|
-
processUnhandledLines(responseText) {
|
|
290
|
-
this.compileErrorProcessor.processUnhandledLines(responseText);
|
|
291
|
-
}
|
|
292
309
|
/**
|
|
293
310
|
* Send command to step over
|
|
294
311
|
*/
|
|
295
312
|
stepOver() {
|
|
313
|
+
this.logger.log('stepOver');
|
|
296
314
|
this.clearCache();
|
|
297
|
-
return this.requestPipeline.executeCommand('over', false);
|
|
315
|
+
return this.requestPipeline.executeCommand('over', { waitForPrompt: false, insertAtFront: true });
|
|
298
316
|
}
|
|
299
317
|
stepInto() {
|
|
318
|
+
this.logger.log('stepInto');
|
|
300
319
|
this.clearCache();
|
|
301
|
-
return this.requestPipeline.executeCommand('step', false);
|
|
320
|
+
return this.requestPipeline.executeCommand('step', { waitForPrompt: false, insertAtFront: true });
|
|
302
321
|
}
|
|
303
322
|
stepOut() {
|
|
323
|
+
this.logger.log('stepOut');
|
|
304
324
|
this.clearCache();
|
|
305
|
-
return this.requestPipeline.executeCommand('out', false);
|
|
325
|
+
return this.requestPipeline.executeCommand('out', { waitForPrompt: false, insertAtFront: true });
|
|
306
326
|
}
|
|
307
327
|
/**
|
|
308
328
|
* Tell the brightscript program to continue (i.e. resume program)
|
|
309
329
|
*/
|
|
310
330
|
continue() {
|
|
331
|
+
this.logger.log('continue');
|
|
311
332
|
this.clearCache();
|
|
312
|
-
return this.requestPipeline.executeCommand('c', false);
|
|
333
|
+
return this.requestPipeline.executeCommand('c', { waitForPrompt: false, insertAtFront: true });
|
|
313
334
|
}
|
|
314
335
|
/**
|
|
315
336
|
* Tell the brightscript program to pause (fall into debug mode)
|
|
316
337
|
*/
|
|
317
338
|
pause() {
|
|
339
|
+
this.logger.log('pause');
|
|
318
340
|
this.clearCache();
|
|
319
|
-
//send the kill signal, which breaks into debugger mode
|
|
320
|
-
|
|
341
|
+
//send the kill signal, which breaks into debugger mode. This gets written immediately, regardless of debugger prompt status.
|
|
342
|
+
this.requestPipeline.write('\x03;');
|
|
321
343
|
}
|
|
322
344
|
/**
|
|
323
345
|
* Clears the state, which means that everything will be retrieved fresh next time it is requested
|
|
324
346
|
*/
|
|
325
347
|
clearCache() {
|
|
348
|
+
this.logger.info('Clearing TelnetAdapter cache');
|
|
326
349
|
this.cache = {};
|
|
327
350
|
this.isAtDebuggerPrompt = false;
|
|
328
351
|
}
|
|
@@ -331,24 +354,26 @@ class TelnetAdapter {
|
|
|
331
354
|
* @param command the command to execute. If the command does not start with `print` the command will be prefixed with `print ` because
|
|
332
355
|
*/
|
|
333
356
|
async evaluate(command) {
|
|
357
|
+
this.logger.log('evaluate ', { command });
|
|
334
358
|
if (!this.isAtDebuggerPrompt) {
|
|
335
359
|
throw new Error('Cannot run evaluate: debugger is not paused');
|
|
336
360
|
}
|
|
337
361
|
//clear the cache (we don't know what command the user entered)
|
|
338
362
|
this.clearCache();
|
|
339
363
|
//don't wait for the output...we don't know what command the user entered
|
|
340
|
-
let responseText = await this.requestPipeline.executeCommand(command, true);
|
|
364
|
+
let responseText = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
|
|
341
365
|
//we know that if we got a response, we are back at a debugger prompt
|
|
342
366
|
this.isAtDebuggerPrompt = true;
|
|
343
367
|
return responseText;
|
|
344
368
|
}
|
|
345
369
|
async getStackTrace() {
|
|
370
|
+
this.logger.log(TelnetAdapter.prototype.getStackTrace.name);
|
|
346
371
|
if (!this.isAtDebuggerPrompt) {
|
|
347
372
|
throw new Error('Cannot get stack trace: debugger is not paused');
|
|
348
373
|
}
|
|
349
374
|
return this.resolve('stackTrace', async () => {
|
|
350
375
|
//perform a request to load the stack trace
|
|
351
|
-
let responseText = await this.requestPipeline.executeCommand('bt', true);
|
|
376
|
+
let responseText = (await this.requestPipeline.executeCommand('bt', { waitForPrompt: true })).trim();
|
|
352
377
|
let regexp = /#(\d+)\s+(?:function|sub)\s+([\$\w\d]+).*\s+file\/line:\s+(.*)\((\d+)\)/ig;
|
|
353
378
|
let matches;
|
|
354
379
|
let frames = [];
|
|
@@ -375,16 +400,6 @@ class TelnetAdapter {
|
|
|
375
400
|
return frames;
|
|
376
401
|
});
|
|
377
402
|
}
|
|
378
|
-
/**
|
|
379
|
-
* Runs a regex to get the content between telnet commands
|
|
380
|
-
* @param value
|
|
381
|
-
*/
|
|
382
|
-
getExpressionDetails(value) {
|
|
383
|
-
const match = /(.*?)\r?\nBrightscript Debugger>\s*/is.exec(value);
|
|
384
|
-
if (match) {
|
|
385
|
-
return match[1];
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
403
|
/**
|
|
389
404
|
* Runs a regex to check if the target is an object and get the type if it is
|
|
390
405
|
* @param value
|
|
@@ -410,14 +425,15 @@ class TelnetAdapter {
|
|
|
410
425
|
* @param scope
|
|
411
426
|
*/
|
|
412
427
|
async getScopeVariables(scope) {
|
|
428
|
+
this.logger.log('getScopeVariables', { scope });
|
|
413
429
|
if (!this.isAtDebuggerPrompt) {
|
|
414
430
|
throw new Error('Cannot resolve variable: debugger is not paused');
|
|
415
431
|
}
|
|
416
432
|
return this.resolve(`Scope Variables`, async () => {
|
|
417
433
|
let data;
|
|
418
434
|
let vars = [];
|
|
419
|
-
data = await this.requestPipeline.executeCommand(`var`, true);
|
|
420
|
-
let splitData = data.split('\n');
|
|
435
|
+
data = await this.requestPipeline.executeCommand(`var`, { waitForPrompt: true });
|
|
436
|
+
let splitData = data.trim().split('\n');
|
|
421
437
|
for (const line of splitData) {
|
|
422
438
|
let match;
|
|
423
439
|
if (!line.includes('Brightscript Debugger') && (match = this.getFirstWord(line))) {
|
|
@@ -436,122 +452,141 @@ class TelnetAdapter {
|
|
|
436
452
|
* @param expression
|
|
437
453
|
*/
|
|
438
454
|
async getVariable(expression) {
|
|
455
|
+
const logger = this.logger.createLogger('[getVariable]');
|
|
456
|
+
logger.info('begin', { expression });
|
|
439
457
|
if (!this.isAtDebuggerPrompt) {
|
|
440
458
|
throw new Error('Cannot resolve variable: debugger is not paused');
|
|
441
459
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
`
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
`
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
`
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
460
|
+
let expressionType = await this.getVariableType(expression);
|
|
461
|
+
let lowerExpressionType = expressionType ? expressionType.toLowerCase() : null;
|
|
462
|
+
let data;
|
|
463
|
+
//if the expression type is a string, we need to wrap the expression in quotes BEFORE we run the print so we can accurately capture the full string value
|
|
464
|
+
if (lowerExpressionType === 'string' || lowerExpressionType === 'rostring') {
|
|
465
|
+
data = await this.requestPipeline.executeCommand(`print "--string-wrap--" + ${expression} + "--string-wrap--"`, { waitForPrompt: true });
|
|
466
|
+
//write a for loop to print every value from the array. This gets around the `...` after the 100th item issue in the roku print call
|
|
467
|
+
}
|
|
468
|
+
else if (['roarray', 'rolist', 'roxmllist', 'robytearray'].includes(lowerExpressionType)) {
|
|
469
|
+
const command = [
|
|
470
|
+
`for each vscodeLoopItem in ${expression} : print ` +
|
|
471
|
+
` "vscode_type_start:" + type(vscodeLoopItem) + ":vscode_type_stop "`,
|
|
472
|
+
` "vscode_is_string:"; (invalid <> GetInterface(vscodeLoopItem, "ifString"))`,
|
|
473
|
+
` vscodeLoopItem :` +
|
|
474
|
+
` end for`
|
|
475
|
+
].join(';');
|
|
476
|
+
data = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
|
|
477
|
+
}
|
|
478
|
+
else if (['roassociativearray', 'rosgnode'].includes(lowerExpressionType)) {
|
|
479
|
+
const command = [
|
|
480
|
+
`for each vscodeLoopKey in ${expression}.keys(): print` +
|
|
481
|
+
` "vscode_key_start:" + vscodeLoopKey + ":vscode_key_stop "`,
|
|
482
|
+
` "vscode_type_start:" + type(${expression}[vscodeLoopKey]) + ":vscode_type_stop "`,
|
|
483
|
+
` "vscode_is_string:"; (invalid <> GetInterface(${expression}[vscodeLoopKey], "ifString"))`,
|
|
484
|
+
` ${expression}[vscodeLoopKey] :` +
|
|
485
|
+
' end for'
|
|
486
|
+
].join(';');
|
|
487
|
+
data = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
data = await this.requestPipeline.executeCommand(`print ${expression}`, { waitForPrompt: true });
|
|
491
|
+
}
|
|
492
|
+
logger.info('expression details', { data });
|
|
493
|
+
//remove excess whitespace
|
|
494
|
+
data = data.trim();
|
|
495
|
+
if (lowerExpressionType === 'string' || lowerExpressionType === 'rostring') {
|
|
496
|
+
data = data.trim().replace(/--string-wrap--/g, '');
|
|
497
|
+
//add an escape character in front of any existing quotes
|
|
498
|
+
data = data.replace(/"/g, '\\"');
|
|
499
|
+
//wrap the string value with literal quote marks
|
|
500
|
+
data = '"' + data + '"';
|
|
501
|
+
}
|
|
502
|
+
let highLevelType = this.getHighLevelType(expressionType);
|
|
503
|
+
let children;
|
|
504
|
+
if (highLevelType === interfaces_1.HighLevelType.array || ['roassociativearray', 'rosgnode', 'roxmllist', 'robytearray'].includes(lowerExpressionType)) {
|
|
505
|
+
//the print statment will always have 1 trailing newline, so remove that.
|
|
506
|
+
data = util_1.util.removeTrailingNewline(data);
|
|
507
|
+
//the array/associative array print is a loop of every value, so handle that
|
|
508
|
+
children = this.getForLoopPrintedChildren(expression, data);
|
|
509
|
+
children.push({
|
|
510
|
+
name: '[[count]]',
|
|
511
|
+
value: children.length.toString(),
|
|
512
|
+
type: 'integer',
|
|
513
|
+
highLevelType: interfaces_1.HighLevelType.primative,
|
|
514
|
+
evaluateName: children.length.toString(),
|
|
515
|
+
presentationHint: 'virtual',
|
|
516
|
+
keyType: KeyType.legacy,
|
|
517
|
+
children: undefined
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
else if (highLevelType === interfaces_1.HighLevelType.object) {
|
|
521
|
+
children = this.getObjectChildren(expression, data.trim());
|
|
522
|
+
}
|
|
523
|
+
else if (highLevelType === interfaces_1.HighLevelType.unknown) {
|
|
524
|
+
logger.warn('there was an issue evaluating this variable', { expression });
|
|
525
|
+
data = '<UNKNOWN>';
|
|
526
|
+
}
|
|
527
|
+
if (['rostring', 'roint', 'rointeger', 'rolonginteger', 'rofloat', 'rodouble', 'roboolean', 'rointrinsicdouble'].includes(lowerExpressionType)) {
|
|
528
|
+
return {
|
|
529
|
+
name: expression,
|
|
530
|
+
value: util_1.util.removeTrailingNewline(data),
|
|
531
|
+
type: expressionType,
|
|
532
|
+
highLevelType: interfaces_1.HighLevelType.primative,
|
|
533
|
+
evaluateName: expression,
|
|
534
|
+
children: []
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
//add a computed `[[children]]` property to allow expansion of node children
|
|
538
|
+
if (lowerExpressionType === 'rosgnode') {
|
|
539
|
+
let nodeChildren = {
|
|
540
|
+
name: '[[children]]',
|
|
541
|
+
type: 'roArray',
|
|
542
|
+
highLevelType: 'array',
|
|
543
|
+
presentationHint: 'virtual',
|
|
544
|
+
evaluateName: `${expression}.getChildren(-1, 0)`,
|
|
545
|
+
children: []
|
|
546
|
+
};
|
|
547
|
+
children.push(nodeChildren);
|
|
548
|
+
}
|
|
549
|
+
//xml elements won't display on their own, so we need to create some sub elements
|
|
550
|
+
if (lowerExpressionType === 'roxmlelement') {
|
|
551
|
+
children.push({
|
|
552
|
+
//look up the name of the xml element
|
|
553
|
+
...await this.getVariable(`${expression}.GetName()`),
|
|
554
|
+
name: '[[name]]',
|
|
555
|
+
presentationHint: 'virtual'
|
|
556
|
+
});
|
|
557
|
+
children.push({
|
|
558
|
+
name: '[[attributes]]',
|
|
559
|
+
type: 'roAssociativeArray',
|
|
560
|
+
highLevelType: interfaces_1.HighLevelType.array,
|
|
561
|
+
evaluateName: `${expression}.GetAttributes()`,
|
|
562
|
+
presentationHint: 'virtual',
|
|
563
|
+
children: []
|
|
564
|
+
});
|
|
565
|
+
//add a computed `[[children]]` property to allow expansion of child elements
|
|
566
|
+
children.push({
|
|
567
|
+
name: '[[children]]',
|
|
568
|
+
type: 'roArray',
|
|
569
|
+
highLevelType: interfaces_1.HighLevelType.array,
|
|
570
|
+
evaluateName: `${expression}.GetChildNodes()`,
|
|
571
|
+
presentationHint: 'virtual',
|
|
572
|
+
children: []
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
//if this item is an array or a list, add the item count to the end of the type
|
|
576
|
+
if (highLevelType === interfaces_1.HighLevelType.array) {
|
|
577
|
+
//TODO re-enable once we find how to refresh watch/variables panel, since lazy loaded arrays can't show a length
|
|
578
|
+
//expressionType += `(${children.length})`;
|
|
579
|
+
}
|
|
580
|
+
let container = {
|
|
581
|
+
name: expression,
|
|
582
|
+
evaluateName: expression,
|
|
583
|
+
type: expressionType,
|
|
584
|
+
value: data.trim(),
|
|
585
|
+
highLevelType: highLevelType,
|
|
586
|
+
children: children
|
|
587
|
+
};
|
|
588
|
+
logger.info('end', { container });
|
|
589
|
+
return container;
|
|
555
590
|
}
|
|
556
591
|
/**
|
|
557
592
|
* In order to get around the `...` issue in printed arrays, `getVariable` now prints every value from an array or associative array in a for loop.
|
|
@@ -626,22 +661,22 @@ class TelnetAdapter {
|
|
|
626
661
|
let collectionEnd;
|
|
627
662
|
if (line.includes('<Component: roList>')) {
|
|
628
663
|
collectionEnd = ')';
|
|
629
|
-
child.highLevelType = HighLevelType.array;
|
|
664
|
+
child.highLevelType = interfaces_1.HighLevelType.array;
|
|
630
665
|
child.type = objectType;
|
|
631
666
|
}
|
|
632
667
|
else if (line.includes('<Component: roArray>')) {
|
|
633
668
|
collectionEnd = ']';
|
|
634
|
-
child.highLevelType = HighLevelType.array;
|
|
669
|
+
child.highLevelType = interfaces_1.HighLevelType.array;
|
|
635
670
|
child.type = this.getObjectType(line);
|
|
636
671
|
}
|
|
637
672
|
else if (line.includes('<Component: roByteArray>')) {
|
|
638
673
|
collectionEnd = ']';
|
|
639
|
-
child.highLevelType = HighLevelType.array;
|
|
674
|
+
child.highLevelType = interfaces_1.HighLevelType.array;
|
|
640
675
|
child.type = this.getObjectType(line);
|
|
641
676
|
}
|
|
642
677
|
else if (line.includes('<Component: roAssociativeArray>') || isRoSGNode) {
|
|
643
678
|
collectionEnd = '}';
|
|
644
|
-
child.highLevelType = HighLevelType.object;
|
|
679
|
+
child.highLevelType = interfaces_1.HighLevelType.object;
|
|
645
680
|
child.type = this.getObjectType(line);
|
|
646
681
|
}
|
|
647
682
|
let collectionLineList = [line];
|
|
@@ -655,34 +690,24 @@ class TelnetAdapter {
|
|
|
655
690
|
}
|
|
656
691
|
//we have reached the end of the collection. scrap children because they need evaluated in a separate call to compute their types
|
|
657
692
|
child.children = [];
|
|
658
|
-
if (isRoSGNode) {
|
|
659
|
-
let nodeChildrenProperty = {
|
|
660
|
-
name: '[[children]]',
|
|
661
|
-
type: 'roArray',
|
|
662
|
-
highLevelType: 'array',
|
|
663
|
-
evaluateName: `${child.evaluateName}.getChildren(-1,0)`,
|
|
664
|
-
children: []
|
|
665
|
-
};
|
|
666
|
-
child.children.push(nodeChildrenProperty);
|
|
667
|
-
}
|
|
668
693
|
//this if block must pre-seek the `line.indexOf('<Component') > -1` line because roInvalid is a component too.
|
|
669
694
|
}
|
|
670
695
|
else if (objectType === 'roInvalid') {
|
|
671
|
-
child.highLevelType = HighLevelType.uninitialized;
|
|
696
|
+
child.highLevelType = interfaces_1.HighLevelType.uninitialized;
|
|
672
697
|
child.type = 'roInvalid';
|
|
673
698
|
child.value = 'roInvalid';
|
|
674
699
|
child.children = undefined;
|
|
675
700
|
}
|
|
676
701
|
else if (line.includes('<Component:')) {
|
|
677
702
|
//handle things like nodes
|
|
678
|
-
child.highLevelType = HighLevelType.object;
|
|
703
|
+
child.highLevelType = interfaces_1.HighLevelType.object;
|
|
679
704
|
child.type = objectType;
|
|
680
705
|
}
|
|
681
706
|
else {
|
|
682
707
|
//is some primative type
|
|
683
708
|
child.type = type;
|
|
684
709
|
child.value = line.trim();
|
|
685
|
-
child.highLevelType = HighLevelType.primative;
|
|
710
|
+
child.highLevelType = interfaces_1.HighLevelType.primative;
|
|
686
711
|
child.children = undefined;
|
|
687
712
|
}
|
|
688
713
|
children.push(child);
|
|
@@ -728,7 +753,7 @@ class TelnetAdapter {
|
|
|
728
753
|
else {
|
|
729
754
|
child.type = this.getPrimativeTypeFromValue(line);
|
|
730
755
|
child.value = line;
|
|
731
|
-
child.highLevelType = HighLevelType.primative;
|
|
756
|
+
child.highLevelType = interfaces_1.HighLevelType.primative;
|
|
732
757
|
}
|
|
733
758
|
children.push(child);
|
|
734
759
|
arrayIndex++;
|
|
@@ -773,7 +798,7 @@ class TelnetAdapter {
|
|
|
773
798
|
child = {
|
|
774
799
|
name: line,
|
|
775
800
|
type: '<ERROR>',
|
|
776
|
-
highLevelType: HighLevelType.uninitialized,
|
|
801
|
+
highLevelType: interfaces_1.HighLevelType.uninitialized,
|
|
777
802
|
evaluateName: undefined,
|
|
778
803
|
variablePath: [],
|
|
779
804
|
elementCount: -1,
|
|
@@ -798,7 +823,7 @@ class TelnetAdapter {
|
|
|
798
823
|
else {
|
|
799
824
|
child.type = this.getPrimativeTypeFromValue(trimmedLine);
|
|
800
825
|
child.value = lineParseResult.value;
|
|
801
|
-
child.highLevelType = HighLevelType.primative;
|
|
826
|
+
child.highLevelType = interfaces_1.HighLevelType.primative;
|
|
802
827
|
}
|
|
803
828
|
}
|
|
804
829
|
children.push(child);
|
|
@@ -806,7 +831,7 @@ class TelnetAdapter {
|
|
|
806
831
|
return children;
|
|
807
832
|
}
|
|
808
833
|
catch (e) {
|
|
809
|
-
throw new Error(`Unable to parse BrightScript object: ${e.message}. Data: ${data}`);
|
|
834
|
+
throw new Error(`Unable to parse BrightScript object: ${JSON.stringify(e.message)}. Data: ${data}`);
|
|
810
835
|
}
|
|
811
836
|
}
|
|
812
837
|
/**
|
|
@@ -815,24 +840,24 @@ class TelnetAdapter {
|
|
|
815
840
|
*/
|
|
816
841
|
getHighLevelType(expressionType) {
|
|
817
842
|
if (!expressionType) {
|
|
818
|
-
|
|
843
|
+
return interfaces_1.HighLevelType.unknown;
|
|
819
844
|
}
|
|
820
845
|
expressionType = expressionType.toLowerCase();
|
|
821
846
|
let primativeTypes = ['boolean', 'integer', 'longinteger', 'float', 'double', 'string', 'rostring', 'invalid'];
|
|
822
847
|
if (primativeTypes.includes(expressionType)) {
|
|
823
|
-
return HighLevelType.primative;
|
|
848
|
+
return interfaces_1.HighLevelType.primative;
|
|
824
849
|
}
|
|
825
850
|
else if (expressionType === 'roarray' || expressionType === 'rolist') {
|
|
826
|
-
return HighLevelType.array;
|
|
851
|
+
return interfaces_1.HighLevelType.array;
|
|
827
852
|
}
|
|
828
853
|
else if (expressionType === 'function') {
|
|
829
|
-
return HighLevelType.function;
|
|
854
|
+
return interfaces_1.HighLevelType.function;
|
|
830
855
|
}
|
|
831
856
|
else if (expressionType === '<uninitialized>') {
|
|
832
|
-
return HighLevelType.uninitialized;
|
|
857
|
+
return interfaces_1.HighLevelType.uninitialized;
|
|
833
858
|
}
|
|
834
859
|
else {
|
|
835
|
-
return HighLevelType.object;
|
|
860
|
+
return interfaces_1.HighLevelType.object;
|
|
836
861
|
}
|
|
837
862
|
}
|
|
838
863
|
/**
|
|
@@ -845,17 +870,10 @@ class TelnetAdapter {
|
|
|
845
870
|
}
|
|
846
871
|
expression = `Type(${expression})`;
|
|
847
872
|
return this.resolve(`${expression}`, async () => {
|
|
848
|
-
|
|
849
|
-
let
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
//remove whitespace
|
|
853
|
-
typeValue = typeValue.trim();
|
|
854
|
-
return typeValue;
|
|
855
|
-
}
|
|
856
|
-
else {
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
873
|
+
var _a;
|
|
874
|
+
let data = await this.requestPipeline.executeCommand(`print ${expression}`, { waitForPrompt: true });
|
|
875
|
+
//remove whitespace
|
|
876
|
+
return (_a = data === null || data === void 0 ? void 0 : data.trim()) !== null && _a !== void 0 ? _a : null;
|
|
859
877
|
});
|
|
860
878
|
}
|
|
861
879
|
/**
|
|
@@ -866,11 +884,15 @@ class TelnetAdapter {
|
|
|
866
884
|
resolve(key, factory) {
|
|
867
885
|
try {
|
|
868
886
|
if (this.cache[key]) {
|
|
887
|
+
this.logger.debug(`resolve cache "${key}": already exists`);
|
|
888
|
+
return this.cache[key];
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
this.logger.debug(`resolve cache "${key}": calling factory`);
|
|
892
|
+
const result = factory();
|
|
893
|
+
this.cache[key] = Promise.resolve(result);
|
|
869
894
|
return this.cache[key];
|
|
870
895
|
}
|
|
871
|
-
const result = factory();
|
|
872
|
-
this.cache[key] = Promise.resolve(result);
|
|
873
|
-
return this.cache[key];
|
|
874
896
|
}
|
|
875
897
|
catch (e) {
|
|
876
898
|
return Promise.reject(e);
|
|
@@ -880,13 +902,14 @@ class TelnetAdapter {
|
|
|
880
902
|
* Get a list of threads. The first thread in the list is the active thread
|
|
881
903
|
*/
|
|
882
904
|
async getThreads() {
|
|
905
|
+
this.logger.log('getThreads');
|
|
883
906
|
if (!this.isAtDebuggerPrompt) {
|
|
884
|
-
|
|
907
|
+
this.logger.log('Cannot get threads: debugger is not paused');
|
|
885
908
|
return [];
|
|
886
909
|
}
|
|
887
910
|
return this.resolve('threads', async () => {
|
|
888
|
-
let data = await this.requestPipeline.executeCommand('threads', true);
|
|
889
|
-
let dataString = data.toString();
|
|
911
|
+
let data = await this.requestPipeline.executeCommand('threads', { waitForPrompt: true });
|
|
912
|
+
let dataString = data.toString().trim();
|
|
890
913
|
let matches = /^\s+(\d+\*)\s+(.*)\((\d+)\)\s+(.*)/gm.exec(dataString);
|
|
891
914
|
let threads = [];
|
|
892
915
|
if (matches) {
|
|
@@ -930,20 +953,6 @@ class TelnetAdapter {
|
|
|
930
953
|
//needs to be async to match the DebugProtocolAdapter implementation
|
|
931
954
|
return Promise.resolve();
|
|
932
955
|
}
|
|
933
|
-
/**
|
|
934
|
-
* Make sure any active Brightscript Debugger threads are exited
|
|
935
|
-
*/
|
|
936
|
-
async exitActiveBrightscriptDebugger() {
|
|
937
|
-
if (this.requestPipeline) {
|
|
938
|
-
let commandsExecuted = 0;
|
|
939
|
-
do {
|
|
940
|
-
let data = await this.requestPipeline.executeCommand(`exit`, false);
|
|
941
|
-
// This seems to work without the delay but I wonder about slower devices
|
|
942
|
-
// await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](100);
|
|
943
|
-
commandsExecuted++;
|
|
944
|
-
} while (commandsExecuted < 10);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
956
|
// #region Rendezvous Tracker pass though functions
|
|
948
957
|
/**
|
|
949
958
|
* Passes the debug functions used to locate the client files and lines to the RendezvousTracker
|
|
@@ -977,14 +986,6 @@ var EventName;
|
|
|
977
986
|
(function (EventName) {
|
|
978
987
|
EventName["suspend"] = "suspend";
|
|
979
988
|
})(EventName = exports.EventName || (exports.EventName = {}));
|
|
980
|
-
var HighLevelType;
|
|
981
|
-
(function (HighLevelType) {
|
|
982
|
-
HighLevelType["primative"] = "primative";
|
|
983
|
-
HighLevelType["array"] = "array";
|
|
984
|
-
HighLevelType["function"] = "function";
|
|
985
|
-
HighLevelType["object"] = "object";
|
|
986
|
-
HighLevelType["uninitialized"] = "uninitialized";
|
|
987
|
-
})(HighLevelType = exports.HighLevelType || (exports.HighLevelType = {}));
|
|
988
989
|
var KeyType;
|
|
989
990
|
(function (KeyType) {
|
|
990
991
|
KeyType["string"] = "String";
|
|
@@ -999,151 +1000,4 @@ var PrimativeType;
|
|
|
999
1000
|
PrimativeType["integer"] = "Integer";
|
|
1000
1001
|
PrimativeType["float"] = "Float";
|
|
1001
1002
|
})(PrimativeType = exports.PrimativeType || (exports.PrimativeType = {}));
|
|
1002
|
-
class RequestPipeline {
|
|
1003
|
-
constructor(client) {
|
|
1004
|
-
this.client = client;
|
|
1005
|
-
this.requests = [];
|
|
1006
|
-
this.isAtDebuggerPrompt = false;
|
|
1007
|
-
this.currentRequest = undefined;
|
|
1008
|
-
this.emitter = new EventEmitter();
|
|
1009
|
-
this.connect();
|
|
1010
|
-
}
|
|
1011
|
-
get isProcessing() {
|
|
1012
|
-
return this.currentRequest !== undefined;
|
|
1013
|
-
}
|
|
1014
|
-
get hasRequests() {
|
|
1015
|
-
return this.requests.length > 0;
|
|
1016
|
-
}
|
|
1017
|
-
on(eventName, handler) {
|
|
1018
|
-
this.emitter.on(eventName, handler);
|
|
1019
|
-
return () => {
|
|
1020
|
-
this.emitter.removeListener(eventName, handler);
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
emit(eventName, data) {
|
|
1024
|
-
this.emitter.emit(eventName, data);
|
|
1025
|
-
}
|
|
1026
|
-
connect() {
|
|
1027
|
-
let allResponseText = '';
|
|
1028
|
-
let lastPartialLine = '';
|
|
1029
|
-
this.client.addListener('data', (data) => {
|
|
1030
|
-
let responseText = data.toString();
|
|
1031
|
-
const cumulative = lastPartialLine + responseText;
|
|
1032
|
-
//ensure all debugger prompts appear completely on their own line
|
|
1033
|
-
responseText = util_1.util.ensureDebugPromptOnOwnLine(responseText);
|
|
1034
|
-
if (!cumulative.endsWith('\n') && !util_1.util.checkForDebuggerPrompt(cumulative)) {
|
|
1035
|
-
// buffer was split and was not the result of a prompt, save the partial line
|
|
1036
|
-
lastPartialLine += responseText;
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
1039
|
-
if (lastPartialLine) {
|
|
1040
|
-
// there was leftover lines, join the partial lines back together
|
|
1041
|
-
responseText = lastPartialLine + responseText;
|
|
1042
|
-
lastPartialLine = '';
|
|
1043
|
-
}
|
|
1044
|
-
//forward all raw console output
|
|
1045
|
-
this.emit('console-output', responseText);
|
|
1046
|
-
allResponseText += responseText;
|
|
1047
|
-
let foundDebuggerPrompt = util_1.util.checkForDebuggerPrompt(allResponseText);
|
|
1048
|
-
//if we are not processing, immediately broadcast the latest data
|
|
1049
|
-
if (!this.isProcessing) {
|
|
1050
|
-
this.emit('unhandled-console-output', allResponseText);
|
|
1051
|
-
allResponseText = '';
|
|
1052
|
-
if (foundDebuggerPrompt) {
|
|
1053
|
-
this.isAtDebuggerPrompt = true;
|
|
1054
|
-
if (this.hasRequests) {
|
|
1055
|
-
// There are requests waiting to be processed
|
|
1056
|
-
this.process();
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
else {
|
|
1061
|
-
//if responseText produced a prompt, return the responseText
|
|
1062
|
-
if (foundDebuggerPrompt) {
|
|
1063
|
-
//resolve the command's promise (if it cares)
|
|
1064
|
-
this.isAtDebuggerPrompt = true;
|
|
1065
|
-
this.currentRequest.onComplete(allResponseText);
|
|
1066
|
-
allResponseText = '';
|
|
1067
|
-
this.currentRequest = undefined;
|
|
1068
|
-
//try to run the next request
|
|
1069
|
-
this.process();
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
});
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Schedule a command to be run. Resolves with the result once the command finishes
|
|
1076
|
-
* @param commandFunction
|
|
1077
|
-
* @param waitForPrompt - if true, the promise will wait until we find a prompt, and return all output in between. If false, the promise will immediately resolve
|
|
1078
|
-
* @param forceExecute - if true, it is assumed the command can be run at any time and will be executed immediately
|
|
1079
|
-
* @param silent - if true, the command will be hidden from the output
|
|
1080
|
-
*/
|
|
1081
|
-
executeCommand(command, waitForPrompt, forceExecute = false, silent = false) {
|
|
1082
|
-
console.debug(`Execute command (and${waitForPrompt ? '' : ' don\'t'} wait for prompt):`, command);
|
|
1083
|
-
return new Promise((resolve, reject) => {
|
|
1084
|
-
let executeCommand = () => {
|
|
1085
|
-
let commandText = `${command}\r\n`;
|
|
1086
|
-
if (!silent) {
|
|
1087
|
-
this.emit('console-output', command);
|
|
1088
|
-
}
|
|
1089
|
-
console.log(`TELNET WRITE: "${commandText}"`);
|
|
1090
|
-
this.client.write(commandText);
|
|
1091
|
-
if (waitForPrompt) {
|
|
1092
|
-
// The act of executing this command means we are no longer at the debug prompt
|
|
1093
|
-
this.isAtDebuggerPrompt = false;
|
|
1094
|
-
}
|
|
1095
|
-
};
|
|
1096
|
-
let request = {
|
|
1097
|
-
executeCommand: executeCommand,
|
|
1098
|
-
onComplete: (data) => {
|
|
1099
|
-
console.debug(`Command finished (${waitForPrompt ? 'after waiting for prompt' : 'did not wait for prompt'}):`, command);
|
|
1100
|
-
console.debug('Data:', data);
|
|
1101
|
-
resolve(data);
|
|
1102
|
-
},
|
|
1103
|
-
waitForPrompt: waitForPrompt
|
|
1104
|
-
};
|
|
1105
|
-
if (!waitForPrompt) {
|
|
1106
|
-
if (!this.isProcessing || forceExecute) {
|
|
1107
|
-
//fire and forget the command
|
|
1108
|
-
request.executeCommand();
|
|
1109
|
-
//the command doesn't care about the output, resolve it immediately
|
|
1110
|
-
request.onComplete(undefined);
|
|
1111
|
-
}
|
|
1112
|
-
else {
|
|
1113
|
-
// Skip this request as the device is not ready to accept the command or it can not be run at any time
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
else {
|
|
1117
|
-
this.requests.push(request);
|
|
1118
|
-
if (this.isAtDebuggerPrompt) {
|
|
1119
|
-
//start processing since we are already at a debug prompt (safe to call multiple times)
|
|
1120
|
-
this.process();
|
|
1121
|
-
}
|
|
1122
|
-
else {
|
|
1123
|
-
// do not run the command until the device is at a debug prompt.
|
|
1124
|
-
// this will be detected in the data listener in the connect function
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
/**
|
|
1130
|
-
* Internal request processing function
|
|
1131
|
-
*/
|
|
1132
|
-
process() {
|
|
1133
|
-
if (this.isProcessing || !this.hasRequests) {
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
//get the oldest command
|
|
1137
|
-
let nextRequest = this.requests.shift();
|
|
1138
|
-
this.currentRequest = nextRequest;
|
|
1139
|
-
//run the request. the data listener will handle launching the next request once this one has finished processing
|
|
1140
|
-
nextRequest.executeCommand();
|
|
1141
|
-
}
|
|
1142
|
-
destroy() {
|
|
1143
|
-
this.client.removeAllListeners();
|
|
1144
|
-
this.client.destroy();
|
|
1145
|
-
this.client = undefined;
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
exports.RequestPipeline = RequestPipeline;
|
|
1149
1003
|
//# sourceMappingURL=TelnetAdapter.js.map
|