UNPKG

chrome-devtools-frontend

Version:
251 lines (232 loc) • 8.49 kB
// Copyright 2023 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 type {Chrome} from '../../../extension-api/ExtensionAPI.js'; import type {Value, WasmInterface} from '../src/CustomFormatters.js'; import {WorkerPlugin} from '../src/DevToolsPluginHost.js'; import type {WasmValue} from '../src/WasmTypes.js'; import type {HostInterface} from '../src/WorkerRPC.js'; import type {Debugger} from './RealBackend.js'; export class TestHostInterface implements HostInterface { getWasmLinearMemory(_offset: number, _length: number, _stopId: unknown): ArrayBuffer { throw new Error('Method not implemented.'); } getWasmLocal(_local: number, _stopId: unknown): WasmValue { throw new Error('Method not implemented.'); } getWasmGlobal(_global: number, _stopId: unknown): WasmValue { throw new Error('Method not implemented.'); } getWasmOp(_op: number, _stopId: unknown): WasmValue { throw new Error('Method not implemented.'); } reportResourceLoad( _resourceUrl: string, _status: {success: boolean, errorMessage?: string|undefined, size?: number|undefined}): Promise<void> { return Promise.resolve(); } } export function makeURL(path: string): string { return new URL(path, document.baseURI).href; } export async function createWorkerPlugin(debug?: Debugger): Promise<Chrome.DevTools.LanguageExtensionPlugin> { return await WorkerPlugin.create([], true).then(p => { if (debug) { p.getWasmLinearMemory = debug.getWasmLinearMemory.bind(debug); p.getWasmLocal = debug.getWasmLocal.bind(debug); p.getWasmGlobal = debug.getWasmGlobal.bind(debug); p.getWasmOp = debug.getWasmOp.bind(debug); } /* eslint-disable-next-line no-debugger */ debugger; // Halt in the debugger to let developers set breakpoints in C++. return p; }); } export function relativePathname(url: URL, base: URL): string { const baseSplit = base.pathname.split('/'); const urlSplit = url.pathname.split('/'); let i = 0; for (; i < Math.min(baseSplit.length, urlSplit.length); ++i) { if (baseSplit[i] !== urlSplit[i]) { break; } } const result = new Array(baseSplit.length - i); result.fill('..'); result.push(...urlSplit.slice(i).filter(p => p.length > 0)); return result.join('/'); } export function nonNull<T>(value: T|null|undefined): T { assert.exists(value); return value; } export function remoteObject(value: Chrome.DevTools.RemoteObject|Chrome.DevTools.ForeignObject|null): Chrome.DevTools.RemoteObject { assert.exists(value); assert(value.type !== 'reftype'); return value; } export class TestWasmInterface implements WasmInterface { memory = new ArrayBuffer(0); locals = new Map<number, WasmValue>(); globals = new Map<number, WasmValue>(); stack = new Map<number, WasmValue>(); readMemory(offset: number, length: number): Uint8Array<ArrayBuffer> { return new Uint8Array(this.memory, offset, length); } getOp(op: number): WasmValue { const val = this.stack.get(op); if (val !== undefined) { return val; } throw new Error(`No stack entry ${op}`); } getLocal(local: number): WasmValue { const val = this.locals.get(local); if (val !== undefined) { return val; } throw new Error(`No local ${local}`); } getGlobal(global: number): WasmValue { const val = this.globals.get(global); if (val !== undefined) { return val; } throw new Error(`No global ${global}`); } } export class TestValue implements Value { private dataView: DataView<ArrayBuffer>; members: {[key: string]: TestValue, [key: number]: TestValue}; location: number; size: number; typeNames: string[]; static fromInt8(value: number, typeName = 'int8_t'): TestValue { const content = new DataView(new ArrayBuffer(1)); content.setInt8(0, value); return new TestValue(content, typeName); } static fromInt16(value: number, typeName = 'int16_t'): TestValue { const content = new DataView(new ArrayBuffer(2)); content.setInt16(0, value, true); return new TestValue(content, typeName); } static fromInt32(value: number, typeName = 'int32_t'): TestValue { const content = new DataView(new ArrayBuffer(4)); content.setInt32(0, value, true); return new TestValue(content, typeName); } static fromInt64(value: bigint, typeName = 'int64_t'): TestValue { const content = new DataView(new ArrayBuffer(8)); content.setBigInt64(0, value, true); return new TestValue(content, typeName); } static fromUint8(value: number, typeName = 'uint8_t'): TestValue { const content = new DataView(new ArrayBuffer(1)); content.setUint8(0, value); return new TestValue(content, typeName); } static fromUint16(value: number, typeName = 'uint16_t'): TestValue { const content = new DataView(new ArrayBuffer(2)); content.setUint16(0, value, true); return new TestValue(content, typeName); } static fromUint32(value: number, typeName = 'uint32_t'): TestValue { const content = new DataView(new ArrayBuffer(4)); content.setUint32(0, value, true); return new TestValue(content, typeName); } static fromUint64(value: bigint, typeName = 'uint64_t'): TestValue { const content = new DataView(new ArrayBuffer(8)); content.setBigUint64(0, value, true); return new TestValue(content, typeName); } static fromFloat32(value: number, typeName = 'float'): TestValue { const content = new DataView(new ArrayBuffer(4)); content.setFloat32(0, value, true); return new TestValue(content, typeName); } static fromFloat64(value: number, typeName = 'double'): TestValue { const content = new DataView(new ArrayBuffer(8)); content.setFloat64(0, value, true); return new TestValue(content, typeName); } static pointerTo(pointeeOrElements: TestValue|TestValue[], address?: number): TestValue { const content = new DataView(new ArrayBuffer(4)); const elements = Array.isArray(pointeeOrElements) ? pointeeOrElements : [pointeeOrElements]; address = address ?? elements[0].location; content.setUint32(0, address, true); const space = elements[0].typeNames[0].endsWith('*') ? '' : ' '; const members: Record<string|number, TestValue> = {'*': elements[0]}; for (let i = 0; i < elements.length; ++i) { members[i] = elements[i]; } const value = new TestValue(content, `${elements[0].typeNames[0]}${space}*`, members); return value; } static fromMembers(typeName: string, members: {[key: string]: TestValue, [key: number]: TestValue}): TestValue { return new TestValue(new DataView(new ArrayBuffer(0)), typeName, members); } asInt8(): number { return this.dataView.getInt8(0); } asInt16(): number { return this.dataView.getInt16(0, true); } asInt32(): number { return this.dataView.getInt32(0, true); } asInt64(): bigint { return this.dataView.getBigInt64(0, true); } asUint8(): number { return this.dataView.getUint8(0); } asUint16(): number { return this.dataView.getUint16(0, true); } asUint32(): number { return this.dataView.getUint32(0, true); } asUint64(): bigint { return this.dataView.getBigUint64(0, true); } asFloat32(): number { return this.dataView.getFloat32(0, true); } asFloat64(): number { return this.dataView.getFloat64(0, true); } asDataView(offset?: number, size?: number): DataView<ArrayBuffer> { offset = this.location + (offset ?? 0); size = Math.min(size ?? this.size, this.size - Math.max(0, offset)); return new DataView(this.dataView.buffer, offset, size); } getMembers(): string[] { return Object.keys(this.members); } $(member: string|number): Value { if (typeof member === 'number' || !member.includes('.')) { return this.members[member]; } let value: Value = this; for (const prop of member.split('.')) { value = value.$(prop); } return value; } constructor( content: DataView<ArrayBuffer>, typeName: string, members?: {[key: string]: TestValue, [key: number]: TestValue}) { this.location = 0; this.size = content.byteLength; this.typeNames = [typeName]; this.members = members || {}; this.dataView = content; } } declare global { /* eslint-disable-next-line @typescript-eslint/naming-convention */ let __karma__: unknown; }