diff --git a/lib/values/array.ts b/lib/values/array.ts index 888c34d..5144630 100644 --- a/lib/values/array.ts +++ b/lib/values/array.ts @@ -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; }, diff --git a/lib/values/function.ts b/lib/values/function.ts index 0bd101b..81e4be0 100644 --- a/lib/values/function.ts +++ b/lib/values/function.ts @@ -14,7 +14,7 @@ interface FunctionConstructor extends Function { (...args: string[]): (...args: any[]) => any; new (...args: string[]): (...args: any[]) => any; prototype: Function; - async(func: (await: (val: T) => Awaited, args: ArgsT) => RetT): Promise; + async(func: (await: (val: T) => Awaited) => (...args: ArgsT) => RetT): (...args: ArgsT) => Promise; } interface CallableFunction extends Function { diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index aa92604..34a2426 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -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]; diff --git a/src/me/topchetoeu/jscript/compilation/control/TryStatement.java b/src/me/topchetoeu/jscript/compilation/control/TryStatement.java index 430a943..a0c763b 100644 --- a/src/me/topchetoeu/jscript/compilation/control/TryStatement.java +++ b/src/me/topchetoeu/jscript/compilation/control/TryStatement.java @@ -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; diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 1ea750c..92a8a4d 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -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; } } diff --git a/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java b/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java index ee5eefa..5839e19 100644 --- a/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java +++ b/src/me/topchetoeu/jscript/engine/scope/LocalScopeRecord.java @@ -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); } diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index efd2488..fd5bcf3 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -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); } diff --git a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java index b3bcf1f..627ff18 100644 --- a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java +++ b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java @@ -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; }