feat: implement timers in java
This commit is contained in:
parent
9c65bacbac
commit
63b04019cf
54
lib/core.ts
54
lib/core.ts
@ -22,10 +22,18 @@ interface Internals {
|
||||
map: typeof Map;
|
||||
set: typeof Set;
|
||||
|
||||
timers: {
|
||||
setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number,
|
||||
setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number,
|
||||
clearTimeout: (id: number) => void,
|
||||
clearInterval: (id: number) => void,
|
||||
}
|
||||
|
||||
markSpecial(...funcs: Function[]): void;
|
||||
getEnv(func: Function): Environment | undefined;
|
||||
setEnv<T>(func: T, env: Environment): T;
|
||||
apply(func: Function, thisArg: any, args: any[], env?: Environment): any;
|
||||
bind(func: Function, thisArg: any): any;
|
||||
delay(timeout: number, callback: Function): () => void;
|
||||
pushMessage(micro: boolean, func: Function, thisArg: any, args: any[]): void;
|
||||
|
||||
@ -54,24 +62,26 @@ interface Internals {
|
||||
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
||||
|
||||
try {
|
||||
const values = {
|
||||
Object: env.global.Object = internals.object,
|
||||
Function: env.global.Function = internals.function,
|
||||
Array: env.global.Array = internals.array,
|
||||
Promise: env.global.Promise = internals.promise,
|
||||
Boolean: env.global.Boolean = internals.bool,
|
||||
Number: env.global.Number = internals.number,
|
||||
String: env.global.String = internals.string,
|
||||
Symbol: env.global.Symbol = internals.symbol,
|
||||
Error: env.global.Error = internals.error,
|
||||
SyntaxError: env.global.SyntaxError = internals.syntax,
|
||||
TypeError: env.global.TypeError = internals.type,
|
||||
RangeError: env.global.RangeError = internals.range,
|
||||
RegExp: env.global.RegExp = internals.regexp,
|
||||
Map: env.global.Map = internals.map,
|
||||
Set: env.global.Set = internals.set,
|
||||
}
|
||||
const Array = values.Array;
|
||||
const Array = env.global.Array = internals.array;
|
||||
env.global.Object = internals.object;
|
||||
env.global.Function = internals.function;
|
||||
env.global.Promise = internals.promise;
|
||||
env.global.Boolean = internals.bool;
|
||||
env.global.Number = internals.number;
|
||||
env.global.String = internals.string;
|
||||
env.global.Symbol = internals.symbol;
|
||||
env.global.Error = internals.error;
|
||||
env.global.SyntaxError = internals.syntax;
|
||||
env.global.TypeError = internals.type;
|
||||
env.global.RangeError = internals.range;
|
||||
env.global.RegExp = internals.regexp;
|
||||
env.global.Map = internals.map;
|
||||
env.global.Set = internals.set;
|
||||
env.global.setInterval = internals.bind(internals.timers.setInterval, internals.timers);
|
||||
env.global.setTimeout = internals.bind(internals.timers.setTimeout, internals.timers);
|
||||
env.global.clearInterval = internals.bind(internals.timers.clearInterval, internals.timers);
|
||||
env.global.clearTimeout = internals.bind(internals.timers.clearTimeout, internals.timers);
|
||||
const log = env.global.log = internals.bind(internals.log, internals);
|
||||
|
||||
env.setProto('object', env.global.Object.prototype);
|
||||
env.setProto('function', env.global.Function.prototype);
|
||||
@ -88,14 +98,6 @@ try {
|
||||
|
||||
(env.global.Object.prototype as any).__proto__ = null;
|
||||
|
||||
internals.getEnv(run)?.setProto('array', Array.prototype);
|
||||
globalThis.log = (...args) => internals.apply(internals.log, internals, args);
|
||||
|
||||
run('timeout');
|
||||
|
||||
env.global.log = log;
|
||||
env.global.NewObject = internals.object;
|
||||
|
||||
log('Loaded polyfills!');
|
||||
}
|
||||
catch (e: any) {
|
||||
|
@ -1,13 +0,0 @@
|
||||
var { define, run } = (() => {
|
||||
const modules: Record<string, Function> = {};
|
||||
|
||||
function define(name: string, func: Function) {
|
||||
modules[name] = func;
|
||||
}
|
||||
function run(name: string) {
|
||||
if (typeof modules[name] === 'function') return modules[name]();
|
||||
else throw "The module '" + name + "' doesn't exist.";
|
||||
}
|
||||
|
||||
return { define, run };
|
||||
})();
|
143
lib/regex.ts
143
lib/regex.ts
@ -1,143 +0,0 @@
|
||||
define("regex", () => {
|
||||
// var RegExp = env.global.RegExp = env.internals.RegExp;
|
||||
|
||||
// setProps(RegExp.prototype as RegExp, env, {
|
||||
// [Symbol.typeName]: 'RegExp',
|
||||
|
||||
// test(val) {
|
||||
// return !!this.exec(val);
|
||||
// },
|
||||
// toString() {
|
||||
// return '/' + this.source + '/' + this.flags;
|
||||
// },
|
||||
|
||||
// [Symbol.match](target) {
|
||||
// if (this.global) {
|
||||
// const res: string[] = [];
|
||||
// let val;
|
||||
// while (val = this.exec(target)) {
|
||||
// res.push(val[0]);
|
||||
// }
|
||||
// this.lastIndex = 0;
|
||||
// return res;
|
||||
// }
|
||||
// else {
|
||||
// const res = this.exec(target);
|
||||
// if (!this.sticky) this.lastIndex = 0;
|
||||
// return res;
|
||||
// }
|
||||
// },
|
||||
// [Symbol.matchAll](target) {
|
||||
// let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||
|
||||
// return {
|
||||
// next: (): IteratorResult<RegExpResult, undefined> => {
|
||||
// const val = pattern?.exec(target);
|
||||
|
||||
// if (val === null || val === undefined) {
|
||||
// pattern = undefined;
|
||||
// return { done: true };
|
||||
// }
|
||||
// else return { value: val };
|
||||
// },
|
||||
// [Symbol.iterator]() { return this; }
|
||||
// }
|
||||
// },
|
||||
// [Symbol.split](target, limit, sensible) {
|
||||
// const pattern = new this.constructor(this, this.flags + "g") as RegExp;
|
||||
// let match: RegExpResult | null;
|
||||
// let lastEnd = 0;
|
||||
// const res: string[] = [];
|
||||
|
||||
// while ((match = pattern.exec(target)) !== null) {
|
||||
// let added: string[] = [];
|
||||
|
||||
// if (match.index >= target.length) break;
|
||||
|
||||
// if (match[0].length === 0) {
|
||||
// added = [ target.substring(lastEnd, pattern.lastIndex), ];
|
||||
// if (pattern.lastIndex < target.length) added.push(...match.slice(1));
|
||||
// }
|
||||
// else if (match.index - lastEnd > 0) {
|
||||
// added = [ target.substring(lastEnd, match.index), ...match.slice(1) ];
|
||||
// }
|
||||
// else {
|
||||
// for (let i = 1; i < match.length; i++) {
|
||||
// res[res.length - match.length + i] = match[i];
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (sensible) {
|
||||
// if (limit !== undefined && res.length + added.length >= limit) break;
|
||||
// else res.push(...added);
|
||||
// }
|
||||
// else {
|
||||
// for (let i = 0; i < added.length; i++) {
|
||||
// if (limit !== undefined && res.length >= limit) return res;
|
||||
// else res.push(added[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// lastEnd = pattern.lastIndex;
|
||||
// }
|
||||
|
||||
// if (lastEnd < target.length) {
|
||||
// res.push(target.substring(lastEnd));
|
||||
// }
|
||||
|
||||
// return res;
|
||||
// },
|
||||
// [Symbol.replace](target, replacement) {
|
||||
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
|
||||
// let match: RegExpResult | null;
|
||||
// let lastEnd = 0;
|
||||
// const res: string[] = [];
|
||||
|
||||
// // log(pattern.toString());
|
||||
|
||||
// while ((match = pattern.exec(target)) !== null) {
|
||||
// const indices = match.indices![0];
|
||||
// res.push(target.substring(lastEnd, indices[0]));
|
||||
// if (replacement instanceof Function) {
|
||||
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
|
||||
// }
|
||||
// else {
|
||||
// res.push(replacement);
|
||||
// }
|
||||
// lastEnd = indices[1];
|
||||
// if (!pattern.global) break;
|
||||
// }
|
||||
|
||||
// if (lastEnd < target.length) {
|
||||
// res.push(target.substring(lastEnd));
|
||||
// }
|
||||
|
||||
// return res.join('');
|
||||
// },
|
||||
// [Symbol.search](target, reverse, start) {
|
||||
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||
|
||||
|
||||
// if (!reverse) {
|
||||
// pattern.lastIndex = (start as any) | 0;
|
||||
// const res = pattern.exec(target);
|
||||
// if (res) return res.index;
|
||||
// else return -1;
|
||||
// }
|
||||
// else {
|
||||
// start ??= target.length;
|
||||
// start |= 0;
|
||||
// let res: RegExpResult | null = null;
|
||||
|
||||
// while (true) {
|
||||
// const tmp = pattern.exec(target);
|
||||
// if (tmp === null || tmp.index > start) break;
|
||||
// res = tmp;
|
||||
// }
|
||||
|
||||
// if (res && res.index <= start) return res.index;
|
||||
// else return -1;
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
});
|
@ -1,38 +0,0 @@
|
||||
define("timeout", () => {
|
||||
const timeouts: Record<number, () => void> = { };
|
||||
const intervals: Record<number, () => void> = { };
|
||||
let timeoutI = 0, intervalI = 0;
|
||||
|
||||
env.global.setTimeout = (func, delay, ...args) => {
|
||||
if (typeof func !== 'function') throw new env.global.TypeError("func must be a function.");
|
||||
delay = (delay ?? 0) - 0;
|
||||
const cancelFunc = internals.delay(delay, () => internals.apply(func, undefined, args));
|
||||
timeouts[++timeoutI] = cancelFunc;
|
||||
return timeoutI;
|
||||
};
|
||||
env.global.setInterval = (func, delay, ...args) => {
|
||||
if (typeof func !== 'function') throw new env.global.TypeError("func must be a function.");
|
||||
delay = (delay ?? 0) - 0;
|
||||
|
||||
const i = ++intervalI;
|
||||
intervals[i] = internals.delay(delay, callback);
|
||||
|
||||
return i;
|
||||
|
||||
function callback() {
|
||||
internals.apply(func, undefined, args);
|
||||
intervals[i] = internals.delay(delay!, callback);
|
||||
}
|
||||
};
|
||||
|
||||
env.global.clearTimeout = (id) => {
|
||||
const func = timeouts[id];
|
||||
if (func) func();
|
||||
timeouts[id] = undefined!;
|
||||
};
|
||||
env.global.clearInterval = (id) => {
|
||||
const func = intervals[id];
|
||||
if (func) func();
|
||||
intervals[id] = undefined!;
|
||||
};
|
||||
});
|
@ -1,9 +1,6 @@
|
||||
{
|
||||
"files": [
|
||||
"lib.d.ts",
|
||||
"modules.ts",
|
||||
"regex.ts",
|
||||
"timeout.ts",
|
||||
"core.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
|
@ -24,9 +24,14 @@ public class FunctionPolyfill {
|
||||
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
||||
|
||||
return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
|
||||
var resArgs = new Object[args.length + callArgs.length];
|
||||
System.arraycopy(args, 0, resArgs, 0, args.length);
|
||||
System.arraycopy(callArgs, 0, resArgs, args.length, callArgs.length);
|
||||
Object[] resArgs;
|
||||
|
||||
if (args.length == 0) resArgs = callArgs;
|
||||
else {
|
||||
resArgs = new Object[args.length + callArgs.length];
|
||||
System.arraycopy(args, 0, resArgs, 0, args.length);
|
||||
System.arraycopy(callArgs, 0, resArgs, args.length, callArgs.length);
|
||||
}
|
||||
|
||||
return func.call(ctx, thisArg, resArgs);
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
package me.topchetoeu.jscript.polyfills;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.engine.Environment;
|
||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||
|
@ -20,6 +20,8 @@ public class Internals {
|
||||
promise, map, set, regexp,
|
||||
error, syntax, type, range;
|
||||
|
||||
@Native public final TimerPolyfills timers = new TimerPolyfills();
|
||||
|
||||
@Native public void markSpecial(FunctionValue ...funcs) {
|
||||
for (var func : funcs) {
|
||||
func.special = true;
|
||||
@ -37,6 +39,9 @@ public class Internals {
|
||||
if (env != null) ctx = new Context(env, ctx.message);
|
||||
return func.call(ctx, thisArg, args.toArray());
|
||||
}
|
||||
@Native public FunctionValue bind(Context ctx, FunctionValue func, Object thisArg) throws InterruptedException {
|
||||
return FunctionPolyfill.bind(ctx, func, thisArg);
|
||||
}
|
||||
@Native public FunctionValue delay(Context ctx, double delay, FunctionValue callback) throws InterruptedException {
|
||||
var thread = new Thread((Runnable)() -> {
|
||||
var ms = (long)delay;
|
||||
|
60
src/me/topchetoeu/jscript/polyfills/TimerPolyfills.java
Normal file
60
src/me/topchetoeu/jscript/polyfills/TimerPolyfills.java
Normal file
@ -0,0 +1,60 @@
|
||||
package me.topchetoeu.jscript.polyfills;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||
import me.topchetoeu.jscript.interop.Native;
|
||||
|
||||
public class TimerPolyfills {
|
||||
private HashMap<Integer, Thread> threads = new HashMap<>();
|
||||
|
||||
private int i = 0;
|
||||
|
||||
@Native public int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) {
|
||||
var thread = new Thread(() -> {
|
||||
var ms = (long)delay;
|
||||
var ns = (int)((delay - ms) * 10000000);
|
||||
|
||||
try {
|
||||
Thread.sleep(ms, ns);
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
|
||||
ctx.message.engine.pushMsg(false, ctx.message, func, null, args);
|
||||
});
|
||||
thread.start();
|
||||
|
||||
threads.put(++i, thread);
|
||||
|
||||
return i;
|
||||
}
|
||||
@Native public int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) {
|
||||
var thread = new Thread(() -> {
|
||||
var ms = (long)delay;
|
||||
var ns = (int)((delay - ms) * 10000000);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(ms, ns);
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
|
||||
ctx.message.engine.pushMsg(false, ctx.message, func, null, args);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
|
||||
threads.put(++i, thread);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
@Native public void clearTimeout(Context ctx, int i) {
|
||||
var thread = threads.remove(i);
|
||||
if (thread != null) thread.interrupt();
|
||||
}
|
||||
@Native public void clearInterval(Context ctx, int i) {
|
||||
clearTimeout(ctx, i);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user