@sebastianwessel/quickjs
Version:
A typescript package to execute JavaScript and TypeScript code in a WebAssembly QuickJS sandbox
82 lines (81 loc) • 3.24 kB
JavaScript
import { Scope } from 'quickjs-emscripten-core';
export const provideTimingFunctions = (ctx, max) => {
const scope = new Scope();
const timeouts = new Map();
let timeoutCounter = 0;
const intervals = new Map();
let intervalCounter = 0;
const _setTimeout = ctx.newFunction('setTimeout', (vmFnHandle, timeoutHandle) => {
const currentCounter = timeoutCounter++;
if (timeouts.size + 1 > max.maxTimeoutCount) {
throw new Error(`Client tries to use setTimeout, which exceeds the limit of max ${max.maxTimeoutCount} concurrent running timeout functions`);
}
const vmFnHandleCopy = vmFnHandle.dup();
scope.manage(vmFnHandleCopy);
const timeout = timeoutHandle ? ctx.dump(timeoutHandle) : undefined;
const timeoutID = setTimeout(() => {
const t = timeouts.get(currentCounter);
if (t) {
clearTimeout(t);
timeouts.delete(currentCounter);
}
ctx.callFunction(vmFnHandleCopy, ctx.undefined);
}, timeout);
timeouts.set(currentCounter, timeoutID);
return ctx.newNumber(currentCounter);
});
scope.manage(_setTimeout);
ctx.setProp(ctx.global, 'setTimeout', _setTimeout);
const _clearTimeout = ctx.newFunction('clearTimeout', timeoutHandle => {
const id = ctx.dump(timeoutHandle);
timeoutHandle.dispose();
const t = timeouts.get(id);
if (t) {
clearTimeout(t);
timeouts.delete(id);
}
});
scope.manage(_clearTimeout);
ctx.setProp(ctx.global, 'clearTimeout', _clearTimeout);
const _setInterval = ctx.newFunction('setInterval', (vmFnHandle, intervalHandle) => {
const currentCounter = intervalCounter++;
if (intervals.size + 1 > max.maxIntervalCount) {
throw new Error(`Client tries to use setInterval, which exceeds the limit of max ${max.maxIntervalCount} concurrent running interval functions`);
}
const vmFnHandleCopy = vmFnHandle.dup();
scope.manage(vmFnHandleCopy);
const interval = ctx.dump(intervalHandle);
const intervalID = setInterval(() => {
ctx.callFunction(vmFnHandleCopy, ctx.undefined);
}, interval);
intervals.set(currentCounter, intervalID);
return ctx.newNumber(currentCounter);
});
scope.manage(_setInterval);
ctx.setProp(ctx.global, 'setInterval', _setInterval);
const _clearInterval = ctx.newFunction('clearInterval', intervalHandle => {
const id = ctx.dump(intervalHandle);
intervalHandle.dispose();
const t = intervals.get(id);
if (t) {
clearInterval(t);
intervals.delete(id);
}
});
scope.manage(_clearInterval);
ctx.setProp(ctx.global, 'clearInterval', _clearInterval);
const dispose = () => {
for (const [_key, value] of timeouts) {
clearTimeout(value);
}
timeouts.clear();
timeoutCounter = 0;
for (const [_key, value] of intervals) {
clearInterval(value);
}
intervals.clear();
intervalCounter = 0;
scope.dispose();
};
return { dispose };
};