summaryrefslogtreecommitdiff
path: root/extensions/timers
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/timers')
-rw-r--r--extensions/timers/01_timers.js595
-rw-r--r--extensions/timers/02_performance.js569
-rw-r--r--extensions/timers/Cargo.toml28
-rw-r--r--extensions/timers/README.md5
-rw-r--r--extensions/timers/benches/timers_ops.rs40
-rw-r--r--extensions/timers/lib.rs193
6 files changed, 0 insertions, 1430 deletions
diff --git a/extensions/timers/01_timers.js b/extensions/timers/01_timers.js
deleted file mode 100644
index a00d5d9b9..000000000
--- a/extensions/timers/01_timers.js
+++ /dev/null
@@ -1,595 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const {
- ArrayPrototypeIndexOf,
- ArrayPrototypePush,
- ArrayPrototypeShift,
- ArrayPrototypeSplice,
- DateNow,
- Error,
- FunctionPrototypeBind,
- Map,
- MapPrototypeDelete,
- MapPrototypeGet,
- MapPrototypeHas,
- MapPrototypeSet,
- MathMax,
- Number,
- String,
- TypeError,
- } = window.__bootstrap.primordials;
-
- // Shamelessly cribbed from extensions/fetch/11_streams.js
- class AssertionError extends Error {
- constructor(msg) {
- super(msg);
- this.name = "AssertionError";
- }
- }
-
- /**
- * @param {unknown} cond
- * @param {string=} msg
- * @returns {asserts cond}
- */
- function assert(cond, msg = "Assertion failed.") {
- if (!cond) {
- throw new AssertionError(msg);
- }
- }
-
- function opStopGlobalTimer() {
- core.opSync("op_global_timer_stop");
- }
-
- function opStartGlobalTimer(timeout) {
- return core.opSync("op_global_timer_start", timeout);
- }
-
- async function opWaitGlobalTimer() {
- await core.opAsync("op_global_timer");
- }
-
- function opNow() {
- return core.opSync("op_now");
- }
-
- function sleepSync(millis = 0) {
- return core.opSync("op_sleep_sync", millis);
- }
-
- // Derived from https://github.com/vadimg/js_bintrees. MIT Licensed.
-
- class RBNode {
- constructor(data) {
- this.data = data;
- this.left = null;
- this.right = null;
- this.red = true;
- }
-
- getChild(dir) {
- return dir ? this.right : this.left;
- }
-
- setChild(dir, val) {
- if (dir) {
- this.right = val;
- } else {
- this.left = val;
- }
- }
- }
-
- class RBTree {
- #comparator = null;
- #root = null;
-
- constructor(comparator) {
- this.#comparator = comparator;
- this.#root = null;
- }
-
- /** Returns `null` if tree is empty. */
- min() {
- let res = this.#root;
- if (res === null) {
- return null;
- }
- while (res.left !== null) {
- res = res.left;
- }
- return res.data;
- }
-
- /** Returns node `data` if found, `null` otherwise. */
- find(data) {
- let res = this.#root;
- while (res !== null) {
- const c = this.#comparator(data, res.data);
- if (c === 0) {
- return res.data;
- } else {
- res = res.getChild(c > 0);
- }
- }
- return null;
- }
-
- /** returns `true` if inserted, `false` if duplicate. */
- insert(data) {
- let ret = false;
-
- if (this.#root === null) {
- // empty tree
- this.#root = new RBNode(data);
- ret = true;
- } else {
- const head = new RBNode(null); // fake tree root
-
- let dir = 0;
- let last = 0;
-
- // setup
- let gp = null; // grandparent
- let ggp = head; // grand-grand-parent
- let p = null; // parent
- let node = this.#root;
- ggp.right = this.#root;
-
- // search down
- while (true) {
- if (node === null) {
- // insert new node at the bottom
- node = new RBNode(data);
- p.setChild(dir, node);
- ret = true;
- } else if (isRed(node.left) && isRed(node.right)) {
- // color flip
- node.red = true;
- node.left.red = false;
- node.right.red = false;
- }
-
- // fix red violation
- if (isRed(node) && isRed(p)) {
- const dir2 = ggp.right === gp;
-
- assert(gp);
- if (node === p.getChild(last)) {
- ggp.setChild(dir2, singleRotate(gp, !last));
- } else {
- ggp.setChild(dir2, doubleRotate(gp, !last));
- }
- }
-
- const cmp = this.#comparator(node.data, data);
-
- // stop if found
- if (cmp === 0) {
- break;
- }
-
- last = dir;
- dir = Number(cmp < 0); // Fix type
-
- // update helpers
- if (gp !== null) {
- ggp = gp;
- }
- gp = p;
- p = node;
- node = node.getChild(dir);
- }
-
- // update root
- this.#root = head.right;
- }
-
- // make root black
- this.#root.red = false;
-
- return ret;
- }
-
- /** Returns `true` if removed, `false` if not found. */
- remove(data) {
- if (this.#root === null) {
- return false;
- }
-
- const head = new RBNode(null); // fake tree root
- let node = head;
- node.right = this.#root;
- let p = null; // parent
- let gp = null; // grand parent
- let found = null; // found item
- let dir = 1;
-
- while (node.getChild(dir) !== null) {
- const last = dir;
-
- // update helpers
- gp = p;
- p = node;
- node = node.getChild(dir);
-
- const cmp = this.#comparator(data, node.data);
-
- dir = cmp > 0;
-
- // save found node
- if (cmp === 0) {
- found = node;
- }
-
- // push the red node down
- if (!isRed(node) && !isRed(node.getChild(dir))) {
- if (isRed(node.getChild(!dir))) {
- const sr = singleRotate(node, dir);
- p.setChild(last, sr);
- p = sr;
- } else if (!isRed(node.getChild(!dir))) {
- const sibling = p.getChild(!last);
- if (sibling !== null) {
- if (
- !isRed(sibling.getChild(!last)) &&
- !isRed(sibling.getChild(last))
- ) {
- // color flip
- p.red = false;
- sibling.red = true;
- node.red = true;
- } else {
- assert(gp);
- const dir2 = gp.right === p;
-
- if (isRed(sibling.getChild(last))) {
- gp.setChild(dir2, doubleRotate(p, last));
- } else if (isRed(sibling.getChild(!last))) {
- gp.setChild(dir2, singleRotate(p, last));
- }
-
- // ensure correct coloring
- const gpc = gp.getChild(dir2);
- assert(gpc);
- gpc.red = true;
- node.red = true;
- assert(gpc.left);
- gpc.left.red = false;
- assert(gpc.right);
- gpc.right.red = false;
- }
- }
- }
- }
- }
-
- // replace and remove if found
- if (found !== null) {
- found.data = node.data;
- assert(p);
- p.setChild(p.right === node, node.getChild(node.left === null));
- }
-
- // update root and make it black
- this.#root = head.right;
- if (this.#root !== null) {
- this.#root.red = false;
- }
-
- return found !== null;
- }
- }
-
- function isRed(node) {
- return node !== null && node.red;
- }
-
- function singleRotate(root, dir) {
- const save = root.getChild(!dir);
- assert(save);
-
- root.setChild(!dir, save.getChild(dir));
- save.setChild(dir, root);
-
- root.red = true;
- save.red = false;
-
- return save;
- }
-
- function doubleRotate(root, dir) {
- root.setChild(!dir, singleRotate(root.getChild(!dir), !dir));
- return singleRotate(root, dir);
- }
-
- const { console } = globalThis;
-
- // Timeout values > TIMEOUT_MAX are set to 1.
- const TIMEOUT_MAX = 2 ** 31 - 1;
-
- let globalTimeoutDue = null;
-
- let nextTimerId = 1;
- const idMap = new Map();
- const dueTree = new RBTree((a, b) => a.due - b.due);
-
- function clearGlobalTimeout() {
- globalTimeoutDue = null;
- opStopGlobalTimer();
- }
-
- let pendingEvents = 0;
- const pendingFireTimers = [];
-
- /** Process and run a single ready timer macrotask.
- * This function should be registered through Deno.core.setMacrotaskCallback.
- * Returns true when all ready macrotasks have been processed, false if more
- * ready ones are available. The Isolate future would rely on the return value
- * to repeatedly invoke this function until depletion. Multiple invocations
- * of this function one at a time ensures newly ready microtasks are processed
- * before next macrotask timer callback is invoked. */
- function handleTimerMacrotask() {
- if (pendingFireTimers.length > 0) {
- fire(ArrayPrototypeShift(pendingFireTimers));
- return pendingFireTimers.length === 0;
- }
- return true;
- }
-
- async function setGlobalTimeout(due, now) {
- // Since JS and Rust don't use the same clock, pass the time to rust as a
- // relative time value. On the Rust side we'll turn that into an absolute
- // value again.
- const timeout = due - now;
- assert(timeout >= 0);
- // Send message to the backend.
- globalTimeoutDue = due;
- pendingEvents++;
- // FIXME(bartlomieju): this is problematic, because `clearGlobalTimeout`
- // is synchronous. That means that timer is cancelled, but this promise is still pending
- // until next turn of event loop. This leads to "leaking of async ops" in tests;
- // because `clearTimeout/clearInterval` might be the last statement in test function
- // `opSanitizer` will immediately complain that there is pending op going on, unless
- // some timeout/defer is put in place to allow promise resolution.
- // Ideally `clearGlobalTimeout` doesn't return until this op is resolved, but
- // I'm not if that's possible.
- opStartGlobalTimer(timeout);
- await opWaitGlobalTimer();
- pendingEvents--;
- prepareReadyTimers();
- }
-
- function prepareReadyTimers() {
- const now = DateNow();
- // Bail out if we're not expecting the global timer to fire.
- if (globalTimeoutDue === null || pendingEvents > 0) {
- return;
- }
- // After firing the timers that are due now, this will hold the first timer
- // list that hasn't fired yet.
- let nextDueNode;
- while ((nextDueNode = dueTree.min()) !== null && nextDueNode.due <= now) {
- dueTree.remove(nextDueNode);
- // Fire all the timers in the list.
- for (const timer of nextDueNode.timers) {
- // With the list dropped, the timer is no longer scheduled.
- timer.scheduled = false;
- // Place the callback to pending timers to fire.
- ArrayPrototypePush(pendingFireTimers, timer);
- }
- }
- setOrClearGlobalTimeout(nextDueNode && nextDueNode.due, now);
- }
-
- function setOrClearGlobalTimeout(due, now) {
- if (due == null) {
- clearGlobalTimeout();
- } else {
- setGlobalTimeout(due, now);
- }
- }
-
- function schedule(timer, now) {
- assert(!timer.scheduled);
- assert(now <= timer.due);
- // Find or create the list of timers that will fire at point-in-time `due`.
- const maybeNewDueNode = { due: timer.due, timers: [] };
- let dueNode = dueTree.find(maybeNewDueNode);
- if (dueNode === null) {
- dueTree.insert(maybeNewDueNode);
- dueNode = maybeNewDueNode;
- }
- // Append the newly scheduled timer to the list and mark it as scheduled.
- ArrayPrototypePush(dueNode.timers, timer);
- timer.scheduled = true;
- // If the new timer is scheduled to fire before any timer that existed before,
- // update the global timeout to reflect this.
- if (globalTimeoutDue === null || globalTimeoutDue > timer.due) {
- setOrClearGlobalTimeout(timer.due, now);
- }
- }
-
- function unschedule(timer) {
- // Check if our timer is pending scheduling or pending firing.
- // If either is true, they are not in tree, and their idMap entry
- // will be deleted soon. Remove it from queue.
- let index = -1;
- if ((index = ArrayPrototypeIndexOf(pendingFireTimers, timer)) >= 0) {
- ArrayPrototypeSplice(pendingFireTimers, index);
- return;
- }
- // If timer is not in the 2 pending queues and is unscheduled,
- // it is not in the tree.
- if (!timer.scheduled) {
- return;
- }
- const searchKey = { due: timer.due, timers: [] };
- // Find the list of timers that will fire at point-in-time `due`.
- const list = dueTree.find(searchKey).timers;
- if (list.length === 1) {
- // Time timer is the only one in the list. Remove the entire list.
- assert(list[0] === timer);
- dueTree.remove(searchKey);
- // If the unscheduled timer was 'next up', find when the next timer that
- // still exists is due, and update the global alarm accordingly.
- if (timer.due === globalTimeoutDue) {
- const nextDueNode = dueTree.min();
- setOrClearGlobalTimeout(
- nextDueNode && nextDueNode.due,
- DateNow(),
- );
- }
- } else {
- // Multiple timers that are due at the same point in time.
- // Remove this timer from the list.
- const index = ArrayPrototypeIndexOf(list, timer);
- assert(index > -1);
- ArrayPrototypeSplice(list, index, 1);
- }
- }
-
- function fire(timer) {
- // If the timer isn't found in the ID map, that means it has been cancelled
- // between the timer firing and the promise callback (this function).
- if (!MapPrototypeHas(idMap, timer.id)) {
- return;
- }
- // Reschedule the timer if it is a repeating one, otherwise drop it.
- if (!timer.repeat) {
- // One-shot timer: remove the timer from this id-to-timer map.
- MapPrototypeDelete(idMap, timer.id);
- } else {
- // Interval timer: compute when timer was supposed to fire next.
- // However make sure to never schedule the next interval in the past.
- const now = DateNow();
- timer.due = MathMax(now, timer.due + timer.delay);
- schedule(timer, now);
- }
- // Call the user callback. Intermediate assignment is to avoid leaking `this`
- // to it, while also keeping the stack trace neat when it shows up in there.
- const callback = timer.callback;
- if ("function" === typeof callback) {
- callback();
- } else {
- (0, eval)(callback);
- }
- }
-
- function checkThis(thisArg) {
- if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) {
- throw new TypeError("Illegal invocation");
- }
- }
-
- function setTimer(
- cb,
- delay,
- args,
- repeat,
- ) {
- // If the callack is a function, bind `args` to the callback and bind `this` to globalThis(global).
- // otherwise call `String` on it, and `eval` it on calls; do not pass variardic args to the string
- let callback;
-
- if ("function" === typeof cb) {
- callback = FunctionPrototypeBind(cb, globalThis, ...args);
- } else {
- callback = String(cb);
- args = []; // args are ignored
- }
- // In the browser, the delay value must be coercible to an integer between 0
- // and INT32_MAX. Any other value will cause the timer to fire immediately.
- // We emulate this behavior.
- const now = DateNow();
- if (delay > TIMEOUT_MAX) {
- console.warn(
- `${delay} does not fit into` +
- " a 32-bit signed integer." +
- "\nTimeout duration was set to 1.",
- );
- delay = 1;
- }
- delay = MathMax(0, delay | 0);
-
- // Create a new, unscheduled timer object.
- const timer = {
- id: nextTimerId++,
- callback,
- args,
- delay,
- due: now + delay,
- repeat,
- scheduled: false,
- };
- // Register the timer's existence in the id-to-timer map.
- MapPrototypeSet(idMap, timer.id, timer);
- // Schedule the timer in the due table.
- schedule(timer, now);
- return timer.id;
- }
-
- function setTimeout(
- cb,
- delay = 0,
- ...args
- ) {
- delay >>>= 0;
- checkThis(this);
- return setTimer(cb, delay, args, false);
- }
-
- function setInterval(
- cb,
- delay = 0,
- ...args
- ) {
- delay >>>= 0;
- checkThis(this);
- return setTimer(cb, delay, args, true);
- }
-
- function clearTimer(id) {
- id >>>= 0;
- const timer = MapPrototypeGet(idMap, id);
- if (timer === undefined) {
- // Timer doesn't exist any more or never existed. This is not an error.
- return;
- }
- // Unschedule the timer if it is currently scheduled, and forget about it.
- unschedule(timer);
- MapPrototypeDelete(idMap, timer.id);
- }
-
- function clearTimeout(id = 0) {
- id >>>= 0;
- if (id === 0) {
- return;
- }
- clearTimer(id);
- }
-
- function clearInterval(id = 0) {
- id >>>= 0;
- if (id === 0) {
- return;
- }
- clearTimer(id);
- }
-
- window.__bootstrap.timers = {
- clearInterval,
- setInterval,
- clearTimeout,
- setTimeout,
- handleTimerMacrotask,
- opStopGlobalTimer,
- opStartGlobalTimer,
- opNow,
- sleepSync,
- };
-})(this);
diff --git a/extensions/timers/02_performance.js b/extensions/timers/02_performance.js
deleted file mode 100644
index f752ba933..000000000
--- a/extensions/timers/02_performance.js
+++ /dev/null
@@ -1,569 +0,0 @@
-// 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);
diff --git a/extensions/timers/Cargo.toml b/extensions/timers/Cargo.toml
deleted file mode 100644
index eeaef5749..000000000
--- a/extensions/timers/Cargo.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-[package]
-name = "deno_timers"
-version = "0.12.0"
-authors = ["the Deno authors"]
-edition = "2018"
-license = "MIT"
-readme = "README.md"
-repository = "https://github.com/denoland/deno"
-description = "Timers API implementation for Deno"
-
-[lib]
-path = "lib.rs"
-
-[dependencies]
-deno_core = { version = "0.96.0", path = "../../core" }
-tokio = { version = "1.8.1", features = ["full"] }
-
-[dev-dependencies]
-deno_bench_util = { version = "0.8.0", path = "../../bench_util" }
-deno_url = { version = "0.14.0", path = "../url" }
-deno_web = { version = "0.45.0", path = "../web" }
-deno_webidl = { version = "0.14.0", path = "../webidl" }
-
-[[bench]]
-name = "timers_ops"
-harness = false
diff --git a/extensions/timers/README.md b/extensions/timers/README.md
deleted file mode 100644
index 5a2a8e516..000000000
--- a/extensions/timers/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# deno_timers
-
-This crate implements the timers API.
-
-Spec: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
diff --git a/extensions/timers/benches/timers_ops.rs b/extensions/timers/benches/timers_ops.rs
deleted file mode 100644
index 269d9627d..000000000
--- a/extensions/timers/benches/timers_ops.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use deno_core::Extension;
-
-use deno_bench_util::bench_or_profile;
-use deno_bench_util::bencher::{benchmark_group, Bencher};
-use deno_bench_util::{bench_js_async, bench_js_sync};
-use deno_web::BlobStore;
-
-fn setup() -> Vec<Extension> {
- vec![
- deno_webidl::init(),
- deno_url::init(),
- deno_web::init(BlobStore::default(), None),
- deno_timers::init::<deno_timers::NoTimersPermission>(),
- Extension::builder()
- .js(vec![
- ("setup",
- Box::new(|| Ok(r#"
- const { opNow, setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
- Deno.core.setMacrotaskCallback(handleTimerMacrotask);
- "#.to_owned())),
- ),
- ])
- .state(|state| {
- state.put(deno_timers::NoTimersPermission{});
- Ok(())
- })
- .build()
- ]
-}
-
-fn bench_op_now(b: &mut Bencher) {
- bench_js_sync(b, r#"opNow();"#, setup);
-}
-
-fn bench_set_timeout(b: &mut Bencher) {
- bench_js_async(b, r#"setTimeout(() => {}, 0);"#, setup);
-}
-
-benchmark_group!(benches, bench_op_now, bench_set_timeout,);
-bench_or_profile!(benches);
diff --git a/extensions/timers/lib.rs b/extensions/timers/lib.rs
deleted file mode 100644
index 5f65ae3ef..000000000
--- a/extensions/timers/lib.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-//! This module helps deno implement timers.
-//!
-//! As an optimization, we want to avoid an expensive calls into rust for every
-//! setTimeout in JavaScript. Thus in //js/timers.ts a data structure is
-//! implemented that calls into Rust for only the smallest timeout. Thus we
-//! only need to be able to start, cancel and await a single timer (or Delay, as Tokio
-//! calls it) for an entire Isolate. This is what is implemented here.
-
-use deno_core::error::AnyError;
-use deno_core::futures;
-use deno_core::futures::channel::oneshot;
-use deno_core::futures::FutureExt;
-use deno_core::futures::TryFutureExt;
-use deno_core::include_js_files;
-use deno_core::op_async;
-use deno_core::op_sync;
-use deno_core::Extension;
-use deno_core::OpState;
-use std::cell::RefCell;
-use std::future::Future;
-use std::pin::Pin;
-use std::rc::Rc;
-use std::thread::sleep;
-use std::time::Duration;
-use std::time::Instant;
-
-pub trait TimersPermission {
- fn allow_hrtime(&mut self) -> bool;
- fn check_unstable(&self, state: &OpState, api_name: &'static str);
-}
-
-pub struct NoTimersPermission;
-
-impl TimersPermission for NoTimersPermission {
- fn allow_hrtime(&mut self) -> bool {
- false
- }
- fn check_unstable(&self, _: &OpState, _: &'static str) {}
-}
-
-pub fn init<P: TimersPermission + 'static>() -> Extension {
- Extension::builder()
- .js(include_js_files!(
- prefix "deno:extensions/timers",
- "01_timers.js",
- "02_performance.js",
- ))
- .ops(vec![
- ("op_global_timer_stop", op_sync(op_global_timer_stop)),
- ("op_global_timer_start", op_sync(op_global_timer_start)),
- ("op_global_timer", op_async(op_global_timer)),
- ("op_now", op_sync(op_now::<P>)),
- ("op_sleep_sync", op_sync(op_sleep_sync::<P>)),
- ])
- .state(|state| {
- state.put(GlobalTimer::default());
- state.put(StartTime::now());
- Ok(())
- })
- .build()
-}
-
-pub type StartTime = Instant;
-
-type TimerFuture = Pin<Box<dyn Future<Output = Result<(), ()>>>>;
-
-#[derive(Default)]
-pub struct GlobalTimer {
- tx: Option<oneshot::Sender<()>>,
- pub future: Option<TimerFuture>,
-}
-
-impl GlobalTimer {
- pub fn cancel(&mut self) {
- if let Some(tx) = self.tx.take() {
- tx.send(()).ok();
- }
- }
-
- pub fn new_timeout(&mut self, deadline: Instant) {
- if self.tx.is_some() {
- self.cancel();
- }
- assert!(self.tx.is_none());
- self.future.take();
-
- let (tx, rx) = oneshot::channel();
- self.tx = Some(tx);
-
- let delay = tokio::time::sleep_until(deadline.into()).boxed_local();
- let rx = rx
- .map_err(|err| panic!("Unexpected error in receiving channel {:?}", err));
-
- let fut = futures::future::select(delay, rx)
- .then(|_| futures::future::ok(()))
- .boxed_local();
- self.future = Some(fut);
- }
-}
-
-pub fn op_global_timer_stop(
- state: &mut OpState,
- _args: (),
- _: (),
-) -> Result<(), AnyError> {
- let global_timer = state.borrow_mut::<GlobalTimer>();
- global_timer.cancel();
- Ok(())
-}
-
-// Set up a timer that will be later awaited by JS promise.
-// It's a separate op, because canceling a timeout immediately
-// after setting it caused a race condition (because Tokio timeout)
-// might have been registered after next event loop tick.
-//
-// See https://github.com/denoland/deno/issues/7599 for more
-// details.
-pub fn op_global_timer_start(
- state: &mut OpState,
- timeout: u64,
- _: (),
-) -> Result<(), AnyError> {
- // According to spec, minimum allowed timeout is 4 ms.
- // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
- // TODO(#10974) Per spec this is actually a little more complicated than this.
- // The minimum timeout depends on the nesting level of the timeout.
- let timeout = std::cmp::max(timeout, 4);
-
- let deadline = Instant::now() + Duration::from_millis(timeout);
- let global_timer = state.borrow_mut::<GlobalTimer>();
- global_timer.new_timeout(deadline);
- Ok(())
-}
-
-pub async fn op_global_timer(
- state: Rc<RefCell<OpState>>,
- _args: (),
- _: (),
-) -> Result<(), AnyError> {
- let maybe_timer_fut = {
- let mut s = state.borrow_mut();
- let global_timer = s.borrow_mut::<GlobalTimer>();
- global_timer.future.take()
- };
- if let Some(timer_fut) = maybe_timer_fut {
- let _ = timer_fut.await;
- }
- Ok(())
-}
-
-// Returns a milliseconds and nanoseconds subsec
-// since the start time of the deno runtime.
-// If the High precision flag is not set, the
-// nanoseconds are rounded on 2ms.
-pub fn op_now<TP>(
- state: &mut OpState,
- _argument: (),
- _: (),
-) -> Result<f64, AnyError>
-where
- TP: TimersPermission + 'static,
-{
- let start_time = state.borrow::<StartTime>();
- let seconds = start_time.elapsed().as_secs();
- let mut subsec_nanos = start_time.elapsed().subsec_nanos() as f64;
- let reduced_time_precision = 2_000_000.0; // 2ms in nanoseconds
-
- // If the permission is not enabled
- // Round the nano result on 2 milliseconds
- // see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
- if !state.borrow_mut::<TP>().allow_hrtime() {
- subsec_nanos -= subsec_nanos % reduced_time_precision;
- }
-
- let result = (seconds * 1_000) as f64 + (subsec_nanos / 1_000_000.0);
-
- Ok(result)
-}
-
-pub fn op_sleep_sync<TP>(
- state: &mut OpState,
- millis: u64,
- _: (),
-) -> Result<(), AnyError>
-where
- TP: TimersPermission + 'static,
-{
- state.borrow::<TP>().check_unstable(state, "Deno.sleepSync");
- sleep(Duration::from_millis(millis));
- Ok(())
-}