Create generators #3
@ -74,8 +74,8 @@ interface MathObject {
|
||||
//@ts-ignore
|
||||
declare const arguments: IArguments;
|
||||
declare const Math: MathObject;
|
||||
declare const NaN: number;
|
||||
declare const Infinity: number;
|
||||
const NaN = 0 / 0;
|
||||
const Infinity = 1 / 0;
|
||||
|
||||
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;
|
||||
|
@ -365,5 +365,5 @@ setProps(Array.prototype, {
|
||||
});
|
||||
|
||||
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;
|
||||
new (...args: string[]): (...args: any[]) => any;
|
||||
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, RetT, TNext>(
|
||||
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
|
||||
): (...args: ArgsT) => Generator<T, RetT, TNext>;
|
||||
}
|
||||
|
||||
interface CallableFunction extends Function {
|
||||
@ -43,11 +51,15 @@ setProps(Function.prototype, {
|
||||
apply(thisArg, args) {
|
||||
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
|
||||
var len = args.length - 0;
|
||||
var newArgs: any[] = [];
|
||||
let newArgs: any[];
|
||||
if (Array.isArray(args)) newArgs = args;
|
||||
else {
|
||||
newArgs = [];
|
||||
|
||||
while (len >= 0) {
|
||||
len--;
|
||||
newArgs[len] = args[len];
|
||||
while (len >= 0) {
|
||||
len--;
|
||||
newArgs[len] = args[len];
|
||||
}
|
||||
}
|
||||
|
||||
return internals.apply(this, thisArg, newArgs);
|
||||
@ -81,5 +93,9 @@ setProps(Function, {
|
||||
async(func) {
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||
return internals.makeAsync(func);
|
||||
},
|
||||
generator(func) {
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||
return internals.makeGenerator(func);
|
||||
}
|
||||
})
|
@ -54,7 +54,7 @@ public class Main {
|
||||
|
||||
public static void main(String args[]) {
|
||||
var in = new BufferedReader(new InputStreamReader(System.in));
|
||||
engine = new TypescriptEngine(new File("."));
|
||||
engine = new PolyfillEngine(new File("."));
|
||||
var scope = engine.global().globalChild();
|
||||
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;
|
||||
var handled = prevError == null;
|
||||
if (prevError != Runners.NO_RETURN) prevReturn = Runners.NO_RETURN;
|
||||
|
||||
while (!tryStack.isEmpty()) {
|
||||
var tmp = tryStack.get(tryStack.size() - 1);
|
||||
var remove = false;
|
||||
|
||||
if (tmp.state == TryCtx.STATE_TRY) {
|
||||
if (prevError != null) {
|
||||
if (prevError != Runners.NO_RETURN) {
|
||||
remove = true;
|
||||
if (tmp.state == TryCtx.STATE_TRY) {
|
||||
tmp.jumpPtr = tmp.end;
|
||||
|
||||
if (tmp.hasCatch) {
|
||||
tmp.state = TryCtx.STATE_CATCH;
|
||||
scope.catchVars.add(new ValueVariable(false, prevError));
|
||||
prevError = Runners.NO_RETURN;
|
||||
codePtr = tmp.catchStart;
|
||||
handled = true;
|
||||
remove = false;
|
||||
}
|
||||
else if (tmp.hasFinally) {
|
||||
tmp.state = TryCtx.STATE_FINALLY_THREW;
|
||||
tmp.err = prevError;
|
||||
tmp.err = new EngineException(prevError);
|
||||
prevError = Runners.NO_RETURN;
|
||||
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;
|
||||
else tmp.jumpPtr = tmp.end;
|
||||
|
||||
@ -218,7 +233,6 @@ public class CodeFrame {
|
||||
remove = true;
|
||||
}
|
||||
|
||||
if (!handled) throw prevError;
|
||||
if (remove) tryStack.remove(tryStack.size() - 1);
|
||||
else {
|
||||
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);
|
||||
else if (tryCtx.state == TryCtx.STATE_TRY) {
|
||||
@ -279,15 +294,11 @@ public class CodeFrame {
|
||||
else return nextNoTry(ctx);
|
||||
}
|
||||
|
||||
public void handleReturn(Object value) {
|
||||
|
||||
}
|
||||
|
||||
public Object run(CallContext ctx) throws InterruptedException {
|
||||
try {
|
||||
start(ctx);
|
||||
while (true) {
|
||||
var res = next(ctx, null);
|
||||
var res = next(ctx, Runners.NO_RETURN, Runners.NO_RETURN);
|
||||
if (res != Runners.NO_RETURN) return res;
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ public class Parsing {
|
||||
reserved.add("void");
|
||||
reserved.add("null");
|
||||
reserved.add("this");
|
||||
reserved.add("NaN");
|
||||
reserved.add("Infinity");
|
||||
reserved.add("if");
|
||||
reserved.add("else");
|
||||
reserved.add("try");
|
||||
@ -58,7 +56,6 @@ public class Parsing {
|
||||
reserved.add("break");
|
||||
reserved.add("continue");
|
||||
reserved.add("debug");
|
||||
reserved.add("let");
|
||||
reserved.add("implements");
|
||||
reserved.add("interface");
|
||||
reserved.add("package");
|
||||
@ -66,17 +63,18 @@ public class Parsing {
|
||||
reserved.add("protected");
|
||||
reserved.add("public");
|
||||
reserved.add("static");
|
||||
reserved.add("yield");
|
||||
// Although the standards allow it, these are keywords in newer ES, so we won't allow them
|
||||
// Although ES5 allow these, we will comply to ES6 here
|
||||
reserved.add("const");
|
||||
// reserved.add("await");
|
||||
reserved.add("let");
|
||||
reserved.add("async");
|
||||
reserved.add("super");
|
||||
// These are allowed too, however our parser considers them keywords
|
||||
reserved.add("undefined");
|
||||
reserved.add("arguments");
|
||||
reserved.add("globalThis");
|
||||
reserved.add("window");
|
||||
reserved.add("self");
|
||||
// We allow yield and await, because they're part of the custom async and generator functions
|
||||
}
|
||||
|
||||
|
||||
@ -1040,12 +1038,6 @@ public class Parsing {
|
||||
if (id.result.equals("null")) {
|
||||
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")) {
|
||||
return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ public class AsyncFunction extends FunctionValue {
|
||||
awaited = null;
|
||||
|
||||
try {
|
||||
var res = frame.next(ctx, err == Runners.NO_RETURN ? null : new EngineException(err));
|
||||
var res = frame.next(ctx, val, err);
|
||||
err = Runners.NO_RETURN;
|
||||
if (res != Runners.NO_RETURN) {
|
||||
promise.fulfill(ctx, res);
|
||||
break;
|
||||
|
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);
|
||||
}
|
||||
|
||||
@Native
|
||||
public boolean isArr(Object val) {
|
||||
return val instanceof ArrayValue;
|
||||
}
|
||||
|
||||
@NativeGetter("symbolProto")
|
||||
public ObjectValue symbolProto(CallContext ctx) {
|
||||
return ctx.engine().symbolProto();
|
||||
@ -241,6 +246,11 @@ public class Internals {
|
||||
if (func instanceof CodeFunction) return new AsyncFunction((CodeFunction)func);
|
||||
else throw EngineException.ofType("Can't create an async function with a non-js function.");
|
||||
}
|
||||
@Native
|
||||
public GeneratorFunction makeGenerator(FunctionValue func) {
|
||||
if (func instanceof CodeFunction) return new GeneratorFunction((CodeFunction)func);
|
||||
else throw EngineException.ofType("Can't create a generator with a non-js function.");
|
||||
}
|
||||
|
||||
@NativeGetter("err")
|
||||
public ObjectValue errProto(CallContext ctx) {
|
||||
|
Loading…
Reference in New Issue
Block a user