UNPKG

chrome-devtools-frontend

Version:
196 lines 5.37 kB
// Copyright 2025 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. /** * Small utility class to build scope and range trees. * * This class allows construction of scope/range trees that will be rejected by the encoder. * Use this class if you guarantee proper nesting yourself and don't want to pay for the * checks, otherwise use the `SafeScopeInfoBuilder`. * * This class will also silently ignore calls that would fail otherwise. E.g. calling * `end*` without a matching `start*`. */ export class ScopeInfoBuilder { #scopes = []; #ranges = []; #scopeStack = []; #rangeStack = []; #knownScopes = new Set(); #keyToScope = new Map(); #lastScope = null; addNullScope() { this.#scopes.push(null); return this; } startScope(line, column, options) { const scope = { start: { line, column }, end: { line, column }, variables: options?.variables?.slice(0) ?? [], children: [], isStackFrame: Boolean(options?.isStackFrame) }; if (options?.name !== undefined) scope.name = options.name; if (options?.kind !== undefined) scope.kind = options.kind; if (this.#scopeStack.length > 0) { scope.parent = this.#scopeStack.at(-1); } this.#scopeStack.push(scope); this.#knownScopes.add(scope); if (options?.key !== undefined) this.#keyToScope.set(options.key, scope); return this; } setScopeName(name) { const scope = this.#scopeStack.at(-1); if (scope) scope.name = name; return this; } setScopeKind(kind) { const scope = this.#scopeStack.at(-1); if (scope) scope.kind = kind; return this; } setScopeStackFrame(isStackFrame) { const scope = this.#scopeStack.at(-1); if (scope) scope.isStackFrame = isStackFrame; return this; } setScopeVariables(variables) { const scope = this.#scopeStack.at(-1); if (scope) scope.variables = variables.slice(0); return this; } endScope(line, column) { const scope = this.#scopeStack.pop(); if (!scope) return this; scope.end = { line, column }; if (this.#scopeStack.length === 0) { this.#scopes.push(scope); } else { this.#scopeStack.at(-1).children.push(scope); } this.#lastScope = scope; return this; } /** * @returns The OriginalScope opened with the most recent `startScope` call, but not yet closed. */ currentScope() { return this.#scopeStack.at(-1) ?? null; } /** * @returns The most recent OriginalScope closed with `endScope`. */ lastScope() { return this.#lastScope; } /** * @param option The definition 'scope' of this range can either be the "OriginalScope" directly * (produced by this builder) or the scope's key set while building the scope. */ startRange(line, column, options) { const range = { start: { line, column }, end: { line, column }, isStackFrame: Boolean(options?.isStackFrame), isHidden: Boolean(options?.isHidden), values: options?.values ?? [], children: [] }; if (this.#rangeStack.length > 0) { range.parent = this.#rangeStack.at(-1); } if (options?.scope !== undefined) { range.originalScope = options.scope; } else if (options?.scopeKey !== undefined) { range.originalScope = this.#keyToScope.get(options.scopeKey); } if (options?.callSite) { range.callSite = options.callSite; } this.#rangeStack.push(range); return this; } setRangeDefinitionScope(scope) { const range = this.#rangeStack.at(-1); if (range) range.originalScope = scope; return this; } setRangeDefinitionScopeKey(scopeKey) { const range = this.#rangeStack.at(-1); if (range) range.originalScope = this.#keyToScope.get(scopeKey); return this; } setRangeStackFrame(isStackFrame) { const range = this.#rangeStack.at(-1); if (range) range.isStackFrame = isStackFrame; return this; } setRangeHidden(isHidden) { const range = this.#rangeStack.at(-1); if (range) range.isHidden = isHidden; return this; } setRangeValues(values) { const range = this.#rangeStack.at(-1); if (range) range.values = values; return this; } setRangeCallSite(callSite) { const range = this.#rangeStack.at(-1); if (range) range.callSite = callSite; return this; } endRange(line, column) { const range = this.#rangeStack.pop(); if (!range) return this; range.end = { line, column }; if (this.#rangeStack.length === 0) { this.#ranges.push(range); } else { this.#rangeStack.at(-1).children.push(range); } return this; } build() { const info = { scopes: this.#scopes, ranges: this.#ranges }; this.#scopes = []; this.#ranges = []; this.#knownScopes.clear(); return info; } get scopeStack() { return this.#scopeStack; } get rangeStack() { return this.#rangeStack; } isKnownScope(scope) { return this.#knownScopes.has(scope); } isValidScopeKey(key) { return this.#keyToScope.has(key); } getScopeByValidKey(key) { return this.#keyToScope.get(key); } } //# sourceMappingURL=builder.js.map