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; 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) { includes(el, start) {
return this.indexOf(el, start) >= 0; return this.indexOf(el, start) >= 0;
}, },

View File

@ -14,7 +14,7 @@ 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): 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 { interface CallableFunction extends Function {

View File

@ -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 PolyfillEngine(new File(".")); engine = new TypescriptEngine(new File("."));
var scope = engine.global().globalChild(); var scope = engine.global().globalChild();
var exited = new boolean[1]; var exited = new boolean[1];

View File

@ -37,7 +37,7 @@ public class TryStatement extends Statement {
if (catchBody != null) { if (catchBody != null) {
int tmp = target.size(); int tmp = target.size();
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope; var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
local.define(name); local.define(name, true);
catchBody.compileNoPollution(target, scope); catchBody.compileNoPollution(target, scope);
local.undefine(); local.undefine();
catchN = target.size() - tmp; 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; TryCtx tryCtx = null;
var handled = prevError == null;
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 (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; if (jumpFlag) tmp.jumpPtr = codePtr;
else tmp.jumpPtr = tmp.end; else tmp.jumpPtr = tmp.end;
@ -201,6 +218,7 @@ 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;
@ -208,6 +226,8 @@ public class CodeFrame {
} }
} }
if (!handled) throw prevError;
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) {
try { try {
@ -259,11 +279,15 @@ 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); var res = next(ctx, null);
if (res != Runners.NO_RETURN) return res; if (res != Runners.NO_RETURN) return res;
} }
} }

View File

@ -46,7 +46,7 @@ public class LocalScopeRecord implements ScopeRecord {
public Object getKey(String name) { public Object getKey(String name) {
var capI = captures.indexOf(name); var capI = captures.indexOf(name);
var locI = locals.indexOf(name); var locI = locals.lastIndexOf(name);
if (locI >= 0) return locI; if (locI >= 0) return locI;
if (capI >= 0) return ~capI; if (capI >= 0) return ~capI;
if (parent != null) { if (parent != null) {
@ -65,11 +65,14 @@ public class LocalScopeRecord implements ScopeRecord {
locals.contains(name) || locals.contains(name) ||
parent != null && parent.has(ctx, name); parent != null && parent.has(ctx, name);
} }
public Object define(String name) { public Object define(String name, boolean force) {
if (locals.contains(name)) return locals.indexOf(name); if (!force && locals.contains(name)) return locals.indexOf(name);
locals.add(name); locals.add(name);
return locals.size() - 1; return locals.size() - 1;
} }
public Object define(String name) {
return define(name, false);
}
public void undefine() { public void undefine() {
locals.remove(locals.size() - 1); 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 { 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); 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.CallContext;
import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners; 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.CodeFunction;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.NativeFunction;
@ -18,15 +17,21 @@ public class AsyncFunction extends FunctionValue {
private Object awaited = null; private Object awaited = null;
public final Promise promise = new Promise(); public final Promise promise = new Promise();
public CodeFrame frame; public CodeFrame frame;
private final NativeFunction fulfillFunc = new NativeFunction("", this::fulfill); private final NativeFunction fulfillFunc = new NativeFunction("", (ctx, thisArg, args) -> {
private final NativeFunction rejectFunc = new NativeFunction("", this::reject); 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; return null;
} });
public Object fulfill(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException { private final NativeFunction rejectFunc = new NativeFunction("", (ctx, thisArg, args) -> {
if (args.length == 1) frame.push(args[0]); 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); frame.start(ctx);
@ -35,14 +40,14 @@ public class AsyncFunction extends FunctionValue {
awaited = null; awaited = null;
try { try {
var res = frame.next(ctx); var res = frame.next(ctx, err == Runners.NO_RETURN ? null : new EngineException(err));
if (res != Runners.NO_RETURN) { if (res != Runners.NO_RETURN) {
promise.fulfill(ctx, res); promise.fulfill(ctx, res);
break; break;
} }
} }
catch (EngineException e) { catch (EngineException e) {
promise.reject(e); promise.reject(e.value);
break; break;
} }
@ -52,17 +57,19 @@ public class AsyncFunction extends FunctionValue {
if (awaited instanceof Promise) ((Promise)awaited).then(ctx, fulfillFunc, rejectFunc); if (awaited instanceof Promise) ((Promise)awaited).then(ctx, fulfillFunc, rejectFunc);
else if (Values.isPrimitive(awaited)) frame.push(awaited); else if (Values.isPrimitive(awaited)) frame.push(awaited);
try { else {
var res = Values.getMember(ctx, awaited, "then"); try {
if (res instanceof FunctionValue) { var res = Values.getMember(ctx, awaited, "then");
Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc); if (res instanceof FunctionValue) {
Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc);
break;
}
else frame.push(awaited);
}
catch (EngineException e) {
promise.reject(e);
break; break;
} }
else frame.push(awaited);
}
catch (EngineException e) {
promise.reject(e);
break;
} }
} }
@ -80,8 +87,10 @@ public class AsyncFunction extends FunctionValue {
@Override @Override
public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException { public Object call(CallContext _ctx, Object thisArg, Object... args) throws InterruptedException {
var handler = new CallHandler(); var handler = new CallHandler();
handler.frame = new CodeFrame(thisArg, new Object[] { new NativeFunction("await", handler::await), new ArrayValue(args) }, body); var func = body.call(_ctx, thisArg, new NativeFunction("await", handler::await));
handler.fulfill(_ctx, null, new Object[0]); 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; return handler.promise;
} }