0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 17:34:47 -05:00

fix: stronger input checking for setTimeout; add function overload (#8957)

This commit is contained in:
Anonymous 2021-01-06 05:53:30 -08:00 committed by GitHub
parent d364a0effe
commit 3761d054d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 20 deletions

View file

@ -7,10 +7,51 @@ import {
unitTest, unitTest,
} from "./test_util.ts"; } from "./test_util.ts";
function waitForMs(ms: number): Promise<number> { function waitForMs(ms: number): Promise<void> {
return new Promise((resolve): number => setTimeout(resolve, ms)); 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> { unitTest(async function timeoutSuccess(): Promise<void> {
const promise = deferred(); const promise = deferred();
let count = 0; let count = 0;

View file

@ -274,7 +274,7 @@
} }
const { console } = globalThis; const { console } = globalThis;
const OriginalDate = Date; const OriginalDateNow = Date.now;
// Timeout values > TIMEOUT_MAX are set to 1. // Timeout values > TIMEOUT_MAX are set to 1.
const TIMEOUT_MAX = 2 ** 31 - 1; const TIMEOUT_MAX = 2 ** 31 - 1;
@ -333,7 +333,7 @@
} }
function prepareReadyTimers() { function prepareReadyTimers() {
const now = OriginalDate.now(); const now = OriginalDateNow();
// Bail out if we're not expecting the global timer to fire. // Bail out if we're not expecting the global timer to fire.
if (globalTimeoutDue === null || pendingEvents > 0) { if (globalTimeoutDue === null || pendingEvents > 0) {
return; return;
@ -409,7 +409,7 @@
const nextDueNode = dueTree.min(); const nextDueNode = dueTree.min();
setOrClearGlobalTimeout( setOrClearGlobalTimeout(
nextDueNode && nextDueNode.due, nextDueNode && nextDueNode.due,
OriginalDate.now(), OriginalDateNow(),
); );
} }
} else { } else {
@ -434,14 +434,18 @@
} else { } else {
// Interval timer: compute when timer was supposed to fire next. // Interval timer: compute when timer was supposed to fire next.
// However make sure to never schedule the next interval in the past. // 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); timer.due = Math.max(now, timer.due + timer.delay);
schedule(timer, now); schedule(timer, now);
} }
// Call the user callback. Intermediate assignment is to avoid leaking `this` // 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. // to it, while also keeping the stack trace neat when it shows up in there.
const callback = timer.callback; const callback = timer.callback;
callback(); if ("function" === typeof callback) {
callback();
} else {
eval(callback);
}
} }
function checkThis(thisArg) { 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( function setTimer(
cb, cb,
delay, delay,
args, args,
repeat, repeat,
) { ) {
// Bind `args` to the callback and bind `this` to globalThis(global). // If the callack is a function, bind `args` to the callback and bind `this` to globalThis(global).
const callback = cb.bind(globalThis, ...args); // 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 // 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. // and INT32_MAX. Any other value will cause the timer to fire immediately.
// We emulate this behavior. // We emulate this behavior.
const now = OriginalDate.now(); const now = OriginalDateNow();
if (delay > TIMEOUT_MAX) { if (delay > TIMEOUT_MAX) {
console.warn( console.warn(
`${delay} does not fit into` + `${delay} does not fit into` +
@ -500,7 +506,7 @@
delay = 0, delay = 0,
...args ...args
) { ) {
checkBigInt(delay); delay >>>= 0;
checkThis(this); checkThis(this);
return setTimer(cb, delay, args, false); return setTimer(cb, delay, args, false);
} }
@ -510,13 +516,13 @@
delay = 0, delay = 0,
...args ...args
) { ) {
checkBigInt(delay); delay >>>= 0;
checkThis(this); checkThis(this);
return setTimer(cb, delay, args, true); return setTimer(cb, delay, args, true);
} }
function clearTimer(id) { function clearTimer(id) {
id = Number(id); id >>>= 0;
const timer = idMap.get(id); const timer = idMap.get(id);
if (timer === undefined) { if (timer === undefined) {
// Timer doesn't exist any more or never existed. This is not an error. // Timer doesn't exist any more or never existed. This is not an error.
@ -528,7 +534,7 @@
} }
function clearTimeout(id = 0) { function clearTimeout(id = 0) {
checkBigInt(id); id >>>= 0;
if (id === 0) { if (id === 0) {
return; return;
} }
@ -536,7 +542,7 @@
} }
function clearInterval(id = 0) { function clearInterval(id = 0) {
checkBigInt(id); id >>>= 0;
if (id === 0) { if (id === 0) {
return; return;
} }