roku-debug 0.8.7 → 0.9.0
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 +14 -3
- package/dist/CompileErrorProcessor.d.ts +1 -0
- package/dist/CompileErrorProcessor.js +5 -4
- 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 +16 -14
- package/dist/adapters/DebugProtocolAdapter.js.map +1 -1
- package/dist/adapters/TelnetAdapter.d.ts +7 -51
- package/dist/adapters/TelnetAdapter.js +209 -385
- package/dist/adapters/TelnetAdapter.js.map +1 -1
- package/dist/adapters/TelnetRequestPipeline.d.ts +51 -0
- package/dist/adapters/TelnetRequestPipeline.js +229 -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 -36
- 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 -12
- package/dist/util.js +41 -40
- package/dist/util.js.map +1 -1
- package/package.json +4 -1
- package/roku-debug-0.9.0.tgz +0 -0
- package/roku-debug-0.8.7.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,7 +75,7 @@ class TelnetAdapter {
|
|
|
71
75
|
}, 0);
|
|
72
76
|
}
|
|
73
77
|
async activate() {
|
|
74
|
-
|
|
78
|
+
this.logger.log('Activate TelnetAdapter');
|
|
75
79
|
this.isActivated = true;
|
|
76
80
|
await this.handleStartupIfReady();
|
|
77
81
|
}
|
|
@@ -81,13 +85,13 @@ class TelnetAdapter {
|
|
|
81
85
|
async handleStartupIfReady() {
|
|
82
86
|
var _a;
|
|
83
87
|
if (this.isActivated && this.isAppRunning) {
|
|
84
|
-
|
|
88
|
+
this.logger.log('Handling startup');
|
|
85
89
|
this.emit('start');
|
|
86
90
|
//if we are already sitting at a debugger prompt, we need to emit the first suspend event.
|
|
87
91
|
//If not, then there are probably still messages being received, so let the normal handler
|
|
88
92
|
//emit the suspend event when it's ready
|
|
89
93
|
if (this.isAtDebuggerPrompt === true) {
|
|
90
|
-
|
|
94
|
+
this.logger.log(`At debug prompt, so trigger the 'suspend' event`);
|
|
91
95
|
let threads = await this.getThreads();
|
|
92
96
|
this.emit('suspend', (_a = threads[0]) === null || _a === void 0 ? void 0 : _a.threadId);
|
|
93
97
|
}
|
|
@@ -101,21 +105,21 @@ class TelnetAdapter {
|
|
|
101
105
|
*/
|
|
102
106
|
settle(client, name, maxWaitMilliseconds = 400) {
|
|
103
107
|
const startTime = new Date();
|
|
104
|
-
|
|
108
|
+
this.logger.log('Waiting for telnet client to settle');
|
|
105
109
|
return new Promise((resolve) => {
|
|
106
110
|
let callCount = -1;
|
|
107
|
-
|
|
111
|
+
const handler = () => {
|
|
108
112
|
callCount++;
|
|
109
113
|
let myCallCount = callCount;
|
|
110
114
|
setTimeout(() => {
|
|
111
115
|
//if no other calls have been made since the timeout started, then the listener has settled
|
|
112
116
|
if (myCallCount === callCount) {
|
|
113
117
|
client.removeListener(name, handler);
|
|
114
|
-
|
|
118
|
+
this.logger.log(`Telnet client has settled after ${new Date().getTime() - startTime.getTime()} milliseconds`);
|
|
115
119
|
resolve(callCount);
|
|
116
120
|
}
|
|
117
121
|
}, maxWaitMilliseconds);
|
|
118
|
-
}
|
|
122
|
+
};
|
|
119
123
|
client.addListener(name, handler);
|
|
120
124
|
//call the handler immediately so we have a timeout
|
|
121
125
|
handler();
|
|
@@ -146,15 +150,15 @@ class TelnetAdapter {
|
|
|
146
150
|
* Connect to the telnet session. This should be called before the channel is launched.
|
|
147
151
|
*/
|
|
148
152
|
async connect() {
|
|
149
|
-
|
|
153
|
+
this.logger.log('Establishing telnet connection');
|
|
150
154
|
let deferred = (0, util_1.defer)();
|
|
151
155
|
this.isInMicroDebugger = false;
|
|
152
156
|
this.isNextBreakpointSkipped = false;
|
|
153
157
|
try {
|
|
154
|
-
|
|
158
|
+
this.logger.log('Pressing home button');
|
|
155
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
|
|
156
160
|
await rokuDeploy.pressHomeButton(this.host);
|
|
157
|
-
let client = new
|
|
161
|
+
let client = new net_1.Socket();
|
|
158
162
|
//listen for the close event
|
|
159
163
|
client.addListener('close', (err, data) => {
|
|
160
164
|
this.emit('close');
|
|
@@ -165,13 +169,14 @@ class TelnetAdapter {
|
|
|
165
169
|
});
|
|
166
170
|
const settlePromise = this.settle(client, 'data');
|
|
167
171
|
client.connect(8085, this.host, () => {
|
|
168
|
-
|
|
172
|
+
this.logger.log(`Telnet connection established to ${this.host}`);
|
|
169
173
|
this.connected = true;
|
|
170
174
|
this.emit('connected', this.connected);
|
|
171
175
|
});
|
|
172
176
|
await settlePromise;
|
|
173
177
|
//hook up the pipeline to the socket
|
|
174
|
-
this.requestPipeline = new
|
|
178
|
+
this.requestPipeline = new TelnetRequestPipeline_1.TelnetRequestPipeline(client);
|
|
179
|
+
this.requestPipeline.connect();
|
|
175
180
|
//forward all raw console output
|
|
176
181
|
this.requestPipeline.on('console-output', (output) => {
|
|
177
182
|
this.processBreakpoints(output);
|
|
@@ -197,14 +202,14 @@ class TelnetAdapter {
|
|
|
197
202
|
}
|
|
198
203
|
// short circuit after the output has been sent as console output
|
|
199
204
|
if (hasRuntimeError) {
|
|
200
|
-
|
|
205
|
+
this.logger.log('Detected runtime error in output', { responseText });
|
|
201
206
|
this.isAtDebuggerPrompt = true;
|
|
202
207
|
return;
|
|
203
208
|
}
|
|
204
209
|
this.compileErrorProcessor.processUnhandledLines(responseText);
|
|
205
210
|
let match;
|
|
206
211
|
if (this.isAtCannotContinue(responseText)) {
|
|
207
|
-
|
|
212
|
+
this.logger.log('is at cannot continue');
|
|
208
213
|
this.isAtDebuggerPrompt = true;
|
|
209
214
|
return;
|
|
210
215
|
}
|
|
@@ -213,7 +218,7 @@ class TelnetAdapter {
|
|
|
213
218
|
// eslint-disable-next-line no-cond-assign
|
|
214
219
|
if (match = /\[scrpt.ctx.run.enter\]/i.exec(responseText.trim())) {
|
|
215
220
|
this.isAppRunning = true;
|
|
216
|
-
|
|
221
|
+
this.logger.log('Running beacon detected', { responseText });
|
|
217
222
|
void this.handleStartupIfReady();
|
|
218
223
|
}
|
|
219
224
|
//watch for the end of the program
|
|
@@ -222,28 +227,29 @@ class TelnetAdapter {
|
|
|
222
227
|
this.beginAppExit();
|
|
223
228
|
}
|
|
224
229
|
//watch for debugger prompt output
|
|
225
|
-
if (util_1.util.
|
|
226
|
-
|
|
230
|
+
if (util_1.util.endsWithDebuggerPrompt(responseText)) {
|
|
231
|
+
this.logger.log('Debugger prompt detected in', { responseText });
|
|
227
232
|
//if we are activated AND this is the first time seeing the debugger prompt since a continue/step action
|
|
228
233
|
if (this.isNextBreakpointSkipped) {
|
|
229
|
-
|
|
234
|
+
this.logger.log('This debugger is flagged to be skipped');
|
|
230
235
|
this.isInMicroDebugger = false;
|
|
231
236
|
this.isNextBreakpointSkipped = false;
|
|
232
|
-
void this.requestPipeline.executeCommand('c',
|
|
237
|
+
void this.requestPipeline.executeCommand('c', { waitForPrompt: false, insertAtFront: true });
|
|
233
238
|
}
|
|
234
239
|
else {
|
|
235
240
|
if (this.isActivated && this.isAtDebuggerPrompt === false) {
|
|
236
241
|
this.isAtDebuggerPrompt = true;
|
|
237
|
-
|
|
242
|
+
this.logger.log('Sending the "suspend" event to the client');
|
|
238
243
|
this.emit('suspend');
|
|
239
244
|
}
|
|
240
245
|
else {
|
|
241
|
-
|
|
246
|
+
this.logger.log('Skipping "suspend" event because we are already suspended');
|
|
242
247
|
this.isAtDebuggerPrompt = true;
|
|
243
248
|
}
|
|
244
249
|
}
|
|
245
250
|
}
|
|
246
251
|
else {
|
|
252
|
+
this.logger.debug('responseText does not end with debugger prompt. isAtDebuggerPrompt = false', { responseText });
|
|
247
253
|
this.isAtDebuggerPrompt = false;
|
|
248
254
|
}
|
|
249
255
|
}
|
|
@@ -257,9 +263,9 @@ class TelnetAdapter {
|
|
|
257
263
|
return deferred.promise;
|
|
258
264
|
}
|
|
259
265
|
beginAppExit() {
|
|
260
|
-
|
|
266
|
+
this.logger.log('Beginning app exit');
|
|
261
267
|
this.compileErrorProcessor.compileErrorTimer = setTimeout(() => {
|
|
262
|
-
|
|
268
|
+
this.logger.info('emitting app-exit');
|
|
263
269
|
this.isAppRunning = false;
|
|
264
270
|
this.emit('app-exit');
|
|
265
271
|
}, 200);
|
|
@@ -304,42 +310,42 @@ class TelnetAdapter {
|
|
|
304
310
|
* Send command to step over
|
|
305
311
|
*/
|
|
306
312
|
stepOver() {
|
|
307
|
-
|
|
313
|
+
this.logger.log('stepOver');
|
|
308
314
|
this.clearCache();
|
|
309
|
-
return this.requestPipeline.executeCommand('over', false);
|
|
315
|
+
return this.requestPipeline.executeCommand('over', { waitForPrompt: false, insertAtFront: true });
|
|
310
316
|
}
|
|
311
317
|
stepInto() {
|
|
312
|
-
|
|
318
|
+
this.logger.log('stepInto');
|
|
313
319
|
this.clearCache();
|
|
314
|
-
return this.requestPipeline.executeCommand('step', false);
|
|
320
|
+
return this.requestPipeline.executeCommand('step', { waitForPrompt: false, insertAtFront: true });
|
|
315
321
|
}
|
|
316
322
|
stepOut() {
|
|
317
|
-
|
|
323
|
+
this.logger.log('stepOut');
|
|
318
324
|
this.clearCache();
|
|
319
|
-
return this.requestPipeline.executeCommand('out', false);
|
|
325
|
+
return this.requestPipeline.executeCommand('out', { waitForPrompt: false, insertAtFront: true });
|
|
320
326
|
}
|
|
321
327
|
/**
|
|
322
328
|
* Tell the brightscript program to continue (i.e. resume program)
|
|
323
329
|
*/
|
|
324
330
|
continue() {
|
|
325
|
-
|
|
331
|
+
this.logger.log('continue');
|
|
326
332
|
this.clearCache();
|
|
327
|
-
return this.requestPipeline.executeCommand('c', false);
|
|
333
|
+
return this.requestPipeline.executeCommand('c', { waitForPrompt: false, insertAtFront: true });
|
|
328
334
|
}
|
|
329
335
|
/**
|
|
330
336
|
* Tell the brightscript program to pause (fall into debug mode)
|
|
331
337
|
*/
|
|
332
338
|
pause() {
|
|
333
|
-
|
|
339
|
+
this.logger.log('pause');
|
|
334
340
|
this.clearCache();
|
|
335
|
-
//send the kill signal, which breaks into debugger mode
|
|
336
|
-
|
|
341
|
+
//send the kill signal, which breaks into debugger mode. This gets written immediately, regardless of debugger prompt status.
|
|
342
|
+
this.requestPipeline.write('\x03;');
|
|
337
343
|
}
|
|
338
344
|
/**
|
|
339
345
|
* Clears the state, which means that everything will be retrieved fresh next time it is requested
|
|
340
346
|
*/
|
|
341
347
|
clearCache() {
|
|
342
|
-
|
|
348
|
+
this.logger.info('Clearing TelnetAdapter cache');
|
|
343
349
|
this.cache = {};
|
|
344
350
|
this.isAtDebuggerPrompt = false;
|
|
345
351
|
}
|
|
@@ -348,26 +354,26 @@ class TelnetAdapter {
|
|
|
348
354
|
* @param command the command to execute. If the command does not start with `print` the command will be prefixed with `print ` because
|
|
349
355
|
*/
|
|
350
356
|
async evaluate(command) {
|
|
351
|
-
|
|
357
|
+
this.logger.log('evaluate ', { command });
|
|
352
358
|
if (!this.isAtDebuggerPrompt) {
|
|
353
359
|
throw new Error('Cannot run evaluate: debugger is not paused');
|
|
354
360
|
}
|
|
355
361
|
//clear the cache (we don't know what command the user entered)
|
|
356
362
|
this.clearCache();
|
|
357
363
|
//don't wait for the output...we don't know what command the user entered
|
|
358
|
-
let responseText = await this.requestPipeline.executeCommand(command, true);
|
|
364
|
+
let responseText = await this.requestPipeline.executeCommand(command, { waitForPrompt: true });
|
|
359
365
|
//we know that if we got a response, we are back at a debugger prompt
|
|
360
366
|
this.isAtDebuggerPrompt = true;
|
|
361
367
|
return responseText;
|
|
362
368
|
}
|
|
363
369
|
async getStackTrace() {
|
|
364
|
-
|
|
370
|
+
this.logger.log(TelnetAdapter.prototype.getStackTrace.name);
|
|
365
371
|
if (!this.isAtDebuggerPrompt) {
|
|
366
372
|
throw new Error('Cannot get stack trace: debugger is not paused');
|
|
367
373
|
}
|
|
368
374
|
return this.resolve('stackTrace', async () => {
|
|
369
375
|
//perform a request to load the stack trace
|
|
370
|
-
let responseText = await this.requestPipeline.executeCommand('bt', true);
|
|
376
|
+
let responseText = (await this.requestPipeline.executeCommand('bt', { waitForPrompt: true })).trim();
|
|
371
377
|
let regexp = /#(\d+)\s+(?:function|sub)\s+([\$\w\d]+).*\s+file\/line:\s+(.*)\((\d+)\)/ig;
|
|
372
378
|
let matches;
|
|
373
379
|
let frames = [];
|
|
@@ -394,16 +400,6 @@ class TelnetAdapter {
|
|
|
394
400
|
return frames;
|
|
395
401
|
});
|
|
396
402
|
}
|
|
397
|
-
/**
|
|
398
|
-
* Runs a regex to get the content between telnet commands
|
|
399
|
-
* @param value
|
|
400
|
-
*/
|
|
401
|
-
getExpressionDetails(value) {
|
|
402
|
-
const match = /(.*?)\r?\nBrightscript Debugger>\s*/is.exec(value);
|
|
403
|
-
if (match) {
|
|
404
|
-
return match[1];
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
403
|
/**
|
|
408
404
|
* Runs a regex to check if the target is an object and get the type if it is
|
|
409
405
|
* @param value
|
|
@@ -429,15 +425,15 @@ class TelnetAdapter {
|
|
|
429
425
|
* @param scope
|
|
430
426
|
*/
|
|
431
427
|
async getScopeVariables(scope) {
|
|
432
|
-
|
|
428
|
+
this.logger.log('getScopeVariables', { scope });
|
|
433
429
|
if (!this.isAtDebuggerPrompt) {
|
|
434
430
|
throw new Error('Cannot resolve variable: debugger is not paused');
|
|
435
431
|
}
|
|
436
432
|
return this.resolve(`Scope Variables`, async () => {
|
|
437
433
|
let data;
|
|
438
434
|
let vars = [];
|
|
439
|
-
data = await this.requestPipeline.executeCommand(`var`, true);
|
|
440
|
-
let splitData = data.split('\n');
|
|
435
|
+
data = await this.requestPipeline.executeCommand(`var`, { waitForPrompt: true });
|
|
436
|
+
let splitData = data.trim().split('\n');
|
|
441
437
|
for (const line of splitData) {
|
|
442
438
|
let match;
|
|
443
439
|
if (!line.includes('Brightscript Debugger') && (match = this.getFirstWord(line))) {
|
|
@@ -456,123 +452,141 @@ class TelnetAdapter {
|
|
|
456
452
|
* @param expression
|
|
457
453
|
*/
|
|
458
454
|
async getVariable(expression) {
|
|
459
|
-
|
|
455
|
+
const logger = this.logger.createLogger('[getVariable]');
|
|
456
|
+
logger.info('begin', { expression });
|
|
460
457
|
if (!this.isAtDebuggerPrompt) {
|
|
461
458
|
throw new Error('Cannot resolve variable: debugger is not paused');
|
|
462
459
|
}
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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;
|
|
576
590
|
}
|
|
577
591
|
/**
|
|
578
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.
|
|
@@ -647,22 +661,22 @@ class TelnetAdapter {
|
|
|
647
661
|
let collectionEnd;
|
|
648
662
|
if (line.includes('<Component: roList>')) {
|
|
649
663
|
collectionEnd = ')';
|
|
650
|
-
child.highLevelType = HighLevelType.array;
|
|
664
|
+
child.highLevelType = interfaces_1.HighLevelType.array;
|
|
651
665
|
child.type = objectType;
|
|
652
666
|
}
|
|
653
667
|
else if (line.includes('<Component: roArray>')) {
|
|
654
668
|
collectionEnd = ']';
|
|
655
|
-
child.highLevelType = HighLevelType.array;
|
|
669
|
+
child.highLevelType = interfaces_1.HighLevelType.array;
|
|
656
670
|
child.type = this.getObjectType(line);
|
|
657
671
|
}
|
|
658
672
|
else if (line.includes('<Component: roByteArray>')) {
|
|
659
673
|
collectionEnd = ']';
|
|
660
|
-
child.highLevelType = HighLevelType.array;
|
|
674
|
+
child.highLevelType = interfaces_1.HighLevelType.array;
|
|
661
675
|
child.type = this.getObjectType(line);
|
|
662
676
|
}
|
|
663
677
|
else if (line.includes('<Component: roAssociativeArray>') || isRoSGNode) {
|
|
664
678
|
collectionEnd = '}';
|
|
665
|
-
child.highLevelType = HighLevelType.object;
|
|
679
|
+
child.highLevelType = interfaces_1.HighLevelType.object;
|
|
666
680
|
child.type = this.getObjectType(line);
|
|
667
681
|
}
|
|
668
682
|
let collectionLineList = [line];
|
|
@@ -676,34 +690,24 @@ class TelnetAdapter {
|
|
|
676
690
|
}
|
|
677
691
|
//we have reached the end of the collection. scrap children because they need evaluated in a separate call to compute their types
|
|
678
692
|
child.children = [];
|
|
679
|
-
if (isRoSGNode) {
|
|
680
|
-
let nodeChildrenProperty = {
|
|
681
|
-
name: '[[children]]',
|
|
682
|
-
type: 'roArray',
|
|
683
|
-
highLevelType: 'array',
|
|
684
|
-
evaluateName: `${child.evaluateName}.getChildren(-1,0)`,
|
|
685
|
-
children: []
|
|
686
|
-
};
|
|
687
|
-
child.children.push(nodeChildrenProperty);
|
|
688
|
-
}
|
|
689
693
|
//this if block must pre-seek the `line.indexOf('<Component') > -1` line because roInvalid is a component too.
|
|
690
694
|
}
|
|
691
695
|
else if (objectType === 'roInvalid') {
|
|
692
|
-
child.highLevelType = HighLevelType.uninitialized;
|
|
696
|
+
child.highLevelType = interfaces_1.HighLevelType.uninitialized;
|
|
693
697
|
child.type = 'roInvalid';
|
|
694
698
|
child.value = 'roInvalid';
|
|
695
699
|
child.children = undefined;
|
|
696
700
|
}
|
|
697
701
|
else if (line.includes('<Component:')) {
|
|
698
702
|
//handle things like nodes
|
|
699
|
-
child.highLevelType = HighLevelType.object;
|
|
703
|
+
child.highLevelType = interfaces_1.HighLevelType.object;
|
|
700
704
|
child.type = objectType;
|
|
701
705
|
}
|
|
702
706
|
else {
|
|
703
707
|
//is some primative type
|
|
704
708
|
child.type = type;
|
|
705
709
|
child.value = line.trim();
|
|
706
|
-
child.highLevelType = HighLevelType.primative;
|
|
710
|
+
child.highLevelType = interfaces_1.HighLevelType.primative;
|
|
707
711
|
child.children = undefined;
|
|
708
712
|
}
|
|
709
713
|
children.push(child);
|
|
@@ -749,7 +753,7 @@ class TelnetAdapter {
|
|
|
749
753
|
else {
|
|
750
754
|
child.type = this.getPrimativeTypeFromValue(line);
|
|
751
755
|
child.value = line;
|
|
752
|
-
child.highLevelType = HighLevelType.primative;
|
|
756
|
+
child.highLevelType = interfaces_1.HighLevelType.primative;
|
|
753
757
|
}
|
|
754
758
|
children.push(child);
|
|
755
759
|
arrayIndex++;
|
|
@@ -794,7 +798,7 @@ class TelnetAdapter {
|
|
|
794
798
|
child = {
|
|
795
799
|
name: line,
|
|
796
800
|
type: '<ERROR>',
|
|
797
|
-
highLevelType: HighLevelType.uninitialized,
|
|
801
|
+
highLevelType: interfaces_1.HighLevelType.uninitialized,
|
|
798
802
|
evaluateName: undefined,
|
|
799
803
|
variablePath: [],
|
|
800
804
|
elementCount: -1,
|
|
@@ -819,7 +823,7 @@ class TelnetAdapter {
|
|
|
819
823
|
else {
|
|
820
824
|
child.type = this.getPrimativeTypeFromValue(trimmedLine);
|
|
821
825
|
child.value = lineParseResult.value;
|
|
822
|
-
child.highLevelType = HighLevelType.primative;
|
|
826
|
+
child.highLevelType = interfaces_1.HighLevelType.primative;
|
|
823
827
|
}
|
|
824
828
|
}
|
|
825
829
|
children.push(child);
|
|
@@ -836,24 +840,24 @@ class TelnetAdapter {
|
|
|
836
840
|
*/
|
|
837
841
|
getHighLevelType(expressionType) {
|
|
838
842
|
if (!expressionType) {
|
|
839
|
-
|
|
843
|
+
return interfaces_1.HighLevelType.unknown;
|
|
840
844
|
}
|
|
841
845
|
expressionType = expressionType.toLowerCase();
|
|
842
846
|
let primativeTypes = ['boolean', 'integer', 'longinteger', 'float', 'double', 'string', 'rostring', 'invalid'];
|
|
843
847
|
if (primativeTypes.includes(expressionType)) {
|
|
844
|
-
return HighLevelType.primative;
|
|
848
|
+
return interfaces_1.HighLevelType.primative;
|
|
845
849
|
}
|
|
846
850
|
else if (expressionType === 'roarray' || expressionType === 'rolist') {
|
|
847
|
-
return HighLevelType.array;
|
|
851
|
+
return interfaces_1.HighLevelType.array;
|
|
848
852
|
}
|
|
849
853
|
else if (expressionType === 'function') {
|
|
850
|
-
return HighLevelType.function;
|
|
854
|
+
return interfaces_1.HighLevelType.function;
|
|
851
855
|
}
|
|
852
856
|
else if (expressionType === '<uninitialized>') {
|
|
853
|
-
return HighLevelType.uninitialized;
|
|
857
|
+
return interfaces_1.HighLevelType.uninitialized;
|
|
854
858
|
}
|
|
855
859
|
else {
|
|
856
|
-
return HighLevelType.object;
|
|
860
|
+
return interfaces_1.HighLevelType.object;
|
|
857
861
|
}
|
|
858
862
|
}
|
|
859
863
|
/**
|
|
@@ -866,17 +870,10 @@ class TelnetAdapter {
|
|
|
866
870
|
}
|
|
867
871
|
expression = `Type(${expression})`;
|
|
868
872
|
return this.resolve(`${expression}`, async () => {
|
|
869
|
-
|
|
870
|
-
let
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
//remove whitespace
|
|
874
|
-
typeValue = typeValue.trim();
|
|
875
|
-
return typeValue;
|
|
876
|
-
}
|
|
877
|
-
else {
|
|
878
|
-
return null;
|
|
879
|
-
}
|
|
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;
|
|
880
877
|
});
|
|
881
878
|
}
|
|
882
879
|
/**
|
|
@@ -887,11 +884,11 @@ class TelnetAdapter {
|
|
|
887
884
|
resolve(key, factory) {
|
|
888
885
|
try {
|
|
889
886
|
if (this.cache[key]) {
|
|
890
|
-
|
|
887
|
+
this.logger.debug(`resolve cache "${key}": already exists`);
|
|
891
888
|
return this.cache[key];
|
|
892
889
|
}
|
|
893
890
|
else {
|
|
894
|
-
|
|
891
|
+
this.logger.debug(`resolve cache "${key}": calling factory`);
|
|
895
892
|
const result = factory();
|
|
896
893
|
this.cache[key] = Promise.resolve(result);
|
|
897
894
|
return this.cache[key];
|
|
@@ -905,14 +902,14 @@ class TelnetAdapter {
|
|
|
905
902
|
* Get a list of threads. The first thread in the list is the active thread
|
|
906
903
|
*/
|
|
907
904
|
async getThreads() {
|
|
908
|
-
|
|
905
|
+
this.logger.log('getThreads');
|
|
909
906
|
if (!this.isAtDebuggerPrompt) {
|
|
910
|
-
|
|
907
|
+
this.logger.log('Cannot get threads: debugger is not paused');
|
|
911
908
|
return [];
|
|
912
909
|
}
|
|
913
910
|
return this.resolve('threads', async () => {
|
|
914
|
-
let data = await this.requestPipeline.executeCommand('threads', true);
|
|
915
|
-
let dataString = data.toString();
|
|
911
|
+
let data = await this.requestPipeline.executeCommand('threads', { waitForPrompt: true });
|
|
912
|
+
let dataString = data.toString().trim();
|
|
916
913
|
let matches = /^\s+(\d+\*)\s+(.*)\((\d+)\)\s+(.*)/gm.exec(dataString);
|
|
917
914
|
let threads = [];
|
|
918
915
|
if (matches) {
|
|
@@ -956,20 +953,6 @@ class TelnetAdapter {
|
|
|
956
953
|
//needs to be async to match the DebugProtocolAdapter implementation
|
|
957
954
|
return Promise.resolve();
|
|
958
955
|
}
|
|
959
|
-
/**
|
|
960
|
-
* Make sure any active Brightscript Debugger threads are exited
|
|
961
|
-
*/
|
|
962
|
-
async exitActiveBrightscriptDebugger() {
|
|
963
|
-
if (this.requestPipeline) {
|
|
964
|
-
let commandsExecuted = 0;
|
|
965
|
-
do {
|
|
966
|
-
let data = await this.requestPipeline.executeCommand(`exit`, false);
|
|
967
|
-
// This seems to work without the delay but I wonder about slower devices
|
|
968
|
-
// await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](100);
|
|
969
|
-
commandsExecuted++;
|
|
970
|
-
} while (commandsExecuted < 10);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
956
|
// #region Rendezvous Tracker pass though functions
|
|
974
957
|
/**
|
|
975
958
|
* Passes the debug functions used to locate the client files and lines to the RendezvousTracker
|
|
@@ -1003,14 +986,6 @@ var EventName;
|
|
|
1003
986
|
(function (EventName) {
|
|
1004
987
|
EventName["suspend"] = "suspend";
|
|
1005
988
|
})(EventName = exports.EventName || (exports.EventName = {}));
|
|
1006
|
-
var HighLevelType;
|
|
1007
|
-
(function (HighLevelType) {
|
|
1008
|
-
HighLevelType["primative"] = "primative";
|
|
1009
|
-
HighLevelType["array"] = "array";
|
|
1010
|
-
HighLevelType["function"] = "function";
|
|
1011
|
-
HighLevelType["object"] = "object";
|
|
1012
|
-
HighLevelType["uninitialized"] = "uninitialized";
|
|
1013
|
-
})(HighLevelType = exports.HighLevelType || (exports.HighLevelType = {}));
|
|
1014
989
|
var KeyType;
|
|
1015
990
|
(function (KeyType) {
|
|
1016
991
|
KeyType["string"] = "String";
|
|
@@ -1025,155 +1000,4 @@ var PrimativeType;
|
|
|
1025
1000
|
PrimativeType["integer"] = "Integer";
|
|
1026
1001
|
PrimativeType["float"] = "Float";
|
|
1027
1002
|
})(PrimativeType = exports.PrimativeType || (exports.PrimativeType = {}));
|
|
1028
|
-
class RequestPipeline {
|
|
1029
|
-
constructor(client) {
|
|
1030
|
-
this.client = client;
|
|
1031
|
-
this.requests = [];
|
|
1032
|
-
this.isAtDebuggerPrompt = false;
|
|
1033
|
-
this.currentRequest = undefined;
|
|
1034
|
-
this.emitter = new EventEmitter();
|
|
1035
|
-
/**
|
|
1036
|
-
* Used to help with logging
|
|
1037
|
-
*/
|
|
1038
|
-
this.commandIdSequence = 0;
|
|
1039
|
-
this.connect();
|
|
1040
|
-
}
|
|
1041
|
-
get isProcessing() {
|
|
1042
|
-
return this.currentRequest !== undefined;
|
|
1043
|
-
}
|
|
1044
|
-
get hasRequests() {
|
|
1045
|
-
return this.requests.length > 0;
|
|
1046
|
-
}
|
|
1047
|
-
on(eventName, handler) {
|
|
1048
|
-
this.emitter.on(eventName, handler);
|
|
1049
|
-
return () => {
|
|
1050
|
-
this.emitter.removeListener(eventName, handler);
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
emit(eventName, data) {
|
|
1054
|
-
this.emitter.emit(eventName, data);
|
|
1055
|
-
}
|
|
1056
|
-
connect() {
|
|
1057
|
-
let allResponseText = '';
|
|
1058
|
-
let lastPartialLine = '';
|
|
1059
|
-
this.client.addListener('data', (data) => {
|
|
1060
|
-
let responseText = data.toString();
|
|
1061
|
-
util_1.util.logDebugFenced('Raw telnet data', responseText);
|
|
1062
|
-
const cumulative = lastPartialLine + responseText;
|
|
1063
|
-
//ensure all debugger prompts appear completely on their own line
|
|
1064
|
-
responseText = util_1.util.ensureDebugPromptOnOwnLine(responseText);
|
|
1065
|
-
if (!cumulative.endsWith('\n') && !util_1.util.checkForDebuggerPrompt(cumulative)) {
|
|
1066
|
-
// buffer was split and was not the result of a prompt, save the partial line
|
|
1067
|
-
lastPartialLine += responseText;
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
if (lastPartialLine) {
|
|
1071
|
-
// there was leftover lines, join the partial lines back together
|
|
1072
|
-
responseText = lastPartialLine + responseText;
|
|
1073
|
-
lastPartialLine = '';
|
|
1074
|
-
}
|
|
1075
|
-
//forward all raw console output
|
|
1076
|
-
this.emit('console-output', responseText);
|
|
1077
|
-
allResponseText += responseText;
|
|
1078
|
-
let foundDebuggerPrompt = util_1.util.checkForDebuggerPrompt(allResponseText);
|
|
1079
|
-
//if we are not processing, immediately broadcast the latest data
|
|
1080
|
-
if (!this.isProcessing) {
|
|
1081
|
-
this.emit('unhandled-console-output', allResponseText);
|
|
1082
|
-
allResponseText = '';
|
|
1083
|
-
if (foundDebuggerPrompt) {
|
|
1084
|
-
this.isAtDebuggerPrompt = true;
|
|
1085
|
-
if (this.hasRequests) {
|
|
1086
|
-
// There are requests waiting to be processed
|
|
1087
|
-
this.process();
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
else {
|
|
1092
|
-
//if responseText produced a prompt, return the responseText
|
|
1093
|
-
if (foundDebuggerPrompt) {
|
|
1094
|
-
//resolve the command's promise (if it cares)
|
|
1095
|
-
this.isAtDebuggerPrompt = true;
|
|
1096
|
-
this.currentRequest.onComplete(allResponseText);
|
|
1097
|
-
allResponseText = '';
|
|
1098
|
-
this.currentRequest = undefined;
|
|
1099
|
-
//try to run the next request
|
|
1100
|
-
this.process();
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Schedule a command to be run. Resolves with the result once the command finishes
|
|
1107
|
-
* @param commandFunction
|
|
1108
|
-
* @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
|
|
1109
|
-
* @param forceExecute - if true, it is assumed the command can be run at any time and will be executed immediately
|
|
1110
|
-
* @param silent - if true, the command will be hidden from the output
|
|
1111
|
-
*/
|
|
1112
|
-
executeCommand(command, waitForPrompt, forceExecute = false, silent = false) {
|
|
1113
|
-
const commandId = this.commandIdSequence++;
|
|
1114
|
-
util_1.util.logDebug(`Command ${commandId} execute ${JSON.stringify(command)} and ${waitForPrompt ? '' : ' don\'t'} wait for prompt): \n`, command, '\n');
|
|
1115
|
-
return new Promise((resolve, reject) => {
|
|
1116
|
-
let executeCommand = () => {
|
|
1117
|
-
let commandText = `${command}\r\n`;
|
|
1118
|
-
if (!silent) {
|
|
1119
|
-
this.emit('console-output', command);
|
|
1120
|
-
}
|
|
1121
|
-
this.client.write(commandText);
|
|
1122
|
-
if (waitForPrompt) {
|
|
1123
|
-
// The act of executing this command means we are no longer at the debug prompt
|
|
1124
|
-
this.isAtDebuggerPrompt = false;
|
|
1125
|
-
}
|
|
1126
|
-
};
|
|
1127
|
-
let request = {
|
|
1128
|
-
executeCommand: executeCommand,
|
|
1129
|
-
onComplete: (data) => {
|
|
1130
|
-
util_1.util.logDebugFenced(`Command ${commandId} execute ${JSON.stringify(command)} result`, data);
|
|
1131
|
-
resolve(data);
|
|
1132
|
-
},
|
|
1133
|
-
waitForPrompt: waitForPrompt
|
|
1134
|
-
};
|
|
1135
|
-
if (!waitForPrompt) {
|
|
1136
|
-
if (!this.isProcessing || forceExecute) {
|
|
1137
|
-
//fire and forget the command
|
|
1138
|
-
request.executeCommand();
|
|
1139
|
-
//the command doesn't care about the output, resolve it immediately
|
|
1140
|
-
request.onComplete(undefined);
|
|
1141
|
-
}
|
|
1142
|
-
else {
|
|
1143
|
-
// Skip this request as the device is not ready to accept the command or it can not be run at any time
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
else {
|
|
1147
|
-
this.requests.push(request);
|
|
1148
|
-
if (this.isAtDebuggerPrompt) {
|
|
1149
|
-
//start processing since we are already at a debug prompt (safe to call multiple times)
|
|
1150
|
-
this.process();
|
|
1151
|
-
}
|
|
1152
|
-
else {
|
|
1153
|
-
// do not run the command until the device is at a debug prompt.
|
|
1154
|
-
// this will be detected in the data listener in the connect function
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Internal request processing function
|
|
1161
|
-
*/
|
|
1162
|
-
process() {
|
|
1163
|
-
if (this.isProcessing || !this.hasRequests) {
|
|
1164
|
-
return;
|
|
1165
|
-
}
|
|
1166
|
-
//get the oldest command
|
|
1167
|
-
let nextRequest = this.requests.shift();
|
|
1168
|
-
this.currentRequest = nextRequest;
|
|
1169
|
-
//run the request. the data listener will handle launching the next request once this one has finished processing
|
|
1170
|
-
nextRequest.executeCommand();
|
|
1171
|
-
}
|
|
1172
|
-
destroy() {
|
|
1173
|
-
this.client.removeAllListeners();
|
|
1174
|
-
this.client.destroy();
|
|
1175
|
-
this.client = undefined;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
exports.RequestPipeline = RequestPipeline;
|
|
1179
1003
|
//# sourceMappingURL=TelnetAdapter.js.map
|