fix: await in async func throws properly

This commit is contained in:
TopchetoEU 2023-08-25 17:00:47 +03:00
parent 396d7ca5a2
commit 991f957f44
No known key found for this signature in database
GPG Key ID: 24E57B2E9C61AD19
8 changed files with 76 additions and 31 deletions

View File

@ -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;
},

View File

@ -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 {

View File

@ -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];

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,6 +57,7 @@ public class AsyncFunction extends FunctionValue {
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) {
@ -65,6 +71,7 @@ public class AsyncFunction extends FunctionValue {
break;
}
}
}
frame.end(ctx);
return null;
@ -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;
}