| 499 | } |
| 500 | |
| 501 | export class StackFrame implements IStackFrame { |
| 502 | |
| 503 | private scopes: Promise<Scope[]> | undefined; |
| 504 | |
| 505 | constructor( |
| 506 | public readonly thread: Thread, |
| 507 | public readonly frameId: number, |
| 508 | public readonly source: Source, |
| 509 | public readonly name: string, |
| 510 | public readonly presentationHint: string | undefined, |
| 511 | public readonly range: IRange, |
| 512 | private readonly index: number, |
| 513 | public readonly canRestart: boolean, |
| 514 | public readonly instructionPointerReference?: string |
| 515 | ) { } |
| 516 | |
| 517 | getId(): string { |
| 518 | return `stackframe:${this.thread.getId()}:${this.index}:${this.source.name}`; |
| 519 | } |
| 520 | |
| 521 | getScopes(): Promise<IScope[]> { |
| 522 | if (!this.scopes) { |
| 523 | this.scopes = this.thread.session.scopes(this.frameId, this.thread.threadId).then(response => { |
| 524 | if (!response || !response.body || !response.body.scopes) { |
| 525 | return []; |
| 526 | } |
| 527 | |
| 528 | const usedIds = new Set<number>(); |
| 529 | return response.body.scopes.map(rs => { |
| 530 | // form the id based on the name and location so that it's the |
| 531 | // same across multiple pauses to retain expansion state |
| 532 | let id = 0; |
| 533 | do { |
| 534 | id = stringHash(`${rs.name}:${rs.line}:${rs.column}`, id); |
| 535 | } while (usedIds.has(id)); |
| 536 | |
| 537 | usedIds.add(id); |
| 538 | return new Scope(this, id, rs.name, rs.variablesReference, rs.expensive, rs.namedVariables, rs.indexedVariables, |
| 539 | rs.line && rs.column && rs.endLine && rs.endColumn ? new Range(rs.line, rs.column, rs.endLine, rs.endColumn) : undefined); |
| 540 | |
| 541 | }); |
| 542 | }, err => [new ErrorScope(this, 0, err.message)]); |
| 543 | } |
| 544 | |
| 545 | return this.scopes; |
| 546 | } |
| 547 | |
| 548 | async getMostSpecificScopes(range: IRange): Promise<IScope[]> { |
| 549 | const scopes = await this.getScopes(); |
| 550 | const nonExpensiveScopes = scopes.filter(s => !s.expensive); |
| 551 | const haveRangeInfo = nonExpensiveScopes.some(s => !!s.range); |
| 552 | if (!haveRangeInfo) { |
| 553 | return nonExpensiveScopes; |
| 554 | } |
| 555 | |
| 556 | const scopesContainingRange = nonExpensiveScopes.filter(scope => scope.range && Range.containsRange(scope.range, range)) |
| 557 | .sort((first, second) => (first.range!.endLineNumber - first.range!.startLineNumber) - (second.range!.endLineNumber - second.range!.startLineNumber)); |
| 558 | return scopesContainingRange.length ? scopesContainingRange : nonExpensiveScopes; |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…