summaryrefslogtreecommitdiff
path: root/ext/timers/02_performance.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/timers/02_performance.js')
-rw-r--r--ext/timers/02_performance.js569
1 files changed, 569 insertions, 0 deletions
diff --git a/ext/timers/02_performance.js b/ext/timers/02_performance.js
new file mode 100644
index 000000000..f752ba933
--- /dev/null
+++ b/ext/timers/02_performance.js
@@ -0,0 +1,569 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+"use strict";
+
+((window) => {
+ const {
+ ArrayPrototypeFilter,
+ ArrayPrototypeFind,
+ ArrayPrototypePush,
+ ArrayPrototypeReverse,
+ ArrayPrototypeSlice,
+ ObjectKeys,
+ Symbol,
+ SymbolFor,
+ SymbolToStringTag,
+ TypeError,
+ } = window.__bootstrap.primordials;
+
+ const { webidl, structuredClone } = window.__bootstrap;
+ const consoleInternal = window.__bootstrap.console;
+ const { opNow } = window.__bootstrap.timers;
+ const { DOMException } = window.__bootstrap.domException;
+
+ const illegalConstructorKey = Symbol("illegalConstructorKey");
+ const customInspect = SymbolFor("Deno.customInspect");
+ let performanceEntries = [];
+
+ webidl.converters["PerformanceMarkOptions"] = webidl
+ .createDictionaryConverter(
+ "PerformanceMarkOptions",
+ [
+ {
+ key: "detail",
+ converter: webidl.converters.any,
+ },
+ {
+ key: "startTime",
+ converter: webidl.converters.DOMHighResTimeStamp,
+ },
+ ],
+ );
+
+ webidl.converters["DOMString or DOMHighResTimeStamp"] = (V, opts) => {
+ if (webidl.type(V) === "Number" && V !== null) {
+ return webidl.converters.DOMHighResTimeStamp(V, opts);
+ }
+ return webidl.converters.DOMString(V, opts);
+ };
+
+ webidl.converters["PerformanceMeasureOptions"] = webidl
+ .createDictionaryConverter(
+ "PerformanceMeasureOptions",
+ [
+ {
+ key: "detail",
+ converter: webidl.converters.any,
+ },
+ {
+ key: "start",
+ converter: webidl.converters["DOMString or DOMHighResTimeStamp"],
+ },
+ {
+ key: "duration",
+ converter: webidl.converters.DOMHighResTimeStamp,
+ },
+ {
+ key: "end",
+ converter: webidl.converters["DOMString or DOMHighResTimeStamp"],
+ },
+ ],
+ );
+
+ webidl.converters["DOMString or PerformanceMeasureOptions"] = (V, opts) => {
+ if (webidl.type(V) === "Object" && V !== null) {
+ return webidl.converters["PerformanceMeasureOptions"](V, opts);
+ }
+ return webidl.converters.DOMString(V, opts);
+ };
+
+ function findMostRecent(
+ name,
+ type,
+ ) {
+ return ArrayPrototypeFind(
+ ArrayPrototypeReverse(ArrayPrototypeSlice(performanceEntries)),
+ (entry) => entry.name === name && entry.entryType === type,
+ );
+ }
+
+ function convertMarkToTimestamp(mark) {
+ if (typeof mark === "string") {
+ const entry = findMostRecent(mark, "mark");
+ if (!entry) {
+ throw new DOMException(
+ `Cannot find mark: "${mark}".`,
+ "SyntaxError",
+ );
+ }
+ return entry.startTime;
+ }
+ if (mark < 0) {
+ throw new TypeError("Mark cannot be negative.");
+ }
+ return mark;
+ }
+
+ function filterByNameType(
+ name,
+ type,
+ ) {
+ return ArrayPrototypeFilter(
+ performanceEntries,
+ (entry) =>
+ (name ? entry.name === name : true) &&
+ (type ? entry.entryType === type : true),
+ );
+ }
+
+ const now = opNow;
+
+ const _name = Symbol("[[name]]");
+ const _entryType = Symbol("[[entryType]]");
+ const _startTime = Symbol("[[startTime]]");
+ const _duration = Symbol("[[duration]]");
+ class PerformanceEntry {
+ [_name] = "";
+ [_entryType] = "";
+ [_startTime] = 0;
+ [_duration] = 0;
+
+ get name() {
+ webidl.assertBranded(this, PerformanceEntry);
+ return this[_name];
+ }
+
+ get entryType() {
+ webidl.assertBranded(this, PerformanceEntry);
+ return this[_entryType];
+ }
+
+ get startTime() {
+ webidl.assertBranded(this, PerformanceEntry);
+ return this[_startTime];
+ }
+
+ get duration() {
+ webidl.assertBranded(this, PerformanceEntry);
+ return this[_duration];
+ }
+
+ constructor(
+ name = null,
+ entryType = null,
+ startTime = null,
+ duration = null,
+ key = undefined,
+ ) {
+ if (key !== illegalConstructorKey) {
+ webidl.illegalConstructor();
+ }
+ this[webidl.brand] = webidl.brand;
+
+ this[_name] = name;
+ this[_entryType] = entryType;
+ this[_startTime] = startTime;
+ this[_duration] = duration;
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, PerformanceEntry);
+ return {
+ name: this[_name],
+ entryType: this[_entryType],
+ startTime: this[_startTime],
+ duration: this[_duration],
+ };
+ }
+
+ [customInspect](inspect) {
+ return inspect(consoleInternal.createFilteredInspectProxy({
+ object: this,
+ evaluate: this instanceof PerformanceEntry,
+ keys: [
+ "name",
+ "entryType",
+ "startTime",
+ "duration",
+ ],
+ }));
+ }
+ }
+ webidl.configurePrototype(PerformanceEntry);
+
+ const _detail = Symbol("[[detail]]");
+ class PerformanceMark extends PerformanceEntry {
+ [SymbolToStringTag] = "PerformanceMark";
+
+ [_detail] = null;
+
+ get detail() {
+ webidl.assertBranded(this, PerformanceMark);
+ return this[_detail];
+ }
+
+ get entryType() {
+ webidl.assertBranded(this, PerformanceMark);
+ return "mark";
+ }
+
+ constructor(
+ name,
+ options = {},
+ ) {
+ const prefix = "Failed to construct 'PerformanceMark'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ name = webidl.converters.DOMString(name, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ options = webidl.converters.PerformanceMarkOptions(options, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ const { detail = null, startTime = now() } = options;
+
+ super(name, "mark", startTime, 0, illegalConstructorKey);
+ this[webidl.brand] = webidl.brand;
+ if (startTime < 0) {
+ throw new TypeError("startTime cannot be negative");
+ }
+ this[_detail] = structuredClone(detail);
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, PerformanceMark);
+ return {
+ name: this.name,
+ entryType: this.entryType,
+ startTime: this.startTime,
+ duration: this.duration,
+ detail: this.detail,
+ };
+ }
+
+ [customInspect](inspect) {
+ return inspect(consoleInternal.createFilteredInspectProxy({
+ object: this,
+ evaluate: this instanceof PerformanceMark,
+ keys: [
+ "name",
+ "entryType",
+ "startTime",
+ "duration",
+ "detail",
+ ],
+ }));
+ }
+ }
+ webidl.configurePrototype(PerformanceMark);
+
+ class PerformanceMeasure extends PerformanceEntry {
+ [SymbolToStringTag] = "PerformanceMeasure";
+
+ [_detail] = null;
+
+ get detail() {
+ webidl.assertBranded(this, PerformanceMeasure);
+ return this[_detail];
+ }
+
+ get entryType() {
+ webidl.assertBranded(this, PerformanceMeasure);
+ return "measure";
+ }
+
+ constructor(
+ name = null,
+ startTime = null,
+ duration = null,
+ detail = null,
+ key = undefined,
+ ) {
+ if (key !== illegalConstructorKey) {
+ webidl.illegalConstructor();
+ }
+
+ super(name, "measure", startTime, duration, key);
+ this[webidl.brand] = webidl.brand;
+ this[_detail] = structuredClone(detail);
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, PerformanceMeasure);
+ return {
+ name: this.name,
+ entryType: this.entryType,
+ startTime: this.startTime,
+ duration: this.duration,
+ detail: this.detail,
+ };
+ }
+
+ [customInspect](inspect) {
+ return inspect(consoleInternal.createFilteredInspectProxy({
+ object: this,
+ evaluate: this instanceof PerformanceMeasure,
+ keys: [
+ "name",
+ "entryType",
+ "startTime",
+ "duration",
+ "detail",
+ ],
+ }));
+ }
+ }
+ webidl.configurePrototype(PerformanceMeasure);
+
+ class Performance {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ clearMarks(markName = undefined) {
+ webidl.assertBranded(this, Performance);
+ if (markName !== undefined) {
+ markName = webidl.converters.DOMString(markName, {
+ prefix: "Failed to execute 'clearMarks' on 'Performance'",
+ context: "Argument 1",
+ });
+
+ performanceEntries = ArrayPrototypeFilter(
+ performanceEntries,
+ (entry) => !(entry.name === markName && entry.entryType === "mark"),
+ );
+ } else {
+ performanceEntries = ArrayPrototypeFilter(
+ performanceEntries,
+ (entry) => entry.entryType !== "mark",
+ );
+ }
+ }
+
+ clearMeasures(measureName = undefined) {
+ webidl.assertBranded(this, Performance);
+ if (measureName !== undefined) {
+ measureName = webidl.converters.DOMString(measureName, {
+ prefix: "Failed to execute 'clearMeasures' on 'Performance'",
+ context: "Argument 1",
+ });
+
+ performanceEntries = ArrayPrototypeFilter(
+ performanceEntries,
+ (entry) =>
+ !(entry.name === measureName && entry.entryType === "measure"),
+ );
+ } else {
+ performanceEntries = ArrayPrototypeFilter(
+ performanceEntries,
+ (entry) => entry.entryType !== "measure",
+ );
+ }
+ }
+
+ getEntries() {
+ webidl.assertBranded(this, Performance);
+ return filterByNameType();
+ }
+
+ getEntriesByName(
+ name,
+ type = undefined,
+ ) {
+ webidl.assertBranded(this, Performance);
+ const prefix = "Failed to execute 'getEntriesByName' on 'Performance'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ name = webidl.converters.DOMString(name, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ if (type !== undefined) {
+ type = webidl.converters.DOMString(type, {
+ prefix,
+ context: "Argument 2",
+ });
+ }
+
+ return filterByNameType(name, type);
+ }
+
+ getEntriesByType(type) {
+ webidl.assertBranded(this, Performance);
+ const prefix = "Failed to execute 'getEntriesByName' on 'Performance'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ type = webidl.converters.DOMString(type, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ return filterByNameType(undefined, type);
+ }
+
+ mark(
+ markName,
+ markOptions = {},
+ ) {
+ webidl.assertBranded(this, Performance);
+ const prefix = "Failed to execute 'mark' on 'Performance'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ markName = webidl.converters.DOMString(markName, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ markOptions = webidl.converters.PerformanceMarkOptions(markOptions, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ // 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 PerformanceMark(markName, markOptions);
+ // 3.1.1.7 Queue entry - not implemented
+ ArrayPrototypePush(performanceEntries, entry);
+ return entry;
+ }
+
+ measure(
+ measureName,
+ startOrMeasureOptions = {},
+ endMark = undefined,
+ ) {
+ webidl.assertBranded(this, Performance);
+ const prefix = "Failed to execute 'measure' on 'Performance'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+
+ measureName = webidl.converters.DOMString(measureName, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ startOrMeasureOptions = webidl.converters
+ ["DOMString or PerformanceMeasureOptions"](startOrMeasureOptions, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ if (endMark !== undefined) {
+ endMark = webidl.converters.DOMString(endMark, {
+ prefix,
+ context: "Argument 3",
+ });
+ }
+
+ if (
+ startOrMeasureOptions && typeof startOrMeasureOptions === "object" &&
+ ObjectKeys(startOrMeasureOptions).length > 0
+ ) {
+ 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;
+ 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;
+ 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 PerformanceMeasure(
+ measureName,
+ startTime,
+ endTime - startTime,
+ typeof startOrMeasureOptions === "object"
+ ? startOrMeasureOptions.detail ?? null
+ : null,
+ illegalConstructorKey,
+ );
+ ArrayPrototypePush(performanceEntries, entry);
+ return entry;
+ }
+
+ now() {
+ webidl.assertBranded(this, Performance);
+ return now();
+ }
+
+ toJSON() {
+ webidl.assertBranded(this, Performance);
+ return {};
+ }
+
+ [customInspect](inspect) {
+ return inspect(consoleInternal.createFilteredInspectProxy({
+ object: this,
+ evaluate: this instanceof Performance,
+ keys: [],
+ }));
+ }
+
+ get [SymbolToStringTag]() {
+ return "Performance";
+ }
+ }
+ webidl.configurePrototype(Performance);
+
+ window.__bootstrap.performance = {
+ PerformanceEntry,
+ PerformanceMark,
+ PerformanceMeasure,
+ Performance,
+ performance: webidl.createBranded(Performance),
+ };
+})(this);