Create generators #3
@ -74,8 +74,8 @@ interface MathObject {
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
declare const arguments: IArguments;
|
declare const arguments: IArguments;
|
||||||
declare const Math: MathObject;
|
declare const Math: MathObject;
|
||||||
declare const NaN: number;
|
const NaN = 0 / 0;
|
||||||
declare const Infinity: number;
|
const Infinity = 1 / 0;
|
||||||
|
|
||||||
declare var setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
|
declare var setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
|
||||||
declare var setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
|
declare var setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
|
||||||
|
@ -365,5 +365,5 @@ setProps(Array.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setProps(Array, {
|
setProps(Array, {
|
||||||
isArray(val: any) { return (val instanceof Array); }
|
isArray(val: any) { return internals.isArr(val); }
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,15 @@ interface FunctionConstructor extends Function {
|
|||||||
(...args: string[]): (...args: any[]) => any;
|
(...args: string[]): (...args: any[]) => any;
|
||||||
new (...args: string[]): (...args: any[]) => any;
|
new (...args: string[]): (...args: any[]) => any;
|
||||||
prototype: Function;
|
prototype: Function;
|
||||||
async<ArgsT extends any[], RetT>(func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT): (...args: ArgsT) => Promise<RetT>;
|
async<ArgsT extends any[], RetT>(
|
||||||
|
func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT
|
||||||
|
): (...args: ArgsT) => Promise<RetT>;
|
||||||
|
asyncGenerator<ArgsT extends any[], RetT>(
|
||||||
|
func: (await: <T>(val: T) => Awaited<T>, _yield: <T>(val: T) => void) => (...args: ArgsT) => RetT
|
||||||
|
): (...args: ArgsT) => AsyncGenerator<RetT>;
|
||||||
|
generator<ArgsT extends any[], T = unknown, RetT = unknown, TNext = unknown>(
|
||||||
|
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
|
||||||
|
): (...args: ArgsT) => Generator<T, RetT, TNext>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CallableFunction extends Function {
|
interface CallableFunction extends Function {
|
||||||
@ -43,11 +51,15 @@ setProps(Function.prototype, {
|
|||||||
apply(thisArg, args) {
|
apply(thisArg, args) {
|
||||||
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
|
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
|
||||||
var len = args.length - 0;
|
var len = args.length - 0;
|
||||||
var newArgs: any[] = [];
|
let newArgs: any[];
|
||||||
|
if (Array.isArray(args)) newArgs = args;
|
||||||
|
else {
|
||||||
|
newArgs = [];
|
||||||
|
|
||||||
while (len >= 0) {
|
while (len >= 0) {
|
||||||
len--;
|
len--;
|
||||||
newArgs[len] = args[len];
|
newArgs[len] = args[len];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return internals.apply(this, thisArg, newArgs);
|
return internals.apply(this, thisArg, newArgs);
|
||||||
@ -80,6 +92,90 @@ setProps(Function.prototype, {
|
|||||||
setProps(Function, {
|
setProps(Function, {
|
||||||
async(func) {
|
async(func) {
|
||||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||||
return internals.makeAsync(func);
|
|
||||||
|
return function (this: any) {
|
||||||
|
const args = arguments;
|
||||||
|
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
const gen = Function.generator(func as any).apply(this, args as any);
|
||||||
|
|
||||||
|
(function next(type: 'none' | 'err' | 'ret', val?: any) {
|
||||||
|
try {
|
||||||
|
let result;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'err': result = gen.throw(val); break;
|
||||||
|
case 'ret': result = gen.next(val); break;
|
||||||
|
case 'none': result = gen.next(); break;
|
||||||
|
}
|
||||||
|
if (result.done) res(result.value);
|
||||||
|
else Promise.resolve(result.value).then(
|
||||||
|
v => next('ret', v),
|
||||||
|
v => next('err', v)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
rej(e);
|
||||||
|
}
|
||||||
|
})('none');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
asyncGenerator(func) {
|
||||||
|
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||||
|
|
||||||
|
|
||||||
|
return function(this: any) {
|
||||||
|
const gen = Function.generator<any[], ['await' | 'yield', any]>((_yield) => func(
|
||||||
|
val => _yield(['await', val]) as any,
|
||||||
|
val => _yield(['yield', val])
|
||||||
|
)).apply(this, arguments as any);
|
||||||
|
|
||||||
|
const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => {
|
||||||
|
let res;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (type) {
|
||||||
|
case 'val': res = gen.next(val); break;
|
||||||
|
case 'ret': res = gen.return(val); break;
|
||||||
|
case 'err': res = gen.throw(val); break;
|
||||||
|
default: res = gen.next(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) { return reject(e); }
|
||||||
|
|
||||||
|
if (res.done) return { done: true, res: <any>res };
|
||||||
|
else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then(
|
||||||
|
v => next(resolve, reject, 'val', v),
|
||||||
|
v => next(resolve, reject, 'err', v),
|
||||||
|
)
|
||||||
|
else resolve({ done: false, value: res.value[1] });
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
const args = arguments;
|
||||||
|
if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none'));
|
||||||
|
else return new Promise((res, rej) => next(res, rej, 'val', args[0]));
|
||||||
|
},
|
||||||
|
return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)),
|
||||||
|
throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)),
|
||||||
|
[Symbol.asyncIterator]() { return this; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
generator(func) {
|
||||||
|
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||||
|
const gen = internals.makeGenerator(func);
|
||||||
|
return (...args: any[]) => {
|
||||||
|
const it = gen(args);
|
||||||
|
|
||||||
|
return {
|
||||||
|
next: it.next,
|
||||||
|
return: it.return,
|
||||||
|
throw: it.throw,
|
||||||
|
[Symbol.iterator]() { return this; }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -54,7 +54,7 @@ public class Main {
|
|||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
var in = new BufferedReader(new InputStreamReader(System.in));
|
var in = new BufferedReader(new InputStreamReader(System.in));
|
||||||
engine = new TypescriptEngine(new File("."));
|
engine = new PolyfillEngine(new File("."));
|
||||||
var scope = engine.global().globalChild();
|
var scope = engine.global().globalChild();
|
||||||
var exited = new boolean[1];
|
var exited = new boolean[1];
|
||||||
|
|
||||||
|
@ -157,32 +157,47 @@ public class CodeFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object next(CallContext ctx, EngineException prevError) throws InterruptedException {
|
public Object next(CallContext ctx, Object prevReturn, Object prevError) throws InterruptedException {
|
||||||
TryCtx tryCtx = null;
|
TryCtx tryCtx = null;
|
||||||
var handled = prevError == null;
|
if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN;
|
||||||
|
|
||||||
while (!tryStack.isEmpty()) {
|
while (!tryStack.isEmpty()) {
|
||||||
var tmp = tryStack.get(tryStack.size() - 1);
|
var tmp = tryStack.get(tryStack.size() - 1);
|
||||||
var remove = false;
|
var remove = false;
|
||||||
|
|
||||||
if (tmp.state == TryCtx.STATE_TRY) {
|
if (prevError != Runners.NO_RETURN) {
|
||||||
if (prevError != null) {
|
remove = true;
|
||||||
|
if (tmp.state == TryCtx.STATE_TRY) {
|
||||||
tmp.jumpPtr = tmp.end;
|
tmp.jumpPtr = tmp.end;
|
||||||
|
|
||||||
if (tmp.hasCatch) {
|
if (tmp.hasCatch) {
|
||||||
tmp.state = TryCtx.STATE_CATCH;
|
tmp.state = TryCtx.STATE_CATCH;
|
||||||
scope.catchVars.add(new ValueVariable(false, prevError));
|
scope.catchVars.add(new ValueVariable(false, prevError));
|
||||||
|
prevError = Runners.NO_RETURN;
|
||||||
codePtr = tmp.catchStart;
|
codePtr = tmp.catchStart;
|
||||||
handled = true;
|
remove = false;
|
||||||
}
|
}
|
||||||
else if (tmp.hasFinally) {
|
else if (tmp.hasFinally) {
|
||||||
tmp.state = TryCtx.STATE_FINALLY_THREW;
|
tmp.state = TryCtx.STATE_FINALLY_THREW;
|
||||||
tmp.err = prevError;
|
tmp.err = new EngineException(prevError);
|
||||||
|
prevError = Runners.NO_RETURN;
|
||||||
codePtr = tmp.finallyStart;
|
codePtr = tmp.finallyStart;
|
||||||
handled = true;
|
remove = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) {
|
}
|
||||||
|
else if (prevReturn != Runners.NO_RETURN) {
|
||||||
|
remove = true;
|
||||||
|
if (tmp.hasFinally && tmp.state <= TryCtx.STATE_CATCH) {
|
||||||
|
tmp.state = TryCtx.STATE_FINALLY_RETURNED;
|
||||||
|
tmp.retVal = prevReturn;
|
||||||
|
prevReturn = Runners.NO_RETURN;
|
||||||
|
codePtr = tmp.finallyStart;
|
||||||
|
remove = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tmp.state == TryCtx.STATE_TRY) {
|
||||||
|
if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) {
|
||||||
if (jumpFlag) tmp.jumpPtr = codePtr;
|
if (jumpFlag) tmp.jumpPtr = codePtr;
|
||||||
else tmp.jumpPtr = tmp.end;
|
else tmp.jumpPtr = tmp.end;
|
||||||
|
|
||||||
@ -218,7 +233,6 @@ public class CodeFrame {
|
|||||||
remove = true;
|
remove = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) throw prevError;
|
|
||||||
if (remove) tryStack.remove(tryStack.size() - 1);
|
if (remove) tryStack.remove(tryStack.size() - 1);
|
||||||
else {
|
else {
|
||||||
tryCtx = tmp;
|
tryCtx = tmp;
|
||||||
@ -226,7 +240,8 @@ public class CodeFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) throw prevError;
|
if (prevError != Runners.NO_RETURN) throw new EngineException(prevError);
|
||||||
|
if (prevReturn != Runners.NO_RETURN) return prevReturn;
|
||||||
|
|
||||||
if (tryCtx == null) return nextNoTry(ctx);
|
if (tryCtx == null) return nextNoTry(ctx);
|
||||||
else if (tryCtx.state == TryCtx.STATE_TRY) {
|
else if (tryCtx.state == TryCtx.STATE_TRY) {
|
||||||
@ -279,15 +294,11 @@ public class CodeFrame {
|
|||||||
else return nextNoTry(ctx);
|
else return nextNoTry(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleReturn(Object value) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object run(CallContext ctx) throws InterruptedException {
|
public Object run(CallContext ctx) throws InterruptedException {
|
||||||
try {
|
try {
|
||||||
start(ctx);
|
start(ctx);
|
||||||
while (true) {
|
while (true) {
|
||||||
var res = next(ctx, null);
|
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
if (res != Runners.NO_RETURN) return res;
|
if (res != Runners.NO_RETURN) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,6 @@ public class Parsing {
|
|||||||
reserved.add("void");
|
reserved.add("void");
|
||||||
reserved.add("null");
|
reserved.add("null");
|
||||||
reserved.add("this");
|
reserved.add("this");
|
||||||
reserved.add("NaN");
|
|
||||||
reserved.add("Infinity");
|
|
||||||
reserved.add("if");
|
reserved.add("if");
|
||||||
reserved.add("else");
|
reserved.add("else");
|
||||||
reserved.add("try");
|
reserved.add("try");
|
||||||
@ -58,7 +56,6 @@ public class Parsing {
|
|||||||
reserved.add("break");
|
reserved.add("break");
|
||||||
reserved.add("continue");
|
reserved.add("continue");
|
||||||
reserved.add("debug");
|
reserved.add("debug");
|
||||||
reserved.add("let");
|
|
||||||
reserved.add("implements");
|
reserved.add("implements");
|
||||||
reserved.add("interface");
|
reserved.add("interface");
|
||||||
reserved.add("package");
|
reserved.add("package");
|
||||||
@ -66,17 +63,18 @@ public class Parsing {
|
|||||||
reserved.add("protected");
|
reserved.add("protected");
|
||||||
reserved.add("public");
|
reserved.add("public");
|
||||||
reserved.add("static");
|
reserved.add("static");
|
||||||
reserved.add("yield");
|
// Although ES5 allow these, we will comply to ES6 here
|
||||||
// Although the standards allow it, these are keywords in newer ES, so we won't allow them
|
|
||||||
reserved.add("const");
|
reserved.add("const");
|
||||||
// reserved.add("await");
|
reserved.add("let");
|
||||||
reserved.add("async");
|
reserved.add("async");
|
||||||
|
reserved.add("super");
|
||||||
// These are allowed too, however our parser considers them keywords
|
// These are allowed too, however our parser considers them keywords
|
||||||
reserved.add("undefined");
|
reserved.add("undefined");
|
||||||
reserved.add("arguments");
|
reserved.add("arguments");
|
||||||
reserved.add("globalThis");
|
reserved.add("globalThis");
|
||||||
reserved.add("window");
|
reserved.add("window");
|
||||||
reserved.add("self");
|
reserved.add("self");
|
||||||
|
// We allow yield and await, because they're part of the custom async and generator functions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -983,7 +981,14 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static ParseRes<? extends Statement> parseSimple(String filename, List<Token> tokens, int i, boolean statement) {
|
public static ParseRes<? extends Statement> parseSimple(String filename, List<Token> tokens, int i, boolean statement) {
|
||||||
var res = new ArrayList<>(List.of(
|
var res = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!statement) {
|
||||||
|
res.add(parseObject(filename, tokens, i));
|
||||||
|
res.add(parseFunction(filename, tokens, i, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.addAll(List.of(
|
||||||
parseVariable(filename, tokens, i),
|
parseVariable(filename, tokens, i),
|
||||||
parseLiteral(filename, tokens, i),
|
parseLiteral(filename, tokens, i),
|
||||||
parseString(filename, tokens, i),
|
parseString(filename, tokens, i),
|
||||||
@ -999,11 +1004,6 @@ public class Parsing {
|
|||||||
parseDelete(filename, tokens, i)
|
parseDelete(filename, tokens, i)
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!statement) {
|
|
||||||
res.add(parseObject(filename, tokens, i));
|
|
||||||
res.add(parseFunction(filename, tokens, i, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseRes.any(res.toArray(ParseRes[]::new));
|
return ParseRes.any(res.toArray(ParseRes[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1040,12 +1040,6 @@ public class Parsing {
|
|||||||
if (id.result.equals("null")) {
|
if (id.result.equals("null")) {
|
||||||
return ParseRes.res(new ConstantStatement(loc, Values.NULL), 1);
|
return ParseRes.res(new ConstantStatement(loc, Values.NULL), 1);
|
||||||
}
|
}
|
||||||
if (id.result.equals("NaN")) {
|
|
||||||
return ParseRes.res(new ConstantStatement(loc, Double.NaN), 1);
|
|
||||||
}
|
|
||||||
if (id.result.equals("Infinity")) {
|
|
||||||
return ParseRes.res(new ConstantStatement(loc, Double.POSITIVE_INFINITY), 1);
|
|
||||||
}
|
|
||||||
if (id.result.equals("this")) {
|
if (id.result.equals("this")) {
|
||||||
return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
|
return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
|
||||||
}
|
}
|
||||||
@ -1803,7 +1797,6 @@ public class Parsing {
|
|||||||
parseContinue(filename, tokens, i),
|
parseContinue(filename, tokens, i),
|
||||||
parseBreak(filename, tokens, i),
|
parseBreak(filename, tokens, i),
|
||||||
parseDebug(filename, tokens, i),
|
parseDebug(filename, tokens, i),
|
||||||
parseValueStatement(filename, tokens, i),
|
|
||||||
parseIf(filename, tokens, i),
|
parseIf(filename, tokens, i),
|
||||||
parseWhile(filename, tokens, i),
|
parseWhile(filename, tokens, i),
|
||||||
parseSwitch(filename, tokens, i),
|
parseSwitch(filename, tokens, i),
|
||||||
@ -1812,7 +1805,8 @@ public class Parsing {
|
|||||||
parseDoWhile(filename, tokens, i),
|
parseDoWhile(filename, tokens, i),
|
||||||
parseCatch(filename, tokens, i),
|
parseCatch(filename, tokens, i),
|
||||||
parseCompound(filename, tokens, i),
|
parseCompound(filename, tokens, i),
|
||||||
parseFunction(filename, tokens, i, true)
|
parseFunction(filename, tokens, i, true),
|
||||||
|
parseValueStatement(filename, tokens, i)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.polyfills;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.CallContext;
|
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
|
||||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
|
||||||
|
|
||||||
public class AsyncFunction extends FunctionValue {
|
|
||||||
public final CodeFunction body;
|
|
||||||
|
|
||||||
private class CallHandler {
|
|
||||||
private boolean awaiting = false;
|
|
||||||
private Object awaited = null;
|
|
||||||
public final Promise promise = new Promise();
|
|
||||||
public CodeFrame frame;
|
|
||||||
private final NativeFunction fulfillFunc = new NativeFunction("", (ctx, thisArg, args) -> {
|
|
||||||
if (args.length == 0) exec(ctx, null, Runners.NO_RETURN);
|
|
||||||
else exec(ctx, args[0], Runners.NO_RETURN);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
private final NativeFunction rejectFunc = new NativeFunction("", (ctx, thisArg, args) -> {
|
|
||||||
if (args.length == 0) exec(ctx, Runners.NO_RETURN, null);
|
|
||||||
else exec(ctx, Runners.NO_RETURN, args[0]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
public Object exec(CallContext ctx, Object val, Object err) throws InterruptedException {
|
|
||||||
if (val != Runners.NO_RETURN) frame.push(val);
|
|
||||||
|
|
||||||
frame.start(ctx);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
awaiting = false;
|
|
||||||
awaited = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var res = frame.next(ctx, err == Runners.NO_RETURN ? null : new EngineException(err));
|
|
||||||
if (res != Runners.NO_RETURN) {
|
|
||||||
promise.fulfill(ctx, res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
promise.reject(e.value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!awaiting) continue;
|
|
||||||
|
|
||||||
frame.pop();
|
|
||||||
|
|
||||||
if (awaited instanceof Promise) ((Promise)awaited).then(ctx, fulfillFunc, rejectFunc);
|
|
||||||
else if (Values.isPrimitive(awaited)) frame.push(awaited);
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
var res = Values.getMember(ctx, awaited, "then");
|
|
||||||
if (res instanceof FunctionValue) {
|
|
||||||
Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else frame.push(awaited);
|
|
||||||
}
|
|
||||||
catch (EngineException e) {
|
|
||||||
promise.reject(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.end(ctx);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object await(CallContext ctx, Object thisArg, Object[] args) {
|
|
||||||
this.awaiting = true;
|
|
||||||
this.awaited = args[0];
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException {
|
|
||||||
var handler = new CallHandler();
|
|
||||||
var func = body.call(_ctx, thisArg, new NativeFunction("await", handler::await));
|
|
||||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
|
||||||
handler.frame = new CodeFrame(thisArg, args, (CodeFunction)func);
|
|
||||||
handler.exec(_ctx, Runners.NO_RETURN, Runners.NO_RETURN);
|
|
||||||
return handler.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AsyncFunction(CodeFunction body) {
|
|
||||||
super(body.name, body.length);
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
99
src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java
Normal file
99
src/me/topchetoeu/jscript/polyfills/GeneratorFunction.java
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package me.topchetoeu.jscript.polyfills;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.CallContext;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
|
public class GeneratorFunction extends FunctionValue {
|
||||||
|
public final CodeFunction factory;
|
||||||
|
|
||||||
|
public static class Generator {
|
||||||
|
private boolean yielding = true;
|
||||||
|
private boolean done = false;
|
||||||
|
public CodeFrame frame;
|
||||||
|
|
||||||
|
private ObjectValue next(CallContext ctx, Object inducedValue, Object inducedReturn, Object inducedError) throws InterruptedException {
|
||||||
|
if (done) {
|
||||||
|
if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError);
|
||||||
|
var res = new ObjectValue();
|
||||||
|
res.defineProperty("done", true);
|
||||||
|
res.defineProperty("value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object res = null;
|
||||||
|
if (inducedValue != Runners.NO_RETURN) frame.push(inducedValue);
|
||||||
|
frame.start(ctx);
|
||||||
|
yielding = false;
|
||||||
|
while (!yielding) {
|
||||||
|
try {
|
||||||
|
res = frame.next(ctx, inducedReturn, inducedError);
|
||||||
|
inducedReturn = inducedError = Runners.NO_RETURN;
|
||||||
|
if (res != Runners.NO_RETURN) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (EngineException e) {
|
||||||
|
done = true;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.end(ctx);
|
||||||
|
if (done) frame = null;
|
||||||
|
else res = frame.pop();
|
||||||
|
|
||||||
|
var obj = new ObjectValue();
|
||||||
|
obj.defineProperty("done", done);
|
||||||
|
obj.defineProperty("value", res);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Native
|
||||||
|
public ObjectValue next(CallContext ctx, Object... args) throws InterruptedException {
|
||||||
|
if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN);
|
||||||
|
}
|
||||||
|
@Native("throw")
|
||||||
|
public ObjectValue _throw(CallContext ctx, Object error) throws InterruptedException {
|
||||||
|
return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error);
|
||||||
|
}
|
||||||
|
@Native("return")
|
||||||
|
public ObjectValue _return(CallContext ctx, Object value) throws InterruptedException {
|
||||||
|
return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (done) return "Generator [closed]";
|
||||||
|
if (yielding) return "Generator [suspended]";
|
||||||
|
return "Generator " + (done ? "[closed]" : "[suspended]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object yield(CallContext ctx, Object thisArg, Object[] args) {
|
||||||
|
this.yielding = true;
|
||||||
|
return args.length > 0 ? args[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException {
|
||||||
|
var handler = new Generator();
|
||||||
|
var func = factory.call(_ctx, thisArg, new NativeFunction("yield", handler::yield));
|
||||||
|
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||||
|
handler.frame = new CodeFrame(thisArg, args, (CodeFunction)func);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeneratorFunction(CodeFunction factory) {
|
||||||
|
super(factory.name, factory.length);
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,11 @@ public class Internals {
|
|||||||
return Values.toNumber(ctx, val);
|
return Values.toNumber(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Native
|
||||||
|
public boolean isArr(Object val) {
|
||||||
|
return val instanceof ArrayValue;
|
||||||
|
}
|
||||||
|
|
||||||
@NativeGetter("symbolProto")
|
@NativeGetter("symbolProto")
|
||||||
public ObjectValue symbolProto(CallContext ctx) {
|
public ObjectValue symbolProto(CallContext ctx) {
|
||||||
return ctx.engine().symbolProto();
|
return ctx.engine().symbolProto();
|
||||||
@ -237,9 +242,9 @@ public class Internals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Native
|
@Native
|
||||||
public AsyncFunction makeAsync(FunctionValue func) {
|
public GeneratorFunction makeGenerator(FunctionValue func) {
|
||||||
if (func instanceof CodeFunction) return new AsyncFunction((CodeFunction)func);
|
if (func instanceof CodeFunction) return new GeneratorFunction((CodeFunction)func);
|
||||||
else throw EngineException.ofType("Can't create an async function with a non-js function.");
|
else throw EngineException.ofType("Can't create a generator with a non-js function.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeGetter("err")
|
@NativeGetter("err")
|
||||||
|
Loading…
Reference in New Issue
Block a user