mirror of
https://github.com/denoland/deno
synced 2024-10-30 06:43:53 +00:00
fix: stronger input checking for setTimeout; add function overload (#8957)
This commit is contained in:
parent
d364a0effe
commit
3761d054d0
2 changed files with 67 additions and 20 deletions
|
@ -7,10 +7,51 @@ import {
|
|||
unitTest,
|
||||
} from "./test_util.ts";
|
||||
|
||||
function waitForMs(ms: number): Promise<number> {
|
||||
function waitForMs(ms: number): Promise<void> {
|
||||
return new Promise((resolve): number => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
unitTest(async function functionParameterBindingSuccess(): Promise<void> {
|
||||
const promise = deferred();
|
||||
let count = 0;
|
||||
|
||||
const nullProto = (newCount: number): void => {
|
||||
count = newCount;
|
||||
promise.resolve();
|
||||
};
|
||||
|
||||
Reflect.setPrototypeOf(nullProto, null);
|
||||
|
||||
setTimeout(nullProto, 500, 1);
|
||||
await promise;
|
||||
// count should be reassigned
|
||||
assertEquals(count, 1);
|
||||
});
|
||||
|
||||
unitTest(async function stringifyAndEvalNonFunctions(): Promise<void> {
|
||||
// eval can only access global scope
|
||||
const global = globalThis as unknown as {
|
||||
globalPromise: ReturnType<typeof deferred>;
|
||||
globalCount: number;
|
||||
};
|
||||
global.globalPromise = deferred();
|
||||
global.globalCount = 0;
|
||||
|
||||
const notAFunction =
|
||||
"globalThis.globalCount++; globalThis.globalPromise.resolve();" as unknown as () =>
|
||||
void;
|
||||
|
||||
setTimeout(notAFunction, 500);
|
||||
|
||||
await global.globalPromise;
|
||||
|
||||
// count should be incremented
|
||||
assertEquals(global.globalCount, 1);
|
||||
|
||||
Reflect.deleteProperty(global, "globalPromise");
|
||||
Reflect.deleteProperty(global, "globalCount");
|
||||
});
|
||||
|
||||
unitTest(async function timeoutSuccess(): Promise<void> {
|
||||
const promise = deferred();
|
||||
let count = 0;
|
||||
|
|
|
@ -274,7 +274,7 @@
|
|||
}
|
||||
|
||||
const { console } = globalThis;
|
||||
const OriginalDate = Date;
|
||||
const OriginalDateNow = Date.now;
|
||||
|
||||
// Timeout values > TIMEOUT_MAX are set to 1.
|
||||
const TIMEOUT_MAX = 2 ** 31 - 1;
|
||||
|
@ -333,7 +333,7 @@
|
|||
}
|
||||
|
||||
function prepareReadyTimers() {
|
||||
const now = OriginalDate.now();
|
||||
const now = OriginalDateNow();
|
||||
// Bail out if we're not expecting the global timer to fire.
|
||||
if (globalTimeoutDue === null || pendingEvents > 0) {
|
||||
return;
|
||||
|
@ -409,7 +409,7 @@
|
|||
const nextDueNode = dueTree.min();
|
||||
setOrClearGlobalTimeout(
|
||||
nextDueNode && nextDueNode.due,
|
||||
OriginalDate.now(),
|
||||
OriginalDateNow(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -434,14 +434,18 @@
|
|||
} 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 = OriginalDate.now();
|
||||
const now = OriginalDateNow();
|
||||
timer.due = Math.max(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;
|
||||
callback();
|
||||
if ("function" === typeof callback) {
|
||||
callback();
|
||||
} else {
|
||||
eval(callback);
|
||||
}
|
||||
}
|
||||
|
||||
function checkThis(thisArg) {
|
||||
|
@ -450,24 +454,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
function checkBigInt(n) {
|
||||
if (typeof n === "bigint") {
|
||||
throw new TypeError("Cannot convert a BigInt value to a number");
|
||||
}
|
||||
}
|
||||
|
||||
function setTimer(
|
||||
cb,
|
||||
delay,
|
||||
args,
|
||||
repeat,
|
||||
) {
|
||||
// Bind `args` to the callback and bind `this` to globalThis(global).
|
||||
const callback = cb.bind(globalThis, ...args);
|
||||
// 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 = Function.prototype.bind.call(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 = OriginalDate.now();
|
||||
const now = OriginalDateNow();
|
||||
if (delay > TIMEOUT_MAX) {
|
||||
console.warn(
|
||||
`${delay} does not fit into` +
|
||||
|
@ -500,7 +506,7 @@
|
|||
delay = 0,
|
||||
...args
|
||||
) {
|
||||
checkBigInt(delay);
|
||||
delay >>>= 0;
|
||||
checkThis(this);
|
||||
return setTimer(cb, delay, args, false);
|
||||
}
|
||||
|
@ -510,13 +516,13 @@
|
|||
delay = 0,
|
||||
...args
|
||||
) {
|
||||
checkBigInt(delay);
|
||||
delay >>>= 0;
|
||||
checkThis(this);
|
||||
return setTimer(cb, delay, args, true);
|
||||
}
|
||||
|
||||
function clearTimer(id) {
|
||||
id = Number(id);
|
||||
id >>>= 0;
|
||||
const timer = idMap.get(id);
|
||||
if (timer === undefined) {
|
||||
// Timer doesn't exist any more or never existed. This is not an error.
|
||||
|
@ -528,7 +534,7 @@
|
|||
}
|
||||
|
||||
function clearTimeout(id = 0) {
|
||||
checkBigInt(id);
|
||||
id >>>= 0;
|
||||
if (id === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -536,7 +542,7 @@
|
|||
}
|
||||
|
||||
function clearInterval(id = 0) {
|
||||
checkBigInt(id);
|
||||
id >>>= 0;
|
||||
if (id === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue