| // Copyright 2024 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import * as Platform from '../../core/platform/platform.js'; | |
| import {ContentData} from './ContentData.js'; | |
| import type {DeferredContent} from './ContentProvider.js'; | |
| interface FunctionBodyOffset { | |
| start: number; | |
| end: number; | |
| } | |
| /** | |
| * Metadata to map between bytecode #offsets and line numbers in the | |
| * disassembly for WebAssembly modules. | |
| */ | |
| export class WasmDisassembly extends ContentData { | |
| readonly lines: string[]; | |
| readonly #offsets: number[]; | |
| #functionBodyOffsets: FunctionBodyOffset[]; | |
| // Wasm can be potentially very large, so we calculate `text' lazily. | |
| #cachedText?: string; | |
| constructor(lines: string[], offsets: number[], functionBodyOffsets: FunctionBodyOffset[]) { | |
| super('', /* isBase64 */ false, 'text/x-wast', 'utf-8'); | |
| if (lines.length !== offsets.length) { | |
| throw new Error('Lines and offsets don\'t match'); | |
| } | |
| this.lines = lines; | |
| this.#offsets = offsets; | |
| this.#functionBodyOffsets = functionBodyOffsets; | |
| } | |
| override get text(): string { | |
| if (typeof this.#cachedText === 'undefined') { | |
| this.#cachedText = this.lines.join('\n'); | |
| } | |
| return this.#cachedText; | |
| } | |
| override get isEmpty(): boolean { | |
| // Don't trigger unnecessary concatenating. Only check whether we have no lines, or a single empty line. | |
| return this.lines.length === 0 || (this.lines.length === 1 && this.lines[0].length === 0); | |
| } | |
| get lineNumbers(): number { | |
| return this.#offsets.length; | |
| } | |
| bytecodeOffsetToLineNumber(bytecodeOffset: number): number { | |
| return Platform.ArrayUtilities.upperBound(this.#offsets, bytecodeOffset, | |
| Platform.ArrayUtilities.DEFAULT_COMPARATOR) - | |
| 1; | |
| } | |
| lineNumberToBytecodeOffset(lineNumber: number): number { | |
| return this.#offsets[lineNumber]; | |
| } | |
| /** | |
| * returns an iterable enumerating all the non-breakable line numbers in the disassembly | |
| */ | |
| * nonBreakableLineNumbers(): Iterable<number> { | |
| let lineNumber = 0; | |
| let functionIndex = 0; | |
| while (lineNumber < this.lineNumbers) { | |
| if (functionIndex < this.#functionBodyOffsets.length) { | |
| const offset = this.lineNumberToBytecodeOffset(lineNumber); | |
| if (offset >= this.#functionBodyOffsets[functionIndex].start) { | |
| lineNumber = this.bytecodeOffsetToLineNumber(this.#functionBodyOffsets[functionIndex++].end) + 1; | |
| continue; | |
| } | |
| } | |
| yield lineNumber++; | |
| } | |
| } | |
| /** | |
| * @deprecated Used during migration from `DeferredContent` to `ContentData`. | |
| */ | |
| override asDeferedContent(): DeferredContent { | |
| return {content: '', isEncoded: false, wasmDisassemblyInfo: this}; | |
| } | |
| } |