1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-26 09:10:40 -05:00
denoland-deno/cli/js/web/performance.ts
2020-07-14 15:24:17 -04:00

332 lines
9.2 KiB
TypeScript

// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { now as opNow } from "../ops/timers.ts";
import { customInspect, inspect } from "./console.ts";
import { cloneValue, setFunctionName } from "./util.ts";
let performanceEntries: PerformanceEntryList = [];
function findMostRecent(
name: string,
type: "mark" | "measure",
): PerformanceEntry | undefined {
return performanceEntries
.slice()
.reverse()
.find((entry) => entry.name === name && entry.entryType === type);
}
function convertMarkToTimestamp(mark: string | number): number {
if (typeof mark === "string") {
const entry = findMostRecent(mark, "mark");
if (!entry) {
throw new SyntaxError(`Cannot find mark: "${mark}".`);
}
return entry.startTime;
}
if (mark < 0) {
throw new TypeError("Mark cannot be negative.");
}
return mark;
}
function filterByNameType(
name?: string,
type?: "mark" | "measure",
): PerformanceEntryList {
return performanceEntries.filter(
(entry) =>
(name ? entry.name === name : true) &&
(type ? entry.entryType === type : true),
);
}
function now(): number {
const res = opNow();
return res.seconds * 1e3 + res.subsecNanos / 1e6;
}
export class PerformanceEntryImpl implements PerformanceEntry {
#name: string;
#entryType: string;
#startTime: number;
#duration: number;
get name(): string {
return this.#name;
}
get entryType(): string {
return this.#entryType;
}
get startTime(): number {
return this.#startTime;
}
get duration(): number {
return this.#duration;
}
constructor(
name: string,
entryType: string,
startTime: number,
duration: number,
) {
this.#name = name;
this.#entryType = entryType;
this.#startTime = startTime;
this.#duration = duration;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toJSON(): any {
return {
name: this.#name,
entryType: this.#entryType,
startTime: this.#startTime,
duration: this.#duration,
};
}
[customInspect](): string {
return `${this.constructor.name} { name: "${this.name}", entryType: "${this.entryType}", startTime: ${this.startTime}, duration: ${this.duration} }`;
}
}
export class PerformanceMarkImpl extends PerformanceEntryImpl
implements PerformanceMark {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
#detail: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get detail(): any {
return this.#detail;
}
get entryType(): "mark" {
return "mark";
}
constructor(
name: string,
{ detail = null, startTime = now() }: PerformanceMarkOptions = {},
) {
super(name, "mark", startTime, 0);
if (startTime < 0) {
throw new TypeError("startTime cannot be negative");
}
this.#detail = cloneValue(detail);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toJSON(): any {
return {
name: this.name,
entryType: this.entryType,
startTime: this.startTime,
duration: this.duration,
detail: this.detail,
};
}
[customInspect](): string {
return this.detail
? `${this.constructor.name} {\n detail: ${
inspect(this.detail, { depth: 3 })
},\n name: "${this.name}",\n entryType: "${this.entryType}",\n startTime: ${this.startTime},\n duration: ${this.duration}\n}`
: `${this.constructor.name} { detail: ${this.detail}, name: "${this.name}", entryType: "${this.entryType}", startTime: ${this.startTime}, duration: ${this.duration} }`;
}
}
export class PerformanceMeasureImpl extends PerformanceEntryImpl
implements PerformanceMeasure {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
#detail: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get detail(): any {
return this.#detail;
}
get entryType(): "measure" {
return "measure";
}
constructor(
name: string,
startTime: number,
duration: number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
detail: any = null,
) {
super(name, "measure", startTime, duration);
this.#detail = cloneValue(detail);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toJSON(): any {
return {
name: this.name,
entryType: this.entryType,
startTime: this.startTime,
duration: this.duration,
detail: this.detail,
};
}
[customInspect](): string {
return this.detail
? `${this.constructor.name} {\n detail: ${
inspect(this.detail, { depth: 3 })
},\n name: "${this.name}",\n entryType: "${this.entryType}",\n startTime: ${this.startTime},\n duration: ${this.duration}\n}`
: `${this.constructor.name} { detail: ${this.detail}, name: "${this.name}", entryType: "${this.entryType}", startTime: ${this.startTime}, duration: ${this.duration} }`;
}
}
export class PerformanceImpl implements Performance {
clearMarks(markName?: string): void {
if (markName == null) {
performanceEntries = performanceEntries.filter(
(entry) => entry.entryType !== "mark",
);
} else {
performanceEntries = performanceEntries.filter(
(entry) => !(entry.name === markName && entry.entryType === "mark"),
);
}
}
clearMeasures(measureName?: string): void {
if (measureName == null) {
performanceEntries = performanceEntries.filter(
(entry) => entry.entryType !== "measure",
);
} else {
performanceEntries = performanceEntries.filter(
(entry) =>
!(entry.name === measureName && entry.entryType === "measure"),
);
}
}
getEntries(): PerformanceEntryList {
return filterByNameType();
}
getEntriesByName(
name: string,
type?: "mark" | "measure",
): PerformanceEntryList {
return filterByNameType(name, type);
}
getEntriesByType(type: "mark" | "measure"): PerformanceEntryList {
return filterByNameType(undefined, type);
}
mark(
markName: string,
options: PerformanceMarkOptions = {},
): PerformanceMark {
// 3.1.1.1 If the global object is a Window object and markName uses the
// same name as a read only attribute in the PerformanceTiming interface,
// throw a SyntaxError. - not implemented
const entry = new PerformanceMarkImpl(markName, options);
// 3.1.1.7 Queue entry - not implemented
performanceEntries.push(entry);
return entry;
}
measure(
measureName: string,
options?: PerformanceMeasureOptions,
): PerformanceMeasure;
measure(
measureName: string,
startMark?: string,
endMark?: string,
): PerformanceMeasure;
measure(
measureName: string,
startOrMeasureOptions: string | PerformanceMeasureOptions = {},
endMark?: string,
): PerformanceMeasure {
if (startOrMeasureOptions && typeof startOrMeasureOptions === "object") {
if (endMark) {
throw new TypeError("Options cannot be passed with endMark.");
}
if (
!("start" in startOrMeasureOptions) &&
!("end" in startOrMeasureOptions)
) {
throw new TypeError("A start or end mark must be supplied in options.");
}
if (
"start" in startOrMeasureOptions &&
"duration" in startOrMeasureOptions &&
"end" in startOrMeasureOptions
) {
throw new TypeError(
"Cannot specify start, end, and duration together in options.",
);
}
}
let endTime: number;
if (endMark) {
endTime = convertMarkToTimestamp(endMark);
} else if (
typeof startOrMeasureOptions === "object" &&
"end" in startOrMeasureOptions
) {
endTime = convertMarkToTimestamp(startOrMeasureOptions.end!);
} else if (
typeof startOrMeasureOptions === "object" &&
"start" in startOrMeasureOptions &&
"duration" in startOrMeasureOptions
) {
const start = convertMarkToTimestamp(startOrMeasureOptions.start!);
const duration = convertMarkToTimestamp(startOrMeasureOptions.duration!);
endTime = start + duration;
} else {
endTime = now();
}
let startTime: number;
if (
typeof startOrMeasureOptions === "object" &&
"start" in startOrMeasureOptions
) {
startTime = convertMarkToTimestamp(startOrMeasureOptions.start!);
} else if (
typeof startOrMeasureOptions === "object" &&
"end" in startOrMeasureOptions &&
"duration" in startOrMeasureOptions
) {
const end = convertMarkToTimestamp(startOrMeasureOptions.end!);
const duration = convertMarkToTimestamp(startOrMeasureOptions.duration!);
startTime = end - duration;
} else if (typeof startOrMeasureOptions === "string") {
startTime = convertMarkToTimestamp(startOrMeasureOptions);
} else {
startTime = 0;
}
const entry = new PerformanceMeasureImpl(
measureName,
startTime,
endTime - startTime,
typeof startOrMeasureOptions === "object"
? startOrMeasureOptions.detail ?? null
: null,
);
performanceEntries.push(entry);
return entry;
}
now(): number {
return now();
}
}
setFunctionName(PerformanceEntryImpl, "PerformanceEntry");
setFunctionName(PerformanceMarkImpl, "PerformanceMark");
setFunctionName(PerformanceMeasureImpl, "PerformanceMeasure");
setFunctionName(PerformanceImpl, "Performance");