1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-26 16:09:27 -05:00

fix: spec conformance for performance API (#10887)

This commit is contained in:
Leo K 2021-07-05 13:17:11 +02:00 committed by GitHub
parent a6c840d150
commit 220104f577
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 238 additions and 100 deletions

View file

@ -82,12 +82,12 @@ unitTest(function performanceMeasure() {
}); });
unitTest(function performanceIllegalConstructor() { unitTest(function performanceIllegalConstructor() {
assertThrows(() => new Performance(), TypeError, "Illegal constructor."); assertThrows(() => new Performance(), TypeError, "Illegal constructor");
assertEquals(Performance.length, 0); assertEquals(Performance.length, 0);
}); });
unitTest(function performanceEntryIllegalConstructor() { unitTest(function performanceEntryIllegalConstructor() {
assertThrows(() => new PerformanceEntry(), TypeError, "Illegal constructor."); assertThrows(() => new PerformanceEntry(), TypeError, "Illegal constructor");
assertEquals(PerformanceEntry.length, 0); assertEquals(PerformanceEntry.length, 0);
}); });
@ -95,6 +95,6 @@ unitTest(function performanceMeasureIllegalConstructor() {
assertThrows( assertThrows(
() => new PerformanceMeasure(), () => new PerformanceMeasure(),
TypeError, TypeError,
"Illegal constructor.", "Illegal constructor",
); );
}); });

View file

@ -10,6 +10,58 @@
const customInspect = Symbol.for("Deno.customInspect"); const customInspect = Symbol.for("Deno.customInspect");
let performanceEntries = []; 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( function findMostRecent(
name, name,
type, type,
@ -50,26 +102,34 @@
const now = opNow; const now = opNow;
const _name = Symbol("[[name]]");
const _entryType = Symbol("[[entryType]]");
const _startTime = Symbol("[[startTime]]");
const _duration = Symbol("[[duration]]");
class PerformanceEntry { class PerformanceEntry {
#name = ""; [_name] = "";
#entryType = ""; [_entryType] = "";
#startTime = 0; [_startTime] = 0;
#duration = 0; [_duration] = 0;
get name() { get name() {
return this.#name; webidl.assertBranded(this, PerformanceEntry);
return this[_name];
} }
get entryType() { get entryType() {
return this.#entryType; webidl.assertBranded(this, PerformanceEntry);
return this[_entryType];
} }
get startTime() { get startTime() {
return this.#startTime; webidl.assertBranded(this, PerformanceEntry);
return this[_startTime];
} }
get duration() { get duration() {
return this.#duration; webidl.assertBranded(this, PerformanceEntry);
return this[_duration];
} }
constructor( constructor(
@ -77,41 +137,48 @@
entryType = null, entryType = null,
startTime = null, startTime = null,
duration = null, duration = null,
key = null, key = undefined,
) { ) {
if (key != illegalConstructorKey) { if (key !== illegalConstructorKey) {
throw new TypeError("Illegal constructor."); webidl.illegalConstructor();
} }
this.#name = name; this[webidl.brand] = webidl.brand;
this.#entryType = entryType;
this.#startTime = startTime; this[_name] = name;
this.#duration = duration; this[_entryType] = entryType;
this[_startTime] = startTime;
this[_duration] = duration;
} }
toJSON() { toJSON() {
webidl.assertBranded(this, PerformanceEntry);
return { return {
name: this.#name, name: this[_name],
entryType: this.#entryType, entryType: this[_entryType],
startTime: this.#startTime, startTime: this[_startTime],
duration: this.#duration, duration: this[_duration],
}; };
} }
[customInspect]() { [customInspect](inspect) {
return `${this.constructor.name} { name: "${this.name}", entryType: "${this.entryType}", startTime: ${this.startTime}, duration: ${this.duration} }`; return `${this.constructor.name} ${inspect(this.toJSON())}`;
} }
} }
webidl.configurePrototype(PerformanceEntry);
const _detail = Symbol("[[detail]]");
class PerformanceMark extends PerformanceEntry { class PerformanceMark extends PerformanceEntry {
[Symbol.toStringTag] = "PerformanceMark"; [Symbol.toStringTag] = "PerformanceMark";
#detail = null; [_detail] = null;
get detail() { get detail() {
return this.#detail; webidl.assertBranded(this, PerformanceMark);
return this[_detail];
} }
get entryType() { get entryType() {
webidl.assertBranded(this, PerformanceMark);
return "mark"; return "mark";
} }
@ -122,28 +189,28 @@
const prefix = "Failed to construct 'PerformanceMark'"; const prefix = "Failed to construct 'PerformanceMark'";
webidl.requiredArguments(arguments.length, 1, { prefix }); webidl.requiredArguments(arguments.length, 1, { prefix });
// ensure options is object-ish, or null-ish name = webidl.converters.DOMString(name, {
switch (typeof options) { prefix,
case "object": // includes null context: "Argument 1",
case "function": });
case "undefined": {
break;
}
default: {
throw new TypeError("Invalid options");
}
}
const { detail = null, startTime = now() } = options ?? {}; options = webidl.converters.PerformanceMarkOptions(options, {
prefix,
context: "Argument 2",
});
const { detail = null, startTime = now() } = options;
super(name, "mark", startTime, 0, illegalConstructorKey); super(name, "mark", startTime, 0, illegalConstructorKey);
this[webidl.brand] = webidl.brand;
if (startTime < 0) { if (startTime < 0) {
throw new TypeError("startTime cannot be negative"); throw new TypeError("startTime cannot be negative");
} }
this.#detail = structuredClone(detail); this[_detail] = structuredClone(detail);
} }
toJSON() { toJSON() {
webidl.assertBranded(this, PerformanceMark);
return { return {
name: this.name, name: this.name,
entryType: this.entryType, entryType: this.entryType,
@ -153,43 +220,45 @@
}; };
} }
[customInspect]() { [customInspect](inspect) {
return this.detail return `${this.constructor.name} ${inspect(this.toJSON())}`;
? `${this.constructor.name} {\n detail: ${
JSON.stringify(this.detail, null, 2)
},\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} }`;
} }
} }
webidl.configurePrototype(PerformanceMark);
class PerformanceMeasure extends PerformanceEntry { class PerformanceMeasure extends PerformanceEntry {
[Symbol.toStringTag] = "PerformanceMeasure"; [Symbol.toStringTag] = "PerformanceMeasure";
#detail = null; [_detail] = null;
get detail() { get detail() {
return this.#detail; webidl.assertBranded(this, PerformanceMeasure);
return this[_detail];
} }
get entryType() { get entryType() {
webidl.assertBranded(this, PerformanceMeasure);
return "measure"; return "measure";
} }
constructor( constructor(
name, name = null,
startTime, startTime = null,
duration, duration = null,
detail = null, detail = null,
key, key = undefined,
) { ) {
if (key != illegalConstructorKey) { if (key !== illegalConstructorKey) {
throw new TypeError("Illegal constructor."); webidl.illegalConstructor();
} }
super(name, "measure", startTime, duration, illegalConstructorKey);
this.#detail = structuredClone(detail); super(name, "measure", startTime, duration, key);
this[webidl.brand] = webidl.brand;
this[_detail] = structuredClone(detail);
} }
toJSON() { toJSON() {
webidl.assertBranded(this, PerformanceMeasure);
return { return {
name: this.name, name: this.name,
entryType: this.entryType, entryType: this.entryType,
@ -199,70 +268,117 @@
}; };
} }
[customInspect]() { [customInspect](inspect) {
return this.detail return `${this.constructor.name} ${inspect(this.toJSON())}`;
? `${this.constructor.name} {\n detail: ${
JSON.stringify(this.detail, null, 2)
},\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} }`;
} }
} }
webidl.configurePrototype(PerformanceMeasure);
class Performance { class Performance {
constructor(key = null) { constructor() {
if (key != illegalConstructorKey) { webidl.illegalConstructor();
throw new TypeError("Illegal constructor.");
}
} }
clearMarks(markName) { clearMarks(markName = undefined) {
if (markName == null) { webidl.assertBranded(this, Performance);
performanceEntries = performanceEntries.filter( if (markName !== undefined) {
(entry) => entry.entryType !== "mark", markName = webidl.converters.DOMString(markName, {
); prefix: "Failed to execute 'clearMarks' on 'Performance'",
} else { context: "Argument 1",
});
performanceEntries = performanceEntries.filter( performanceEntries = performanceEntries.filter(
(entry) => !(entry.name === markName && entry.entryType === "mark"), (entry) => !(entry.name === markName && entry.entryType === "mark"),
); );
} else {
performanceEntries = performanceEntries.filter(
(entry) => entry.entryType !== "mark",
);
} }
} }
clearMeasures(measureName) { clearMeasures(measureName = undefined) {
if (measureName == null) { webidl.assertBranded(this, Performance);
performanceEntries = performanceEntries.filter( if (measureName !== undefined) {
(entry) => entry.entryType !== "measure", measureName = webidl.converters.DOMString(measureName, {
); prefix: "Failed to execute 'clearMeasures' on 'Performance'",
} else { context: "Argument 1",
});
performanceEntries = performanceEntries.filter( performanceEntries = performanceEntries.filter(
(entry) => (entry) =>
!(entry.name === measureName && entry.entryType === "measure"), !(entry.name === measureName && entry.entryType === "measure"),
); );
} else {
performanceEntries = performanceEntries.filter(
(entry) => entry.entryType !== "measure",
);
} }
} }
getEntries() { getEntries() {
webidl.assertBranded(this, Performance);
return filterByNameType(); return filterByNameType();
} }
getEntriesByName( getEntriesByName(
name, name,
type, 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); return filterByNameType(name, type);
} }
getEntriesByType(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); return filterByNameType(undefined, type);
} }
mark( mark(
markName, markName,
options = {}, 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 // 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, // same name as a read only attribute in the PerformanceTiming interface,
// throw a SyntaxError. - not implemented // throw a SyntaxError. - not implemented
const entry = new PerformanceMark(markName, options); const entry = new PerformanceMark(markName, markOptions);
// 3.1.1.7 Queue entry - not implemented // 3.1.1.7 Queue entry - not implemented
performanceEntries.push(entry); performanceEntries.push(entry);
return entry; return entry;
@ -271,8 +387,30 @@
measure( measure(
measureName, measureName,
startOrMeasureOptions = {}, startOrMeasureOptions = {},
endMark, 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 ( if (
startOrMeasureOptions && typeof startOrMeasureOptions === "object" && startOrMeasureOptions && typeof startOrMeasureOptions === "object" &&
Object.keys(startOrMeasureOptions).length > 0 Object.keys(startOrMeasureOptions).length > 0
@ -350,17 +488,30 @@
} }
now() { now() {
webidl.assertBranded(this, Performance);
return now(); return now();
} }
toJSON() {
webidl.assertBranded(this, Performance);
return {};
} }
const performance = new Performance(illegalConstructorKey); [customInspect](inspect) {
return `${this.constructor.name} ${inspect(this.toJSON())}`;
}
get [Symbol.toStringTag]() {
return "Performance";
}
}
webidl.configurePrototype(Performance);
window.__bootstrap.performance = { window.__bootstrap.performance = {
PerformanceEntry, PerformanceEntry,
PerformanceMark, PerformanceMark,
PerformanceMeasure, PerformanceMeasure,
Performance, Performance,
performance, performance: webidl.createBranded(Performance),
}; };
})(this); })(this);

View file

@ -612,6 +612,7 @@
}; };
converters.DOMTimeStamp = converters["unsigned long long"]; converters.DOMTimeStamp = converters["unsigned long long"];
converters.DOMHighResTimeStamp = converters["double"];
converters.Function = convertCallbackFunction; converters.Function = convertCallbackFunction;

View file

@ -283,12 +283,8 @@
"idlharness.any.html": [ "idlharness.any.html": [
"Performance interface: existence and properties of interface object", "Performance interface: existence and properties of interface object",
"Performance interface: existence and properties of interface prototype object", "Performance interface: existence and properties of interface prototype object",
"Performance interface: operation now()",
"Performance interface: attribute timeOrigin", "Performance interface: attribute timeOrigin",
"Performance interface: operation toJSON()",
"Stringification of performance",
"Performance interface: performance must inherit property \"timeOrigin\" with the proper type", "Performance interface: performance must inherit property \"timeOrigin\" with the proper type",
"Performance interface: performance must inherit property \"toJSON()\" with the proper type",
"Performance interface: default toJSON operation on performance", "Performance interface: default toJSON operation on performance",
"Window interface: attribute performance" "Window interface: attribute performance"
], ],
@ -507,17 +503,7 @@
"clear_one_mark.any.html": true, "clear_one_mark.any.html": true,
"clear_one_measure.any.html": true, "clear_one_measure.any.html": true,
"entry_type.any.html": true, "entry_type.any.html": true,
"idlharness.any.html": [ "idlharness.any.html": true,
"PerformanceMark interface: attribute detail",
"PerformanceMeasure interface object length",
"PerformanceMeasure interface: attribute detail",
"Performance interface: operation mark(DOMString, optional PerformanceMarkOptions)",
"Performance interface: operation clearMarks(optional DOMString)",
"Performance interface: operation measure(DOMString, optional (DOMString or PerformanceMeasureOptions), optional DOMString)",
"Performance interface: operation clearMeasures(optional DOMString)",
"Performance interface: calling mark(DOMString, optional PerformanceMarkOptions) on performance with too few arguments must throw TypeError",
"Performance interface: calling measure(DOMString, optional (DOMString or PerformanceMeasureOptions), optional DOMString) on performance with too few arguments must throw TypeError"
],
"mark-entry-constructor.any.html": true, "mark-entry-constructor.any.html": true,
"mark-errors.any.html": true, "mark-errors.any.html": true,
"mark-l3.any.html": false, "mark-l3.any.html": false,