Files
react-native-devtools-frontend/front_end/models/source_map_scopes/ScopeChainModel.ts
T
Simon Zünd 3dba79f655 [scopes] Resolve source mapped 'this' on the model layer
This moves the last of the "extra properties" to the model layer so
we can get rid of some code on the view layer.

R=pfaffe@chromium.org

Bug: 40277685
Change-Id: I486095e947b12d184f63b616a365bb307f50c5c9
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5720868
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
2024-07-19 18:59:14 +00:00

85 lines
3.1 KiB
TypeScript

// Copyright 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Common from '../../core/common/common.js';
import * as SDK from '../../core/sdk/sdk.js';
import {resolveScopeChain} from './NamesResolver.js';
/**
* This class is responsible for resolving / updating the scope chain for a specific {@link SDK.DebuggerModel.CallFrame}
* instance.
*
* There are several sources that can influence the scope view:
* - Debugger plugins can provide the whole scope info (e.g. from DWARF)
* - Source Maps can provide OR augment scope info
*
* Source maps can be enabled/disabled dynamically and debugger plugins can attach debug info after the fact.
*
* This class tracks all that and sends events with the latest scope chain for a specific call frame.
*/
export class ScopeChainModel extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
readonly #callFrame: SDK.DebuggerModel.CallFrame;
/** We use the `Throttler` here to make sure that `#boundUpdate` is not run multiple times simultanously */
readonly #throttler = new Common.Throttler.Throttler(5);
readonly #boundUpdate = this.#update.bind(this);
constructor(callFrame: SDK.DebuggerModel.CallFrame) {
super();
this.#callFrame = callFrame;
this.#callFrame.debuggerModel.addEventListener(
SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
this.#callFrame.debuggerModel.sourceMapManager().addEventListener(
SDK.SourceMapManager.Events.SourceMapAttached, this.#sourceMapAttached, this);
void this.#throttler.schedule(this.#boundUpdate);
}
dispose(): void {
this.#callFrame.debuggerModel.removeEventListener(
SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
this.#callFrame.debuggerModel.sourceMapManager().removeEventListener(
SDK.SourceMapManager.Events.SourceMapAttached, this.#sourceMapAttached, this);
this.listeners?.clear();
}
async #update(): Promise<void> {
const scopeChain = await resolveScopeChain(this.#callFrame);
this.dispatchEventToListeners(Events.ScopeChainUpdated, new ScopeChain(scopeChain));
}
#debugInfoAttached(event: Common.EventTarget.EventTargetEvent<SDK.Script.Script>): void {
if (event.data === this.#callFrame.script) {
void this.#throttler.schedule(this.#boundUpdate);
}
}
#sourceMapAttached(event: Common.EventTarget
.EventTargetEvent<{client: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap}>): void {
if (event.data.client === this.#callFrame.script) {
void this.#throttler.schedule(this.#boundUpdate);
}
}
}
export const enum Events {
ScopeChainUpdated = 'ScopeChainUpdated',
}
export type EventTypes = {
[Events.ScopeChainUpdated]: ScopeChain,
};
/**
* A scope chain ready to be shown in the UI with debugging info applied.
*/
export class ScopeChain {
readonly scopeChain: SDK.DebuggerModel.ScopeChainEntry[];
constructor(scopeChain: SDK.DebuggerModel.ScopeChainEntry[]) {
this.scopeChain = scopeChain;
}
}