Merge pull request #3 from TopchetoEU/TopchetoEU/Generators
Create generators
This commit is contained in:
commit
7b19f7b399
@ -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 = unknown, RetT = unknown, TNext = unknown>(
|
||||
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
|
||||
): (...args: ArgsT) => Generator<T, RetT, TNext>;
|
||||
}
|
||||
|
||||
interface CallableFunction extends Function {
|
||||
@ -43,12 +51,16 @@ 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];
|
||||
}
|
||||
}
|
||||
|
||||
return internals.apply(this, thisArg, newArgs);
|
||||
},
|
||||
@ -80,6 +92,90 @@ setProps(Function.prototype, {
|
||||
setProps(Function, {
|
||||
async(func) {
|
||||
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[]) {
|
||||
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 (prevError != Runners.NO_RETURN) {
|
||||
remove = true;
|
||||
if (tmp.state == TryCtx.STATE_TRY) {
|
||||
if (prevError != null) {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -983,7 +981,14 @@ public class Parsing {
|
||||
}
|
||||
@SuppressWarnings("all")
|
||||
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),
|
||||
parseLiteral(filename, tokens, i),
|
||||
parseString(filename, tokens, i),
|
||||
@ -999,11 +1004,6 @@ public class Parsing {
|
||||
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));
|
||||
}
|
||||
|
||||
@ -1040,12 +1040,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);
|
||||
}
|
||||
@ -1803,7 +1797,6 @@ public class Parsing {
|
||||
parseContinue(filename, tokens, i),
|
||||
parseBreak(filename, tokens, i),
|
||||
parseDebug(filename, tokens, i),
|
||||
parseValueStatement(filename, tokens, i),
|
||||
parseIf(filename, tokens, i),
|
||||
parseWhile(filename, tokens, i),
|
||||
parseSwitch(filename, tokens, i),
|
||||
@ -1812,7 +1805,8 @@ public class Parsing {
|
||||
parseDoWhile(filename, tokens, i),
|
||||
parseCatch(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);
|
||||
}
|
||||
|
||||
@Native
|
||||
public boolean isArr(Object val) {
|
||||
return val instanceof ArrayValue;
|
||||
}
|
||||
|
||||
@NativeGetter("symbolProto")
|
||||
public ObjectValue symbolProto(CallContext ctx) {
|
||||
return ctx.engine().symbolProto();
|
||||
@ -237,9 +242,9 @@ public class Internals {
|
||||
}
|
||||
|
||||
@Native
|
||||
public AsyncFunction makeAsync(FunctionValue func) {
|
||||
if (func instanceof CodeFunction) return new AsyncFunction((CodeFunction)func);
|
||||
else throw EngineException.ofType("Can't create an async function with a non-js function.");
|
||||
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")
|
||||
|
Loading…
Reference in New Issue
Block a user