feat: implement timers in java
This commit is contained in:
parent
9c65bacbac
commit
63b04019cf
56
lib/core.ts
56
lib/core.ts
@ -17,15 +17,23 @@ interface Internals {
|
|||||||
syntax: SyntaxErrorConstructor;
|
syntax: SyntaxErrorConstructor;
|
||||||
type: TypeErrorConstructor;
|
type: TypeErrorConstructor;
|
||||||
range: RangeErrorConstructor;
|
range: RangeErrorConstructor;
|
||||||
|
|
||||||
regexp: typeof RegExp;
|
regexp: typeof RegExp;
|
||||||
map: typeof Map;
|
map: typeof Map;
|
||||||
set: typeof Set;
|
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;
|
markSpecial(...funcs: Function[]): void;
|
||||||
getEnv(func: Function): Environment | undefined;
|
getEnv(func: Function): Environment | undefined;
|
||||||
setEnv<T>(func: T, env: Environment): T;
|
setEnv<T>(func: T, env: Environment): T;
|
||||||
apply(func: Function, thisArg: any, args: any[], env?: Environment): any;
|
apply(func: Function, thisArg: any, args: any[], env?: Environment): any;
|
||||||
|
bind(func: Function, thisArg: any): any;
|
||||||
delay(timeout: number, callback: Function): () => void;
|
delay(timeout: number, callback: Function): () => void;
|
||||||
pushMessage(micro: boolean, func: Function, thisArg: any, args: any[]): 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];
|
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const values = {
|
const Array = env.global.Array = internals.array;
|
||||||
Object: env.global.Object = internals.object,
|
env.global.Object = internals.object;
|
||||||
Function: env.global.Function = internals.function,
|
env.global.Function = internals.function;
|
||||||
Array: env.global.Array = internals.array,
|
env.global.Promise = internals.promise;
|
||||||
Promise: env.global.Promise = internals.promise,
|
env.global.Boolean = internals.bool;
|
||||||
Boolean: env.global.Boolean = internals.bool,
|
env.global.Number = internals.number;
|
||||||
Number: env.global.Number = internals.number,
|
env.global.String = internals.string;
|
||||||
String: env.global.String = internals.string,
|
env.global.Symbol = internals.symbol;
|
||||||
Symbol: env.global.Symbol = internals.symbol,
|
env.global.Error = internals.error;
|
||||||
Error: env.global.Error = internals.error,
|
env.global.SyntaxError = internals.syntax;
|
||||||
SyntaxError: env.global.SyntaxError = internals.syntax,
|
env.global.TypeError = internals.type;
|
||||||
TypeError: env.global.TypeError = internals.type,
|
env.global.RangeError = internals.range;
|
||||||
RangeError: env.global.RangeError = internals.range,
|
env.global.RegExp = internals.regexp;
|
||||||
RegExp: env.global.RegExp = internals.regexp,
|
env.global.Map = internals.map;
|
||||||
Map: env.global.Map = internals.map,
|
env.global.Set = internals.set;
|
||||||
Set: 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);
|
||||||
const Array = values.Array;
|
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('object', env.global.Object.prototype);
|
||||||
env.setProto('function', env.global.Function.prototype);
|
env.setProto('function', env.global.Function.prototype);
|
||||||
@ -88,14 +98,6 @@ try {
|
|||||||
|
|
||||||
(env.global.Object.prototype as any).__proto__ = null;
|
(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!');
|
log('Loaded polyfills!');
|
||||||
}
|
}
|
||||||
catch (e: any) {
|
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": [
|
"files": [
|
||||||
"lib.d.ts",
|
"lib.d.ts",
|
||||||
"modules.ts",
|
|
||||||
"regex.ts",
|
|
||||||
"timeout.ts",
|
|
||||||
"core.ts"
|
"core.ts"
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
@ -24,9 +24,14 @@ public class FunctionPolyfill {
|
|||||||
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function.");
|
||||||
|
|
||||||
return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
|
return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> {
|
||||||
var resArgs = new Object[args.length + callArgs.length];
|
Object[] resArgs;
|
||||||
System.arraycopy(args, 0, resArgs, 0, args.length);
|
|
||||||
System.arraycopy(callArgs, 0, resArgs, args.length, callArgs.length);
|
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);
|
return func.call(ctx, thisArg, resArgs);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
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.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
@ -20,6 +20,8 @@ public class Internals {
|
|||||||
promise, map, set, regexp,
|
promise, map, set, regexp,
|
||||||
error, syntax, type, range;
|
error, syntax, type, range;
|
||||||
|
|
||||||
|
@Native public final TimerPolyfills timers = new TimerPolyfills();
|
||||||
|
|
||||||
@Native public void markSpecial(FunctionValue ...funcs) {
|
@Native public void markSpecial(FunctionValue ...funcs) {
|
||||||
for (var func : funcs) {
|
for (var func : funcs) {
|
||||||
func.special = true;
|
func.special = true;
|
||||||
@ -37,6 +39,9 @@ public class Internals {
|
|||||||
if (env != null) ctx = new Context(env, ctx.message);
|
if (env != null) ctx = new Context(env, ctx.message);
|
||||||
return func.call(ctx, thisArg, args.toArray());
|
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 {
|
@Native public FunctionValue delay(Context ctx, double delay, FunctionValue callback) throws InterruptedException {
|
||||||
var thread = new Thread((Runnable)() -> {
|
var thread = new Thread((Runnable)() -> {
|
||||||
var ms = (long)delay;
|
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