Rework assert* functions
diff --git a/ui/src/base/assert.ts b/ui/src/base/assert.ts index 3ce1523..d0632a0 100644 --- a/ui/src/base/assert.ts +++ b/ui/src/base/assert.ts
@@ -12,68 +12,83 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Assertion utilities for runtime validation and TypeScript type narrowing. +// Assertion and check utilities for runtime validation and TypeScript type +// narrowing. // -// These functions provide fail-fast semantics: if an assertion fails, an -// exception is thrown immediately. This makes bugs easier to catch and debug -// by surfacing issues at the point of failure rather than propagating invalid -// state. +// Two flavors: +// - assert*() — void. Narrows the type of the input variable in place for +// subsequent code. Use as a standalone statement. +// - checkExists() — returns the narrowed value. Use in expressions, +// assignments, and function arguments. // -// In addition to runtime checks, these assertions help TypeScript narrow types. -// For example, after calling assertExists(x), TypeScript knows x is non-null. +// All functions throw immediately on failure (fail-fast). -export function assertExists<A>( - value: A | null | undefined, - optMsg?: string, -): A { +// Throws if |value| is null or undefined. Returns the value with null and +// undefined stripped from the type, for use in expressions. +export function checkExists<T>(value: T, msg?: string): NonNullable<T> { if (value === null || value === undefined) { - throw new Error(optMsg ?? 'Value is null or undefined'); + throw new Error(msg ?? 'Value is null or undefined'); } return value; } -// assertExists trips over NULLs, but in many contexts NULL is a valid SQL value -// we have to work with. -export function assertDefined<T>(value: T | undefined, optMsg?: string): T { - if (value === undefined) { - throw new Error(optMsg ?? 'Value is undefined'); +// Throws if |value| is null or undefined. Narrows the type of |value| to +// exclude null and undefined for all subsequent code. +export function assertExists<T>( + value: T, + msg?: string, +): asserts value is NonNullable<T> { + if (value === null || value === undefined) { + throw new Error(msg ?? 'Value is null or undefined'); } - return value; } -// Asserts that the value is an instance of the given class. Returns the value -// with a narrowed type if the assertion passes, otherwise throws an error. -export function assertIsInstance<T>( +// Throws if |value| is undefined. Narrows the type of |value| to exclude +// undefined for all subsequent code. Unlike assertExists(), this permits null. +export function assertDefined<T>( + value: T | undefined, + msg?: string, +): asserts value is T { + if (value === undefined) throw new Error(msg ?? 'Value is undefined'); +} + +// Throws if |value| is not an instance of |cls|. Narrows the type of |value| +// to |T| for all subsequent code. +export function assertInstanceOf<T>( value: unknown, - clazz: abstract new (...args: never[]) => T, - optMsg?: string, -): T { - assertTrue( - value instanceof clazz, - optMsg ?? `Value is not an instance of ${clazz.name}`, - ); - return value as T; -} - -export function assertTrue(value: boolean, optMsg?: string) { - if (!value) { - throw new Error(optMsg ?? 'Failed assertion'); + cls: abstract new (...args: never[]) => T, + msg?: string, +): asserts value is T { + if (!(value instanceof cls)) { + throw new Error(msg ?? `Value is not instance of '${cls.name}'`); } } -export function assertFalse(value: boolean, optMsg?: string) { - assertTrue(!value, optMsg); +export function checkInstanceOf<T>( + value: unknown, + cls: abstract new (...args: never[]) => T, + msg?: string, +): T { + if (!(value instanceof cls)) { + throw new Error(msg ?? `Value is not instance of '${cls.name}'`); + } + return value; } -// This function serves two purposes. -// 1) A runtime check - if we are ever called, we throw an exception. -// This is useful for checking that code we suspect should never be reached is -// actually never reached. -// 2) A compile time check where typescript asserts that the value passed can be -// cast to the "never" type. -// This is useful for ensuring we exhaustively check union types. -export function assertUnreachable(value: never, optMsg?: string): never { +// Throws if |value| is not truthy. +export function assertTrue(value: boolean, msg?: string) { + if (!value) throw new Error(msg ?? 'Value is not truthy'); +} + +// Throws if |value| is truthy. +export function assertFalse(value: boolean, msg?: string) { + if (value) throw new Error(msg ?? 'Value is not falsy'); +} + +// Throws unconditionally at runtime. At compile time, requires |value| to be +// of type 'never', ensuring exhaustive checks of union types and enums. +export function assertUnreachable(value: never, msg?: string): never { throw new Error( - optMsg ?? `This code should not be reachable ${value as unknown}`, + msg ?? `This code should not be reachable ${value as unknown}`, ); }
diff --git a/ui/src/base/assert_unittest.ts b/ui/src/base/assert_unittest.ts new file mode 100644 index 0000000..1142608 --- /dev/null +++ b/ui/src/base/assert_unittest.ts
@@ -0,0 +1,199 @@ +// Copyright (C) 2026 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + assertDefined, + assertFalse, + assertInstanceOf, + assertExists, + assertTrue, + assertUnreachable, + checkExists, +} from './assert'; + +class Cls {} +class SubCls extends Cls {} +class Unrelated {} + +function takesString(x: string) { + void x; +} + +function takesNull(x: null) { + void x; +} + +function takesCls(x: Cls) { + void x; +} + +describe('ensureNonNullish', () => { + test('non-nullish', () => { + expect(() => { + const x = 'foo' as string | null | undefined; + takesString(checkExists(x)); + }).not.toThrow(); + }); + test('null', () => { + expect(() => { + const x = null as string | null | undefined; + takesString(checkExists(x)); // BOOM + }).toThrow(); + }); + test('undefined', () => { + expect(() => { + const x = undefined as string | null | undefined; + takesString(checkExists(x)); // BOOM + }).toThrow(); + }); + test('falsy', () => { + expect(() => { + checkExists(false); + checkExists(0); + checkExists(''); + checkExists(0n); + }).not.toThrow(); + }); +}); + +describe('assertNonNullish', () => { + test('string', () => { + expect(() => { + const x = 'foo' as string | null | undefined; + assertExists(x); + takesString(x); + }).not.toThrow(); + }); + test('null', () => { + expect(() => { + const x = null as string | null | undefined; + assertExists(x); // BOOM + takesString(x); + }).toThrow(); + }); + test('undefined', () => { + expect(() => { + const x = undefined as string | null | undefined; + assertExists(x); // BOOM + takesString(x); + }).toThrow(); + }); + test('falsy', () => { + expect(() => { + assertExists(false); + assertExists(0); + assertExists(''); + assertExists(0n); + }).not.toThrow(); + }); +}); + +describe('assertDefined', () => { + test('defined', () => { + expect(() => { + const x = 'foo' as string | undefined; + assertDefined(x); + takesString(x); + }).not.toThrow(); + }); + test('null', () => { + expect(() => { + const x = null as null | undefined; + assertDefined(x); + takesNull(x); + }).not.toThrow(); + }); + test('undefined', () => { + expect(() => { + const x = undefined as string | undefined; + assertDefined(x); // BOOM + takesString(x); + }).toThrow(); + }); + test('falsy', () => { + expect(() => { + assertDefined(false); + assertDefined(0); + assertDefined(''); + assertDefined(0n); + }).not.toThrow(); + }); +}); + +describe('assertInstanceOf', () => { + test('is instance', () => { + expect(() => { + const x = new Cls() as unknown; + assertInstanceOf(x, Cls); + takesCls(x); + }).not.toThrow(); + }); + test('is subclass', () => { + expect(() => { + const x = new SubCls() as unknown; + assertInstanceOf(x, Cls); + takesCls(x); + }).not.toThrow(); + }); + test('is not instance', () => { + expect(() => { + const x = new Unrelated() as unknown; + assertInstanceOf(x, Cls); + takesCls(x); + }).toThrow(); + }); +}); + +describe('assertTrue', () => { + test('true', () => { + expect(() => { + assertTrue(true); + }).not.toThrow(); + }); + test('false', () => { + expect(() => { + assertTrue(false); + }).toThrow(); + }); +}); + +describe('assertFalse', () => { + test('false', () => { + expect(() => { + assertFalse(false); + }).not.toThrow(); + }); + test('true', () => { + expect(() => { + assertFalse(true); + }).toThrow(); + }); +}); + +describe('assertUnreachable', () => { + test('', () => { + expect(() => { + assertUnreachable(null as never); + }).toThrow(); + }); + test('', () => { + expect(() => { + const x = 'foo' as const; + if (x === 'foo') { + return; + } + assertUnreachable(x); + }).not.toThrow(); + }); +});
diff --git a/ui/src/base/dom_utils.ts b/ui/src/base/dom_utils.ts index f418b1f..47dfcd5 100644 --- a/ui/src/base/dom_utils.ts +++ b/ui/src/base/dom_utils.ts
@@ -67,15 +67,6 @@ } } -// Safely cast an Element to an HTMLElement. -// Throws if the element is not an HTMLElement. -export function toHTMLElement(el: Element): HTMLElement { - if (!(el instanceof HTMLElement)) { - throw new Error('Element is not an HTMLElement'); - } - return el as HTMLElement; -} - // Return true if EventTarget is or is inside an editable element. // Editable elements incluce: <input type="text">, <textarea>, or elements with // the |contenteditable| attribute set.
diff --git a/ui/src/base/dom_utils_unittest.ts b/ui/src/base/dom_utils_unittest.ts index 8b91047..d2f8bc5 100644 --- a/ui/src/base/dom_utils_unittest.ts +++ b/ui/src/base/dom_utils_unittest.ts
@@ -17,7 +17,6 @@ elementIsEditable, findRef, isOrContains, - toHTMLElement, } from './dom_utils'; describe('isOrContains', () => { @@ -65,21 +64,6 @@ }); }); -describe('toHTMLElement', () => { - it('should convert a div to an HTMLElement', () => { - const divElement: Element = document.createElement('div'); - expect(toHTMLElement(divElement)).toEqual(divElement); - }); - - it('should fail to convert an svg element to an HTMLElement', () => { - const svgElement = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'svg', - ); - expect(() => toHTMLElement(svgElement)).toThrow(Error); - }); -}); - describe('elementIsEditable', () => { test('text input', () => { const el = document.createElement('input');
diff --git a/ui/src/base/object_utils.ts b/ui/src/base/object_utils.ts index a404475..5745b2c 100644 --- a/ui/src/base/object_utils.ts +++ b/ui/src/base/object_utils.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists} from './assert'; +import {checkExists} from './assert'; import {exists} from './utils'; export type PathKey = string | number; @@ -71,7 +71,7 @@ const pathClone = [...path]; let o = obj; while (pathClone.length > 1) { - const p = assertExists(pathClone.shift()); + const p = checkExists(pathClone.shift()); o = o[p]; }
diff --git a/ui/src/components/details/thread_slice_details_tab.ts b/ui/src/components/details/thread_slice_details_tab.ts index 2838524..c471ee4 100644 --- a/ui/src/components/details/thread_slice_details_tab.ts +++ b/ui/src/components/details/thread_slice_details_tab.ts
@@ -36,7 +36,7 @@ import {asSliceSqlId} from '../sql_utils/core_types'; import {DurationWidget} from '../widgets/duration'; import {Grid, GridCell, GridHeaderCell} from '../../widgets/grid'; -import {assertIsInstance} from '../../base/assert'; +import {assertInstanceOf} from '../../base/assert'; import {Trace} from '../../public/trace'; import {TrackEventDetailsPanel} from '../../public/details_panel'; import {TrackEventSelection} from '../../public/selection'; @@ -213,10 +213,11 @@ private readonly attrs: ThreadSliceDetailsPanelAttrs; constructor(trace: Trace, attrs?: ThreadSliceDetailsPanelAttrs) { - // Rationale for the assertIsInstance: ThreadSliceDetailsPanel requires a + // Rationale for the assertInstanceOf: ThreadSliceDetailsPanel requires a // TraceImpl (because of flows) but here we must take a Trace interface, // because this track is exposed to plugins (which see only Trace). - this.trace = assertIsInstance(trace, TraceImpl); + assertInstanceOf(trace, TraceImpl); + this.trace = trace; this.attrs = attrs ?? {}; }
diff --git a/ui/src/components/query_flamegraph.ts b/ui/src/components/query_flamegraph.ts index 05f3a3c..2ff6d4f 100644 --- a/ui/src/components/query_flamegraph.ts +++ b/ui/src/components/query_flamegraph.ts
@@ -233,9 +233,8 @@ metrics: ReadonlyArray<QueryFlamegraphMetric>, state: FlamegraphState, ) { - const metric = assertExists( - metrics.find((x) => state.selectedMetricName === x.name), - ); + const metric = metrics.find((x) => state.selectedMetricName === x.name); + assertExists(metric); const engine = this.trace.engine; this.queryLimiter.schedule(async () => { this.data = undefined;
diff --git a/ui/src/components/tracks/slice_track.ts b/ui/src/components/tracks/slice_track.ts index 7b49ea4..97ff1de 100644 --- a/ui/src/components/tracks/slice_track.ts +++ b/ui/src/components/tracks/slice_track.ts
@@ -1218,7 +1218,8 @@ this.trace.timeline.highlightedSliceId = this.hoveredSlice?.id; if (this.hoveredSlice === undefined) { if (this.attrs.onSliceOut) { - this.attrs.onSliceOut({slice: assertExists(prevHoveredSlice)}); + assertExists(prevHoveredSlice); + this.attrs.onSliceOut({slice: prevHoveredSlice}); } } else { if (this.attrs.onSliceOver) {
diff --git a/ui/src/components/widgets/charts/echart_view.ts b/ui/src/components/widgets/charts/echart_view.ts index 48aae91..71b1eb7 100644 --- a/ui/src/components/widgets/charts/echart_view.ts +++ b/ui/src/components/widgets/charts/echart_view.ts
@@ -49,7 +49,7 @@ } from 'echarts/components'; import {CanvasRenderer} from 'echarts/renderers'; import type {EChartsType} from 'echarts/core'; -import {assertExists, assertIsInstance} from '../../../base/assert'; +import {assertInstanceOf} from '../../../base/assert'; import {classNames} from '../../../base/classnames'; import {SimpleResizeObserver} from '../../../base/resize_observer'; import {Spinner} from '../../../widgets/spinner'; @@ -283,10 +283,8 @@ private initChart(attrs: EChartViewAttrs, dom: Element): void { if (attrs.option === undefined) return; - const container = assertIsInstance( - assertExists(dom.querySelector('.pf-echart-view__canvas')), - HTMLElement, - ); + const container = dom.querySelector('.pf-echart-view__canvas'); + assertInstanceOf(container, HTMLElement); // Read theme colors and register/update the ECharts theme const colors = getChartThemeColors(container);
diff --git a/ui/src/components/widgets/sql/pivot_table/pivot_table_state.ts b/ui/src/components/widgets/sql/pivot_table/pivot_table_state.ts index fa104a9..adc7298 100644 --- a/ui/src/components/widgets/sql/pivot_table/pivot_table_state.ts +++ b/ui/src/components/widgets/sql/pivot_table/pivot_table_state.ts
@@ -341,7 +341,9 @@ for (const it = res.iter({}); it.valid(); it.next()) { const row: Row = {}; for (const column of res.columns()) { - row[assertExists(aliasToIds.get(column))] = it.get(column); + const index = aliasToIds.get(column); + assertExists(index); + row[index] = it.get(column); } rows.push(row); }
diff --git a/ui/src/components/widgets/sql/pivot_table/pivot_tree_node.ts b/ui/src/components/widgets/sql/pivot_table/pivot_tree_node.ts index 1d1715e..15c1735 100644 --- a/ui/src/components/widgets/sql/pivot_table/pivot_tree_node.ts +++ b/ui/src/components/widgets/sql/pivot_table/pivot_tree_node.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists, assertDefined, assertTrue} from '../../../../base/assert'; +import {assertTrue, assertExists, assertDefined} from '../../../../base/assert'; import {Row, SqlValue} from '../../../../trace_processor/query_result'; import {Filter, StandardFilters} from '../table/filters'; import {TableColumn} from '../table/table_column'; @@ -127,7 +127,8 @@ getPivotValue(index: number): SqlValue | undefined { if (index > this.getPivotIndex()) return undefined; if (index === this.getPivotIndex()) return this.pivotValue; - return assertExists(this.parent).getPivotValue(index); + assertExists(this.parent); + return this.parent.getPivotValue(index); } /** @@ -168,7 +169,9 @@ let valueNode: PivotTreeNode = this; let autoExpanded = true; for (let i = pivotIndex; i < this.getPivotIndex(); i++) { - valueNode = assertExists(valueNode.parent); + const parent = valueNode.parent; + assertExists(parent); + valueNode = parent; autoExpanded = autoExpanded && valueNode.children.size === 1; } return autoExpanded ? 'auto_expanded' : 'pivoted_value'; @@ -207,7 +210,8 @@ ); this.children.clear(); for (const child of sorted) { - this.children.set(assertDefined(child.pivotValue), child); + assertDefined(child.pivotValue); + this.children.set(child.pivotValue, child); } } @@ -239,9 +243,10 @@ } private getFilter(): Filter { + assertDefined(this.pivotValue); return StandardFilters.valueEquals( this.config.pivots[this.getPivotIndex()].column, - assertDefined(this.pivotValue), + this.pivotValue, ); } @@ -263,7 +268,9 @@ }), ); } - return assertExists(this.children.get(value)); + const child = this.children.get(value); + assertExists(child); + return child; } private update() { @@ -325,10 +332,9 @@ assertTrue(index !== -1); // For pivot sorting, we only compare the pivot values at the given depth. if (index + 1 === lhs.depth) { - const cmp = compareSqlValues( - assertDefined(lhs.pivotValue), - assertDefined(rhs.pivotValue), - ); + assertDefined(lhs.pivotValue); + assertDefined(rhs.pivotValue); + const cmp = compareSqlValues(lhs.pivotValue, rhs.pivotValue); if (cmp !== 0) return direction === 'ASC' ? cmp : -cmp; } }
diff --git a/ui/src/core/app_impl.ts b/ui/src/core/app_impl.ts index d29f546..1567b7f 100644 --- a/ui/src/core/app_impl.ts +++ b/ui/src/core/app_impl.ts
@@ -14,7 +14,7 @@ import {AsyncLimiter} from '../base/async_limiter'; import {defer} from '../base/deferred'; -import {assertExists, assertTrue} from '../base/assert'; +import {assertTrue, checkExists} from '../base/assert'; import {ServiceWorkerController} from '../frontend/service_worker_controller'; import {App} from '../public/app'; import {SqlPackage} from '../public/extra_sql_packages'; @@ -122,7 +122,7 @@ // Singleton. private static _instance: AppImpl; static get instance(): AppImpl { - return assertExists(AppImpl._instance); + return checkExists(AppImpl._instance); } readonly timestampFormat: Setting<TimestampFormat>;
diff --git a/ui/src/core/load_trace.ts b/ui/src/core/load_trace.ts index 0c9258a..12fb061 100644 --- a/ui/src/core/load_trace.ts +++ b/ui/src/core/load_trace.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists, assertTrue} from '../base/assert'; +import {assertTrue, checkExists} from '../base/assert'; import {time, Time, TimeSpan} from '../base/time'; import {cacheTrace} from './cache_manager'; import { @@ -169,7 +169,7 @@ engine.onResponseReceived = () => raf.scheduleFullRedraw(); if (isMetatracingEnabled()) { - engine.enableMetatrace(assertExists(getEnabledMetatracingCategories())); + engine.enableMetatrace(checkExists(getEnabledMetatracingCategories())); } return engine; } @@ -497,7 +497,7 @@ // The max() is so the query returns NULL if the tz info doesn't exist. const queryTz = `select max(int_value) as tzOffMin from metadata where name = 'timezone_off_mins'`; - const resTz = await assertExists(engine).query(queryTz); + const resTz = await engine.query(queryTz); const tzOffMin = resTz.firstRow({tzOffMin: NUM_NULL}).tzOffMin ?? 0; // This is the offset between the unix epoch and ts in the ts domain.
diff --git a/ui/src/core/page_manager.ts b/ui/src/core/page_manager.ts index 3b2ab95..6a510b8 100644 --- a/ui/src/core/page_manager.ts +++ b/ui/src/core/page_manager.ts
@@ -13,7 +13,7 @@ // limitations under the License. import m from 'mithril'; -import {assertExists, assertTrue} from '../base/assert'; +import {assertTrue, assertExists} from '../base/assert'; import {Registry} from '../base/registry'; import {PageHandler, PageManager} from '../public/page'; import {Analytics} from '../public/analytics'; @@ -67,7 +67,8 @@ // If either the route doesn't exist or requires a trace but the trace // is not loaded, fall back on the default route. const renderedPage = - maybeRenderedPage ?? assertExists(this.renderPageForRoute('/', '')); + maybeRenderedPage ?? this.renderPageForRoute('/', ''); + assertExists(renderedPage); return [key, renderedPage]; }) .map(([key, page]) => {
diff --git a/ui/src/core/plugin_manager.ts b/ui/src/core/plugin_manager.ts index 0c957a0..2963631 100644 --- a/ui/src/core/plugin_manager.ts +++ b/ui/src/core/plugin_manager.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists} from '../base/assert'; +import {checkExists} from '../base/assert'; import {Registry} from '../base/registry'; import {PerfettoPlugin, PerfettoPluginStatic} from '../public/plugin'; import {Trace} from '../public/trace'; @@ -157,7 +157,7 @@ pluginDescriptor: PerfettoPluginStatic<T>, ): T { const plugin = this.registry.get(pluginDescriptor.id); - return assertExists(plugin.traceContext).instance as T; + return checkExists(plugin.traceContext).instance as T; } isCorePlugin(pluginId: string): boolean {
diff --git a/ui/src/core/trace_stream.ts b/ui/src/core/trace_stream.ts index 274322a..a94028a 100644 --- a/ui/src/core/trace_stream.ts +++ b/ui/src/core/trace_stream.ts
@@ -13,7 +13,7 @@ // limitations under the License. import {defer, Deferred} from '../base/deferred'; -import {assertExists, assertTrue} from '../base/assert'; +import {checkExists, assertTrue} from '../base/assert'; import {exists} from '../base/utils'; import {TraceChunk, TraceStream} from '../public/stream'; @@ -44,13 +44,13 @@ } private onLoad() { - const pendingRead = assertExists(this.pendingRead); + const pendingRead = checkExists(this.pendingRead); this.pendingRead = undefined; if (this.reader.error) { pendingRead.reject(this.reader.error); return; } - const res = assertExists(this.reader.result) as ArrayBuffer; + const res = checkExists(this.reader.result) as ArrayBuffer; this.bytesRead += res.byteLength; pendingRead.resolve({ data: new Uint8Array(res),
diff --git a/ui/src/core/track_manager_unittest.ts b/ui/src/core/track_manager_unittest.ts index fc8106a..c854f29 100644 --- a/ui/src/core/track_manager_unittest.ts +++ b/ui/src/core/track_manager_unittest.ts
@@ -99,7 +99,8 @@ describe('TrackManager', () => { it('calls render on the track', () => { - const entry = assertExists(trackManager.getWrappedTrack(td.uri)); + const entry = trackManager.getWrappedTrack(td.uri); + assertExists(entry); entry.render(dummyCtx); expect(mockTrack.render).toHaveBeenCalledTimes(1); @@ -107,10 +108,12 @@ }); it('reuses tracks across render cycles', () => { - const first = assertExists(trackManager.getWrappedTrack(td.uri)); + const first = trackManager.getWrappedTrack(td.uri); + assertExists(first); first.render(dummyCtx); - const second = assertExists(trackManager.getWrappedTrack(td.uri)); + const second = trackManager.getWrappedTrack(td.uri); + assertExists(second); second.render(dummyCtx); expect(first).toBe(second); @@ -118,7 +121,8 @@ }); it('contains crash inside render()', () => { - const entry = assertExists(trackManager.getWrappedTrack(td.uri)); + const entry = trackManager.getWrappedTrack(td.uri); + assertExists(entry); const e = new Error('test error'); // Mock crash inside render @@ -133,7 +137,8 @@ }); it('does not call render after crash', () => { - const entry = assertExists(trackManager.getWrappedTrack(td.uri)); + const entry = trackManager.getWrappedTrack(td.uri); + assertExists(entry); const e = new Error('test error'); // Mock crash inside render @@ -150,12 +155,14 @@ }); it('exposes the track renderer', () => { - const entry = assertExists(trackManager.getWrappedTrack(td.uri)); + const entry = trackManager.getWrappedTrack(td.uri); + assertExists(entry); expect(entry.track).toBe(mockTrack); }); it('exposes the track descriptor', () => { - const entry = assertExists(trackManager.getWrappedTrack(td.uri)); + const entry = trackManager.getWrappedTrack(td.uri); + assertExists(entry); expect(entry.desc).toBe(td); }); });
diff --git a/ui/src/core_plugins/dev.perfetto.CoreCommands/index.ts b/ui/src/core_plugins/dev.perfetto.CoreCommands/index.ts index 33b01c6..00acaf7 100644 --- a/ui/src/core_plugins/dev.perfetto.CoreCommands/index.ts +++ b/ui/src/core_plugins/dev.perfetto.CoreCommands/index.ts
@@ -43,7 +43,7 @@ import {getTimeSpanOfSelectionOrVisibleWindow} from '../../public/utils'; import {Workspace} from '../../public/workspace'; import {showModal} from '../../widgets/modal'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import {Setting} from '../../public/settings'; import {toggleHelp} from '../../frontend/help_modal'; @@ -288,7 +288,7 @@ const app = AppImpl.instance; // Rgister macros from settings first. - const settingMacros = assertExists(CoreCommands.macrosSetting).get(); + const settingMacros = checkExists(CoreCommands.macrosSetting).get(); for (const macro of settingMacros) { ctx.commands.registerMacro(macro); }
diff --git a/ui/src/core_plugins/dev.perfetto.MultiTraceOpen/multi_trace_controller_unittest.ts b/ui/src/core_plugins/dev.perfetto.MultiTraceOpen/multi_trace_controller_unittest.ts index ccae870..af7eb97 100644 --- a/ui/src/core_plugins/dev.perfetto.MultiTraceOpen/multi_trace_controller_unittest.ts +++ b/ui/src/core_plugins/dev.perfetto.MultiTraceOpen/multi_trace_controller_unittest.ts
@@ -79,7 +79,9 @@ onProgress(1.0); if (this.errors.has(file.name)) { - throw new Error(assertExists(this.errors.get(file.name)?.message)); + const message = this.errors.get(file.name)?.message; + assertExists(message); + throw new Error(message); } const result = this.results.get(file.name); if (result) {
diff --git a/ui/src/engine/wasm_bridge.ts b/ui/src/engine/wasm_bridge.ts index ca1e4a7..e1e41a3 100644 --- a/ui/src/engine/wasm_bridge.ts +++ b/ui/src/engine/wasm_bridge.ts
@@ -13,7 +13,7 @@ // limitations under the License. import {defer} from '../base/deferred'; -import {assertExists, assertTrue} from '../base/assert'; +import {assertTrue, assertExists} from '../base/assert'; // The 64-bit variant of TraceProcessor wasm is always built in all build // configurations and we can depend on it from typescript. @@ -123,9 +123,11 @@ // This function is bound and passed to Initialize and is called by the C++ // code while in the ccall(trace_processor_on_rpc_request). private onReply(heapPtrArg: bigint | number, size: number) { + assertExists(this.messagePort); + const heapPtr = this.wasmPtrCast(heapPtrArg); const data = this.connection.HEAPU8.slice(heapPtr, heapPtr + size); - assertExists(this.messagePort).postMessage(data, [data.buffer]); + this.messagePort.postMessage(data, [data.buffer]); } private appendAndLogErr(line: string) {
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts index 8cf7a49..3a2637d 100644 --- a/ui/src/frontend/help_modal.ts +++ b/ui/src/frontend/help_modal.ts
@@ -13,7 +13,6 @@ // limitations under the License. import m from 'mithril'; -import {assertExists} from '../base/assert'; import {AppImpl} from '../core/app_impl'; import {HotkeyGlyphs, Keycap} from '../widgets/hotkey_glyphs'; import {showModal} from '../widgets/modal'; @@ -24,6 +23,8 @@ NotSupportedError, } from '../base/keyboard_layout_map'; import {KeyMapping} from './timeline_page/wasd_navigation_handler'; +import {exists} from '../base/utils'; +import {Command} from '../public/command'; export function toggleHelp() { AppImpl.instance.analytics.logEvent('User Actions', 'Show help'); @@ -158,7 +159,9 @@ m( 'table', AppImpl.instance.commands.commands - .filter(({defaultHotkey}) => defaultHotkey) + .filter((command): command is Required<Command> => + exists(command.defaultHotkey), + ) .sort((a, b) => a.name.localeCompare(b.name)) .map(({defaultHotkey, name}) => { return m( @@ -167,7 +170,7 @@ 'td', m(HotkeyGlyphs, { spacing: 'large', - hotkey: assertExists(defaultHotkey), + hotkey: defaultHotkey, }), ), m('td', name),
diff --git a/ui/src/frontend/omnibox.ts b/ui/src/frontend/omnibox.ts index 2a70d88..50650ce 100644 --- a/ui/src/frontend/omnibox.ts +++ b/ui/src/frontend/omnibox.ts
@@ -16,7 +16,7 @@ import {classNames} from '../base/classnames'; import {findRef} from '../base/dom_utils'; import {FuzzyFinder, FuzzySegment} from '../base/fuzzy'; -import {assertExists, assertUnreachable} from '../base/assert'; +import {assertExists, assertUnreachable, checkExists} from '../base/assert'; import {isString} from '../base/object_utils'; import {exists} from '../base/utils'; import {AppImpl} from '../core/app_impl'; @@ -74,7 +74,8 @@ private renderPromptOmnibox(): m.Children { const omnibox = AppImpl.instance.omnibox; - const prompt = assertExists(omnibox.pendingPrompt); + const prompt = omnibox.pendingPrompt; + assertExists(prompt); let options: OmniboxOption[] | undefined = undefined; @@ -192,7 +193,7 @@ private renderRegisteredMode(): m.Children { const omnibox = AppImpl.instance.omnibox; - const desc = assertExists(omnibox.activeRegisteredMode); + const desc = checkExists(omnibox.activeRegisteredMode); return m(OmniboxWidget, { value: omnibox.text, placeholder: desc.placeholder,
diff --git a/ui/src/frontend/rpc_http_dialog.ts b/ui/src/frontend/rpc_http_dialog.ts index 1235d45..573dc2c 100644 --- a/ui/src/frontend/rpc_http_dialog.ts +++ b/ui/src/frontend/rpc_http_dialog.ts
@@ -14,7 +14,7 @@ import m from 'mithril'; import protos from '../protos'; -import {assertExists} from '../base/assert'; +import {checkExists, assertUnreachable} from '../base/assert'; import {VERSION} from '../gen/perfetto_version'; import {HttpRpcEngine} from '../trace_processor/http_rpc_engine'; import {showModal} from '../widgets/modal'; @@ -155,7 +155,8 @@ // No RPC = exit immediately to the WASM UI. return; } - const tpStatus = assertExists(state.status); + + const tpStatus = checkExists(state.status); function forceWasm() { AppImpl.instance.httpRpc.newEngineMode = 'FORCE_BUILTIN_WASM'; @@ -179,8 +180,7 @@ forceWasm(); return; default: - const x: never = result; - throw new Error(`Unsupported result ${x}`); + assertUnreachable(result); } } }
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts index d1c73a6..5785f9f 100644 --- a/ui/src/frontend/sidebar.ts +++ b/ui/src/frontend/sidebar.ts
@@ -44,7 +44,7 @@ import {classNames} from '../base/classnames'; import {formatHotkey} from '../base/hotkeys'; import {assetSrc} from '../base/assets'; -import {assertExists} from '../base/assert'; +import {checkExists} from '../base/assert'; import {Icon} from '../widgets/icon'; import {Button} from '../widgets/button'; import {Router} from '../core/router'; @@ -156,7 +156,7 @@ action: () => { enableMetatracing(); engine.enableMetatrace( - assertExists(getEnabledMetatracingCategories()), + checkExists(getEnabledMetatracingCategories()), ); }, }, @@ -167,7 +167,7 @@ }); } else { enableMetatracing(); - engine.enableMetatrace(assertExists(getEnabledMetatracingCategories())); + engine.enableMetatrace(checkExists(getEnabledMetatracingCategories())); } }
diff --git a/ui/src/frontend/timeline_page/minimap.ts b/ui/src/frontend/timeline_page/minimap.ts index 41b0168..639bf24 100644 --- a/ui/src/frontend/timeline_page/minimap.ts +++ b/ui/src/frontend/timeline_page/minimap.ts
@@ -15,7 +15,7 @@ import m from 'mithril'; import {Size2D} from '../../base/geom'; import {HighPrecisionTimeSpan} from '../../base/high_precision_time_span'; -import {assertExists, assertUnreachable} from '../../base/assert'; +import {assertUnreachable, assertExists} from '../../base/assert'; import {Time, time, TimeSpan} from '../../base/time'; import {TimeScale} from '../../base/time_scale'; import {colorForCpu} from '../../components/colorizer'; @@ -59,7 +59,8 @@ } private renderBrushes(trace: TraceImpl, element: Element) { - const timeline = assertExists(findRef(element, MINIMAP_REF)); + const timeline = findRef(element, MINIMAP_REF); + assertExists(timeline); const timelineWidth = timeline.getBoundingClientRect().width; const traceTime = trace.traceInfo; const pxBounds = {left: 0, right: timelineWidth};
diff --git a/ui/src/frontend/timeline_page/timeline_header.ts b/ui/src/frontend/timeline_page/timeline_header.ts index a98fb29..e98b61b 100644 --- a/ui/src/frontend/timeline_page/timeline_header.ts +++ b/ui/src/frontend/timeline_page/timeline_header.ts
@@ -15,9 +15,8 @@ import m from 'mithril'; import {canvasSave} from '../../base/canvas_utils'; import {DisposableStack} from '../../base/disposable_stack'; -import {toHTMLElement} from '../../base/dom_utils'; import {Rect2D, Size2D} from '../../base/geom'; -import {assertExists} from '../../base/assert'; +import {assertExists, assertInstanceOf} from '../../base/assert'; import {TimeScale} from '../../base/time_scale'; import {ZonedInteractionHandler} from '../../base/zoned_interaction_handler'; import {TraceImpl} from '../../core/trace_impl'; @@ -108,8 +107,8 @@ } oncreate({dom}: m.VnodeDOM<TimelineHeaderAttrs>) { - const timelineHeaderElement = toHTMLElement(dom); - this.interactions = new ZonedInteractionHandler(timelineHeaderElement); + assertInstanceOf(dom, HTMLElement); + this.interactions = new ZonedInteractionHandler(dom); this.trash.use(this.interactions); } @@ -141,7 +140,8 @@ const visibleWindow = this.trace.timeline.visibleWindow; const timescale = new TimeScale(visibleWindow, timelineRect); - assertExists(this.interactions).update([ + assertExists(this.interactions); + this.interactions.update([ shiftDragPanInteraction(this.trace, timelineRect, timescale), wheelNavigationInteraction(this.trace, timelineRect, timescale), {
diff --git a/ui/src/frontend/timeline_page/timeline_page.ts b/ui/src/frontend/timeline_page/timeline_page.ts index 185e71e..9f0dc38 100644 --- a/ui/src/frontend/timeline_page/timeline_page.ts +++ b/ui/src/frontend/timeline_page/timeline_page.ts
@@ -14,7 +14,7 @@ import m from 'mithril'; import {DisposableStack} from '../../base/disposable_stack'; -import {toHTMLElement} from '../../base/dom_utils'; +import {assertInstanceOf} from '../../base/assert'; import {Rect2D} from '../../base/geom'; import {TimeScale} from '../../base/time_scale'; import {AppImpl} from '../../core/app_impl'; @@ -149,9 +149,11 @@ m(ResizeHandle, { onResize: (deltaPx: number) => { if (this.pinnedTracksHeight === 'auto') { - this.pinnedTracksHeight = toHTMLElement( - document.querySelector('.pf-timeline-page__pinned-track-tree')!, - ).getBoundingClientRect().height; + const treeEl = document.querySelector( + '.pf-timeline-page__pinned-track-tree', + ); + assertInstanceOf(treeEl, HTMLElement); + this.pinnedTracksHeight = treeEl.getBoundingClientRect().height; } this.pinnedTracksHeight = this.pinnedTracksHeight + deltaPx; m.redraw(); @@ -176,10 +178,11 @@ oncreate(vnode: m.VnodeDOM<TimelinePageAttrs>) { const {attrs, dom} = vnode; + assertInstanceOf(dom, HTMLElement); // Handles WASD keybindings to pan & zoom const panZoomHandler = new KeyboardNavigationHandler({ - element: toHTMLElement(dom), + element: dom, onPanned: (pannedPx: number) => { if (!this.timelineBounds) return; const timeline = attrs.trace.timeline;
diff --git a/ui/src/frontend/timeline_page/track_tree_view.ts b/ui/src/frontend/timeline_page/track_tree_view.ts index 788efbe..e86d1e2 100644 --- a/ui/src/frontend/timeline_page/track_tree_view.ts +++ b/ui/src/frontend/timeline_page/track_tree_view.ts
@@ -26,7 +26,7 @@ import m from 'mithril'; import {classNames} from '../../base/classnames'; import {DisposableStack} from '../../base/disposable_stack'; -import {findRef, toHTMLElement} from '../../base/dom_utils'; +import {findRef} from '../../base/dom_utils'; import { HorizontalBounds, Rect2D, @@ -36,7 +36,7 @@ } from '../../base/geom'; import {HighPrecisionTime} from '../../base/high_precision_time'; import {HighPrecisionTimeSpan} from '../../base/high_precision_time_span'; -import {assertExists} from '../../base/assert'; +import {assertExists, assertInstanceOf} from '../../base/assert'; import {Time} from '../../base/time'; import {TimeScale} from '../../base/time_scale'; import { @@ -398,15 +398,14 @@ // or change every update cycle. This chunk of code hooks the // ZonedInteractionHandler back up again if the DOM element is present, // otherwise it just removes it. - const interactionTarget = findRef(dom, TRACK_CONTAINER_REF) ?? undefined; + const interactionTarget = findRef(dom, TRACK_CONTAINER_REF); if (interactionTarget !== this.interactions?.target) { this.interactions?.[Symbol.dispose](); if (!interactionTarget) { this.interactions = undefined; } else { - this.interactions = new ZonedInteractionHandler( - toHTMLElement(interactionTarget), - ); + assertInstanceOf(interactionTarget, HTMLElement); + this.interactions = new ZonedInteractionHandler(interactionTarget); } } } @@ -589,7 +588,8 @@ const areaSelection = trace.selection.selection.kind === 'area' && trace.selection.selection; - assertExists(this.interactions).update([ + assertExists(this.interactions); + this.interactions.update([ shiftDragPanInteraction(trace, timelineRect, timescale), areaSelection !== false && { id: 'start-edit',
diff --git a/ui/src/plugins/com.android.DayExplorer/index.ts b/ui/src/plugins/com.android.DayExplorer/index.ts index 3e79241..e97fcb1 100644 --- a/ui/src/plugins/com.android.DayExplorer/index.ts +++ b/ui/src/plugins/com.android.DayExplorer/index.ts
@@ -30,7 +30,7 @@ import SupportPlugin from '../com.android.AndroidLongBatterySupport'; import {Store} from '../../base/store'; import {z} from 'zod'; -import {assertExists} from '../../base/assert'; +import {assertExists, checkExists} from '../../base/assert'; const DAY_EXPLORER_TRACK_KIND = 'day_explorer_counter_track'; @@ -153,6 +153,8 @@ id: 'day_explorer_flamegraph_selection', name: 'Day Explorer Flamegraph', render: (selection: AreaSelection) => { + const store = checkExists(this.store); + const selectionChanged = previousSelection === undefined || !areaSelectionsEqual(previousSelection, selection); @@ -166,7 +168,6 @@ if (flameagraphWithMetrics === undefined) { return undefined; } - const store = assertExists(this.store); const {flamegraph, metrics} = flameagraphWithMetrics; return { isLoading: false, @@ -174,6 +175,7 @@ metrics, state: store.state.areaSelectionFlamegraphState, onStateChange: (state) => { + assertExists(this.store); store.edit((draft) => { draft.areaSelectionFlamegraphState = state; }); @@ -188,6 +190,8 @@ trace: Trace, currentSelection: AreaSelection, ): QueryFlamegraphWithMetrics | undefined { + assertExists(this.store); + // The flame graph will be shown when any day explorer track is in the area // selection. The selection is used to filter by time, but not by track. All // day explorer tracks are considered for the graph. @@ -239,8 +243,8 @@ ], nameColumnLabel: 'Component', }); - const store = assertExists(this.store); - store.edit((draft) => { + + this.store.edit((draft) => { draft.areaSelectionFlamegraphState = Flamegraph.updateState( draft.areaSelectionFlamegraphState, metrics,
diff --git a/ui/src/plugins/dev.perfetto.AggregateProfiles/index.ts b/ui/src/plugins/dev.perfetto.AggregateProfiles/index.ts index d244d07..bd51613 100644 --- a/ui/src/plugins/dev.perfetto.AggregateProfiles/index.ts +++ b/ui/src/plugins/dev.perfetto.AggregateProfiles/index.ts
@@ -24,7 +24,7 @@ AGGREGATE_PROFILES_PAGE_STATE_SCHEMA, } from './types'; import {Store} from '../../base/store'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; export default class implements PerfettoPlugin { static readonly id = 'dev.perfetto.AggregateProfiles'; @@ -43,7 +43,7 @@ if (profiles.length === 0) { return; } - const store = assertExists(this.store); + const store = checkExists(this.store); trace.pages.registerPage({ route: '/aggregateprofiles', render: () =>
diff --git a/ui/src/plugins/dev.perfetto.AutoPinAndExpandTracks/index.ts b/ui/src/plugins/dev.perfetto.AutoPinAndExpandTracks/index.ts index f66a662..a309950 100644 --- a/ui/src/plugins/dev.perfetto.AutoPinAndExpandTracks/index.ts +++ b/ui/src/plugins/dev.perfetto.AutoPinAndExpandTracks/index.ts
@@ -18,7 +18,7 @@ import {PerfettoPlugin} from '../../public/plugin'; import {Track} from '../../public/track'; import {z} from 'zod'; -import {assertIsInstance} from '../../base/assert'; +import {assertInstanceOf} from '../../base/assert'; import {RouteArg, RouteArgs} from '../../public/route_schema'; import {arrayEquals} from '../../base/array_utils'; @@ -182,7 +182,8 @@ name: 'Import by name: Pinned tracks', callback: async () => { const files = document.querySelector('.pinned_tracks_import_selector'); - assertIsInstance<HTMLInputElement>(files, HTMLInputElement).click(); + assertInstanceOf(files, HTMLInputElement); + files.click(); }, });
diff --git a/ui/src/plugins/dev.perfetto.CpuProfile/index.ts b/ui/src/plugins/dev.perfetto.CpuProfile/index.ts index 3d35887..badf75f 100644 --- a/ui/src/plugins/dev.perfetto.CpuProfile/index.ts +++ b/ui/src/plugins/dev.perfetto.CpuProfile/index.ts
@@ -27,7 +27,7 @@ QueryFlamegraphWithMetrics, } from '../../components/query_flamegraph'; import {Flamegraph, FLAMEGRAPH_STATE_SCHEMA} from '../../widgets/flamegraph'; -import {assertExists} from '../../base/assert'; +import {assertExists, checkExists} from '../../base/assert'; import {Store} from '../../base/store'; import {z} from 'zod'; @@ -72,7 +72,6 @@ where not is_idle `); - const store = assertExists(this.store); const it = result.iter({ utid: NUM, upid: NUM_NULL, @@ -94,9 +93,10 @@ ctx, uri, utid, - store.state.detailsPanelFlamegraphState, + this.store.state.detailsPanelFlamegraphState, (state) => { - store.edit((draft) => { + assertExists(this.store); + this.store.edit((draft) => { draft.detailsPanelFlamegraphState = state; }); }, @@ -142,7 +142,7 @@ return undefined; } const {flamegraph, metrics} = flamegraphWithMetrics; - const store = assertExists(this.store); + const store = checkExists(this.store); return { isLoading: false, content: flamegraph.render({ @@ -211,7 +211,7 @@ ], nameColumnLabel: 'Symbol', }); - const store = assertExists(this.store); + const store = checkExists(this.store); store.edit((draft) => { draft.areaSelectionFlamegraphState = Flamegraph.updateState( draft.areaSelectionFlamegraphState, @@ -223,7 +223,7 @@ } async function selectCpuProfileCallsite(trace: Trace) { - const profile = await assertExists(trace.engine).query(` + const profile = await checkExists(trace.engine).query(` select utid, upid from cpu_profile_stack_sample join thread using(utid)
diff --git a/ui/src/plugins/dev.perfetto.DataExplorer/query_builder/widgets.ts b/ui/src/plugins/dev.perfetto.DataExplorer/query_builder/widgets.ts index d678cf8..408221b 100644 --- a/ui/src/plugins/dev.perfetto.DataExplorer/query_builder/widgets.ts +++ b/ui/src/plugins/dev.perfetto.DataExplorer/query_builder/widgets.ts
@@ -35,8 +35,8 @@ import {showModal} from '../../../widgets/modal'; import {Editor} from '../../../widgets/editor'; import {ResizeHandle} from '../../../widgets/resize_handle'; -import {findRef, toHTMLElement} from '../../../base/dom_utils'; -import {assertExists} from '../../../base/assert'; +import {findRef} from '../../../base/dom_utils'; +import {assertInstanceOf} from '../../../base/assert'; /** * Round action button with consistent styling for Data Explorer. @@ -1067,6 +1067,8 @@ autofocus?: boolean; } +const EDITOR_REF = 'editor'; + export class ResizableSqlEditor implements m.ClassComponent<ResizableSqlEditorAttrs> { @@ -1074,14 +1076,15 @@ private editorElement?: HTMLElement; oncreate({dom}: m.VnodeDOM<ResizableSqlEditorAttrs>) { - this.editorElement = toHTMLElement(assertExists(findRef(dom, 'editor'))); - this.editorElement.style.height = '400px'; + const element = findRef(dom, EDITOR_REF); + assertInstanceOf(element, HTMLElement); + element.style.height = '400px'; } view({attrs}: m.CVnode<ResizableSqlEditorAttrs>) { return [ m(Editor, { - ref: 'editor', + ref: EDITOR_REF, text: attrs.sql, onUpdate: attrs.onUpdate, onExecute: attrs.onExecute,
diff --git a/ui/src/plugins/dev.perfetto.HeapProfile/index.ts b/ui/src/plugins/dev.perfetto.HeapProfile/index.ts index 51c1bc6..054282e 100644 --- a/ui/src/plugins/dev.perfetto.HeapProfile/index.ts +++ b/ui/src/plugins/dev.perfetto.HeapProfile/index.ts
@@ -26,7 +26,6 @@ import {FLAMEGRAPH_STATE_SCHEMA} from '../../widgets/flamegraph'; import {Store} from '../../base/store'; import {z} from 'zod'; -import {assertExists} from '../../base/assert'; import { isProfileDescriptor, ProfileDescriptor, @@ -39,6 +38,7 @@ AreaSelectionTab, } from '../../public/selection'; import {HeapProfileFlamegraphDetailsPanel} from './heap_profile_details_panel'; +import {checkExists} from '../../base/assert'; const EVENT_TABLE_NAME = 'heap_profile_events'; @@ -194,7 +194,7 @@ const group = trackGroupsPlugin.getGroupForProcess(upid); if (!group) continue; - const store = assertExists(this.store); + const store = checkExists(this.store); const uri = trackUri(upid, heapType); const descriptor = profileDescriptor(heapType); const track: Track = { @@ -279,7 +279,7 @@ id: `heap_profiler_flamegraph_selection_${descriptor.heapName}`, name: `${descriptor.label} flamegraph`, render: (selection: AreaSelection) => { - const store = assertExists(this.store); + const store = checkExists(this.store); const selectionChanged = previousSelection === undefined || !areaSelectionsEqual(previousSelection, selection);
diff --git a/ui/src/plugins/dev.perfetto.InstrumentsSamplesProfile/index.ts b/ui/src/plugins/dev.perfetto.InstrumentsSamplesProfile/index.ts index 4dee48e..9d9c829 100644 --- a/ui/src/plugins/dev.perfetto.InstrumentsSamplesProfile/index.ts +++ b/ui/src/plugins/dev.perfetto.InstrumentsSamplesProfile/index.ts
@@ -21,7 +21,7 @@ NUM_NULL, STR_NULL, } from '../../trace_processor/query_result'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import {getThreadUriPrefix} from '../../public/utils'; import {TrackNode} from '../../public/workspace'; import ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups'; @@ -85,7 +85,7 @@ join thread using (utid) where callsite_id is not null and upid is not null `); - const store = assertExists(this.store); + const store = checkExists(this.store); for (const it = pResult.iter({upid: NUM}); it.valid(); it.next()) { const upid = it.upid; const uri = makeUriForProc(upid); @@ -197,7 +197,7 @@ return undefined; } const {flamegraph, metrics} = flamegraphWithMetrics; - const store = assertExists(this.store); + const store = checkExists(this.store); return { isLoading: false, content: flamegraph.render({ @@ -250,7 +250,7 @@ dependencySql: 'include perfetto module appleos.instruments.samples', nameColumnLabel: 'Symbol', }); - const store = assertExists(this.store); + const store = checkExists(this.store); store.edit((draft) => { draft.areaSelectionFlamegraphState = Flamegraph.updateState( draft.areaSelectionFlamegraphState, @@ -262,7 +262,7 @@ } async function selectInstrumentsSample(ctx: Trace) { - const profile = await assertExists(ctx.engine).query(` + const profile = await checkExists(ctx.engine).query(` select upid from instruments_sample join thread using (utid) @@ -293,7 +293,7 @@ ) && trackInfo.tags?.utid === undefined ) { - upids.push(assertExists(trackInfo.tags?.upid)); + upids.push(checkExists(trackInfo.tags?.upid)); } } return upids;
diff --git a/ui/src/plugins/dev.perfetto.KernelTrackEvent/index.ts b/ui/src/plugins/dev.perfetto.KernelTrackEvent/index.ts index 4d72d02..169d02e 100644 --- a/ui/src/plugins/dev.perfetto.KernelTrackEvent/index.ts +++ b/ui/src/plugins/dev.perfetto.KernelTrackEvent/index.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import {PerfettoPlugin} from '../../public/plugin'; import {Trace} from '../../public/trace'; import {COUNTER_TRACK_KIND, SLICE_TRACK_KIND} from '../../public/track_kinds'; @@ -109,7 +109,7 @@ const {trackId, name, utid, upid, cpu, isCounter, scope, tid, pid} = it; const displayTrackName = this.getTrackName( - assertExists(name), + checkExists(name), pid ?? undefined, tid ?? undefined, cpu ?? undefined, @@ -204,21 +204,21 @@ cpu: number | undefined, ): TrackNode { if (upid !== undefined) { - return assertExists( + return checkExists( ctx.plugins .getPlugin(ProcessThreadGroupsPlugin) - .getGroupForProcess(assertExists(upid)), + .getGroupForProcess(checkExists(upid)), ); } if (utid !== undefined) { - return assertExists( + return checkExists( ctx.plugins .getPlugin(ProcessThreadGroupsPlugin) - .getGroupForThread(assertExists(utid)), + .getGroupForThread(checkExists(utid)), ); } if (cpu !== undefined) { - return assertExists( + return checkExists( ctx.plugins .getPlugin(StandardGroupsPlugin) .getOrCreateStandardGroup(ctx.defaultWorkspace, 'CPU'), @@ -226,7 +226,7 @@ } // custom-scoped event: "Kernel -> Kernel track events". if (this.kernelTrackEventsNode === undefined) { - const kernelGroup = assertExists( + const kernelGroup = checkExists( ctx.plugins .getPlugin(StandardGroupsPlugin) .getOrCreateStandardGroup(ctx.defaultWorkspace, 'KERNEL'),
diff --git a/ui/src/plugins/dev.perfetto.LinuxPerf/index.ts b/ui/src/plugins/dev.perfetto.LinuxPerf/index.ts index 93fd5e9..ec96e02 100644 --- a/ui/src/plugins/dev.perfetto.LinuxPerf/index.ts +++ b/ui/src/plugins/dev.perfetto.LinuxPerf/index.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import { QueryFlamegraph, QueryFlamegraphMetric, @@ -79,7 +79,7 @@ this.store = trace.mountStore(LinuxPerfPlugin.id, (init) => this.migrateLinuxPerfPluginState(init), ); - const store = assertExists(this.store); + const store = checkExists(this.store); await this.cacheCounterTypesPerSession(trace); await this.addProcessPerfSamplesTracks(trace, store); await this.addThreadPerfSamplesTracks(trace, store); @@ -447,7 +447,7 @@ return undefined; } const {flamegraph, metrics} = flamegraphWithMetrics; - const store = assertExists(this.store); + const store = checkExists(this.store); return { isLoading: false, content: flamegraph.render({ @@ -571,7 +571,7 @@ aggregatableProperties, }); - const store = assertExists(this.store); + const store = checkExists(this.store); store.edit((draft) => { draft.areaSelectionFlamegraphState = Flamegraph.updateState( draft.areaSelectionFlamegraphState, @@ -606,7 +606,7 @@ } async function selectPerfTracksIfSingleProcess(trace: Trace) { - const profile = await assertExists(trace.engine).query(` + const profile = await checkExists(trace.engine).query(` select distinct upid from perf_sample join thread using (utid) @@ -629,7 +629,7 @@ trackInfo.tags?.utid === undefined ) { ret.push([ - assertExists(trackInfo.tags?.upid), + checkExists(trackInfo.tags?.upid), Number(trackInfo.tags.perfSessionId), ]); }
diff --git a/ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts b/ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts index 89e04ba..376b09e 100644 --- a/ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts +++ b/ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts
@@ -18,7 +18,7 @@ import {STR} from '../../trace_processor/query_result'; import {Select} from '../../widgets/select'; import {Spinner} from '../../widgets/spinner'; -import {assertExists, assertUnreachable} from '../../base/assert'; +import {checkExists, assertUnreachable} from '../../base/assert'; import {Trace} from '../../public/trace'; import {SegmentedButtons} from '../../widgets/segmented_buttons'; import {Editor} from '../../widgets/editor'; @@ -669,7 +669,7 @@ } view({attrs}: m.Vnode<MetricsPageAttrs>) { - const v1Controller = assertExists(this.v1Controller); + const v1Controller = checkExists(this.v1Controller); return m( '.pf-metrics-page', m(
diff --git a/ui/src/plugins/dev.perfetto.ProcessSummary/group_summary_track.ts b/ui/src/plugins/dev.perfetto.ProcessSummary/group_summary_track.ts index b32f269..c44e0ae 100644 --- a/ui/src/plugins/dev.perfetto.ProcessSummary/group_summary_track.ts +++ b/ui/src/plugins/dev.perfetto.ProcessSummary/group_summary_track.ts
@@ -20,7 +20,7 @@ import {ColorScheme} from '../../base/color_scheme'; import {AsyncDisposableStack} from '../../base/disposable_stack'; import {Point2D} from '../../base/geom'; -import {assertExists, assertTrue} from '../../base/assert'; +import {assertTrue, assertExists} from '../../base/assert'; import {Monitor} from '../../base/monitor'; import { CancellationSignal,
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/web_device_proxy/wdp_websocket_stream .ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/web_device_proxy/wdp_websocket_stream .ts index be8bc36..d0f38f8 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/web_device_proxy/wdp_websocket_stream .ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/web_device_proxy/wdp_websocket_stream .ts
@@ -13,7 +13,7 @@ // limitations under the License. import z from 'zod'; -import {assertExists, assertTrue} from '../../../../base/assert'; +import {checkExists, assertTrue} from '../../../../base/assert'; import {ByteStream} from '../../interfaces/byte_stream'; import {base64Decode} from '../../../../base/string_utils'; @@ -39,7 +39,7 @@ // Unmarshall this transparently to caller. const parsed = this.schema.safeParse(JSON.parse(e.data)); this.onData( - new Uint8Array(base64Decode(assertExists(parsed.data).response)), + new Uint8Array(base64Decode(checkExists(parsed.data).response)), ); } else { assertTrue(e.data instanceof ArrayBuffer);
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/webusb/adb_key.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/webusb/adb_key.ts index 4b636bd..f3e2c1f 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/webusb/adb_key.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/adb/webusb/adb_key.ts
@@ -13,7 +13,7 @@ // limitations under the License. import {BigInteger, RSAKey} from 'jsbn-rsa'; -import {assertExists, assertTrue} from '../../../../base/assert'; +import {checkExists, assertTrue} from '../../../../base/assert'; import { base64Decode, base64Encode, @@ -135,8 +135,8 @@ getPublicKey(): string { const rsaKey = new RSAKey(); rsaKey.setPublic( - hexEncode(base64Decode(assertExists(this.jwkPrivate.n))), - hexEncode(base64Decode(assertExists(this.jwkPrivate.e))), + hexEncode(base64Decode(checkExists(this.jwkPrivate.n))), + hexEncode(base64Decode(checkExists(this.jwkPrivate.e))), ); const n0inv = R32.subtract(rsaKey.n.modInverse(R32)).intValue();
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_manager.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_manager.ts index 5f9be71..ee7c79b 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_manager.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_manager.ts
@@ -13,7 +13,7 @@ // limitations under the License. import protos from '../../../protos'; -import {assertExists, assertFalse, assertTrue} from '../../../base/assert'; +import {checkExists, assertFalse, assertTrue} from '../../../base/assert'; import {getOrCreate} from '../../../base/utils'; import {ProbesSchema} from '../serialization_schema'; import {TargetPlatformId} from '../interfaces/target_platform'; @@ -59,7 +59,7 @@ } setProbeEnabled(probeId: string, enabled: boolean) { - const probe = assertExists(this.probesById.get(probeId)); + const probe = checkExists(this.probesById.get(probeId)); this.enabledProbes.set(probeId, enabled); for (const depProbeId of probe.dependencies ?? []) { assertTrue(this.probesById.has(depProbeId)); @@ -102,7 +102,7 @@ */ getProbeEnableDependants(probeId: string): string[] { return Array.from(this.indirectlyEnabledProbes.get(probeId) ?? []).map( - (id) => assertExists(this.probesById.get(id)).title, + (id) => checkExists(this.probesById.get(id)).title, ); } @@ -179,7 +179,7 @@ const seenIds = new Set<string>(); const queueProbe = (probeId: string) => { if (enabledOnly && !this.isProbeEnabled(probeId)) return; - const probe = assertExists(this.probesById.get(probeId)); + const probe = checkExists(this.probesById.get(probeId)); if (orderedProbes.includes(probe)) return; // Already added. if (seenIds.has(probeId)) { throw new Error('Cycle detected in probe ' + probeId);
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_sharing.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_sharing.ts index 3e728eb..c3f9c2f 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_sharing.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_sharing.ts
@@ -14,7 +14,7 @@ import m from 'mithril'; import {GcsUploader} from '../../../base/gcs_uploader'; -import {assertExists} from '../../../base/assert'; +import {checkExists} from '../../../base/assert'; import {CopyableLink} from '../../../widgets/copyable_link'; import {showModal} from '../../../widgets/modal'; import {RecordSessionSchema} from '../serialization_schema'; @@ -31,7 +31,7 @@ const uploader = new GcsUploader(json, {mimeType: 'application/json'}); await uploader.waitForCompletion(); const url = uploader.uploadedUrl; - const hash = assertExists(url.split('/').pop()); + const hash = checkExists(url.split('/').pop()); showModal({ title: 'Permalink', content: m(CopyableLink, {
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/config/trace_config_builder.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/config/trace_config_builder.ts index 881301a..8e749dd 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/config/trace_config_builder.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/config/trace_config_builder.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists, assertFalse} from '../../../base/assert'; +import {checkExists, assertFalse} from '../../../base/assert'; import {getOrCreate} from '../../../base/utils'; import protos from '../../../protos'; @@ -39,7 +39,7 @@ } get defaultBuffer(): BufferConfig { - return assertExists(this.buffers.get(DEFAULT_BUFFER_ID)); + return checkExists(this.buffers.get(DEFAULT_BUFFER_ID)); } // It has get-or-create semantics.
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/memory.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/memory.ts index cebeda9..56e8ade 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/memory.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/memory.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {assertExists} from '../../../base/assert'; +import {checkExists} from '../../../base/assert'; import {splitLinesNonEmpty} from '../../../base/string_utils'; import protos from '../../../protos'; import { @@ -341,7 +341,7 @@ const ds = tc.addDataSource(PROC_STATS_DS_NAME, ADV_PROC_ASSOC_BUF_ID); // Because of the dependency on ADV_PROC_ASSOC_PROBE_ID, we expect // procThreadAssociation() to create the config first. - const cfg = assertExists(ds.processStatsConfig); + const cfg = checkExists(ds.processStatsConfig); cfg.procStatsPollMs = settings.pollMs.value || undefined; cfg.recordProcessAge = settings.procAge.enabled || undefined; cfg.recordProcessRuntime = settings.procRuntime.enabled || undefined;
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/tracing_protocol/tracing_protocol.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/tracing_protocol/tracing_protocol.ts index 2822c7e..cd10b40 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/tracing_protocol/tracing_protocol.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/tracing_protocol/tracing_protocol.ts
@@ -19,7 +19,7 @@ import {ProtoRingBuffer} from '../../../trace_processor/proto_ring_buffer'; import {defer} from '../../../base/deferred'; import {exists} from '../../../base/utils'; -import {assertExists, assertFalse, assertTrue} from '../../../base/assert'; +import {checkExists, assertFalse, assertTrue} from '../../../base/assert'; import {PacketAssembler} from './packet_assembler'; import {ResizableArrayBuffer} from '../../../base/resizable_array_buffer'; @@ -70,12 +70,12 @@ const frameData = await repsponsePromise; const rxFrame = protos.IPCFrame.decode(frameData); assertTrue(rxFrame.msg === 'msgBindServiceReply'); - const replyMsg = assertExists(rxFrame.msgBindServiceReply); + const replyMsg = checkExists(rxFrame.msgBindServiceReply); const boundMethods = new Map<string, number>(); assertTrue(replyMsg.success === true); - const serviceId = assertExists(replyMsg.serviceId); - for (const m of assertExists(replyMsg.methods)) { - boundMethods.set(assertExists(m.name), assertExists(m.id)); + const serviceId = checkExists(replyMsg.serviceId); + for (const m of checkExists(replyMsg.methods)) { + boundMethods.set(checkExists(m.name), checkExists(m.id)); } // Now that the details of the RPC methods are known, build and return the // TracingProtocol object, so the caller can finally make calls. @@ -191,8 +191,8 @@ // See 170256902#comment21 const frame = protos.IPCFrame.decode(frameData.slice()); if (frame.msg === 'msgInvokeMethodReply') { - const reply = assertExists(frame.msgInvokeMethodReply); - const pendInvoke = assertExists(this.pendingInvokes.get(frame.requestId)); + const reply = checkExists(frame.msgInvokeMethodReply); + const pendInvoke = checkExists(this.pendingInvokes.get(frame.requestId)); // We process messages without a `replyProto` field (for instance // `FreeBuffers` does not have `replyProto`). However, we ignore messages // without a valid 'success' field.
diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/websocket/async_websocket.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/websocket/async_websocket.ts index b7a96ab..01b9ff0 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/websocket/async_websocket.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/websocket/async_websocket.ts
@@ -13,7 +13,7 @@ // limitations under the License. import {defer, Deferred} from '../../../base/deferred'; -import {assertExists, assertTrue} from '../../../base/assert'; +import {checkExists, assertTrue} from '../../../base/assert'; import {ResizableArrayBuffer} from '../../../base/resizable_array_buffer'; import {utf8Decode, utf8Encode} from '../../../base/string_utils'; @@ -72,7 +72,7 @@ /** Turns this back into a standard WebSocket. */ release(): WebSocket { - const sock = assertExists(this.sock); + const sock = checkExists(this.sock); this.sock = undefined; sock.onmessage = null; sock.onopen = null; @@ -82,7 +82,7 @@ } send(data: string | ArrayBufferLike) { - assertExists(this.sock).send(data); + checkExists(this.sock).send(data); } waitForData(numBytes: number = ANY_SIZE): Promise<Uint8Array> {
diff --git a/ui/src/plugins/dev.perfetto.Thread/index.ts b/ui/src/plugins/dev.perfetto.Thread/index.ts index 00d31d4..cd054bf 100644 --- a/ui/src/plugins/dev.perfetto.Thread/index.ts +++ b/ui/src/plugins/dev.perfetto.Thread/index.ts
@@ -26,7 +26,7 @@ STR, STR_NULL, } from '../../trace_processor/query_result'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; async function listThreads(trace: Trace) { const query = ` @@ -147,6 +147,6 @@ } getThreadMap() { - return assertExists(this.threads); + return checkExists(this.threads); } }
diff --git a/ui/src/plugins/dev.perfetto.TimelineSync/index.ts b/ui/src/plugins/dev.perfetto.TimelineSync/index.ts index 9a56120..f31b953 100644 --- a/ui/src/plugins/dev.perfetto.TimelineSync/index.ts +++ b/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
@@ -17,7 +17,7 @@ import {PerfettoPlugin} from '../../public/plugin'; import {Time, TimeSpan} from '../../base/time'; import {redrawModal, showModal} from '../../widgets/modal'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import {Button, ButtonBar, ButtonVariant} from '../../widgets/button'; import {Intent} from '../../widgets/common'; import {HighPrecisionTimeSpan} from '../../base/high_precision_time_span'; @@ -174,7 +174,7 @@ // Disable any prior session. this.disableTimelineSync(this._sessionId); const selectedClients = new Array<ClientId>(); - const sel = assertExists(clientsSelect).selectedOptions; + const sel = checkExists(clientsSelect).selectedOptions; for (let i = 0; i < sel.length; i++) { const clientId = parseInt(sel[i].value); if (!isNaN(clientId)) selectedClients.push(clientId);
diff --git a/ui/src/plugins/dev.perfetto.TraceProcessorTrack/index.ts b/ui/src/plugins/dev.perfetto.TraceProcessorTrack/index.ts index 6d6d71b..c3eb83d 100644 --- a/ui/src/plugins/dev.perfetto.TraceProcessorTrack/index.ts +++ b/ui/src/plugins/dev.perfetto.TraceProcessorTrack/index.ts
@@ -14,7 +14,7 @@ import {removeFalsyValues} from '../../base/array_utils'; import {AsyncLimiter} from '../../base/async_limiter'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import {Time} from '../../base/time'; import { createAggregationTab, @@ -439,19 +439,19 @@ ) { switch (topLevelGroup) { case 'PROCESS': { - const process = assertExists( + const process = checkExists( ctx.plugins .getPlugin(ProcessThreadGroupsPlugin) - .getGroupForProcess(assertExists(upid)), + .getGroupForProcess(checkExists(upid)), ); this.getGroupByName(process, group, upid).addChildInOrder(track); break; } case 'THREAD': { - const thread = assertExists( + const thread = checkExists( ctx.plugins .getPlugin(ProcessThreadGroupsPlugin) - .getGroupForThread(assertExists(utid)), + .getGroupForThread(checkExists(utid)), ); this.getGroupByName(thread, group, utid).addChildInOrder(track); break; @@ -555,7 +555,7 @@ if (flamegraphWithMetrics === undefined && !isLoading) { return undefined; } - const store = assertExists(this.store); + const store = checkExists(this.store); return { isLoading: isLoading, content: flamegraphWithMetrics?.flamegraph?.render({ @@ -658,7 +658,7 @@ ], nameColumnLabel: 'Slice Name', }); - const store = assertExists(this.store); + const store = checkExists(this.store); store.edit((draft) => { draft.areaSelectionFlamegraphState = Flamegraph.updateState( draft.areaSelectionFlamegraphState, @@ -737,8 +737,8 @@ // Only process upids that have valid track groups const rows: MinimapRow[] = []; const sortedUpids = Array.from(upidOrderMap.keys()).sort((a, b) => { - const orderA = assertExists(upidOrderMap.get(a)); - const orderB = assertExists(upidOrderMap.get(b)); + const orderA = checkExists(upidOrderMap.get(a)); + const orderB = checkExists(upidOrderMap.get(b)); return orderA - orderB; });
diff --git a/ui/src/plugins/dev.perfetto.TraceProcessorTrack/pivot_table_tab.ts b/ui/src/plugins/dev.perfetto.TraceProcessorTrack/pivot_table_tab.ts index 5c11f92..c7a1e36 100644 --- a/ui/src/plugins/dev.perfetto.TraceProcessorTrack/pivot_table_tab.ts +++ b/ui/src/plugins/dev.perfetto.TraceProcessorTrack/pivot_table_tab.ts
@@ -13,7 +13,7 @@ // limitations under the License. import m from 'mithril'; -import {assertExists} from '../../base/assert'; +import {checkExists} from '../../base/assert'; import {Icons} from '../../base/semantic_icons'; import {PivotTable} from '../../components/widgets/sql/pivot_table/pivot_table'; import {PivotTableState} from '../../components/widgets/sql/pivot_table/pivot_table_state'; @@ -88,12 +88,10 @@ private getOrCreateState(): PivotTableState { if (this.state !== undefined) return this.state; const sliceTable = resolveTableDefinition(this.trace, SLICE_TABLE); - const name = assertExists( + const name = checkExists( sliceTable.columns.find((c) => c.column === 'name'), ); - const dur = assertExists( - sliceTable.columns.find((c) => c.column === 'dur'), - ); + const dur = checkExists(sliceTable.columns.find((c) => c.column === 'dur')); this.state = new PivotTableState({ trace: this.trace, table: sliceTable,
diff --git a/ui/src/plugins/dev.perfetto.TrackEvent/index.ts b/ui/src/plugins/dev.perfetto.TrackEvent/index.ts index a375dd4..ca07326 100644 --- a/ui/src/plugins/dev.perfetto.TrackEvent/index.ts +++ b/ui/src/plugins/dev.perfetto.TrackEvent/index.ts
@@ -24,7 +24,7 @@ STR_NULL, } from '../../trace_processor/query_result'; import {TrackNode} from '../../public/workspace'; -import {assertExists, assertTrue} from '../../base/assert'; +import {checkExists, assertTrue} from '../../base/assert'; import {COUNTER_TRACK_KIND, SLICE_TRACK_KIND} from '../../public/track_kinds'; import {createTraceProcessorSliceTrack} from '../dev.perfetto.TraceProcessorTrack/trace_processor_slice_track'; import {TraceProcessorCounterTrack} from '../dev.perfetto.TraceProcessorTrack/trace_processor_counter_track'; @@ -339,7 +339,7 @@ return undefined; } const {flamegraph, metrics} = flamegraphWithMetrics; - const store = assertExists(this.store); + const store = checkExists(this.store); return { isLoading: false, content: flamegraph.render({ @@ -438,7 +438,7 @@ ], nameColumnLabel: 'Symbol', }); - const store = assertExists(this.store); + const store = checkExists(this.store); store.edit((draft) => { draft.areaSelectionFlamegraphState = Flamegraph.updateState( draft.areaSelectionFlamegraphState, @@ -458,13 +458,13 @@ hasChildren: number, ): TrackNode { if (parentId !== undefined) { - return assertExists(trackIdToTrackNode.get(parentId)); + return checkExists(trackIdToTrackNode.get(parentId)); } if (utid !== undefined) { - return assertExists(processGroupsPlugin.getGroupForThread(utid)); + return checkExists(processGroupsPlugin.getGroupForThread(utid)); } if (upid !== undefined) { - return assertExists(processGroupsPlugin.getGroupForProcess(upid)); + return checkExists(processGroupsPlugin.getGroupForProcess(upid)); } if (hasChildren) { return ctx.defaultWorkspace.tracks;
diff --git a/ui/src/plugins/org.chromium.ChromeScrollJank/event_latency_details_panel.ts b/ui/src/plugins/org.chromium.ChromeScrollJank/event_latency_details_panel.ts index 4d71024..de6b48b 100644 --- a/ui/src/plugins/org.chromium.ChromeScrollJank/event_latency_details_panel.ts +++ b/ui/src/plugins/org.chromium.ChromeScrollJank/event_latency_details_panel.ts
@@ -52,7 +52,7 @@ import {renderSliceArguments} from '../../components/details/slice_args'; import {TrackEventRef} from '../../components/widgets/track_event_ref'; import {SLICE_TABLE} from '../../components/widgets/sql/table_definitions'; -import {assertExists, assertTrue} from '../../base/assert'; +import {checkExists, assertTrue} from '../../base/assert'; import { EVENT_LATENCY_TRACK, SCROLL_TIMELINE_TRACK, @@ -523,7 +523,7 @@ } private renderRelatedTrackReferences(): m.Child { - const references = assertExists(this.references); + const references = checkExists(this.references); const children: m.Children = []; if (references.scrollUpdatePluginSliceId !== undefined) { children.push( @@ -556,7 +556,7 @@ } private renderStdlibReferences(): m.Child { - const references = assertExists(this.references); + const references = checkExists(this.references); return m( TreeNode, { @@ -585,7 +585,7 @@ } private getStageReferences(): m.Child { - const parent = assertExists(this.references!.parent); + const parent = checkExists(this.references!.parent); return trackEventRefTreeNode({ trace: this.trace, table: EVENT_LATENCY_TRACK.tableName,
diff --git a/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_details_panel.ts b/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_details_panel.ts index be02be5..050df43 100644 --- a/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_details_panel.ts +++ b/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_details_panel.ts
@@ -19,7 +19,7 @@ import {DetailsShell} from '../../widgets/details_shell'; import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout'; import {Duration, duration, Time, time} from '../../base/time'; -import {assertExists, assertTrue} from '../../base/assert'; +import {checkExists, assertTrue} from '../../base/assert'; import {Section} from '../../widgets/section'; import {Tree, TreeNode} from '../../widgets/tree'; import {Timestamp} from '../../components/widgets/timestamp'; @@ -288,7 +288,7 @@ } private renderRelatedTrackReferences(): m.Child { - const scrollUpdateData = assertExists(this.scrollUpdateData); + const scrollUpdateData = checkExists(this.scrollUpdateData); const children: m.Children = []; if (scrollUpdateData.eventLatencyPluginSliceId !== undefined) { children.push( @@ -321,7 +321,7 @@ } private renderStdlibReferences(): m.Child { - const scrollUpdateData = assertExists(this.scrollUpdateData); + const scrollUpdateData = checkExists(this.scrollUpdateData); return m( TreeNode, {
diff --git a/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_v4_details_panel.ts b/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_v4_details_panel.ts index f89d75b..f63f237 100644 --- a/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_v4_details_panel.ts +++ b/ui/src/plugins/org.chromium.ChromeScrollJank/scroll_timeline_v4_details_panel.ts
@@ -18,7 +18,7 @@ import {NUM, NUM_NULL, STR} from '../../trace_processor/query_result'; import {DetailsShell} from '../../widgets/details_shell'; import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout'; -import {assertExists, assertTrue} from '../../base/assert'; +import {checkExists, assertTrue, assertExists} from '../../base/assert'; import {Section} from '../../widgets/section'; import {Tree, TreeNode} from '../../widgets/tree'; import { @@ -212,7 +212,7 @@ } private renderRelatedTrackReferences(): m.Child { - const frameData = assertExists(this.frameData); + const frameData = checkExists(this.frameData); const children: m.Children = []; if (frameData.firstEventLatencyPluginSliceId !== undefined) { children.push( @@ -245,7 +245,7 @@ } private renderStdlibReferences(): m.Child { - const frameData = assertExists(this.frameData); + const frameData = checkExists(this.frameData); return m( TreeNode, {
diff --git a/ui/src/test/aggregation.test.ts b/ui/src/test/aggregation.test.ts index a428045..ff02655 100644 --- a/ui/src/test/aggregation.test.ts +++ b/ui/src/test/aggregation.test.ts
@@ -14,8 +14,8 @@ import {test, Page} from '@playwright/test'; import {PerfettoTestHelper} from './perfetto_ui_test_helper'; -import {assertExists} from '../base/assert'; import {Locator} from '@playwright/test'; +import {assertExists, checkExists} from '../base/assert'; test.describe.configure({mode: 'serial'}); @@ -87,7 +87,8 @@ 'GPU/GPU Frequency/Gpu 0 Frequency', gpuFreqGroup, ); - const coords = assertExists(await gpuTrack.boundingBox()); + const coords = await gpuTrack.boundingBox(); + assertExists(coords); await page.mouse.move(600, coords.y + 10); await page.mouse.down(); await page.mouse.move(800, coords.y + 60); @@ -106,7 +107,7 @@ 'com.android.systemui 25348/Actual Timeline', sysui, ); - const coords = assertExists(await actualTimeline.boundingBox()); + const coords = checkExists(await actualTimeline.boundingBox()); await page.mouse.move(600, coords.y + 10); await page.mouse.down(); await page.mouse.move(1000, coords.y + 20); @@ -126,7 +127,7 @@ .nth(1); await animThread.scrollIntoViewIfNeeded(); await pth.waitForPerfettoIdle(); - const coords = assertExists(await animThread.boundingBox()); + const coords = checkExists(await animThread.boundingBox()); await page.mouse.move(600, coords.y + 10); await page.mouse.down(); await page.mouse.move(1000, coords.y + 20);
diff --git a/ui/src/test/perfetto_ui_test_helper.ts b/ui/src/test/perfetto_ui_test_helper.ts index bcbbee4..02b4689 100644 --- a/ui/src/test/perfetto_ui_test_helper.ts +++ b/ui/src/test/perfetto_ui_test_helper.ts
@@ -21,7 +21,7 @@ import fs from 'fs'; import path from 'path'; import {IdleDetectorWindow} from '../frontend/idle_detector_interface'; -import {assertExists} from '../base/assert'; +import {checkExists} from '../base/assert'; import {Size2D} from '../base/geom'; import {AppImpl} from '../core/app_impl'; @@ -44,7 +44,7 @@ async sidebarSize(): Promise<Size2D> { if (this.cachedSidebarSize === undefined) { const size = await this.page.locator('main > .pf-sidebar').boundingBox(); - this.cachedSidebarSize = assertExists(size); + this.cachedSidebarSize = checkExists(size); } return this.cachedSidebarSize; } @@ -69,7 +69,7 @@ localStorage.setItem('dismissedPanningHint', 'true'), ); const tracePath = this.getTestTracePath(traceName); - await assertExists(file).setInputFiles(tracePath); + await checkExists(file).setInputFiles(tracePath); await this.waitForPerfettoIdle(); await this.applyTestingStyles(); await this.page.mouse.move(0, 0);
diff --git a/ui/src/test/wattson.test.ts b/ui/src/test/wattson.test.ts index 882a6e4..5f75d07 100644 --- a/ui/src/test/wattson.test.ts +++ b/ui/src/test/wattson.test.ts
@@ -14,7 +14,7 @@ import {test, Page} from '@playwright/test'; import {PerfettoTestHelper} from './perfetto_ui_test_helper'; -import {assertExists} from '../base/assert'; +import {checkExists} from '../base/assert'; test.describe.configure({mode: 'serial'}); @@ -44,7 +44,7 @@ await wattsonGrp.scrollIntoViewIfNeeded(); await pth.toggleTrackGroup(wattsonGrp); const cpuEstimate = pth.locateTrack('Wattson/Cpu0 estimate', wattsonGrp); - const coords = assertExists(await cpuEstimate.boundingBox()); + const coords = checkExists(await cpuEstimate.boundingBox()); await page.keyboard.press('Escape'); await page.mouse.move(600, coords.y + 10); await page.mouse.down();
diff --git a/ui/src/trace_processor/engine.ts b/ui/src/trace_processor/engine.ts index f7c7e92..a0efed4 100644 --- a/ui/src/trace_processor/engine.ts +++ b/ui/src/trace_processor/engine.ts
@@ -14,7 +14,7 @@ import protos from '../protos'; import {defer, Deferred} from '../base/deferred'; -import {assertExists, assertTrue, assertUnreachable} from '../base/assert'; +import {checkExists, assertTrue, assertUnreachable} from '../base/assert'; import {ProtoRingBuffer} from './proto_ring_buffer'; import { createQueryResult, @@ -254,8 +254,8 @@ switch (rpc.response) { case TPM.TPM_APPEND_TRACE_DATA: { - const appendResult = assertExists(rpc.appendResult); - const pendingPromise = assertExists(this.pendingParses.shift()); + const appendResult = checkExists(rpc.appendResult); + const pendingPromise = checkExists(this.pendingParses.shift()); if (exists(appendResult.error) && appendResult.error.length > 0) { pendingPromise.reject(appendResult.error); } else { @@ -264,8 +264,8 @@ break; } case TPM.TPM_FINALIZE_TRACE_DATA: { - const finalizeResult = assertExists(rpc.finalizeDataResult); - const pendingPromise = assertExists(this.pendingEOFs.shift()); + const finalizeResult = checkExists(rpc.finalizeDataResult); + const pendingPromise = checkExists(this.pendingEOFs.shift()); if (exists(finalizeResult.error) && finalizeResult.error.length > 0) { pendingPromise.reject(finalizeResult.error); } else { @@ -274,14 +274,14 @@ break; } case TPM.TPM_RESET_TRACE_PROCESSOR: - assertExists(this.pendingResetTraceProcessors.shift()).resolve(); + checkExists(this.pendingResetTraceProcessors.shift()).resolve(); break; case TPM.TPM_RESTORE_INITIAL_TABLES: - assertExists(this.pendingRestoreTables.shift()).resolve(); + checkExists(this.pendingRestoreTables.shift()).resolve(); break; case TPM.TPM_QUERY_STREAMING: - const qRes = assertExists(rpc.queryResult) as {} as QueryResultBypass; - const pendingQuery = assertExists(this.pendingQueries[0]); + const qRes = checkExists(rpc.queryResult) as {} as QueryResultBypass; + const pendingQuery = checkExists(this.pendingQueries[0]); pendingQuery.appendResultBatch(qRes.rawQueryResult); if (pendingQuery.isComplete()) { this.pendingQueries.shift(); @@ -290,10 +290,10 @@ } break; case TPM.TPM_COMPUTE_METRIC: - const metricRes = assertExists( + const metricRes = checkExists( rpc.metricResult, ) as protos.ComputeMetricResult; - const pendingComputeMetric = assertExists( + const pendingComputeMetric = checkExists( this.pendingComputeMetrics.shift(), ); if (exists(metricRes.error) && metricRes.error.length > 0) { @@ -314,15 +314,15 @@ } break; case TPM.TPM_DISABLE_AND_READ_METATRACE: - const metatraceRes = assertExists( + const metatraceRes = checkExists( rpc.metatrace, ) as protos.DisableAndReadMetatraceResult; - assertExists(this.pendingReadMetatrace).resolve(metatraceRes); + checkExists(this.pendingReadMetatrace).resolve(metatraceRes); this.pendingReadMetatrace = undefined; break; case TPM.TPM_REGISTER_SQL_PACKAGE: - const registerResult = assertExists(rpc.registerSqlPackageResult); - const res = assertExists(this.pendingRegisterSqlPackage); + const registerResult = checkExists(rpc.registerSqlPackageResult); + const res = checkExists(this.pendingRegisterSqlPackage); if (exists(registerResult.error) && registerResult.error.length > 0) { res.reject(registerResult.error); } else { @@ -331,40 +331,40 @@ this.pendingRegisterSqlPackage = undefined; break; case TPM.TPM_SUMMARIZE_TRACE: - const summaryRes = assertExists( + const summaryRes = checkExists( rpc.traceSummaryResult, ) as protos.TraceSummaryResult; - assertExists(this.pendingTraceSummary).resolve(summaryRes); + checkExists(this.pendingTraceSummary).resolve(summaryRes); this.pendingTraceSummary = undefined; break; case TPM.TPM_CREATE_SUMMARIZER: - const createSummarizerRes = assertExists( + const createSummarizerRes = checkExists( rpc.createSummarizerResult, ) as protos.CreateSummarizerResult; - assertExists(this.pendingCreateSummarizer).resolve(createSummarizerRes); + checkExists(this.pendingCreateSummarizer).resolve(createSummarizerRes); this.pendingCreateSummarizer = undefined; break; case TPM.TPM_UPDATE_SUMMARIZER_SPEC: - const updateSummarizerSpecRes = assertExists( + const updateSummarizerSpecRes = checkExists( rpc.updateSummarizerSpecResult, ) as protos.UpdateSummarizerSpecResult; - assertExists(this.pendingUpdateSummarizerSpec).resolve( + checkExists(this.pendingUpdateSummarizerSpec).resolve( updateSummarizerSpecRes, ); this.pendingUpdateSummarizerSpec = undefined; break; case TPM.TPM_QUERY_SUMMARIZER: - const querySummarizerRes = assertExists( + const querySummarizerRes = checkExists( rpc.querySummarizerResult, ) as protos.QuerySummarizerResult; - assertExists(this.pendingQuerySummarizer).resolve(querySummarizerRes); + checkExists(this.pendingQuerySummarizer).resolve(querySummarizerRes); this.pendingQuerySummarizer = undefined; break; case TPM.TPM_DESTROY_SUMMARIZER: - const destroySummarizerRes = assertExists( + const destroySummarizerRes = checkExists( rpc.destroySummarizerResult, ) as protos.DestroySummarizerResult; - assertExists(this.pendingDestroySummarizer).resolve( + checkExists(this.pendingDestroySummarizer).resolve( destroySummarizerRes, ); this.pendingDestroySummarizer = undefined;
diff --git a/ui/src/trace_processor/http_rpc_engine.ts b/ui/src/trace_processor/http_rpc_engine.ts index e3129eb..c7c2b2b 100644 --- a/ui/src/trace_processor/http_rpc_engine.ts +++ b/ui/src/trace_processor/http_rpc_engine.ts
@@ -15,7 +15,7 @@ import protos from '../protos'; import {fetchWithTimeout} from '../base/http_utils'; import {reportError} from '../base/logging'; -import {assertExists} from '../base/assert'; +import {checkExists} from '../base/assert'; import {EngineBase} from '../trace_processor/engine'; const RPC_CONNECT_TIMEOUT_MS = 2000; @@ -69,7 +69,7 @@ for (;;) { const queuedMsg = this.requestQueue.shift(); if (queuedMsg === undefined) break; - assertExists(this.websocket).send(queuedMsg); + checkExists(this.websocket).send(queuedMsg); } this.connected = true; } @@ -89,7 +89,7 @@ } private onWebsocketMessage(e: MessageEvent) { - const blob = assertExists(e.data as Blob); + const blob = checkExists(e.data as Blob); this.queue.push(blob); this.processQueue(); } @@ -99,7 +99,7 @@ this.isProcessingQueue = true; while (this.queue.length > 0) { try { - const blob = assertExists(this.queue.shift()); + const blob = checkExists(this.queue.shift()); const buf = await blob.arrayBuffer(); super.onRpcResponseBytes(new Uint8Array(buf)); } catch (e) {
diff --git a/ui/src/trace_processor/query_result.ts b/ui/src/trace_processor/query_result.ts index 711e4b2..2858d7c 100644 --- a/ui/src/trace_processor/query_result.ts +++ b/ui/src/trace_processor/query_result.ts
@@ -51,7 +51,7 @@ import '../base/static_initializers'; import protobuf from 'protobufjs/minimal'; import {defer, Deferred} from '../base/deferred'; -import {assertExists, assertFalse, assertTrue} from '../base/assert'; +import {checkExists, assertFalse, assertTrue} from '../base/assert'; import {utf8Decode} from '../base/string_utils'; import {Duration, duration, Time, time} from '../base/time'; @@ -624,7 +624,7 @@ if (this.allRowsPromise === undefined) { this.waitAllRows(); // Will populate |this.allRowsPromise|. } - return assertExists(this.allRowsPromise); + return checkExists(this.allRowsPromise); } get errorInfo(): QueryErrorInfo { @@ -916,7 +916,7 @@ this.numColumns = this.columnNames.length; this.batchIdx = nextBatchIdx; - const batch = assertExists(this.resultObj.batches[nextBatchIdx]); + const batch = checkExists(this.resultObj.batches[nextBatchIdx]); this.batchBytes = batch.batchBytes; this.nextCellTypeOff = batch.cellTypesOff; this.cellTypesEnd = batch.cellTypesOff + batch.cellTypesLen;
diff --git a/ui/src/traceconv/index.ts b/ui/src/traceconv/index.ts index bfb8799..c4921ad 100644 --- a/ui/src/traceconv/index.ts +++ b/ui/src/traceconv/index.ts
@@ -66,8 +66,8 @@ } function fsNodeToBuffer(fsNode: traceconv.FileSystemNode): Uint8Array { - const fileSize = assertExists(fsNode.usedBytes); - return new Uint8Array(fsNode.contents.buffer, 0, fileSize); + assertExists(fsNode.usedBytes); + return new Uint8Array(fsNode.contents.buffer, 0, fsNode.usedBytes); } async function runTraceconv(trace: Blob, args: string[]) { @@ -79,10 +79,11 @@ printErr: updateStatus, onRuntimeInitialized: () => deferredRuntimeInitialized.resolve(), }); + assertExists(module.FS.filesystems.WORKERFS); await deferredRuntimeInitialized; module.FS.mkdir('/fs'); module.FS.mount( - assertExists(module.FS.filesystems.WORKERFS), + module.FS.filesystems.WORKERFS, {blobs: [{name: 'trace.proto', data: trace}]}, '/fs', );
diff --git a/ui/src/widgets/flamegraph.ts b/ui/src/widgets/flamegraph.ts index 2fdea3a..988402c 100644 --- a/ui/src/widgets/flamegraph.ts +++ b/ui/src/widgets/flamegraph.ts
@@ -13,7 +13,7 @@ // limitations under the License. import m from 'mithril'; -import {assertExists, assertTrue} from '../base/assert'; +import {checkExists, assertTrue, assertExists} from '../base/assert'; import {Monitor} from '../base/monitor'; import {Button, ButtonBar} from './button'; import {Chip} from './chip'; @@ -642,7 +642,7 @@ const {allRootsCumulativeValue, unfilteredCumulativeValue, nodes} = this.attrs.data; - const unit = assertExists(this.selectedMetric).unit; + assertExists(this.selectedMetric); ctx.font = LABEL_FONT_STYLE; ctx.textBaseline = 'middle'; @@ -665,7 +665,10 @@ let name: string; let colorScheme; if (source.kind === 'ROOT') { - const val = displaySize(allRootsCumulativeValue, unit); + const val = displaySize( + allRootsCumulativeValue, + this.selectedMetric.unit, + ); const percent = displayPercentage( allRootsCumulativeValue, unfilteredCumulativeValue, @@ -888,8 +891,8 @@ unfilteredCumulativeValue, nodeActions, rootActions, - } = assertExists(this.attrs.data); - const {unit, nameColumnLabel} = assertExists(this.selectedMetric); + } = checkExists(this.attrs.data); + const {unit, nameColumnLabel} = checkExists(this.selectedMetric); if (source.kind === 'ROOT') { const val = displaySize(allRootsCumulativeValue, unit); const percent = displayPercentage( @@ -1142,15 +1145,15 @@ } private buildStackString(node: FlamegraphNode, withDetails: boolean): string { - const {nodes, unfilteredCumulativeValue} = assertExists(this.attrs.data); - const metric = assertExists(this.selectedMetric); + const {nodes, unfilteredCumulativeValue} = checkExists(this.attrs.data); + const metric = checkExists(this.selectedMetric); const view = this.attrs.state.view; // Walk via parentId for all modes. Reverse for TOP_DOWN and PIVOT below. const stack: FlamegraphNode[] = []; let currentId = node.id; while (currentId !== -1) { - const current = assertExists(nodes.find((n) => n.id === currentId)); + const current = checkExists(nodes.find((n) => n.id === currentId)); stack.push(current); currentId = current.parentId; }
diff --git a/ui/src/widgets/hotkey_context.ts b/ui/src/widgets/hotkey_context.ts index 8e6b946..e329308 100644 --- a/ui/src/widgets/hotkey_context.ts +++ b/ui/src/widgets/hotkey_context.ts
@@ -14,8 +14,8 @@ import m from 'mithril'; import {checkHotkey, Hotkey} from '../base/hotkeys'; -import {toHTMLElement} from '../base/dom_utils'; import {classNames} from '../base/classnames'; +import {assertInstanceOf} from '../base/assert'; export interface HotkeyConfig { readonly hotkey: Hotkey; @@ -72,15 +72,17 @@ ); } - oncreate(vnode: m.VnodeDOM<HotkeyContextAttrs>) { - const focusable = vnode.attrs.focusable ?? true; + oncreate({attrs, dom}: m.VnodeDOM<HotkeyContextAttrs>) { + assertInstanceOf(dom, HTMLElement); + + const focusable = attrs.focusable ?? true; // If focusable is false, bind to document for global hotkeys. // Otherwise, bind to the element itself. - this.eventTarget = focusable ? vnode.dom : document; + this.eventTarget = focusable ? dom : document; this.eventTarget.addEventListener('keydown', this.onKeyDown); - this.hotkeys = vnode.attrs.hotkeys; - if (vnode.attrs.autoFocus && focusable) { - toHTMLElement(vnode.dom).focus(); + this.hotkeys = attrs.hotkeys; + if (attrs.autoFocus && focusable) { + dom.focus(); } }
diff --git a/ui/src/widgets/nodegraph.ts b/ui/src/widgets/nodegraph.ts index f4b1e4a..77d756f 100644 --- a/ui/src/widgets/nodegraph.ts +++ b/ui/src/widgets/nodegraph.ts
@@ -830,8 +830,8 @@ // Query ports within this NodeGraph instance only (not globally). // Using document.querySelectorAll would pick up ports from other // NodeGraph instances (e.g. hidden tabs), causing incorrect positions. - const container = assertExists(canvasElement); - const allPorts = container.querySelectorAll('.pf-port[data-port]'); + assertExists(canvasElement); + const allPorts = canvasElement.querySelectorAll('.pf-port[data-port]'); allPorts.forEach((portElement) => { const portId = portElement.getAttribute('data-port'); if (!portId) return; @@ -1071,8 +1071,8 @@ // Scope to this NodeGraph instance to avoid matching elements from other // instances (e.g. hidden tabs with the same node IDs). - const scope = assertExists(canvasElement); - const portElement = scope.querySelector(selector); + assertExists(canvasElement); + const portElement = canvasElement.querySelector(selector); if (portElement) { const nodeElement = portElement.closest('.pf-node') as HTMLElement | null; @@ -1188,8 +1188,8 @@ } function getNodeDimensions(nodeId: string): {width: number; height: number} { - const scope = assertExists(canvasElement); - const nodeElement = scope.querySelector(`[data-node="${nodeId}"]`); + assertExists(canvasElement); + const nodeElement = canvasElement.querySelector(`[data-node="${nodeId}"]`); if (nodeElement) { const rect = nodeElement.getBoundingClientRect(); // Divide by zoom to get canvas content space dimensions
diff --git a/ui/src/widgets/popup.ts b/ui/src/widgets/popup.ts index ecae28a..c3dc5e2 100644 --- a/ui/src/widgets/popup.ts +++ b/ui/src/widgets/popup.ts
@@ -17,8 +17,8 @@ import m from 'mithril'; import {MountOptions, Portal, PortalAttrs} from './portal'; import {classNames} from '../base/classnames'; -import {findRef, isOrContains, toHTMLElement} from '../base/dom_utils'; -import {assertExists} from '../base/assert'; +import {findRef, isOrContains} from '../base/dom_utils'; +import {assertExists, assertInstanceOf} from '../base/assert'; import {ExtendedModifiers} from './popper_utils'; // Note: We could just use the Placement type from popper.js instead, which is a @@ -255,9 +255,8 @@ return {container: undefined}; }, onContentMount: (dom: HTMLElement) => { - const popupElement = toHTMLElement( - assertExists(findRef(dom, Popup.POPUP_REF)), - ); + const popupElement = findRef(dom, Popup.POPUP_REF); + assertInstanceOf(popupElement, HTMLElement); this.popupElement = popupElement; this.createOrUpdatePopper(attrs); document.addEventListener('mousedown', this.handleDocMouseDown); @@ -304,7 +303,9 @@ } oncreate({dom}: m.VnodeDOM<PopupAttrs, this>) { - this.triggerElement = assertExists(findRef(dom, Popup.TRIGGER_REF)); + const triggerElement = findRef(dom, Popup.TRIGGER_REF); + assertExists(triggerElement); + this.triggerElement = triggerElement; } onupdate({attrs}: m.VnodeDOM<PopupAttrs, this>) { @@ -475,9 +476,12 @@ } private eventInPopupOrTrigger(e: Event): boolean { - const target = e.target as HTMLElement; - const onTrigger = isOrContains(assertExists(this.triggerElement), target); - const onPopup = isOrContains(assertExists(this.popupElement), target); + assertExists(this.triggerElement); + assertExists(this.popupElement); + assertInstanceOf(e.target, Element); + + const onTrigger = isOrContains(this.triggerElement, e.target); + const onPopup = isOrContains(this.popupElement, e.target); return onTrigger || onPopup; }
diff --git a/ui/src/widgets/settings_shell.ts b/ui/src/widgets/settings_shell.ts index f8e9c63..d6ab2b5 100644 --- a/ui/src/widgets/settings_shell.ts +++ b/ui/src/widgets/settings_shell.ts
@@ -52,17 +52,16 @@ ); } - oncreate(vnode: m.VnodeDOM<SettingsShellAttrs, this>) { - const canary = assertExists( - vnode.dom.querySelector('.pf-settings-shell__title'), - ); - const header = assertExists( - vnode.dom.querySelector('.pf-settings-shell__header'), - ); + oncreate({dom}: m.VnodeDOM<SettingsShellAttrs, this>) { + const canaryElement = dom.querySelector('.pf-settings-shell__title'); + const headerElement = dom.querySelector('.pf-settings-shell__header'); + + assertExists(canaryElement); + assertExists(headerElement); this.observer = new IntersectionObserver( ([entry]) => { - header.classList.toggle( + headerElement.classList.toggle( 'pf-settings-shell__header--stuck', !entry.isIntersecting, ); @@ -70,7 +69,7 @@ {threshold: [0]}, ); - this.observer.observe(canary); + this.observer.observe(canaryElement); } onremove() {
diff --git a/ui/src/widgets/tooltip.ts b/ui/src/widgets/tooltip.ts index dd3da34..f55e0ba 100644 --- a/ui/src/widgets/tooltip.ts +++ b/ui/src/widgets/tooltip.ts
@@ -16,8 +16,8 @@ import m from 'mithril'; import {MountOptions, Portal, PortalAttrs} from './portal'; import {classNames} from '../base/classnames'; -import {findRef, toHTMLElement} from '../base/dom_utils'; -import {assertExists} from '../base/assert'; +import {findRef} from '../base/dom_utils'; +import {assertExists, assertInstanceOf} from '../base/assert'; import {PopupPosition} from './popup'; import {ExtendedModifiers} from './popper_utils'; @@ -126,9 +126,9 @@ return {container: undefined}; }, onContentMount: (dom: HTMLElement) => { - const popupElement = toHTMLElement( - assertExists(findRef(dom, Tooltip.TOOLTIP_REF)), - ); + const popupElement = findRef(dom, Tooltip.TOOLTIP_REF); + assertInstanceOf(popupElement, HTMLElement); + this.tooltipElement = popupElement; this.createOrUpdatePopper(attrs); onTooltipMount(popupElement); @@ -162,7 +162,9 @@ } oncreate({dom}: m.VnodeDOM<TooltipAttrs, this>) { - this.triggerElement = assertExists(findRef(dom, Tooltip.TRIGGER_REF)); + const triggerElement = findRef(dom, Tooltip.TRIGGER_REF); + assertExists(triggerElement); + this.triggerElement = triggerElement; } onupdate({attrs}: m.VnodeDOM<TooltipAttrs, this>) {
diff --git a/ui/src/widgets/virtual_overlay_canvas.ts b/ui/src/widgets/virtual_overlay_canvas.ts index 468afa6..8048b48 100644 --- a/ui/src/widgets/virtual_overlay_canvas.ts +++ b/ui/src/widgets/virtual_overlay_canvas.ts
@@ -31,9 +31,9 @@ import m from 'mithril'; import {DisposableStack} from '../base/disposable_stack'; -import {findRef, toHTMLElement} from '../base/dom_utils'; +import {findRef} from '../base/dom_utils'; import {Rect2D, Size2D} from '../base/geom'; -import {assertExists} from '../base/assert'; +import {assertDefined, assertInstanceOf, checkExists} from '../base/assert'; import {VirtualCanvas} from '../base/virtual_canvas'; import {WebGLRenderer} from '../base/gl/webgl_renderer'; import {Canvas2DRenderer} from '../base/canvas2d_renderer'; @@ -167,9 +167,8 @@ oncreate({attrs, dom}: m.CVnodeDOM<VirtualOverlayCanvasAttrs>) { this.dom = dom; - const canvasContainerElement = toHTMLElement( - assertExists(findRef(dom, CANVAS_CONTAINER_REF)), - ); + const canvasContainerElement = findRef(dom, CANVAS_CONTAINER_REF); + assertInstanceOf(canvasContainerElement, HTMLElement); const {overflowX = 'visible', overflowY = 'visible'} = attrs; // Create the virtual canvas inside the canvas container element. We assume @@ -184,7 +183,7 @@ this.virtualCanvas = virtualCanvas; // Create the canvas rendering context - this.ctx = assertExists(virtualCanvas.canvasElement.getContext('2d')); + this.ctx = checkExists(virtualCanvas.canvasElement.getContext('2d')); // Create WebGL canvas if enabled if (attrs.enableWebGL) { @@ -274,10 +273,12 @@ } private redrawCanvas() { - const ctx = assertExists(this.ctx); - const virtualCanvas = assertExists(this.virtualCanvas); - const attrs = assertExists(this.attrs); - const containerElement = assertExists(this.dom); + const {ctx, virtualCanvas, attrs, dom} = this; + + assertDefined(ctx); + assertDefined(virtualCanvas); + assertDefined(attrs); + assertDefined(dom); // Create the appropriate renderer: WebGLRenderer if available, otherwise // Canvas2DRenderer as fallback. @@ -300,7 +301,7 @@ // Call the user-provided draw callback to render into the canvas attrs.onCanvasRedraw?.({ - dom: containerElement, + dom, ctx, virtualCanvasSize: virtualCanvas.size, canvasRect: virtualCanvas.canvasRect,