"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts" (16 Sep 2020, 6622 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "abstractDebugAdapter.ts": 1.48.2_vs_1.49.0.

    1 /*---------------------------------------------------------------------------------------------
    2  *  Copyright (c) Microsoft Corporation. All rights reserved.
    3  *  Licensed under the MIT License. See License.txt in the project root for license information.
    4  *--------------------------------------------------------------------------------------------*/
    5 
    6 import { Emitter, Event } from 'vs/base/common/event';
    7 import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug';
    8 import { timeout } from 'vs/base/common/async';
    9 import { localize } from 'vs/nls';
   10 
   11 /**
   12  * Abstract implementation of the low level API for a debug adapter.
   13  * Missing is how this API communicates with the debug adapter.
   14  */
   15 export abstract class AbstractDebugAdapter implements IDebugAdapter {
   16     private sequence: number;
   17     private pendingRequests = new Map<number, (e: DebugProtocol.Response) => void>();
   18     private requestCallback: ((request: DebugProtocol.Request) => void) | undefined;
   19     private eventCallback: ((request: DebugProtocol.Event) => void) | undefined;
   20     private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined;
   21     private queue: DebugProtocol.ProtocolMessage[] = [];
   22     protected readonly _onError = new Emitter<Error>();
   23     protected readonly _onExit = new Emitter<number | null>();
   24 
   25     constructor() {
   26         this.sequence = 1;
   27     }
   28 
   29     abstract startSession(): Promise<void>;
   30 
   31     abstract stopSession(): Promise<void>;
   32 
   33     abstract sendMessage(message: DebugProtocol.ProtocolMessage): void;
   34 
   35     get onError(): Event<Error> {
   36         return this._onError.event;
   37     }
   38 
   39     get onExit(): Event<number | null> {
   40         return this._onExit.event;
   41     }
   42 
   43     onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void {
   44         if (this.eventCallback) {
   45             this._onError.fire(new Error(`attempt to set more than one 'Message' callback`));
   46         }
   47         this.messageCallback = callback;
   48     }
   49 
   50     onEvent(callback: (event: DebugProtocol.Event) => void): void {
   51         if (this.eventCallback) {
   52             this._onError.fire(new Error(`attempt to set more than one 'Event' callback`));
   53         }
   54         this.eventCallback = callback;
   55     }
   56 
   57     onRequest(callback: (request: DebugProtocol.Request) => void): void {
   58         if (this.requestCallback) {
   59             this._onError.fire(new Error(`attempt to set more than one 'Request' callback`));
   60         }
   61         this.requestCallback = callback;
   62     }
   63 
   64     sendResponse(response: DebugProtocol.Response): void {
   65         if (response.seq > 0) {
   66             this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`));
   67         } else {
   68             this.internalSend('response', response);
   69         }
   70     }
   71 
   72     sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): number {
   73         const request: any = {
   74             command: command
   75         };
   76         if (args && Object.keys(args).length > 0) {
   77             request.arguments = args;
   78         }
   79         this.internalSend('request', request);
   80         if (typeof timeout === 'number') {
   81             const timer = setTimeout(() => {
   82                 clearTimeout(timer);
   83                 const clb = this.pendingRequests.get(request.seq);
   84                 if (clb) {
   85                     this.pendingRequests.delete(request.seq);
   86                     const err: DebugProtocol.Response = {
   87                         type: 'response',
   88                         seq: 0,
   89                         request_seq: request.seq,
   90                         success: false,
   91                         command,
   92                         message: localize('timeout', "Timeout after {0} ms for '{1}'", timeout, command)
   93                     };
   94                     clb(err);
   95                 }
   96             }, timeout);
   97         }
   98         if (clb) {
   99             // store callback for this request
  100             this.pendingRequests.set(request.seq, clb);
  101         }
  102 
  103         return request.seq;
  104     }
  105 
  106     acceptMessage(message: DebugProtocol.ProtocolMessage): void {
  107         if (this.messageCallback) {
  108             this.messageCallback(message);
  109         } else {
  110             this.queue.push(message);
  111             if (this.queue.length === 1) {
  112                 // first item = need to start processing loop
  113                 this.processQueue();
  114             }
  115         }
  116     }
  117 
  118     /**
  119      * Returns whether we should insert a timeout between processing messageA
  120      * and messageB. Artificially queueing protocol messages guarantees that any
  121      * microtasks for previous message finish before next message is processed.
  122      * This is essential ordering when using promises anywhere along the call path.
  123      *
  124      * For example, take the following, where `chooseAndSendGreeting` returns
  125      * a person name and then emits a greeting event:
  126      *
  127      * ```
  128      * let person: string;
  129      * adapter.onGreeting(() => console.log('hello', person));
  130      * person = await adapter.chooseAndSendGreeting();
  131      * ```
  132      *
  133      * Because the event is dispatched synchronously, it may fire before person
  134      * is assigned if they're processed in the same task. Inserting a task
  135      * boundary avoids this issue.
  136      */
  137     protected needsTaskBoundaryBetween(messageA: DebugProtocol.ProtocolMessage, messageB: DebugProtocol.ProtocolMessage) {
  138         return messageA.type !== 'event' || messageB.type !== 'event';
  139     }
  140 
  141     /**
  142      * Reads and dispatches items from the queue until it is empty.
  143      */
  144     private async processQueue() {
  145         let message: DebugProtocol.ProtocolMessage | undefined;
  146         while (this.queue.length) {
  147             if (!message || this.needsTaskBoundaryBetween(this.queue[0], message)) {
  148                 await timeout(0);
  149             }
  150 
  151             message = this.queue.shift();
  152             if (!message) {
  153                 return; // may have been disposed of
  154             }
  155 
  156             switch (message.type) {
  157                 case 'event':
  158                     if (this.eventCallback) {
  159                         this.eventCallback(<DebugProtocol.Event>message);
  160                     }
  161                     break;
  162                 case 'request':
  163                     if (this.requestCallback) {
  164                         this.requestCallback(<DebugProtocol.Request>message);
  165                     }
  166                     break;
  167                 case 'response':
  168                     const response = <DebugProtocol.Response>message;
  169                     const clb = this.pendingRequests.get(response.request_seq);
  170                     if (clb) {
  171                         this.pendingRequests.delete(response.request_seq);
  172                         clb(response);
  173                     }
  174                     break;
  175             }
  176         }
  177     }
  178 
  179     private internalSend(typ: 'request' | 'response' | 'event', message: DebugProtocol.ProtocolMessage): void {
  180         message.type = typ;
  181         message.seq = this.sequence++;
  182         this.sendMessage(message);
  183     }
  184 
  185     protected async cancelPendingRequests(): Promise<void> {
  186         if (this.pendingRequests.size === 0) {
  187             return Promise.resolve();
  188         }
  189 
  190         const pending = new Map<number, (e: DebugProtocol.Response) => void>();
  191         this.pendingRequests.forEach((value, key) => pending.set(key, value));
  192         await timeout(500);
  193         pending.forEach((callback, request_seq) => {
  194             const err: DebugProtocol.Response = {
  195                 type: 'response',
  196                 seq: 0,
  197                 request_seq,
  198                 success: false,
  199                 command: 'canceled',
  200                 message: 'canceled'
  201             };
  202             callback(err);
  203             this.pendingRequests.delete(request_seq);
  204         });
  205     }
  206 
  207     getPendingRequestIds(): number[] {
  208         return Array.from(this.pendingRequests.keys());
  209     }
  210 
  211     dispose(): void {
  212         this.queue = [];
  213     }
  214 }