fix: await in async func throws properly
This commit is contained in:
parent
396d7ca5a2
commit
991f957f44
@ -297,6 +297,14 @@ setProps(Array.prototype, {
|
||||
|
||||
return -1;
|
||||
},
|
||||
lastIndexOf(el, start) {
|
||||
start = start! | 0;
|
||||
for (var i = this.length; i >= start; i--) {
|
||||
if (i in this && this[i] == el) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
includes(el, start) {
|
||||
return this.indexOf(el, start) >= 0;
|
||||
},
|
||||
|
@ -14,7 +14,7 @@ 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): Promise<RetT>;
|
||||
async<ArgsT extends any[], RetT>(func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT): (...args: ArgsT) => Promise<RetT>;
|
||||
}
|
||||
|
||||
interface CallableFunction extends Function {
|
||||
|
@ -54,7 +54,7 @@ public class Main {
|
||||
|
||||
public static void main(String args[]) {
|
||||
var in = new BufferedReader(new InputStreamReader(System.in));
|
||||
engine = new PolyfillEngine(new File("."));
|
||||
engine = new TypescriptEngine(new File("."));
|
||||
var scope = engine.global().globalChild();
|
||||
var exited = new boolean[1];
|
||||
|
||||
|
@ -37,7 +37,7 @@ public class TryStatement extends Statement {
|
||||
if (catchBody != null) {
|
||||
int tmp = target.size();
|
||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
||||
local.define(name);
|
||||
local.define(name, true);
|
||||
catchBody.compileNoPollution(target, scope);
|
||||
local.undefine();
|
||||
catchN = target.size() - tmp;
|
||||
|
@ -157,15 +157,32 @@ public class CodeFrame {
|
||||
}
|
||||
}
|
||||
|
||||
public Object next(CallContext ctx) throws InterruptedException {
|
||||
public Object next(CallContext ctx, EngineException prevError) throws InterruptedException {
|
||||
TryCtx tryCtx = null;
|
||||
var handled = prevError == null;
|
||||
|
||||
while (!tryStack.isEmpty()) {
|
||||
var tmp = tryStack.get(tryStack.size() - 1);
|
||||
var remove = false;
|
||||
|
||||
if (tmp.state == TryCtx.STATE_TRY) {
|
||||
if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) {
|
||||
if (prevError != null) {
|
||||
tmp.jumpPtr = tmp.end;
|
||||
|
||||
if (tmp.hasCatch) {
|
||||
tmp.state = TryCtx.STATE_CATCH;
|
||||
scope.catchVars.add(new ValueVariable(false, prevError));
|
||||
codePtr = tmp.catchStart;
|
||||
handled = true;
|
||||
}
|
||||
else if (tmp.hasFinally) {
|
||||
tmp.state = TryCtx.STATE_FINALLY_THREW;
|
||||
tmp.err = prevError;
|
||||
codePtr = tmp.finallyStart;
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if (codePtr < tmp.tryStart || codePtr >= tmp.catchStart) {
|
||||
if (jumpFlag) tmp.jumpPtr = codePtr;
|
||||
else tmp.jumpPtr = tmp.end;
|
||||
|
||||
@ -201,6 +218,7 @@ public class CodeFrame {
|
||||
remove = true;
|
||||
}
|
||||
|
||||
if (!handled) throw prevError;
|
||||
if (remove) tryStack.remove(tryStack.size() - 1);
|
||||
else {
|
||||
tryCtx = tmp;
|
||||
@ -208,6 +226,8 @@ public class CodeFrame {
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) throw prevError;
|
||||
|
||||
if (tryCtx == null) return nextNoTry(ctx);
|
||||
else if (tryCtx.state == TryCtx.STATE_TRY) {
|
||||
try {
|
||||
@ -259,11 +279,15 @@ 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);
|
||||
var res = next(ctx, null);
|
||||
if (res != Runners.NO_RETURN) return res;
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
|
||||
public Object getKey(String name) {
|
||||
var capI = captures.indexOf(name);
|
||||
var locI = locals.indexOf(name);
|
||||
var locI = locals.lastIndexOf(name);
|
||||
if (locI >= 0) return locI;
|
||||
if (capI >= 0) return ~capI;
|
||||
if (parent != null) {
|
||||
@ -65,11 +65,14 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
locals.contains(name) ||
|
||||
parent != null && parent.has(ctx, name);
|
||||
}
|
||||
public Object define(String name) {
|
||||
if (locals.contains(name)) return locals.indexOf(name);
|
||||
public Object define(String name, boolean force) {
|
||||
if (!force && locals.contains(name)) return locals.indexOf(name);
|
||||
locals.add(name);
|
||||
return locals.size() - 1;
|
||||
}
|
||||
public Object define(String name) {
|
||||
return define(name, false);
|
||||
}
|
||||
public void undefine() {
|
||||
locals.remove(locals.size() - 1);
|
||||
}
|
||||
|
@ -354,7 +354,8 @@ public class Values {
|
||||
}
|
||||
|
||||
public static Object call(CallContext ctx, Object func, Object thisArg, Object ...args) throws InterruptedException {
|
||||
if (!isFunction(func)) throw EngineException.ofType("Attempted to call a non-function value.");
|
||||
if (!isFunction(func))
|
||||
throw EngineException.ofType("Tried to call a non-function value.");
|
||||
return function(func).call(ctx, thisArg, args);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ 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.ArrayValue;
|
||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||
@ -18,15 +17,21 @@ public class AsyncFunction extends FunctionValue {
|
||||
private Object awaited = null;
|
||||
public final Promise promise = new Promise();
|
||||
public CodeFrame frame;
|
||||
private final NativeFunction fulfillFunc = new NativeFunction("", this::fulfill);
|
||||
private final NativeFunction rejectFunc = new NativeFunction("", this::reject);
|
||||
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);
|
||||
|
||||
private Object reject(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException {
|
||||
if (args.length > 0) promise.reject(ctx, args[0]);
|
||||
return null;
|
||||
}
|
||||
public Object fulfill(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException {
|
||||
if (args.length == 1) frame.push(args[0]);
|
||||
});
|
||||
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);
|
||||
|
||||
@ -35,14 +40,14 @@ public class AsyncFunction extends FunctionValue {
|
||||
awaited = null;
|
||||
|
||||
try {
|
||||
var res = frame.next(ctx);
|
||||
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);
|
||||
promise.reject(e.value);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -52,17 +57,19 @@ public class AsyncFunction extends FunctionValue {
|
||||
|
||||
if (awaited instanceof Promise) ((Promise)awaited).then(ctx, fulfillFunc, rejectFunc);
|
||||
else if (Values.isPrimitive(awaited)) frame.push(awaited);
|
||||
try {
|
||||
var res = Values.getMember(ctx, awaited, "then");
|
||||
if (res instanceof FunctionValue) {
|
||||
Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc);
|
||||
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;
|
||||
}
|
||||
else frame.push(awaited);
|
||||
}
|
||||
catch (EngineException e) {
|
||||
promise.reject(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,8 +87,10 @@ public class AsyncFunction extends FunctionValue {
|
||||
@Override
|
||||
public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException {
|
||||
var handler = new CallHandler();
|
||||
handler.frame = new CodeFrame(thisArg, new Object[] { new NativeFunction("await", handler::await), new ArrayValue(args) }, body);
|
||||
handler.fulfill(_ctx, null, new Object[0]);
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user