From 3475e3a130e8b32d313dabe1f0f0ef08e859d92c Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 24 Aug 2024 22:18:20 +0300 Subject: [PATCH 01/48] refactor: Remove environment-related bloat --- .../topchetoeu/jscript/common/json/JSON.java | 6 +- .../jscript/compilation/parsing/Parsing.java | 2 +- .../me/topchetoeu/jscript/lib/ArrayLib.java | 62 +- .../jscript/lib/AsyncFunctionLib.java | 29 +- .../lib/AsyncGeneratorFunctionLib.java | 9 +- .../jscript/lib/AsyncGeneratorLib.java | 42 +- .../me/topchetoeu/jscript/lib/ConsoleLib.java | 2 +- .../topchetoeu/jscript/lib/EncodingLib.java | 2 +- .../me/topchetoeu/jscript/lib/ErrorLib.java | 12 +- .../me/topchetoeu/jscript/lib/FileLib.java | 16 +- .../topchetoeu/jscript/lib/FilesystemLib.java | 40 +- .../topchetoeu/jscript/lib/FunctionLib.java | 15 +- .../jscript/lib/GeneratorFunctionLib.java | 7 +- .../topchetoeu/jscript/lib/GeneratorLib.java | 26 +- .../me/topchetoeu/jscript/lib/Internals.java | 51 +- .../me/topchetoeu/jscript/lib/JSONLib.java | 2 +- .../me/topchetoeu/jscript/lib/MapLib.java | 20 +- .../me/topchetoeu/jscript/lib/NumberLib.java | 4 +- .../me/topchetoeu/jscript/lib/ObjectLib.java | 78 +- .../me/topchetoeu/jscript/lib/PromiseLib.java | 137 ++-- .../me/topchetoeu/jscript/lib/RegExpLib.java | 32 +- .../me/topchetoeu/jscript/lib/SetLib.java | 16 +- .../me/topchetoeu/jscript/lib/StringLib.java | 66 +- .../me/topchetoeu/jscript/lib/SymbolLib.java | 2 +- .../topchetoeu/jscript/runtime/Childable.java | 5 - .../topchetoeu/jscript/runtime/Compiler.java | 6 +- .../topchetoeu/jscript/runtime/Context.java | 98 --- .../topchetoeu/jscript/runtime/Copyable.java | 5 - .../jscript/runtime/Environment.java | 61 -- .../topchetoeu/jscript/runtime/EventLoop.java | 17 +- .../jscript/runtime/Extensions.java | 77 -- .../me/topchetoeu/jscript/runtime/Frame.java | 99 ++- .../jscript/runtime/InstructionRunner.java | 63 +- .../me/topchetoeu/jscript/runtime/Key.java | 5 - .../jscript/runtime/WrapperProvider.java | 1 + .../jscript/runtime/debug/DebugContext.java | 37 +- .../jscript/runtime/debug/DebugHandler.java | 25 +- .../runtime/environment/Environment.java | 144 ++++ .../jscript/runtime/environment/Key.java | 5 + .../runtime/exceptions/EngineException.java | 24 +- .../jscript/runtime/scope/GlobalScope.java | 26 +- .../jscript/runtime/scope/ValueVariable.java | 6 +- .../jscript/runtime/scope/Variable.java | 6 +- .../jscript/runtime/values/ArrayValue.java | 18 +- .../jscript/runtime/values/CodeFunction.java | 17 +- .../jscript/runtime/values/FunctionValue.java | 12 +- .../runtime/values/NativeFunction.java | 7 +- .../jscript/runtime/values/NativeWrapper.java | 20 +- .../jscript/runtime/values/ObjectValue.java | 133 ++-- .../jscript/runtime/values/ScopeValue.java | 10 +- .../jscript/runtime/values/Value.java | 733 ++++++++++++++++++ .../jscript/runtime/values/Values.java | 101 ++- .../topchetoeu/jscript/utils/JSCompiler.java | 6 +- .../topchetoeu/jscript/utils/JScriptRepl.java | 14 +- .../jscript/utils/debug/ObjectManager.java | 212 +++++ .../jscript/utils/debug/SimpleDebugger.java | 387 +++------ .../jscript/utils/filesystem/Filesystem.java | 6 +- .../jscript/utils/interop/Arguments.java | 46 +- .../utils/interop/NativeWrapperProvider.java | 18 +- .../jscript/utils/modules/Module.java | 6 +- .../jscript/utils/modules/ModuleRepo.java | 25 +- .../jscript/utils/modules/RootModuleRepo.java | 6 +- .../jscript/utils/modules/SourceModule.java | 13 +- .../permissions/PermissionsProvider.java | 6 +- 64 files changed, 1943 insertions(+), 1241 deletions(-) delete mode 100644 src/java/me/topchetoeu/jscript/runtime/Childable.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/Context.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/Copyable.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/Environment.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/Extensions.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/Key.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/environment/Environment.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/environment/Key.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/Value.java create mode 100644 src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index 2aaa8f1..d44270e 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -9,7 +9,7 @@ import me.topchetoeu.jscript.compilation.parsing.Operator; import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.values.ArrayValue; @@ -32,7 +32,7 @@ public class JSON { if (val.isNull()) return Values.NULL; return null; } - private static JSONElement fromJs(Extensions ext, Object val, HashSet prev) { + private static JSONElement fromJs(Environment ext, Object val, HashSet prev) { if (val instanceof Boolean) return JSONElement.bool((boolean)val); if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue()); if (val instanceof String) return JSONElement.string((String)val); @@ -70,7 +70,7 @@ public class JSON { if (val == null) return null; return null; } - public static JSONElement fromJs(Extensions ext, Object val) { + public static JSONElement fromJs(Environment ext, Object val) { return fromJs(ext, val, new HashSet<>()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java index def1449..9ec962a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java @@ -699,7 +699,7 @@ public class Parsing { var values = new ArrayList(); - // Java allows labels, so me uses labels + // Java allows labels, so labels were used loop: while (true) { if (isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { n++; diff --git a/src/java/me/topchetoeu/jscript/lib/ArrayLib.java b/src/java/me/topchetoeu/jscript/lib/ArrayLib.java index cfe9708..ce6a5aa 100644 --- a/src/java/me/topchetoeu/jscript/lib/ArrayLib.java +++ b/src/java/me/topchetoeu/jscript/lib/ArrayLib.java @@ -39,7 +39,7 @@ public class ArrayLib { return __iterator(args); } @Expose public static ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.ctx, () -> new Iterator() { + return Values.toJSIterator(args.env, () -> new Iterator() { private int i = 0; @Override @@ -54,7 +54,7 @@ public class ArrayLib { }); } @Expose public static ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.ctx, () -> new Iterator() { + return Values.toJSIterator(args.env, () -> new Iterator() { private int i = 0; @Override @@ -64,18 +64,18 @@ public class ArrayLib { @Override public Object next() { if (!hasNext()) return null; - return new ArrayValue(args.ctx, i, args.self(ArrayValue.class).get(i++)); + return new ArrayValue(args.env, i, args.self(ArrayValue.class).get(i++)); } }); } @Expose(value = "@@Symbol.iterator") public static ObjectValue __iterator(Arguments args) { - return Values.toJSIterator(args.ctx, args.self(ArrayValue.class)); + return Values.toJSIterator(args.env, args.self(ArrayValue.class)); } @Expose(value = "@@Symbol.asyncIterator") public static ObjectValue __asyncIterator(Arguments args) { - return Values.toJSAsyncIterator(args.ctx, args.self(ArrayValue.class).iterator()); + return Values.toJSAsyncIterator(args.env, args.self(ArrayValue.class).iterator()); } @Expose public static ArrayValue __concat(Arguments args) { @@ -98,7 +98,7 @@ public class ArrayLib { j += n; } else { - res.set(args.ctx, j++, arrs.get(i)); + res.set(args.env, j++, arrs.get(i)); } } @@ -113,7 +113,7 @@ public class ArrayLib { }); arr.sort((a, b) -> { - var res = Values.toNumber(args.ctx, (cmp == null ? defaultCmp : cmp).call(args.ctx, null, a, b)); + var res = Values.toNumber(args.env, (cmp == null ? defaultCmp : cmp).call(args.env, null, a, b)); if (res < 0) return -1; if (res > 0) return 1; return 0; @@ -127,7 +127,7 @@ public class ArrayLib { var start = normalizeI(arr.size(), args.getInt(1, 0), true); var end = normalizeI(arr.size(), args.getInt(2, arr.size()), true); - for (; start < end; start++) arr.set(args.ctx, start, val); + for (; start < end; start++) arr.set(args.env, start, val); return arr; } @@ -136,7 +136,7 @@ public class ArrayLib { for (var i = 0; i < arr.size(); i++) { if (arr.has(i) && !Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, arr ))) return false; } @@ -148,7 +148,7 @@ public class ArrayLib { for (var i = 0; i < arr.size(); i++) { if (arr.has(i) && Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, arr ))) return true; } @@ -161,9 +161,9 @@ public class ArrayLib { for (int i = 0, j = 0; i < arr.size(); i++) { if (arr.has(i) && Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, arr - ))) res.set(args.ctx, j++, arr.get(i)); + ))) res.set(args.env, j++, arr.get(i)); } return res; @@ -174,7 +174,7 @@ public class ArrayLib { res.setSize(arr.size()); for (int i = 0; i < arr.size(); i++) { - if (arr.has(i)) res.set(args.ctx, i, Values.call(args.ctx, args.get(0), args.get(1), arr.get(i), i, arr)); + if (arr.has(i)) res.set(args.env, i, Values.call(args.env, args.get(0), args.get(1), arr.get(i), i, arr)); } return res; } @@ -184,7 +184,7 @@ public class ArrayLib { var thisArg = args.get(1); for (int i = 0; i < arr.size(); i++) { - if (arr.has(i)) func.call(args.ctx, thisArg, arr.get(i), i, arr); + if (arr.has(i)) func.call(args.env, thisArg, arr.get(i), i, arr); } } @@ -205,7 +205,7 @@ public class ArrayLib { for (; i < arr.size(); i++) { if (arr.has(i)) { - res = func.call(args.ctx, null, res, arr.get(i), i, arr); + res = func.call(args.env, null, res, arr.get(i), i, arr); } } @@ -226,7 +226,7 @@ public class ArrayLib { for (; i >= 0; i--) { if (arr.has(i)) { - res = func.call(args.ctx, null, res, arr.get(i), i, arr); + res = func.call(args.env, null, res, arr.get(i), i, arr); } } @@ -255,13 +255,13 @@ public class ArrayLib { depths.push(d + 1); } } - else res.set(args.ctx, res.size(), el); + else res.set(args.env, res.size(), el); } return res; } @Expose public static ArrayValue __flatMap(Arguments args) { - return __flat(new Arguments(args.ctx, __map(args), 1)); + return __flat(new Arguments(args.env, __map(args), 1)); } @Expose public static Object __find(Arguments args) { @@ -269,7 +269,7 @@ public class ArrayLib { for (int i = 0; i < arr.size(); i++) { if (arr.has(i) && Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, args.self ))) return arr.get(i); } @@ -281,7 +281,7 @@ public class ArrayLib { for (var i = arr.size() - 1; i >= 0; i--) { if (arr.has(i) && Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, args.self ))) return arr.get(i); } @@ -294,7 +294,7 @@ public class ArrayLib { for (int i = 0; i < arr.size(); i++) { if (arr.has(i) && Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, args.self ))) return i; } @@ -306,7 +306,7 @@ public class ArrayLib { for (var i = arr.size() - 1; i >= 0; i--) { if (arr.has(i) && Values.toBoolean(Values.call( - args.ctx, args.get(0), args.get(1), + args.env, args.get(0), args.get(1), arr.get(i), i, args.self ))) return i; } @@ -320,7 +320,7 @@ public class ArrayLib { var start = normalizeI(arr.size(), args.getInt(1), true); for (int i = start; i < arr.size(); i++) { - if (Values.strictEquals(args.ctx, arr.get(i), val)) return i; + if (Values.strictEquals(args.env, arr.get(i), val)) return i; } return -1; @@ -331,7 +331,7 @@ public class ArrayLib { var start = normalizeI(arr.size(), args.getInt(1), true); for (int i = arr.size(); i >= start; i--) { - if (Values.strictEquals(args.ctx, arr.get(i), val)) return i; + if (Values.strictEquals(args.env, arr.get(i), val)) return i; } return -1; @@ -353,7 +353,7 @@ public class ArrayLib { var arr = args.self(ArrayValue.class); var values = args.args; - arr.copyFrom(args.ctx, values, 0, arr.size(), values.length); + arr.copyFrom(args.env, values, 0, arr.size(), values.length); return arr.size(); } @@ -372,7 +372,7 @@ public class ArrayLib { var values = args.slice(0).args; arr.move(0, values.length, arr.size()); - arr.copyFrom(args.ctx, values, 0, 0, values.length); + arr.copyFrom(args.env, values, 0, 0, values.length); return arr.size(); } @@ -398,13 +398,13 @@ public class ArrayLib { var res = new ArrayValue(deleteCount); arr.copyTo(res, start, 0, deleteCount); arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount); - arr.copyFrom(args.ctx, items, 0, start, items.length); + arr.copyFrom(args.env, items, 0, start, items.length); arr.setSize(size); return res; } @Expose public static String __toString(Arguments args) { - return __join(new Arguments(args.ctx, args.self, ",")); + return __join(new Arguments(args.env, args.self, ",")); } @Expose public static String __join(Arguments args) { @@ -422,7 +422,7 @@ public class ArrayLib { var el = arr.get(i); if (el == null || el == Values.NULL) continue; - res.append(Values.toString(args.ctx, el)); + res.append(Values.toString(args.env, el)); } return res.toString(); @@ -434,7 +434,7 @@ public class ArrayLib { } @Expose(target = ExposeTarget.STATIC) public static ArrayValue __of(Arguments args) { - return new ArrayValue(args.ctx, args.slice(0).args); + return new ArrayValue(args.env, args.slice(0).args); } @ExposeConstructor public static ArrayValue __constructor(Arguments args) { @@ -448,7 +448,7 @@ public class ArrayLib { else { var val = args.args; res = new ArrayValue(val.length); - res.copyFrom(args.ctx, val, 0, 0, val.length); + res.copyFrom(args.env, val, 0, 0, val.length); } return res; diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java index 2151164..af1f178 100644 --- a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java @@ -1,9 +1,8 @@ package me.topchetoeu.jscript.lib; import me.topchetoeu.jscript.lib.PromiseLib.Handle; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -22,40 +21,43 @@ public class AsyncFunctionLib extends FunctionValue { private boolean awaiting = false; - private void next(Context ctx, Object inducedValue, EngineException inducedError) { + private void next(Environment env, Object inducedValue, EngineException inducedError) { Object res = null; frame.onPush(); awaiting = false; while (!awaiting) { try { - res = frame.next(inducedValue, Values.NO_RETURN, inducedError); + if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); + else if (inducedError != null) res = frame.induceError(inducedError); + else res = frame.next(); + inducedValue = Values.NO_RETURN; inducedError = null; if (res != Values.NO_RETURN) { - promise.fulfill(ctx, res); + promise.fulfill(env, res); break; } } catch (EngineException e) { - promise.reject(ctx, e); + promise.reject(env, e); break; } } frame.onPop(); if (awaiting) { - PromiseLib.handle(ctx, frame.pop(), new Handle() { + PromiseLib.handle(env, frame.pop(), new Handle() { @Override public void onFulfil(Object val) { - next(ctx, val, null); + next(env, val, null); } @Override public void onReject(EngineException err) { - next(ctx, Values.NO_RETURN, err); + next(env, Values.NO_RETURN, err); } - }.defer(ctx)); + }.defer(env)); } } @@ -66,16 +68,15 @@ public class AsyncFunctionLib extends FunctionValue { } @Override - public Object call(Extensions ext, Object thisArg, Object ...args) { + public Object call(Environment env, Object thisArg, Object ...args) { var handler = new AsyncHelper(); - var ctx = Context.of(ext); var newArgs = new Object[args.length + 1]; newArgs[0] = new NativeFunction("await", handler::await); System.arraycopy(args, 0, newArgs, 1, args.length); - handler.frame = new Frame(ctx, thisArg, newArgs, (CodeFunction)func); - handler.next(ctx, Values.NO_RETURN, null); + handler.frame = new Frame(env, thisArg, newArgs, (CodeFunction)func); + handler.next(env, Values.NO_RETURN, null); return handler.promise; } diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java index 43e7689..f6bdbc7 100644 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java @@ -1,8 +1,7 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -14,7 +13,7 @@ public class AsyncGeneratorFunctionLib extends FunctionValue { public final CodeFunction func; @Override - public Object call(Extensions ext, Object thisArg, Object ...args) { + public Object call(Environment ext, Object thisArg, Object ...args) { var handler = new AsyncGeneratorLib(); var newArgs = new Object[args.length + 2]; @@ -22,13 +21,13 @@ public class AsyncGeneratorFunctionLib extends FunctionValue { newArgs[1] = new NativeFunction("yield", handler::yield); System.arraycopy(args, 0, newArgs, 2, args.length); - handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func); + handler.frame = new Frame(ext, thisArg, newArgs, func); return handler; } public AsyncGeneratorFunctionLib(CodeFunction func) { super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); + if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a code function."); this.func = func; } } diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java index 32778ac..cb1ad6a 100644 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java +++ b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java @@ -3,8 +3,8 @@ package me.topchetoeu.jscript.lib; import java.util.Map; import me.topchetoeu.jscript.lib.PromiseLib.Handle; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; @@ -19,10 +19,10 @@ public class AsyncGeneratorLib { private PromiseLib currPromise; public Frame frame; - private void next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) { + private void next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) { if (done) { if (inducedError != null) throw inducedError; - currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of( + currPromise.fulfill(env, new ObjectValue(env, Map.of( "done", true, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn ))); @@ -35,40 +35,44 @@ public class AsyncGeneratorLib { frame.onPush(); while (state == 0) { try { - res = frame.next(inducedValue, inducedReturn, inducedError); + if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); + else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue); + else if (inducedError != null) res = frame.induceError(inducedError); + else res = frame.next(); + inducedValue = inducedReturn = Values.NO_RETURN; inducedError = null; if (res != Values.NO_RETURN) { var obj = new ObjectValue(); - obj.defineProperty(ctx, "done", true); - obj.defineProperty(ctx, "value", res); - currPromise.fulfill(ctx, obj); + obj.defineProperty(env, "done", true); + obj.defineProperty(env, "value", res); + currPromise.fulfill(env, obj); break; } } catch (EngineException e) { - currPromise.reject(ctx, e); + currPromise.reject(env, e); break; } } frame.onPop(); if (state == 1) { - PromiseLib.handle(ctx, frame.pop(), new Handle() { + PromiseLib.handle(env, frame.pop(), new Handle() { @Override public void onFulfil(Object val) { - next(ctx, val, Values.NO_RETURN, null); + next(env, val, Values.NO_RETURN, null); } @Override public void onReject(EngineException err) { - next(ctx, Values.NO_RETURN, Values.NO_RETURN, err); + next(env, Values.NO_RETURN, Values.NO_RETURN, err); } - }.defer(ctx)); + }.defer(env)); } else if (state == 2) { var obj = new ObjectValue(); - obj.defineProperty(ctx, "done", false); - obj.defineProperty(ctx, "value", frame.pop()); - currPromise.fulfill(ctx, obj); + obj.defineProperty(env, "done", false); + obj.defineProperty(env, "value", frame.pop()); + currPromise.fulfill(env, obj); } } @@ -90,18 +94,18 @@ public class AsyncGeneratorLib { @Expose public PromiseLib __next(Arguments args) { this.currPromise = new PromiseLib(); - if (args.has(0)) next(args.ctx, args.get(0), Values.NO_RETURN, null); - else next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null); + if (args.has(0)) next(args.env, args.get(0), Values.NO_RETURN, null); + else next(args.env, Values.NO_RETURN, Values.NO_RETURN, null); return this.currPromise; } @Expose public PromiseLib __return(Arguments args) { this.currPromise = new PromiseLib(); - next(args.ctx, Values.NO_RETURN, args.get(0), null); + next(args.env, Values.NO_RETURN, args.get(0), null); return this.currPromise; } @Expose public PromiseLib __throw(Arguments args) { this.currPromise = new PromiseLib(); - next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx)); + next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env)); return this.currPromise; } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java b/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java index 2a7c205..cd08dbf 100644 --- a/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java +++ b/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java @@ -24,7 +24,7 @@ public class ConsoleLib { for (var el : args.args) { if (!first) res.append(" "); first = false; - res.append(Values.toReadable(args.ctx, el).getBytes()); + res.append(Values.toReadable(args.env, el).getBytes()); } for (var line : res.toString().split("\n", -1)) { diff --git a/src/java/me/topchetoeu/jscript/lib/EncodingLib.java b/src/java/me/topchetoeu/jscript/lib/EncodingLib.java index 04ec153..e164ef8 100644 --- a/src/java/me/topchetoeu/jscript/lib/EncodingLib.java +++ b/src/java/me/topchetoeu/jscript/lib/EncodingLib.java @@ -66,7 +66,7 @@ public class EncodingLib { public static String __decode(Arguments args) { var raw = args.convert(0, ArrayList.class); var res = new byte[raw.size()]; - for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, raw.get(i)); + for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.env, raw.get(i)); return new String(res); } diff --git a/src/java/me/topchetoeu/jscript/lib/ErrorLib.java b/src/java/me/topchetoeu/jscript/lib/ErrorLib.java index abda9c3..431f6bd 100644 --- a/src/java/me/topchetoeu/jscript/lib/ErrorLib.java +++ b/src/java/me/topchetoeu/jscript/lib/ErrorLib.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.ConvertException; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; @@ -13,7 +13,7 @@ import me.topchetoeu.jscript.utils.interop.WrapperName; @WrapperName("Error") public class ErrorLib { - private static String toString(Context ctx, Object name, Object message) { + private static String toString(Environment ctx, Object name, Object message) { if (name == null) name = ""; else name = Values.toString(ctx, name).trim(); if (message == null) message = ""; @@ -30,9 +30,9 @@ public class ErrorLib { @ExposeField public static final String __name = "Error"; @Expose public static String __toString(Arguments args) { - if (args.self instanceof ObjectValue) return toString(args.ctx, - Values.getMember(args.ctx, args.self, "name"), - Values.getMember(args.ctx, args.self, "message") + if (args.self instanceof ObjectValue) return toString(args.env, + Values.getMember(args.env, args.self, "name"), + Values.getMember(args.env, args.self, "message") ); else return "[Invalid error]"; } @@ -47,7 +47,7 @@ public class ErrorLib { catch (ConvertException e) {} target.setPrototype(PlaceholderProto.ERROR); - target.defineProperty(args.ctx, "message", Values.toString(args.ctx, message)); + target.defineProperty(args.env, "message", Values.toString(args.env, message)); return target; } diff --git a/src/java/me/topchetoeu/jscript/lib/FileLib.java b/src/java/me/topchetoeu/jscript/lib/FileLib.java index 95b7baa..6a8deb4 100644 --- a/src/java/me/topchetoeu/jscript/lib/FileLib.java +++ b/src/java/me/topchetoeu/jscript/lib/FileLib.java @@ -13,7 +13,7 @@ public class FileLib { public final File fd; @Expose public PromiseLib __pointer(Arguments args) { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { try { return fd.seek(0, 1); } @@ -21,7 +21,7 @@ public class FileLib { }); } @Expose public PromiseLib __length(Arguments args) { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { try { long curr = fd.seek(0, 1); long res = fd.seek(0, 2); @@ -33,26 +33,26 @@ public class FileLib { } @Expose public PromiseLib __read(Arguments args) { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { var n = args.getInt(0); try { var buff = new byte[n]; var res = new ArrayValue(); int resI = fd.read(buff); - for (var i = resI - 1; i >= 0; i--) res.set(args.ctx, i, (int)buff[i]); + for (var i = resI - 1; i >= 0; i--) res.set(args.env, i, (int)buff[i]); return res; } catch (FilesystemException e) { throw e.toEngineException(); } }); } @Expose public PromiseLib __write(Arguments args) { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { var val = args.convert(0, ArrayValue.class); try { var res = new byte[val.size()]; - for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, val.get(i)); + for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.env, val.get(i)); fd.write(res); return null; @@ -61,13 +61,13 @@ public class FileLib { }); } @Expose public PromiseLib __close(Arguments args) { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { fd.close(); return null; }); } @Expose public PromiseLib __seek(Arguments args) { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { var ptr = args.getLong(0); var whence = args.getInt(1); diff --git a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java b/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java index 6582495..d2f9c3c 100644 --- a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java +++ b/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java @@ -4,7 +4,7 @@ import java.io.IOException; import java.util.Iterator; import java.util.Stack; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; @@ -31,21 +31,21 @@ public class FilesystemLib { @ExposeField(target = ExposeTarget.STATIC) public static final int __SEEK_END = 2; - private static Filesystem fs(Context ctx) { - var fs = Filesystem.get(ctx); + private static Filesystem fs(Environment env) { + var fs = Filesystem.get(env); if (fs != null) return fs; throw EngineException.ofError("Current environment doesn't have a file system."); } @Expose(target = ExposeTarget.STATIC) public static String __normalize(Arguments args) { - return fs(args.ctx).normalize(args.convert(String.class)); + return fs(args.env).normalize(args.convert(String.class)); } @Expose(target = ExposeTarget.STATIC) public static PromiseLib __open(Arguments args) { - return PromiseLib.await(args.ctx, () -> { - var fs = fs(args.ctx); + return PromiseLib.await(args.env, () -> { + var fs = fs(args.env); var path = fs.normalize(args.getString(0)); var _mode = Mode.parse(args.getString(1)); @@ -62,7 +62,7 @@ public class FilesystemLib { @Expose(target = ExposeTarget.STATIC) public static ObjectValue __ls(Arguments args) { - return Values.toJSAsyncIterator(args.ctx, new Iterator<>() { + return Values.toJSAsyncIterator(args.env, new Iterator<>() { private boolean failed, done; private File file; private String nextLine; @@ -71,7 +71,7 @@ public class FilesystemLib { if (done) return; if (!failed) { if (file == null) { - var fs = fs(args.ctx); + var fs = fs(args.env); var path = fs.normalize(args.getString(0)); if (fs.stat(path).type != EntryType.FOLDER) { @@ -117,9 +117,9 @@ public class FilesystemLib { } @Expose(target = ExposeTarget.STATIC) public static PromiseLib __mkdir(Arguments args) throws IOException { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { try { - fs(args.ctx).create(args.getString(0), EntryType.FOLDER); + fs(args.env).create(args.getString(0), EntryType.FOLDER); return null; } catch (FilesystemException e) { throw e.toEngineException(); } @@ -128,9 +128,9 @@ public class FilesystemLib { } @Expose(target = ExposeTarget.STATIC) public static PromiseLib __mkfile(Arguments args) throws IOException { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { try { - fs(args.ctx).create(args.getString(0), EntryType.FILE); + fs(args.env).create(args.getString(0), EntryType.FILE); return null; } catch (FilesystemException e) { throw e.toEngineException(); } @@ -138,9 +138,9 @@ public class FilesystemLib { } @Expose(target = ExposeTarget.STATIC) public static PromiseLib __rm(Arguments args) throws IOException { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { try { - var fs = fs(args.ctx); + var fs = fs(args.env); var path = fs.normalize(args.getString(0)); var recursive = args.getBoolean(1); @@ -169,15 +169,15 @@ public class FilesystemLib { } @Expose(target = ExposeTarget.STATIC) public static PromiseLib __stat(Arguments args) throws IOException { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { try { - var fs = fs(args.ctx); + var fs = fs(args.env); var path = fs.normalize(args.getString(0)); var stat = fs.stat(path); var res = new ObjectValue(); - res.defineProperty(args.ctx, "type", stat.type.name); - res.defineProperty(args.ctx, "mode", stat.mode.name); + res.defineProperty(args.env, "type", stat.type.name); + res.defineProperty(args.env, "mode", stat.mode.name); return res; } catch (FilesystemException e) { throw e.toEngineException(); } @@ -185,8 +185,8 @@ public class FilesystemLib { } @Expose(target = ExposeTarget.STATIC) public static PromiseLib __exists(Arguments args) throws IOException { - return PromiseLib.await(args.ctx, () -> { - try { fs(args.ctx).stat(args.getString(0)); return true; } + return PromiseLib.await(args.env, () -> { + try { fs(args.env).stat(args.getString(0)); return true; } catch (FilesystemException e) { return false; } }); } diff --git a/src/java/me/topchetoeu/jscript/lib/FunctionLib.java b/src/java/me/topchetoeu/jscript/lib/FunctionLib.java index e50a927..278bac3 100644 --- a/src/java/me/topchetoeu/jscript/lib/FunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/FunctionLib.java @@ -2,7 +2,6 @@ package me.topchetoeu.jscript.lib; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.CodeFunction; @@ -20,10 +19,10 @@ public class FunctionLib { private static int i; @Expose public static Object __apply(Arguments args) { - return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.convert(1, ArrayValue.class).toArray()); + return args.self(FunctionValue.class).call(args.env, args.get(0), args.convert(1, ArrayValue.class).toArray()); } @Expose public static Object __call(Arguments args) { - return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.slice(1).args); + return args.self(FunctionValue.class).call(args.env, args.get(0), args.slice(1).args); } @Expose public static FunctionValue __bind(Arguments args) { var self = args.self(FunctionValue.class); @@ -40,7 +39,7 @@ public class FunctionLib { System.arraycopy(callArgs.args, 0, resArgs, bindArgs.length, callArgs.n()); } - return self.call(callArgs.ctx, thisArg, resArgs); + return self.call(callArgs.env, thisArg, resArgs); }); } @Expose public static String __toString(Arguments args) { @@ -62,8 +61,6 @@ public class FunctionLib { @ExposeConstructor public static Object __constructor(Arguments args) { - var compiler = Compiler.get(args); - var parts = args.convert(String.class); if (parts.length == 0) parts = new String[] { "" }; @@ -76,8 +73,8 @@ public class FunctionLib { src += "){" + parts[parts.length - 1] + "}"; - var body = compiler.compile(new Filename("jscript", "func/" + i++), src); - var func = new CodeFunction(Context.clean(args.ctx), "", body, new ValueVariable[0]); - return Values.call(args, func, null); + var body = Compiler.get(args.env).compile(new Filename("jscript", "func/" + i++), src); + var func = new CodeFunction(args.env, "", body, new ValueVariable[0]); + return Values.call(args.env, func, null); } } diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java index 7075dae..1316cfb 100644 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java +++ b/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java @@ -1,8 +1,7 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -13,14 +12,14 @@ import me.topchetoeu.jscript.utils.interop.WrapperName; public class GeneratorFunctionLib extends FunctionValue { public final CodeFunction func; - @Override public Object call(Extensions ext, Object thisArg, Object ...args) { + @Override public Object call(Environment env, Object thisArg, Object ...args) { var handler = new GeneratorLib(); var newArgs = new Object[args.length + 1]; newArgs[0] = new NativeFunction("yield", handler::yield); System.arraycopy(args, 0, newArgs, 1, args.length); - handler.frame = new Frame(Context.of(ext), thisArg, newArgs, func); + handler.frame = new Frame(env, thisArg, newArgs, func); return handler; } diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java index 9849c7d..be8b71c 100644 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java +++ b/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; @@ -15,12 +15,12 @@ public class GeneratorLib { private boolean done = false; public Frame frame; - private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) { + private ObjectValue next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) { if (done) { if (inducedError != Values.NO_RETURN) throw inducedError; var res = new ObjectValue(); - res.defineProperty(ctx, "done", true); - res.defineProperty(ctx, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn); + res.defineProperty(env, "done", true); + res.defineProperty(env, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn); return res; } @@ -30,7 +30,11 @@ public class GeneratorLib { frame.onPush(); while (!yielding) { try { - res = frame.next(inducedValue, inducedReturn, inducedError); + if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); + else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue); + else if (inducedError != null) res = frame.induceError(inducedError); + else res = frame.next(); + inducedReturn = Values.NO_RETURN; inducedError = null; if (res != Values.NO_RETURN) { @@ -49,20 +53,20 @@ public class GeneratorLib { else res = frame.pop(); var obj = new ObjectValue(); - obj.defineProperty(ctx, "done", done); - obj.defineProperty(ctx, "value", res); + obj.defineProperty(env, "done", done); + obj.defineProperty(env, "value", res); return obj; } @Expose public ObjectValue __next(Arguments args) { - if (args.n() == 0) return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null); - else return next(args.ctx, args.get(0), Values.NO_RETURN, null); + if (args.n() == 0) return next(args.env, Values.NO_RETURN, Values.NO_RETURN, null); + else return next(args.env, args.get(0), Values.NO_RETURN, null); } @Expose public ObjectValue __throw(Arguments args) { - return next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setExtensions(args.ctx)); + return next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env)); } @Expose public ObjectValue __return(Arguments args) { - return next(args.ctx, Values.NO_RETURN, args.get(0), null); + return next(args.env, Values.NO_RETURN, args.get(0), null); } @Override public String toString() { diff --git a/src/java/me/topchetoeu/jscript/lib/Internals.java b/src/java/me/topchetoeu/jscript/lib/Internals.java index 67564c6..e2633b6 100644 --- a/src/java/me/topchetoeu/jscript/lib/Internals.java +++ b/src/java/me/topchetoeu/jscript/lib/Internals.java @@ -2,10 +2,9 @@ package me.topchetoeu.jscript.lib; import java.util.HashMap; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Environment; import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -26,11 +25,11 @@ public class Internals { @Expose(target = ExposeTarget.STATIC) public static Object __require(Arguments args) { - var repo = ModuleRepo.get(args.ctx); + var repo = ModuleRepo.get(args.env); if (repo != null) { - var res = repo.getModule(args.ctx, ModuleRepo.cwd(args.ctx), args.getString(0)); - res.load(args.ctx); + var res = repo.getModule(args.env, ModuleRepo.cwd(args.env), args.getString(0)); + res.load(args.env); return res.value(); } @@ -43,7 +42,7 @@ public class Internals { var delay = args.getDouble(1); var arguments = args.slice(2).args; - if (!args.ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); + if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); var thread = new Thread(() -> { var ms = (long)delay; @@ -52,13 +51,17 @@ public class Internals { try { Thread.sleep(ms, ns); } catch (InterruptedException e) { return; } - args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false); + args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false); }); thread.start(); - var i = args.ctx.init(I, 1); - args.ctx.add(I, i + 1); - args.ctx.init(THREADS, new HashMap()).put(i, thread); + + args.env.init(I, 1); + args.env.init(THREADS, new HashMap<>()); + var i = args.env.get(I); + + args.env.add(I, i + 1); + args.env.get(THREADS).put(i, thread); return thread; } @@ -68,7 +71,7 @@ public class Internals { var delay = args.getDouble(1); var arguments = args.slice(2).args; - if (!args.ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); + if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); var thread = new Thread(() -> { var ms = (long)delay; @@ -80,13 +83,18 @@ public class Internals { } catch (InterruptedException e) { return; } - args.ctx.get(EventLoop.KEY).pushMsg(() -> func.call(new Context(args.ctx.extensions), null, arguments), false); + args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false); } }); + thread.start(); - var i = args.ctx.init(I, 1); - args.ctx.add(I, i + 1); - args.ctx.init(THREADS, new HashMap()).put(i, thread); + + args.env.init(I, 1); + args.env.init(THREADS, new HashMap<>()); + var i = args.env.get(I); + + args.env.add(I, i + 1); + args.env.get(THREADS).put(i, thread); return thread; } @@ -94,7 +102,7 @@ public class Internals { @Expose(target = ExposeTarget.STATIC) public static void __clearTimeout(Arguments args) { var i = args.getInt(0); - HashMap map = args.ctx.get(THREADS); + HashMap map = args.env.get(THREADS); if (map == null) return; var thread = map.get(i); @@ -132,15 +140,15 @@ public class Internals { @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) public static FileLib __stdin(Arguments args) { - return new FileLib(Filesystem.get(args.ctx).open("std://in", Mode.READ)); + return new FileLib(Filesystem.get(args.env).open("std://in", Mode.READ)); } @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) public static FileLib __stdout(Arguments args) { - return new FileLib(Filesystem.get(args.ctx).open("std://out", Mode.READ)); + return new FileLib(Filesystem.get(args.env).open("std://out", Mode.READ)); } @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) public static FileLib __stderr(Arguments args) { - return new FileLib(Filesystem.get(args.ctx).open("std://err", Mode.READ)); + return new FileLib(Filesystem.get(args.env).open("std://err", Mode.READ)); } @ExposeField(target = ExposeTarget.STATIC) @@ -211,12 +219,11 @@ public class Internals { env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class)); env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class)); - Values.setPrototype(new Context(), wp.getProto(ObjectLib.class), null); + Values.setPrototype(Environment.empty(), wp.getProto(ObjectLib.class), null); env.add(NativeWrapperProvider.KEY, wp); env.add(GlobalScope.KEY, glob); - return env; } } diff --git a/src/java/me/topchetoeu/jscript/lib/JSONLib.java b/src/java/me/topchetoeu/jscript/lib/JSONLib.java index 31a1e11..11bbb55 100644 --- a/src/java/me/topchetoeu/jscript/lib/JSONLib.java +++ b/src/java/me/topchetoeu/jscript/lib/JSONLib.java @@ -19,6 +19,6 @@ public class JSONLib { } @Expose(target = ExposeTarget.STATIC) public static String __stringify(Arguments args) { - return JSON.stringify(JSON.fromJs(args.ctx, args.get(0))); + return JSON.stringify(JSON.fromJs(args.env, args.get(0))); } } diff --git a/src/java/me/topchetoeu/jscript/lib/MapLib.java b/src/java/me/topchetoeu/jscript/lib/MapLib.java index 849bf00..bfda4f7 100644 --- a/src/java/me/topchetoeu/jscript/lib/MapLib.java +++ b/src/java/me/topchetoeu/jscript/lib/MapLib.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.stream.Collectors; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; @@ -36,18 +36,18 @@ public class MapLib { } @Expose public ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.ctx, map + return Values.toJSIterator(args.env, map .entrySet() .stream() - .map(v -> new ArrayValue(args.ctx, v.getKey(), v.getValue())) + .map(v -> new ArrayValue(args.env, v.getKey(), v.getValue())) .collect(Collectors.toList()) ); } @Expose public ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.ctx, map.keySet()); + return Values.toJSIterator(args.env, map.keySet()); } @Expose public ObjectValue __values(Arguments args) { - return Values.toJSIterator(args.ctx, map.values()); + return Values.toJSIterator(args.env, map.values()); } @Expose public Object __get(Arguments args) { @@ -69,19 +69,19 @@ public class MapLib { @Expose public void __forEach(Arguments args) { var keys = new ArrayList<>(map.keySet()); - for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), map.get(el), el, args.self); + for (var el : keys) Values.call(args.env, args.get(0), args.get(1), map.get(el), el, args.self); } - public MapLib(Context ctx, Object iterable) { - for (var el : Values.fromJSIterator(ctx, iterable)) { + public MapLib(Environment env, Object iterable) { + for (var el : Values.fromJSIterator(env, iterable)) { try { - map.put(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1)); + map.put(Values.getMember(env, el, 0), Values.getMember(env, el, 1)); } catch (IllegalArgumentException e) { } } } @ExposeConstructor public static MapLib __constructor(Arguments args) { - return new MapLib(args.ctx, args.get(0)); + return new MapLib(args.env, args.get(0)); } } diff --git a/src/java/me/topchetoeu/jscript/lib/NumberLib.java b/src/java/me/topchetoeu/jscript/lib/NumberLib.java index 07eb00b..1a7aac1 100644 --- a/src/java/me/topchetoeu/jscript/lib/NumberLib.java +++ b/src/java/me/topchetoeu/jscript/lib/NumberLib.java @@ -85,7 +85,7 @@ public class NumberLib { else return args.getDouble(0); } @Expose public static String __toString(Arguments args) { - return Values.toString(args.ctx, args.self); + return Values.toString(args.env, args.self); } @Expose public static String __toFixed(Arguments args) { var digits = args.getInt(0, 0); @@ -98,6 +98,6 @@ public class NumberLib { } @Expose public static double __valueOf(Arguments args) { if (Values.isWrapper(args.self, NumberLib.class)) return Values.wrapper(args.self, NumberLib.class).value; - else return Values.toNumber(args.ctx, args.self); + else return Values.toNumber(args.env, args.self); } } diff --git a/src/java/me/topchetoeu/jscript/lib/ObjectLib.java b/src/java/me/topchetoeu/jscript/lib/ObjectLib.java index c9b2964..2dfbafa 100644 --- a/src/java/me/topchetoeu/jscript/lib/ObjectLib.java +++ b/src/java/me/topchetoeu/jscript/lib/ObjectLib.java @@ -17,8 +17,8 @@ public class ObjectLib { @Expose(target = ExposeTarget.STATIC) public static Object __assign(Arguments args) { for (var obj : args.slice(1).args) { - for (var key : Values.getMembers(args.ctx, obj, true, true)) { - Values.setMember(args.ctx, args.get(0), key, Values.getMember(args.ctx, obj, key)); + for (var key : Values.getMembers(args.env, obj, true, true)) { + Values.setMember(args.env, args.get(0), key, Values.getMember(args.env, obj, key)); } } return args.get(0); @@ -26,14 +26,14 @@ public class ObjectLib { @Expose(target = ExposeTarget.STATIC) public static ObjectValue __create(Arguments args) { var obj = new ObjectValue(); - Values.setPrototype(args.ctx, obj, args.get(0)); + Values.setPrototype(args.env, obj, args.get(0)); if (args.n() >= 1) { var newArgs = new Object[args.n()]; System.arraycopy(args.args, 1, args, 1, args.n() - 1); newArgs[0] = obj; - __defineProperties(new Arguments(args.ctx, null, newArgs)); + __defineProperties(new Arguments(args.env, null, newArgs)); } return obj; @@ -45,31 +45,31 @@ public class ObjectLib { var key = args.get(1); var attrib = args.convert(2, ObjectValue.class); - var hasVal = Values.hasMember(args.ctx, attrib, "value", false); - var hasGet = Values.hasMember(args.ctx, attrib, "get", false); - var hasSet = Values.hasMember(args.ctx, attrib, "set", false); + var hasVal = Values.hasMember(args.env, attrib, "value", false); + var hasGet = Values.hasMember(args.env, attrib, "get", false); + var hasSet = Values.hasMember(args.env, attrib, "set", false); if (hasVal) { if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property."); if (!obj.defineProperty( - args.ctx, key, - Values.getMember(args.ctx, attrib, "value"), - Values.toBoolean(Values.getMember(args.ctx, attrib, "writable")), - Values.toBoolean(Values.getMember(args.ctx, attrib, "configurable")), - Values.toBoolean(Values.getMember(args.ctx, attrib, "enumerable")) + args.env, key, + Values.getMember(args.env, attrib, "value"), + Values.toBoolean(Values.getMember(args.env, attrib, "writable")), + Values.toBoolean(Values.getMember(args.env, attrib, "configurable")), + Values.toBoolean(Values.getMember(args.env, attrib, "enumerable")) )) throw EngineException.ofType("Can't define property '" + key + "'."); } else { - var get = Values.getMember(args.ctx, attrib, "get"); - var set = Values.getMember(args.ctx, attrib, "set"); + var get = Values.getMember(args.env, attrib, "get"); + var set = Values.getMember(args.env, attrib, "set"); if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function."); if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function."); if (!obj.defineProperty( - args.ctx, key, + args.env, key, (FunctionValue)get, (FunctionValue)set, - Values.toBoolean(Values.getMember(args.ctx, attrib, "configurable")), - Values.toBoolean(Values.getMember(args.ctx, attrib, "enumerable")) + Values.toBoolean(Values.getMember(args.env, attrib, "configurable")), + Values.toBoolean(Values.getMember(args.env, attrib, "enumerable")) )) throw EngineException.ofType("Can't define property '" + key + "'."); } @@ -81,7 +81,7 @@ public class ObjectLib { var attrib = args.get(1); for (var key : Values.getMembers(null, attrib, false, false)) { - __defineProperty(new Arguments(args.ctx, null, obj, key, Values.getMember(args.ctx, attrib, key))); + __defineProperty(new Arguments(args.env, null, obj, key, Values.getMember(args.env, attrib, key))); } return obj; @@ -93,8 +93,8 @@ public class ObjectLib { var all = args.getBoolean(1); var res = new ArrayValue(); - for (var key : Values.getMembers(args.ctx, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key); + for (var key : Values.getMembers(args.env, obj, true, false)) { + if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key); } return res; @@ -105,8 +105,8 @@ public class ObjectLib { var obj = args.get(0); var all = args.getBoolean(1); - for (var key : Values.getMembers(args.ctx, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), new ArrayValue(args.ctx, key, Values.getMember(args.ctx, obj, key))); + for (var key : Values.getMembers(args.env, obj, true, false)) { + if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), new ArrayValue(args.env, key, Values.getMember(args.env, obj, key))); } return res; @@ -117,8 +117,8 @@ public class ObjectLib { var obj = args.get(0); var all = args.getBoolean(1); - for (var key : Values.getMembers(args.ctx, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), Values.getMember(args.ctx, obj, key)); + for (var key : Values.getMembers(args.env, obj, true, false)) { + if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), Values.getMember(args.env, obj, key)); } return res; @@ -126,14 +126,14 @@ public class ObjectLib { @Expose(target = ExposeTarget.STATIC) public static ObjectValue __getOwnPropertyDescriptor(Arguments args) { - return Values.getMemberDescriptor(args.ctx, args.get(0), args.get(1)); + return Values.getMemberDescriptor(args.env, args.get(0), args.get(1)); } @Expose(target = ExposeTarget.STATIC) public static ObjectValue __getOwnPropertyDescriptors(Arguments args) { var res = new ObjectValue(); var obj = args.get(0); - for (var key : Values.getMembers(args.ctx, obj, true, true)) { - res.defineProperty(args.ctx, key, Values.getMemberDescriptor(args.ctx, obj, key)); + for (var key : Values.getMembers(args.env, obj, true, true)) { + res.defineProperty(args.env, key, Values.getMemberDescriptor(args.env, obj, key)); } return res; } @@ -144,8 +144,8 @@ public class ObjectLib { var obj = args.get(0); var all = args.getBoolean(1); - for (var key : Values.getMembers(args.ctx, obj, true, true)) { - if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key); + for (var key : Values.getMembers(args.env, obj, true, true)) { + if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key); } return res; @@ -155,24 +155,24 @@ public class ObjectLib { var obj = args.get(0); var res = new ArrayValue(); - for (var key : Values.getMembers(args.ctx, obj, true, true)) { - if (key instanceof Symbol) res.set(args.ctx, res.size(), key); + for (var key : Values.getMembers(args.env, obj, true, true)) { + if (key instanceof Symbol) res.set(args.env, res.size(), key); } return res; } @Expose(target = ExposeTarget.STATIC) public static boolean __hasOwn(Arguments args) { - return Values.hasMember(args.ctx, args.get(0), args.get(1), true); + return Values.hasMember(args.env, args.get(0), args.get(1), true); } @Expose(target = ExposeTarget.STATIC) public static ObjectValue __getPrototypeOf(Arguments args) { - return Values.getPrototype(args.ctx, args.get(0)); + return Values.getPrototype(args.env, args.get(0)); } @Expose(target = ExposeTarget.STATIC) public static Object __setPrototypeOf(Arguments args) { - Values.setPrototype(args.ctx, args.get(0), args.get(1)); + Values.setPrototype(args.env, args.get(0), args.get(1)); return args.get(0); } @@ -180,9 +180,9 @@ public class ObjectLib { public static ObjectValue __fromEntries(Arguments args) { var res = new ObjectValue(); - for (var el : Values.fromJSIterator(args.ctx, args.get(0))) { + for (var el : Values.fromJSIterator(args.env, args.get(0))) { if (el instanceof ArrayValue) { - res.defineProperty(args.ctx, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1)); + res.defineProperty(args.env, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1)); } } @@ -249,15 +249,15 @@ public class ObjectLib { } @Expose public static String __toString(Arguments args) { - var name = Values.getMember(args.ctx, args.self, Symbol.get("Symbol.typeName")); + var name = Values.getMember(args.env, args.self, Symbol.get("Symbol.typeName")); if (name == null) name = "Unknown"; - else name = Values.toString(args.ctx, name); + else name = Values.toString(args.env, name); return "[object " + name + "]"; } @Expose public static boolean __hasOwnProperty(Arguments args) { - return Values.hasMember(args.ctx, args.self, args.get(0), true); + return Values.hasMember(args.env, args.self, args.get(0), true); } @ExposeConstructor diff --git a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java b/src/java/me/topchetoeu/jscript/lib/PromiseLib.java index 66829de..5bd95cb 100644 --- a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java +++ b/src/java/me/topchetoeu/jscript/lib/PromiseLib.java @@ -4,9 +4,8 @@ import java.util.ArrayList; import java.util.List; import me.topchetoeu.jscript.common.ResultRunnable; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.values.ArrayValue; @@ -26,7 +25,7 @@ public class PromiseLib { void onFulfil(Object val); void onReject(EngineException err); - default Handle defer(Extensions loop) { + default Handle defer(Environment loop) { var self = this; return new Handle() { @@ -52,7 +51,7 @@ public class PromiseLib { private boolean handled = false; private Object val; - private void resolveSynchronized(Context ctx, Object val, int newState) { + private void resolveSynchronized(Environment env, Object val, int newState) { this.val = val; this.state = newState; @@ -65,7 +64,7 @@ public class PromiseLib { } if (state == STATE_REJECTED && !handled) { - Values.printError(((EngineException)val).setExtensions(ctx), "(in promise)"); + Values.printError(((EngineException)val).setEnvironment(env), "(in promise)"); } handles = null; @@ -78,24 +77,24 @@ public class PromiseLib { // }, true); } - private synchronized void resolve(Context ctx, Object val, int newState) { + private synchronized void resolve(Environment env, Object val, int newState) { if (this.state != STATE_PENDING || newState == STATE_PENDING) return; - handle(ctx, val, new Handle() { + handle(env, val, new Handle() { @Override public void onFulfil(Object val) { - resolveSynchronized(ctx, val, newState); + resolveSynchronized(env, val, newState); } @Override public void onReject(EngineException err) { - resolveSynchronized(ctx, val, STATE_REJECTED); + resolveSynchronized(env, val, STATE_REJECTED); } }); } - public synchronized void fulfill(Context ctx, Object val) { - resolve(ctx, val, STATE_FULFILLED); + public synchronized void fulfill(Environment env, Object val) { + resolve(env, val, STATE_FULFILLED); } - public synchronized void reject(Context ctx, EngineException val) { - resolve(ctx, val, STATE_REJECTED); + public synchronized void reject(Environment env, EngineException val) { + resolve(env, val, STATE_REJECTED); } private void handle(Handle handle) { @@ -118,37 +117,37 @@ public class PromiseLib { this.val = null; } - public static PromiseLib await(Context ctx, ResultRunnable runner) { + public static PromiseLib await(Environment env, ResultRunnable runner) { var res = new PromiseLib(); new Thread(() -> { try { - res.fulfill(ctx, runner.run()); + res.fulfill(env, runner.run()); } catch (EngineException e) { - res.reject(ctx, e); + res.reject(env, e); } catch (Exception e) { if (e instanceof InterruptException) throw e; else { - res.reject(ctx, EngineException.ofError("Native code failed with " + e.getMessage())); + res.reject(env, EngineException.ofError("Native code failed with " + e.getMessage())); } } }, "Promisifier").start(); return res; } - public static PromiseLib await(Context ctx, Runnable runner) { - return await(ctx, () -> { + public static PromiseLib await(Environment env, Runnable runner) { + return await(env, () -> { runner.run(); return null; }); } - public static void handle(Context ctx, Object obj, Handle handle) { + public static void handle(Environment env, Object obj, Handle handle) { if (Values.isWrapper(obj, PromiseLib.class)) { var promise = Values.wrapper(obj, PromiseLib.class); - handle(ctx, promise, handle); + handle(env, promise, handle); return; } if (obj instanceof PromiseLib) { @@ -159,9 +158,9 @@ public class PromiseLib { var rethrow = new boolean[1]; try { - var then = Values.getMember(ctx, obj, "then"); + var then = Values.getMember(env, obj, "then"); - if (then instanceof FunctionValue) Values.call(ctx, then, obj, + if (then instanceof FunctionValue) Values.call(env, then, obj, new NativeFunction(args -> { try { handle.onFulfil(args.get(0)); } catch (Exception e) { @@ -190,12 +189,12 @@ public class PromiseLib { handle.onFulfil(obj); } - public static PromiseLib ofResolved(Context ctx, Object value) { + public static PromiseLib ofResolved(Environment ctx, Object value) { var res = new PromiseLib(); res.fulfill(ctx, value); return res; } - public static PromiseLib ofRejected(Context ctx, EngineException value) { + public static PromiseLib ofRejected(Environment ctx, EngineException value) { var res = new PromiseLib(); res.reject(ctx, value); return res; @@ -203,11 +202,11 @@ public class PromiseLib { @Expose(value = "resolve", target = ExposeTarget.STATIC) public static PromiseLib __ofResolved(Arguments args) { - return ofResolved(args.ctx, args.get(0)); + return ofResolved(args.env, args.get(0)); } @Expose(value = "reject", target = ExposeTarget.STATIC) public static PromiseLib __ofRejected(Arguments args) { - return ofRejected(args.ctx, new EngineException(args.get(0)).setExtensions(args.ctx)); + return ofRejected(args.env, new EngineException(args.get(0)).setEnvironment(args.env)); } @Expose(target = ExposeTarget.STATIC) @@ -215,7 +214,7 @@ public class PromiseLib { if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); var promises = args.convert(0, ArrayValue.class); - if (promises.size() == 0) return ofRejected(args.ctx, EngineException.ofError("No promises passed to 'Promise.any'.").setExtensions(args.ctx)); + if (promises.size() == 0) return ofRejected(args.env, EngineException.ofError("No promises passed to 'Promise.any'.").setEnvironment(args.env)); var n = new int[] { promises.size() }; var res = new PromiseLib(); var errors = new ArrayValue(); @@ -225,12 +224,12 @@ public class PromiseLib { var val = promises.get(i); if (res.state != STATE_PENDING) break; - handle(args.ctx, val, new Handle() { - public void onFulfil(Object val) { res.fulfill(args.ctx, val); } + handle(args.env, val, new Handle() { + public void onFulfil(Object val) { res.fulfill(args.env, val); } public void onReject(EngineException err) { - errors.set(args.ctx, index, err.value); + errors.set(args.env, index, err.value); n[0]--; - if (n[0] <= 0) res.reject(args.ctx, new EngineException(errors).setExtensions(args.ctx)); + if (n[0] <= 0) res.reject(args.env, new EngineException(errors).setEnvironment(args.env)); } }); } @@ -247,9 +246,9 @@ public class PromiseLib { var val = promises.get(i); if (res.state != STATE_PENDING) break; - handle(args.ctx, val, new Handle() { - @Override public void onFulfil(Object val) { res.fulfill(args.ctx, val); } - @Override public void onReject(EngineException err) { res.reject(args.ctx, err); } + handle(args.env, val, new Handle() { + @Override public void onFulfil(Object val) { res.fulfill(args.env, val); } + @Override public void onReject(EngineException err) { res.reject(args.env, err); } }); } @@ -269,19 +268,19 @@ public class PromiseLib { var index = i; var val = promises.get(i); - handle(args.ctx, val, new Handle() { + handle(args.env, val, new Handle() { @Override public void onFulfil(Object val) { - result.set(args.ctx, index, val); + result.set(args.env, index, val); n[0]--; - if (n[0] <= 0) res.fulfill(args.ctx, result); + if (n[0] <= 0) res.fulfill(args.env, result); } @Override public void onReject(EngineException err) { - res.reject(args.ctx, err); + res.reject(args.env, err); } }); } - if (n[0] <= 0) res.fulfill(args.ctx, result); + if (n[0] <= 0) res.fulfill(args.env, result); return res; } @@ -298,31 +297,31 @@ public class PromiseLib { var index = i; - handle(args.ctx, promises.get(i), new Handle() { + handle(args.env, promises.get(i), new Handle() { @Override public void onFulfil(Object val) { var desc = new ObjectValue(); - desc.defineProperty(args.ctx, "status", "fulfilled"); - desc.defineProperty(args.ctx, "value", val); + desc.defineProperty(args.env, "status", "fulfilled"); + desc.defineProperty(args.env, "value", val); - result.set(args.ctx, index, desc); + result.set(args.env, index, desc); n[0]--; - if (n[0] <= 0) res.fulfill(args.ctx, res); + if (n[0] <= 0) res.fulfill(args.env, res); } @Override public void onReject(EngineException err) { var desc = new ObjectValue(); - desc.defineProperty(args.ctx, "status", "reject"); - desc.defineProperty(args.ctx, "value", err.value); + desc.defineProperty(args.env, "status", "reject"); + desc.defineProperty(args.env, "value", err.value); - result.set(args.ctx, index, desc); + result.set(args.env, index, desc); n[0]--; - if (n[0] <= 0) res.fulfill(args.ctx, res); + if (n[0] <= 0) res.fulfill(args.env, res); } }); } - if (n[0] <= 0) res.fulfill(args.ctx, result); + if (n[0] <= 0) res.fulfill(args.env, result); return res; } @@ -334,22 +333,22 @@ public class PromiseLib { var res = new PromiseLib(); - handle(args.ctx, args.self, new Handle() { + handle(args.env, args.self, new Handle() { @Override public void onFulfil(Object val) { - try { res.fulfill(args.ctx, onFulfill.call(args.ctx, null, val)); } - catch (EngineException e) { res.reject(args.ctx, e); } + try { res.fulfill(args.env, onFulfill.call(args.env, null, val)); } + catch (EngineException e) { res.reject(args.env, e); } } @Override public void onReject(EngineException err) { - try { res.fulfill(args.ctx, onReject.call(args.ctx, null, err.value)); } - catch (EngineException e) { res.reject(args.ctx, e); } + try { res.fulfill(args.env, onReject.call(args.env, null, err.value)); } + catch (EngineException e) { res.reject(args.env, e); } } - }.defer(args.ctx)); + }.defer(args.env)); return res; } @Expose public static Object __catch(Arguments args) { - return __then(new Arguments(args.ctx, args.self, null, args.get(0))); + return __then(new Arguments(args.env, args.self, null, args.get(0))); } @Expose public static Object __finally(Arguments args) { @@ -357,22 +356,22 @@ public class PromiseLib { var res = new PromiseLib(); - handle(args.ctx, args.self, new Handle() { + handle(args.env, args.self, new Handle() { @Override public void onFulfil(Object val) { try { - func.call(args.ctx); - res.fulfill(args.ctx, val); + func.call(args.env); + res.fulfill(args.env, val); } - catch (EngineException e) { res.reject(args.ctx, e); } + catch (EngineException e) { res.reject(args.env, e); } } @Override public void onReject(EngineException err) { try { - func.call(args.ctx); - res.reject(args.ctx, err); + func.call(args.env); + res.reject(args.env, err); } - catch (EngineException e) { res.reject(args.ctx, e); } + catch (EngineException e) { res.reject(args.env, e); } } - }.defer(args.ctx)); + }.defer(args.env)); return res; } @@ -384,19 +383,19 @@ public class PromiseLib { try { func.call( - args.ctx, null, + args.env, null, new NativeFunction(null, _args -> { - res.fulfill(_args.ctx, _args.get(0)); + res.fulfill(_args.env, _args.get(0)); return null; }), new NativeFunction(null, _args -> { - res.reject(_args.ctx, new EngineException(_args.get(0)).setExtensions(_args.ctx)); + res.reject(_args.env, new EngineException(_args.get(0)).setEnvironment(_args.env)); return null; }) ); } catch (EngineException e) { - res.reject(args.ctx, e); + res.reject(args.env, e); } return res; diff --git a/src/java/me/topchetoeu/jscript/lib/RegExpLib.java b/src/java/me/topchetoeu/jscript/lib/RegExpLib.java index 47c8b92..d4cf695 100644 --- a/src/java/me/topchetoeu/jscript/lib/RegExpLib.java +++ b/src/java/me/topchetoeu/jscript/lib/RegExpLib.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.regex.Pattern; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.NativeWrapper; @@ -119,7 +119,7 @@ public class RegExpLib { var res = new ArrayValue(); Object val; while ((val = this.__exec(args)) != Values.NULL) { - res.set(args.ctx, res.size(), Values.getMember(args.ctx, val, 0)); + res.set(args.env, res.size(), Values.getMember(args.env, val, 0)); } lastI = 0; return res; @@ -134,7 +134,7 @@ public class RegExpLib { @Expose("@@Symbol.matchAll") public Object __matchAll(Arguments args) { var pattern = this.toGlobal(); - return Values.toJSIterator(args.ctx, new Iterator() { + return Values.toJSIterator(args.env, new Iterator() { private Object val = null; private boolean updated = false; @@ -167,7 +167,7 @@ public class RegExpLib { while ((match = pattern.__exec(args)) != Values.NULL) { var added = new ArrayList(); var arrMatch = (ArrayValue)match; - int index = (int)Values.toNumber(args.ctx, Values.getMember(args.ctx, match, "index")); + int index = (int)Values.toNumber(args.env, Values.getMember(args.env, match, "index")); var matchVal = (String)arrMatch.get(0); if (index >= target.length()) break; @@ -184,18 +184,18 @@ public class RegExpLib { if (sensible) { if (hasLimit && res.size() + added.size() >= lim) break; - else for (var i = 0; i < added.size(); i++) res.set(args.ctx, res.size(), added.get(i)); + else for (var i = 0; i < added.size(); i++) res.set(args.env, res.size(), added.get(i)); } else { for (var i = 0; i < added.size(); i++) { if (hasLimit && res.size() >= lim) return res; - else res.set(args.ctx, res.size(), added.get(i)); + else res.set(args.env, res.size(), added.get(i)); } } lastEnd = pattern.lastI; } if (lastEnd < target.length()) { - res.set(args.ctx, res.size(), target.substring(lastEnd)); + res.set(args.env, res.size(), target.substring(lastEnd)); } return res; } @@ -209,7 +209,7 @@ public class RegExpLib { var res = new StringBuilder(); while ((match = pattern.__exec(args)) != Values.NULL) { - var indices = (ArrayValue)((ArrayValue)Values.getMember(args.ctx, match, "indices")).get(0); + var indices = (ArrayValue)((ArrayValue)Values.getMember(args.env, match, "indices")).get(0); var arrMatch = (ArrayValue)match; var start = ((Number)indices.get(0)).intValue(); @@ -222,10 +222,10 @@ public class RegExpLib { arrMatch.copyTo(callArgs, 1, 1, arrMatch.size() - 1); callArgs[callArgs.length - 2] = start; callArgs[callArgs.length - 1] = target; - res.append(Values.toString(args.ctx, ((FunctionValue)replacement).call(args.ctx, null, callArgs))); + res.append(Values.toString(args.env, ((FunctionValue)replacement).call(args.env, null, callArgs))); } else { - res.append(Values.toString(args.ctx, replacement)); + res.append(Values.toString(args.env, replacement)); } lastEnd = end; if (!pattern.global) break; @@ -313,26 +313,26 @@ public class RegExpLib { @ExposeConstructor public static RegExpLib __constructor(Arguments args) { - return new RegExpLib(cleanupPattern(args.ctx, args.get(0)), cleanupFlags(args.ctx, args.get(1))); + return new RegExpLib(cleanupPattern(args.env, args.get(0)), cleanupFlags(args.env, args.get(1))); } @Expose(target = ExposeTarget.STATIC) public static RegExpLib __escape(Arguments args) { - return escape(Values.toString(args.ctx, args.get(0)), cleanupFlags(args.ctx, args.get(1))); + return escape(Values.toString(args.env, args.get(0)), cleanupFlags(args.env, args.get(1))); } - private static String cleanupPattern(Context ctx, Object val) { + private static String cleanupPattern(Environment env, Object val) { if (val == null) return "(?:)"; if (val instanceof RegExpLib) return ((RegExpLib)val).source; if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) { return ((RegExpLib)((NativeWrapper)val).wrapped).source; } - var res = Values.toString(ctx, val); + var res = Values.toString(env, val); if (res.equals("")) return "(?:)"; return res; } - private static String cleanupFlags(Context ctx, Object val) { + private static String cleanupFlags(Environment env, Object val) { if (val == null) return ""; - return Values.toString(ctx, val); + return Values.toString(env, val); } private static boolean checkEscaped(String s, int pos) { diff --git a/src/java/me/topchetoeu/jscript/lib/SetLib.java b/src/java/me/topchetoeu/jscript/lib/SetLib.java index 273a27c..d76113a 100644 --- a/src/java/me/topchetoeu/jscript/lib/SetLib.java +++ b/src/java/me/topchetoeu/jscript/lib/SetLib.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.stream.Collectors; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; @@ -24,13 +24,13 @@ public class SetLib { } @Expose public ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.ctx, set.stream().map(v -> new ArrayValue(args.ctx, v, v)).collect(Collectors.toList())); + return Values.toJSIterator(args.env, set.stream().map(v -> new ArrayValue(args.env, v, v)).collect(Collectors.toList())); } @Expose public ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.ctx, set); + return Values.toJSIterator(args.env, set); } @Expose public ObjectValue __values(Arguments args) { - return Values.toJSIterator(args.ctx, set); + return Values.toJSIterator(args.env, set); } @Expose public Object __add(Arguments args) { @@ -55,15 +55,15 @@ public class SetLib { @Expose public void __forEach(Arguments args) { var keys = new ArrayList<>(set); - for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), el, el, args.self); + for (var el : keys) Values.call(args.env, args.get(0), args.get(1), el, el, args.self); } - public SetLib(Context ctx, Object iterable) { - for (var el : Values.fromJSIterator(ctx, iterable)) set.add(el); + public SetLib(Environment env, Object iterable) { + for (var el : Values.fromJSIterator(env, iterable)) set.add(el); } @ExposeConstructor public static SetLib __constructor(Arguments args) { - return new SetLib(args.ctx, args.get(0)); + return new SetLib(args.env, args.get(0)); } } diff --git a/src/java/me/topchetoeu/jscript/lib/StringLib.java b/src/java/me/topchetoeu/jscript/lib/StringLib.java index 93317a3..dba6546 100644 --- a/src/java/me/topchetoeu/jscript/lib/StringLib.java +++ b/src/java/me/topchetoeu/jscript/lib/StringLib.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.lib; import java.util.regex.Pattern; -import me.topchetoeu.jscript.runtime.Environment; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -101,23 +101,23 @@ public class StringLib { var val = passThis(args, "indexOf"); var term = args.get(0); var start = args.getInt(1); - var search = Values.getMember(args.ctx, term, Symbol.get("Symbol.search")); + var search = Values.getMember(args.env, term, Symbol.get("Symbol.search")); if (search instanceof FunctionValue) { - return (int)Values.toNumber(args.ctx, Values.call(args.ctx, search, term, val, false, start)); + return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, false, start)); } - else return val.indexOf(Values.toString(args.ctx, term), start); + else return val.indexOf(Values.toString(args.env, term), start); } @Expose public static int __lastIndexOf(Arguments args) { var val = passThis(args, "lastIndexOf"); var term = args.get(0); var start = args.getInt(1); - var search = Values.getMember(args.ctx, term, Symbol.get("Symbol.search")); + var search = Values.getMember(args.env, term, Symbol.get("Symbol.search")); if (search instanceof FunctionValue) { - return (int)Values.toNumber(args.ctx, Values.call(args.ctx, search, term, val, true, start)); + return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, true, start)); } - else return val.lastIndexOf(Values.toString(args.ctx, term), start); + else return val.lastIndexOf(Values.toString(args.env, term), start); } @Expose public static boolean __includes(Arguments args) { @@ -128,23 +128,23 @@ public class StringLib { var val = passThis(args, "replace"); var term = args.get(0); var replacement = args.get(1); - var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace")); + var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { - return Values.toString(args.ctx, Values.call(args.ctx, replace, term, val, replacement)); + return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement)); } - else return val.replaceFirst(Pattern.quote(Values.toString(args.ctx, term)), Values.toString(args.ctx, replacement)); + else return val.replaceFirst(Pattern.quote(Values.toString(args.env, term)), Values.toString(args.env, replacement)); } @Expose public static String __replaceAll(Arguments args) { var val = passThis(args, "replaceAll"); var term = args.get(0); var replacement = args.get(1); - var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace")); + var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { - return Values.toString(args.ctx, Values.call(args.ctx, replace, term, val, replacement)); + return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement)); } - else return val.replace(Values.toString(args.ctx, term), Values.toString(args.ctx, replacement)); + else return val.replace(Values.toString(args.env, term), Values.toString(args.env, replacement)); } @Expose public static ArrayValue __match(Arguments args) { @@ -154,21 +154,21 @@ public class StringLib { FunctionValue match; try { - var _match = Values.getMember(args.ctx, term, Symbol.get("Symbol.match")); + var _match = Values.getMember(args.env, term, Symbol.get("Symbol.match")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else if (args.ctx.hasNotNull(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(args.ctx, args.ctx.get(Environment.REGEX_CONSTR), Values.toString(args.ctx, term), ""); - _match = Values.getMember(args.ctx, regex, Symbol.get("Symbol.match")); + else if (args.env.hasNotNull(Environment.REGEX_CONSTR)) { + var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), ""); + _match = Values.getMember(args.env, regex, Symbol.get("Symbol.match")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; else throw EngineException.ofError("Regular expressions don't support matching."); } else throw EngineException.ofError("Regular expressions not supported."); } - catch (IllegalArgumentException e) { return new ArrayValue(args.ctx, ""); } + catch (IllegalArgumentException e) { return new ArrayValue(args.env, ""); } - var res = match.call(args.ctx, term, val); + var res = match.call(args.env, term, val); if (res instanceof ArrayValue) return (ArrayValue)res; - else return new ArrayValue(args.ctx, ""); + else return new ArrayValue(args.env, ""); } @Expose public static Object __matchAll(Arguments args) { var val = passThis(args, "matchAll"); @@ -177,20 +177,20 @@ public class StringLib { FunctionValue match = null; try { - var _match = Values.getMember(args.ctx, term, Symbol.get("Symbol.matchAll")); + var _match = Values.getMember(args.env, term, Symbol.get("Symbol.matchAll")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; } catch (IllegalArgumentException e) { } - if (match == null && args.ctx.hasNotNull(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(args.ctx, args.ctx.get(Environment.REGEX_CONSTR), Values.toString(args.ctx, term), "g"); - var _match = Values.getMember(args.ctx, regex, Symbol.get("Symbol.matchAll")); + if (match == null && args.env.hasNotNull(Environment.REGEX_CONSTR)) { + var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), "g"); + var _match = Values.getMember(args.env, regex, Symbol.get("Symbol.matchAll")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; else throw EngineException.ofError("Regular expressions don't support matching."); } else throw EngineException.ofError("Regular expressions not supported."); - return match.call(args.ctx, term, val); + return match.call(args.env, term, val); } @Expose public static ArrayValue __split(Arguments args) { @@ -199,23 +199,23 @@ public class StringLib { var lim = args.get(1); var sensible = args.getBoolean(2); - if (lim != null) lim = Values.toNumber(args.ctx, lim); + if (lim != null) lim = Values.toNumber(args.env, lim); if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace")); + var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { - var tmp = ((FunctionValue)replace).call(args.ctx, term, val, lim, sensible); + var tmp = ((FunctionValue)replace).call(args.env, term, val, lim, sensible); if (tmp instanceof ArrayValue) { var parts = new ArrayValue(((ArrayValue)tmp).size()); - for (int i = 0; i < parts.size(); i++) parts.set(args.ctx, i, Values.toString(args.ctx, ((ArrayValue)tmp).get(i))); + for (int i = 0; i < parts.size(); i++) parts.set(args.env, i, Values.toString(args.env, ((ArrayValue)tmp).get(i))); return parts; } } } String[] parts; - var pattern = Pattern.quote(Values.toString(args.ctx, term)); + var pattern = Pattern.quote(Values.toString(args.env, term)); if (lim == null) parts = val.split(pattern); else if ((double)lim < 1) return new ArrayValue(); @@ -228,7 +228,7 @@ public class StringLib { if (parts.length > limit) res = new ArrayValue(limit); else res = new ArrayValue(parts.length); - for (var i = 0; i < parts.length && i < limit; i++) res.set(args.ctx, i, parts[i]); + for (var i = 0; i < parts.length && i < limit; i++) res.set(args.env, i, parts[i]); return res; } @@ -238,7 +238,7 @@ public class StringLib { for (; i < parts.length; i++) { if (lim != null && (double)lim <= i) break; - res.set(args.ctx, i, parts[i]); + res.set(args.env, i, parts[i]); } return res; @@ -249,7 +249,7 @@ public class StringLib { var start = normalizeI(args.getInt(0), self.length(), false); var end = normalizeI(args.getInt(1, self.length()), self.length(), false); - return __substring(new Arguments(args.ctx, self, start, end)); + return __substring(new Arguments(args.env, self, start, end)); } @Expose public static String __concat(Arguments args) { diff --git a/src/java/me/topchetoeu/jscript/lib/SymbolLib.java b/src/java/me/topchetoeu/jscript/lib/SymbolLib.java index 9de6a74..e6a553d 100644 --- a/src/java/me/topchetoeu/jscript/lib/SymbolLib.java +++ b/src/java/me/topchetoeu/jscript/lib/SymbolLib.java @@ -76,6 +76,6 @@ public class SymbolLib { } @Expose(target = ExposeTarget.STATIC) public static String __keyFor(Arguments args) { - return passThis(new Arguments(args.ctx, args.get(0)), "keyFor").value; + return passThis(new Arguments(args.env, args.get(0)), "keyFor").value; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Childable.java b/src/java/me/topchetoeu/jscript/runtime/Childable.java deleted file mode 100644 index f1f358e..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/Childable.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -public interface Childable { - Object child(); -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/Compiler.java b/src/java/me/topchetoeu/jscript/runtime/Compiler.java index d332da5..ff87201 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Compiler.java +++ b/src/java/me/topchetoeu/jscript/runtime/Compiler.java @@ -2,6 +2,9 @@ package me.topchetoeu.jscript.runtime; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.FunctionBody; +import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.CodeFunction; @@ -11,13 +14,14 @@ public interface Compiler { public FunctionBody compile(Filename filename, String source); - public static Compiler get(Extensions ext) { + public static Compiler get(Environment ext) { return ext.get(KEY, (filename, src) -> { throw EngineException.ofError("No compiler attached to engine."); }); } public static CodeFunction compile(Environment env, Filename filename, String raw) { + DebugContext.get(env).onSource(filename, raw); return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Context.java b/src/java/me/topchetoeu/jscript/runtime/Context.java deleted file mode 100644 index e20a62b..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/Context.java +++ /dev/null @@ -1,98 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -import java.util.Iterator; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; - -public class Context implements Extensions { - public final Context parent; - public final Extensions extensions; - public final Frame frame; - public final int stackSize; - - @Override public void add(Key key, T obj) { - if (extensions != null) extensions.add(key, obj); - } - @Override public T get(Key key) { - if (extensions != null && extensions.has(key)) return extensions.get(key); - return null; - } - @Override public boolean has(Key key) { - return extensions != null && extensions.has(key); - } - @Override public boolean remove(Key key) { - var res = false; - if (extensions != null) res |= extensions.remove(key); - return res; - } - @Override public Iterable> keys() { - if (extensions == null) return List.of(); - else return extensions.keys(); - } - - public FunctionValue compile(Filename filename, String raw) { - DebugContext.get(this).onSource(filename, raw); - var result = new CodeFunction(extensions, filename.toString(), Compiler.get(this).compile(filename, raw), new ValueVariable[0]); - return result; - } - - public Context pushFrame(Frame frame) { - var res = new Context(this, frame.function.extensions, frame, stackSize + 1); - return res; - } - - public Iterable frames() { - var self = this; - return () -> new Iterator() { - private Context curr = self; - - private void update() { - while (curr != null && curr.frame == null) curr = curr.parent; - } - - @Override public boolean hasNext() { - update(); - return curr != null; - } - @Override public Frame next() { - update(); - var res = curr.frame; - curr = curr.parent; - return res; - } - }; - } - - private Context(Context parent, Extensions ext, Frame frame, int stackSize) { - this.parent = parent; - this.extensions = ext; - this.frame = frame; - this.stackSize = stackSize; - - if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) { - throw EngineException.ofRange("Stack overflow!"); - } - } - - public Context() { - this(null, null, null, 0); - } - public Context(Extensions ext) { - this(null, clean(ext), null, 0); - } - - public static Context of(Extensions ext) { - if (ext instanceof Context) return (Context)ext; - return new Context(ext); - } - public static Extensions clean(Extensions ext) { - if (ext instanceof Context) return clean(((Context)ext).extensions); - else return ext; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/Copyable.java b/src/java/me/topchetoeu/jscript/runtime/Copyable.java deleted file mode 100644 index cb2e75e..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/Copyable.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -public interface Copyable { - Object copy(); -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/Environment.java b/src/java/me/topchetoeu/jscript/runtime/Environment.java deleted file mode 100644 index 4e232d8..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/Environment.java +++ /dev/null @@ -1,61 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -import java.util.HashMap; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; - -@SuppressWarnings("unchecked") -public class Environment implements Extensions { - public static final Key COMPILE_FUNC = new Key<>(); - - public static final Key REGEX_CONSTR = new Key<>(); - public static final Key MAX_STACK_COUNT = new Key<>(); - public static final Key HIDE_STACK = new Key<>(); - - public static final Key OBJECT_PROTO = new Key<>(); - public static final Key FUNCTION_PROTO = new Key<>(); - public static final Key ARRAY_PROTO = new Key<>(); - public static final Key BOOL_PROTO = new Key<>(); - public static final Key NUMBER_PROTO = new Key<>(); - public static final Key STRING_PROTO = new Key<>(); - public static final Key SYMBOL_PROTO = new Key<>(); - public static final Key ERROR_PROTO = new Key<>(); - public static final Key SYNTAX_ERR_PROTO = new Key<>(); - public static final Key TYPE_ERR_PROTO = new Key<>(); - public static final Key RANGE_ERR_PROTO = new Key<>(); - - private HashMap, Object> data = new HashMap<>(); - - @Override public void add(Key key, T obj) { - data.put(key, obj); - } - @Override public T get(Key key) { - return (T)data.get(key); - } - @Override public boolean remove(Key key) { - if (data.containsKey(key)) { - data.remove(key); - return true; - } - return false; - } - @Override public boolean has(Key key) { - return data.containsKey(key); - } - @Override public Iterable> keys() { - return data.keySet(); - } - - public static FunctionValue regexConstructor(Extensions ext) { - return ext.init(REGEX_CONSTR, new NativeFunction("RegExp", args -> { - throw EngineException.ofError("Regular expressions not supported.").setExtensions(args.ctx); - })); - } - - public Context context() { - return new Context(this); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index 6046971..9502c7d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -3,13 +3,15 @@ package me.topchetoeu.jscript.runtime; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.ResultRunnable; import me.topchetoeu.jscript.common.events.DataNotifier; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.FunctionValue; public interface EventLoop { public static final Key KEY = new Key<>(); - public static EventLoop get(Extensions ext) { + public static EventLoop get(Environment ext) { if (ext.hasNotNull(KEY)) return ext.get(KEY); else return new EventLoop() { @Override public DataNotifier pushMsg(ResultRunnable runnable, boolean micro) { @@ -23,15 +25,10 @@ public interface EventLoop { return pushMsg(() -> { runnable.run(); return null; }, micro); } - public default DataNotifier pushMsg(boolean micro, Extensions ext, FunctionValue func, Object thisArg, Object ...args) { - return pushMsg(() -> { - return func.call(Context.of(ext), thisArg, args); - }, micro); + public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) { + return pushMsg(() -> func.call(env, thisArg, args), micro); } - public default DataNotifier pushMsg(boolean micro, Extensions ext, Filename filename, String raw, Object thisArg, Object ...args) { - return pushMsg(() -> { - var ctx = Context.of(ext); - return ctx.compile(filename, raw).call(Context.of(ext), thisArg, args); - }, micro); + public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) { + return pushMsg(() -> Compiler.compile(env, filename, raw).call(env, thisArg, args), micro); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Extensions.java b/src/java/me/topchetoeu/jscript/runtime/Extensions.java deleted file mode 100644 index 3410e3e..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/Extensions.java +++ /dev/null @@ -1,77 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -import java.util.List; - -public interface Extensions extends Childable, Copyable { - public static Extensions EMPTY = new Extensions() { - @Override public void add(Key key, T obj) { } - @Override public boolean remove(Key key) { return false; } - - @Override public T get(Key key) { return null; } - @Override public boolean has(Key key) { return false; } - @Override public Iterable> keys() { return List.of(); } - }; - - T get(Key key); - void add(Key key, T obj); - Iterable> keys(); - - boolean has(Key key); - boolean remove(Key key); - - default void add(Key key) { - add(key, null); - } - - default boolean hasNotNull(Key key) { - return has(key) && get(key) != null; - } - - default T get(Key key, T defaultVal) { - if (has(key)) return get(key); - else return defaultVal; - } - - default T init(Key key, T val) { - if (has(key)) return get(key); - else { - add(key, val); - return val; - } - } - @SuppressWarnings("unchecked") - default void addAll(Extensions source) { - if (source == null) return; - for (var key : source.keys()) { - add((Key)key, (Object)source.get(key)); - } - } - - @Override - @SuppressWarnings("unchecked") - default Extensions copy() { - var res = new Environment(); - for (var key : keys()) { - var val = get(key); - if (val instanceof Copyable) val = ((Copyable)val).copy(); - res.add((Key)key, val); - } - return res; - } - @Override - @SuppressWarnings("unchecked") - default Extensions child() { - var res = new Environment(); - for (var key : keys()) { - var val = get(key); - if (val instanceof Childable) val = ((Childable)val).child(); - res.add((Key)key, val); - } - return res; - } - - public static Extensions wrap(Extensions ext) { - if (ext == null) return EMPTY; - else return ext; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 7cdb025..69e7c19 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -5,6 +5,8 @@ import java.util.Stack; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.scope.LocalScope; @@ -16,6 +18,8 @@ import me.topchetoeu.jscript.runtime.values.ScopeValue; import me.topchetoeu.jscript.runtime.values.Values; public class Frame { + public static final Key KEY = new Key<>(); + public static enum TryState { TRY, CATCH, @@ -94,11 +98,13 @@ public class Frame { public final Object[] args; public final Stack tryStack = new Stack<>(); public final CodeFunction function; - public final Context ctx; + public final Environment env; + public Object[] stack = new Object[32]; public int stackPtr = 0; public int codePtr = 0; - public boolean jumpFlag = false, popTryFlag = false; + public boolean jumpFlag = false; + public boolean popTryFlag = false; public void addTry(int start, int end, int catchStart, int finallyStart) { var err = tryStack.empty() ? null : tryStack.peek().error; @@ -137,10 +143,10 @@ public class Frame { System.arraycopy(stack, 0, newStack, 0, stack.length); stack = newStack; } - stack[stackPtr++] = Values.normalize(ctx, val); + stack[stackPtr++] = Values.normalize(env, val); } - public Object next(Object value, Object returnValue, EngineException error) { + private Object next(Object value, Object returnValue, EngineException error) { if (value != Values.NO_RETURN) push(value); Instruction instr = null; @@ -148,18 +154,18 @@ public class Frame { if (returnValue == Values.NO_RETURN && error == null) { try { - if (Thread.currentThread().isInterrupted()) throw new InterruptException(); + if (Thread.interrupted()) throw new InterruptException(); if (instr == null) returnValue = null; else { - DebugContext.get(ctx).onInstruction(ctx, this, instr, Values.NO_RETURN, null, false); + DebugContext.get(env).onInstruction(env, this, instr, Values.NO_RETURN, null, false); try { this.jumpFlag = this.popTryFlag = false; - returnValue = InstructionRunner.exec(ctx, instr, this); + returnValue = InstructionRunner.exec(env, instr, this); } catch (EngineException e) { - error = e.add(ctx, function.name, DebugContext.get(ctx).getMapOrEmpty(function).toLocation(codePtr, true)); + error = e.add(env, function.name, DebugContext.get(env).getMapOrEmpty(function).toLocation(codePtr, true)); } } } @@ -201,6 +207,7 @@ public class Frame { tryStack.pop(); tryStack.push(newCtx); } + error = null; returnValue = Values.NO_RETURN; break; @@ -239,33 +246,75 @@ public class Frame { if (error != null) { var caught = false; - for (var frame : ctx.frames()) { + for (var frame : DebugContext.get(env).getStackFrames()) { for (var tryCtx : frame.tryStack) { if (tryCtx.state == TryState.TRY) caught = true; } } - DebugContext.get(ctx).onInstruction(ctx, this, instr, null, error, caught); + DebugContext.get(env).onInstruction(env, this, instr, null, error, caught); throw error; } if (returnValue != Values.NO_RETURN) { - DebugContext.get(ctx).onInstruction(ctx, this, instr, returnValue, null, false); + DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false); return returnValue; } return Values.NO_RETURN; } - public void onPush() { - DebugContext.get(ctx).onFramePush(ctx, this); + /** + * Executes the next instruction in the frame + */ + public Object next() { + return next(Values.NO_RETURN, Values.NO_RETURN, null); } - public void onPop() { - DebugContext.get(ctx.parent).onFramePop(ctx.parent, this); + /** + * Induces a value on the stack (as if it were returned by the last function call) + * and executes the next instruction in the frame. + * + * @param value The value to induce + */ + public Object next(Object value) { + return next(value, Values.NO_RETURN, null); + } + /** + * Induces a thrown error and executes the next instruction. + * Note that this is different than just throwing the error outside the + * function, as the function executed could have a try-catch which + * would otherwise handle the error + * + * @param error The error to induce + */ + public Object induceError(EngineException error) { + return next(Values.NO_RETURN, Values.NO_RETURN, error); + } + /** + * Induces a return, as if there was a return statement before + * the currently executed instruction and executes the next instruction. + * Note that this is different than just returning the value outside the + * function, as the function executed could have a try-catch which + * would otherwise handle the error + * + * @param value The retunr value to induce + */ + public Object induceReturn(Object value) { + return next(Values.NO_RETURN, value, null); } + public void onPush() { + DebugContext.get(env).onFramePush(env, this); + } + public void onPop() { + DebugContext.get(env).onFramePop(env, this); + } + + /** + * Gets an object proxy of the local scope + */ public ObjectValue getLocalScope() { var names = new String[scope.locals.length]; - var map = DebugContext.get(ctx).getMapOrEmpty(function); + var map = DebugContext.get(env).getMapOrEmpty(function); for (int i = 0; i < scope.locals.length; i++) { var name = "local_" + (i - 2); @@ -279,9 +328,12 @@ public class Frame { return new ScopeValue(scope.locals, names); } + /** + * Gets an object proxy of the capture scope + */ public ObjectValue getCaptureScope() { var names = new String[scope.captures.length]; - var map = DebugContext.get(ctx).getMapOrEmpty(function); + var map = DebugContext.get(env).getMapOrEmpty(function); for (int i = 0; i < scope.captures.length; i++) { var name = "capture_" + (i - 2); @@ -291,16 +343,19 @@ public class Frame { return new ScopeValue(scope.captures, names); } + /** + * Gets an array proxy of the local scope + */ public ObjectValue getValStackScope() { return new ObjectValue() { @Override - protected Object getField(Extensions ext, Object key) { + protected Object getField(Environment ext, Object key) { var i = (int)Values.toNumber(ext, key); if (i < 0 || i >= stackPtr) return null; else return stack[i]; } @Override - protected boolean hasField(Extensions ext, Object key) { + protected boolean hasField(Environment ext, Object key) { return true; } @Override @@ -312,18 +367,18 @@ public class Frame { }; } - public Frame(Context ctx, Object thisArg, Object[] args, CodeFunction func) { + public Frame(Environment env, Object thisArg, Object[] args, CodeFunction func) { + this.env = env; this.args = args.clone(); this.scope = new LocalScope(func.body.localsN, func.captures); this.scope.get(0).set(null, thisArg); var argsObj = new ArrayValue(); for (var i = 0; i < args.length; i++) { - argsObj.set(ctx, i, args[i]); + argsObj.set(env, i, args[i]); } this.scope.get(1).value = argsObj; this.thisArg = thisArg; this.function = func; - this.ctx = ctx.pushFrame(this); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 7a31109..c7bcfb8 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -4,6 +4,7 @@ import java.util.Collections; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; @@ -15,17 +16,17 @@ import me.topchetoeu.jscript.runtime.values.Symbol; import me.topchetoeu.jscript.runtime.values.Values; public class InstructionRunner { - private static Object execReturn(Extensions ext, Instruction instr, Frame frame) { + private static Object execReturn(Environment ext, Instruction instr, Frame frame) { return frame.pop(); } - private static Object execThrow(Extensions ext, Instruction instr, Frame frame) { + private static Object execThrow(Environment ext, Instruction instr, Frame frame) { throw new EngineException(frame.pop()); } - private static Object execThrowSyntax(Extensions ext, Instruction instr, Frame frame) { + private static Object execThrowSyntax(Environment ext, Instruction instr, Frame frame) { throw EngineException.ofSyntax((String)instr.get(0)); } - private static Object execCall(Extensions ext, Instruction instr, Frame frame) { + private static Object execCall(Environment ext, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); @@ -35,7 +36,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execCallNew(Extensions ext, Instruction instr, Frame frame) { + private static Object execCallNew(Environment ext, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); @@ -45,13 +46,13 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execMakeVar(Extensions ext, Instruction instr, Frame frame) { + private static Object execMakeVar(Environment ext, Instruction instr, Frame frame) { var name = (String)instr.get(0); GlobalScope.get(ext).define(ext, name); frame.codePtr++; return Values.NO_RETURN; } - private static Object execDefProp(Extensions ext, Instruction instr, Frame frame) { + private static Object execDefProp(Environment ext, Instruction instr, Frame frame) { var setter = frame.pop(); var getter = frame.pop(); var name = frame.pop(); @@ -66,7 +67,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execKeys(Extensions ext, Instruction instr, Frame frame) { + private static Object execKeys(Environment ext, Instruction instr, Frame frame) { var val = frame.pop(); var members = Values.getMembers(ext, val, false, false); @@ -85,7 +86,7 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execTryStart(Extensions ext, Instruction instr, Frame frame) { + private static Object execTryStart(Environment ext, Instruction instr, Frame frame) { int start = frame.codePtr + 1; int catchStart = (int)instr.get(0); int finallyStart = (int)instr.get(1); @@ -96,12 +97,12 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execTryEnd(Extensions ext, Instruction instr, Frame frame) { + private static Object execTryEnd(Environment ext, Instruction instr, Frame frame) { frame.popTryFlag = true; return Values.NO_RETURN; } - private static Object execDup(Extensions ext, Instruction instr, Frame frame) { + private static Object execDup(Environment ext, Instruction instr, Frame frame) { int count = instr.get(0); for (var i = 0; i < count; i++) { @@ -111,7 +112,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadValue(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadValue(Environment ext, Instruction instr, Frame frame) { switch (instr.type) { case PUSH_UNDEFINED: frame.push(null); break; case PUSH_NULL: frame.push(Values.NULL); break; @@ -121,7 +122,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadVar(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadVar(Environment ext, Instruction instr, Frame frame) { var i = instr.get(0); if (i instanceof String) frame.push(GlobalScope.get(ext).get(ext, (String)i)); @@ -130,24 +131,24 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadObj(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadObj(Environment ext, Instruction instr, Frame frame) { frame.push(new ObjectValue()); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadGlob(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadGlob(Environment ext, Instruction instr, Frame frame) { frame.push(GlobalScope.get(ext).obj); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadArr(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadArr(Environment ext, Instruction instr, Frame frame) { var res = new ArrayValue(); res.setSize(instr.get(0)); frame.push(res); frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadFunc(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadFunc(Environment ext, Instruction instr, Frame frame) { int id = instr.get(0); var captures = new ValueVariable[instr.params.length - 1]; @@ -162,7 +163,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadMember(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadMember(Environment ext, Instruction instr, Frame frame) { var key = frame.pop(); var obj = frame.pop(); @@ -175,7 +176,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execLoadRegEx(Extensions ext, Instruction instr, Frame frame) { + private static Object execLoadRegEx(Environment ext, Instruction instr, Frame frame) { if (ext.hasNotNull(Environment.REGEX_CONSTR)) { frame.push(Values.callNew(ext, ext.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1))); } @@ -186,12 +187,12 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execDiscard(Extensions ext, Instruction instr, Frame frame) { + private static Object execDiscard(Environment ext, Instruction instr, Frame frame) { frame.pop(); frame.codePtr++; return Values.NO_RETURN; } - private static Object execStoreMember(Extensions ext, Instruction instr, Frame frame) { + private static Object execStoreMember(Environment ext, Instruction instr, Frame frame) { var val = frame.pop(); var key = frame.pop(); var obj = frame.pop(); @@ -201,7 +202,7 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execStoreVar(Extensions ext, Instruction instr, Frame frame) { + private static Object execStoreVar(Environment ext, Instruction instr, Frame frame) { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); @@ -211,18 +212,18 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execStoreSelfFunc(Extensions ext, Instruction instr, Frame frame) { + private static Object execStoreSelfFunc(Environment ext, Instruction instr, Frame frame) { frame.scope.locals[(int)instr.get(0)].set(ext, frame.function); frame.codePtr++; return Values.NO_RETURN; } - private static Object execJmp(Extensions ext, Instruction instr, Frame frame) { + private static Object execJmp(Environment ext, Instruction instr, Frame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; return Values.NO_RETURN; } - private static Object execJmpIf(Extensions ext, Instruction instr, Frame frame) { + private static Object execJmpIf(Environment ext, Instruction instr, Frame frame) { if (Values.toBoolean(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -230,7 +231,7 @@ public class InstructionRunner { else frame.codePtr ++; return Values.NO_RETURN; } - private static Object execJmpIfNot(Extensions ext, Instruction instr, Frame frame) { + private static Object execJmpIfNot(Environment ext, Instruction instr, Frame frame) { if (Values.not(frame.pop())) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -239,7 +240,7 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execTypeof(Extensions ext, Instruction instr, Frame frame) { + private static Object execTypeof(Environment ext, Instruction instr, Frame frame) { String name = instr.get(0); Object obj; @@ -256,12 +257,12 @@ public class InstructionRunner { frame.codePtr++; return Values.NO_RETURN; } - private static Object execNop(Extensions ext, Instruction instr, Frame frame) { + private static Object execNop(Environment ext, Instruction instr, Frame frame) { frame.codePtr++; return Values.NO_RETURN; } - private static Object execDelete(Extensions ext, Instruction instr, Frame frame) { + private static Object execDelete(Environment ext, Instruction instr, Frame frame) { var key = frame.pop(); var val = frame.pop(); @@ -270,7 +271,7 @@ public class InstructionRunner { return Values.NO_RETURN; } - private static Object execOperation(Extensions ext, Instruction instr, Frame frame) { + private static Object execOperation(Environment ext, Instruction instr, Frame frame) { Operation op = instr.get(0); var args = new Object[op.operands]; @@ -281,7 +282,7 @@ public class InstructionRunner { return Values.NO_RETURN; } - public static Object exec(Extensions ext, Instruction instr, Frame frame) { + public static Object exec(Environment ext, Instruction instr, Frame frame) { switch (instr.type) { case NOP: return execNop(ext, instr, frame); case RETURN: return execReturn(ext, instr, frame); diff --git a/src/java/me/topchetoeu/jscript/runtime/Key.java b/src/java/me/topchetoeu/jscript/runtime/Key.java deleted file mode 100644 index 656efc8..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/Key.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -public class Key { - -} diff --git a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java index 8665625..ac0555b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java +++ b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.runtime; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.ObjectValue; diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java index f43e7bc..bb9be07 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java @@ -10,10 +10,9 @@ import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.mapping.FunctionMap; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.CodeFunction; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -71,15 +70,19 @@ public class DebugContext { if (maps == null || !(func instanceof CodeFunction)) return FunctionMap.EMPTY; return getMapOrEmpty(((CodeFunction)func).body); } + public List getStackFrames() { + return this.debugger.getStackFrame(); + } - public void onFramePop(Context ctx, Frame frame) { - if (debugger != null) debugger.onFramePop(ctx, frame); + public void onFramePop(Environment env, Frame frame) { + if (debugger != null) debugger.onFramePop(env, frame); } - public void onFramePush(Context ctx, Frame frame) { - if (debugger != null) debugger.onFramePush(ctx, frame); + public void onFramePush(Environment env, Frame frame) { + if (debugger != null) debugger.onFramePush(env, frame); } - public boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { - if (debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught); + + public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { + if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught); else return false; } public void onSource(Filename filename, String source) { @@ -102,26 +105,26 @@ public class DebugContext { this(true); } - public static boolean enabled(Extensions exts) { + public static boolean enabled(Environment exts) { return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE); } - public static DebugContext get(Extensions exts) { + public static DebugContext get(Environment exts) { if (enabled(exts)) return exts.get(KEY); else return new DebugContext(false); } - public static List stackTrace(Context ctx) { + public static List stackTrace(Environment env) { var res = new ArrayList(); - var dbgCtx = get(ctx); + var dbgCtx = get(env); - for (var el : ctx.frames()) { - var name = el.function.name; + for (var frame : dbgCtx.getStackFrames()) { + var name = frame.function.name; - var map = dbgCtx.getMapOrEmpty(el.function); + var map = dbgCtx.getMapOrEmpty(frame.function); Location loc = null; if (map != null) { - loc = map.toLocation(el.codePtr, true); + loc = map.toLocation(frame.codePtr, true); if (loc == null) loc = map.start(); } diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java index cb4b786..aad4c68 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java @@ -1,11 +1,13 @@ package me.topchetoeu.jscript.runtime.debug; +import java.util.List; + import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.mapping.FunctionMap; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; public interface DebugHandler { @@ -35,7 +37,7 @@ public interface DebugHandler { /** * Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned. * This function might pause in order to await debugging commands. - * @param ctx The context of execution + * @param env The context of execution * @param frame The frame in which execution is occuring * @param instruction The instruction which was or will be executed * @param returnVal The return value of the instruction, Values.NO_RETURN if none @@ -43,32 +45,35 @@ public interface DebugHandler { * @param caught Whether or not the error has been caught * @return Whether or not the frame should restart (currently does nothing) */ - boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught); + boolean onInstruction(Environment env, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught); /** * Called immediatly before a frame has been pushed on the frame stack. * This function might pause in order to await debugging commands. - * @param ctx The context of execution + * @param env The context of execution * @param frame The code frame which was pushed */ - void onFramePush(Context ctx, Frame frame); + void onFramePush(Environment env, Frame frame); /** * Called immediatly after a frame has been popped out of the frame stack. * This function might pause in order to await debugging commands. - * @param ctx The context of execution + * @param env The context of execution * @param frame The code frame which was popped out */ - void onFramePop(Context ctx, Frame frame); + void onFramePop(Environment env, Frame frame); + + List getStackFrame(); public static DebugHandler empty() { return new DebugHandler () { - @Override public void onFramePop(Context ctx, Frame frame) { } - @Override public void onFramePush(Context ctx, Frame frame) { } - @Override public boolean onInstruction(Context ctx, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { + @Override public void onFramePop(Environment env, Frame frame) { } + @Override public void onFramePush(Environment env, Frame frame) { } + @Override public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { return false; } @Override public void onSourceLoad(Filename filename, String source) { } @Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { } + @Override public List getStackFrame() { return List.of(); } }; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java new file mode 100644 index 0000000..24c246e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java @@ -0,0 +1,144 @@ +package me.topchetoeu.jscript.runtime.environment; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.function.Supplier; + +import me.topchetoeu.jscript.runtime.Compiler; +import me.topchetoeu.jscript.runtime.values.FunctionValue; +import me.topchetoeu.jscript.runtime.values.ObjectValue; + +public class Environment { + public static final Key COMPILE_FUNC = new Key<>(); + + public static final Key REGEX_CONSTR = new Key<>(); + public static final Key MAX_STACK_COUNT = new Key<>(); + public static final Key HIDE_STACK = new Key<>(); + + public static final Key OBJECT_PROTO = new Key<>(); + public static final Key FUNCTION_PROTO = new Key<>(); + public static final Key ARRAY_PROTO = new Key<>(); + public static final Key BOOL_PROTO = new Key<>(); + public static final Key NUMBER_PROTO = new Key<>(); + public static final Key STRING_PROTO = new Key<>(); + public static final Key SYMBOL_PROTO = new Key<>(); + public static final Key ERROR_PROTO = new Key<>(); + public static final Key SYNTAX_ERR_PROTO = new Key<>(); + public static final Key TYPE_ERR_PROTO = new Key<>(); + public static final Key RANGE_ERR_PROTO = new Key<>(); + + public final Environment parent; + private final Map, Object> map = new HashMap<>(); + private final Set> hidden = new HashSet<>(); + + @SuppressWarnings("unchecked") + public T get(Key key) { + if (map.containsKey(key)) return (T)map.get(key); + else if (!hidden.contains(key) && parent != null) return parent.get(key); + else return null; + } + public boolean has(Key key) { + if (map.containsKey(key)) return true; + else if (!hidden.contains(key) && parent != null) return parent.has(key); + else return false; + } + + @SuppressWarnings("all") + public Set> keys() { + if (parent != null) { + if (map.size() == 0) return (Set)map.keySet(); + + var res = new HashSet(); + res.addAll(parent.keys()); + res.addAll(map.keySet()); + return res; + } + else return (Set)map.keySet(); + } + + public boolean hasNotNull(Key key) { + return get(key) != null; + } + + public T get(Key key, T defaultVal) { + if (has(key)) return get(key); + else return defaultVal; + } + public T get(Key key, Supplier defaultVal) { + if (has(key)) return get(key); + else return defaultVal.get(); + } + + @SuppressWarnings("unchecked") + public Environment add(Key key, T val) { + map.put((Key)key, val); + hidden.remove(key); + return this; + } + @SuppressWarnings("unchecked") + public Environment add(Key key) { + map.put((Key)(Key)key, null); + hidden.remove(key); + return this; + } + @SuppressWarnings("all") + public Environment addAll(Map, ?> map) { + map.putAll((Map)map); + hidden.removeAll(map.keySet()); + return this; + } + public Environment addAll(Environment env) { + this.map.putAll(env.map); + this.hidden.removeAll(env.map.keySet()); + return this; + } + + @SuppressWarnings("unchecked") + public Environment remove(Key key) { + map.remove((Key)key); + hidden.add((Key)key); + return this; + } + + public Environment init(Key key, T val) { + if (!has(key)) this.add(key, val); + return this; + } + public Environment init(Key key, Supplier val) { + if (!has(key)) this.add(key, val.get()); + return this; + } + + public Environment child() { + return new Environment(this); + } + + public Environment(Environment parent) { + this.parent = parent; + } + public Environment() { + this.parent = null; + } + + public static Environment wrap(Environment ext) { + if (ext == null) return empty(); + else return ext; + } + + // public static Environment chain(int id, Environment ...envs) { + // var res = new Environment(); + // for (var env : envs) res.addAll(env); + // return res; + // } + + public static Environment empty() { + return new Environment(); + } + + public static int nextId() { + return new Random().nextInt(); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/environment/Key.java b/src/java/me/topchetoeu/jscript/runtime/environment/Key.java new file mode 100644 index 0000000..67a5156 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/environment/Key.java @@ -0,0 +1,5 @@ +package me.topchetoeu.jscript.runtime.environment; + +public class Key { + +} diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 20d19bc..15f0aed 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -4,9 +4,7 @@ import java.util.ArrayList; import java.util.List; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Environment; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Values; import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; @@ -15,7 +13,7 @@ public class EngineException extends RuntimeException { public static class StackElement { public final Location location; public final String name; - public final Extensions ext; + public final Environment ext; public boolean visible() { return ext == null || !ext.get(Environment.HIDE_STACK, false); @@ -30,12 +28,12 @@ public class EngineException extends RuntimeException { return res.trim(); } - public StackElement(Extensions ext, Location location, String name) { + public StackElement(Environment ext, Location location, String name) { if (name != null) name = name.trim(); if (name.equals("")) name = null; if (ext == null) this.ext = null; - else this.ext = Context.clean(ext); + else this.ext = ext; this.location = location; this.name = name; @@ -44,13 +42,13 @@ public class EngineException extends RuntimeException { public final Object value; public EngineException cause; - public Extensions ext = null; + public Environment env = null; public final List stackTrace = new ArrayList<>(); - public EngineException add(Extensions ext, String name, Location location) { - var el = new StackElement(ext, location, name); + public EngineException add(Environment env, String name, Location location) { + var el = new StackElement(env, location, name); if (el.name == null && el.location == null) return this; - setExtensions(ext); + setEnvironment(env); stackTrace.add(el); return this; } @@ -58,12 +56,12 @@ public class EngineException extends RuntimeException { this.cause = cause; return this; } - public EngineException setExtensions(Extensions ext) { - if (this.ext == null) this.ext = Context.clean(ext); + public EngineException setEnvironment(Environment env) { + if (this.env == null) this.env = env; return this; } - public String toString(Extensions ext) { + public String toString(Environment ext) { var ss = new StringBuilder(); try { ss.append(Values.toString(ext, value)).append('\n'); diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java index 63eed8e..eb26e61 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java @@ -3,8 +3,8 @@ package me.topchetoeu.jscript.runtime.scope; import java.util.HashSet; import java.util.Set; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.NativeFunction; @@ -16,7 +16,7 @@ public class GlobalScope { public final ObjectValue obj; - public boolean has(Extensions ext, String name) { + public boolean has(Environment ext, String name) { return Values.hasMember(ext, obj, name, false); } @@ -26,33 +26,33 @@ public class GlobalScope { return new GlobalScope(obj); } - public Object define(Extensions ext, String name) { + public Object define(Environment ext, String name) { if (Values.hasMember(ext, obj, name, false)) return name; obj.defineProperty(ext, name, null); return name; } - public void define(Extensions ext, String name, Variable val) { + public void define(Environment ext, String name, Variable val) { obj.defineProperty(ext, name, - new NativeFunction("get " + name, args -> val.get(args.ctx)), - new NativeFunction("set " + name, args -> { val.set(args.ctx, args.get(0)); return null; }), + new NativeFunction("get " + name, args -> val.get(args.env)), + new NativeFunction("set " + name, args -> { val.set(args.env, args.get(0)); return null; }), true, true ); } - public void define(Extensions ext, String name, boolean readonly, Object val) { + public void define(Environment ext, String name, boolean readonly, Object val) { obj.defineProperty(ext, name, val, readonly, true, true); } - public void define(Extensions ext, String ...names) { + public void define(Environment ext, String ...names) { for (var n : names) define(ext, n); } - public void define(Extensions ext, boolean readonly, FunctionValue val) { + public void define(Environment ext, boolean readonly, FunctionValue val) { define(ext, val.name, readonly, val); } - public Object get(Extensions ext, String name) { + public Object get(Environment ext, String name) { if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); else return Values.getMember(ext, obj, name); } - public void set(Extensions ext, String name, Object val) { + public void set(Environment ext, String name, Object val) { if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); if (!Values.setMember(ext, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); } @@ -74,7 +74,7 @@ public class GlobalScope { this.obj = val; } - public static GlobalScope get(Extensions ext) { + public static GlobalScope get(Environment ext) { if (ext.has(KEY)) return ext.get(KEY); else return new GlobalScope(); } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java index eb988b3..60b30fb 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.scope; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.Values; public class ValueVariable implements Variable { @@ -11,12 +11,12 @@ public class ValueVariable implements Variable { public boolean readonly() { return readonly; } @Override - public Object get(Extensions ext) { + public Object get(Environment ext) { return value; } @Override - public void set(Extensions ext, Object val) { + public void set(Environment ext, Object val) { if (readonly) return; this.value = Values.normalize(ext, val); } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java index 6d00d80..db5fb89 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java @@ -1,9 +1,9 @@ package me.topchetoeu.jscript.runtime.scope; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; public interface Variable { - Object get(Extensions ext); + Object get(Environment ext); default boolean readonly() { return true; } - default void set(Extensions ext, Object val) { } + default void set(Environment ext, Object val) { } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java index a13c014..0214505 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java @@ -6,7 +6,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; // TODO: Make methods generic public class ArrayValue extends ObjectValue implements Iterable { @@ -41,7 +41,7 @@ public class ArrayValue extends ObjectValue implements Iterable { if (res == UNDEFINED) return null; else return res; } - public void set(Extensions ext, int i, Object val) { + public void set(Environment ext, int i, Object val) { if (i < 0) return; values = alloc(i); @@ -99,7 +99,7 @@ public class ArrayValue extends ObjectValue implements Iterable { } } - public void copyFrom(Extensions ext, Object[] arr, int sourceStart, int destStart, int count) { + public void copyFrom(Environment ext, Object[] arr, int sourceStart, int destStart, int count) { for (var i = 0; i < count; i++) { set(ext, i + destStart, arr[i + sourceStart]); } @@ -131,7 +131,7 @@ public class ArrayValue extends ObjectValue implements Iterable { } @Override - protected Object getField(Extensions ext, Object key) { + protected Object getField(Environment ext, Object key) { if (key instanceof Number) { var i = ((Number)key).doubleValue(); if (i >= 0 && i - Math.floor(i) == 0) { @@ -142,7 +142,7 @@ public class ArrayValue extends ObjectValue implements Iterable { return super.getField(ext, key); } @Override - protected boolean setField(Extensions ext, Object key, Object val) { + protected boolean setField(Environment ext, Object key, Object val) { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { @@ -154,7 +154,7 @@ public class ArrayValue extends ObjectValue implements Iterable { return super.setField(ext, key, val); } @Override - protected boolean hasField(Extensions ext, Object key) { + protected boolean hasField(Environment ext, Object key) { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { @@ -165,7 +165,7 @@ public class ArrayValue extends ObjectValue implements Iterable { return super.hasField(ext, key); } @Override - protected void deleteField(Extensions ext, Object key) { + protected void deleteField(Environment ext, Object key) { if (key instanceof Number) { var i = Values.number(key); if (i >= 0 && i - Math.floor(i) == 0) { @@ -213,7 +213,7 @@ public class ArrayValue extends ObjectValue implements Iterable { values = new Object[cap]; size = 0; } - public ArrayValue(Extensions ext, Object ...values) { + public ArrayValue(Environment ext, Object ...values) { this(); this.values = new Object[values.length]; size = values.length; @@ -221,7 +221,7 @@ public class ArrayValue extends ObjectValue implements Iterable { for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ext, values[i]); } - public static ArrayValue of(Extensions ext, Collection values) { + public static ArrayValue of(Environment ext, Collection values) { return new ArrayValue(ext, values.toArray(Object[]::new)); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java index 2d0df77..ba5a909 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java @@ -1,15 +1,14 @@ package me.topchetoeu.jscript.runtime.values; import me.topchetoeu.jscript.common.FunctionBody; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; public class CodeFunction extends FunctionValue { public final FunctionBody body; public final ValueVariable[] captures; - public Extensions extensions; + public Environment env; // public Location loc() { // for (var instr : body.instructions) { @@ -24,15 +23,13 @@ public class CodeFunction extends FunctionValue { // else return name + "@" + loc; // } - @Override - public Object call(Extensions ext, Object thisArg, Object ...args) { - var frame = new Frame(Context.of(ext), thisArg, args, this); - + @Override public Object call(Environment env, Object thisArg, Object ...args) { + var frame = new Frame(env, thisArg, args, this); frame.onPush(); try { while (true) { - var res = frame.next(Values.NO_RETURN, Values.NO_RETURN, null); + var res = frame.next(); if (res != Values.NO_RETURN) return res; } } @@ -41,10 +38,10 @@ public class CodeFunction extends FunctionValue { } } - public CodeFunction(Extensions extensions, String name, FunctionBody body, ValueVariable[] captures) { + public CodeFunction(Environment env, String name, FunctionBody body, ValueVariable[] captures) { super(name, body.argsN); this.captures = captures; - this.extensions = Context.clean(extensions); + this.env = env; this.body = body; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java index ced4c9a..115f855 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values; import java.util.List; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; public abstract class FunctionValue extends ObjectValue { public String name = ""; @@ -13,26 +13,26 @@ public abstract class FunctionValue extends ObjectValue { return String.format("function %s(...)", name); } - public abstract Object call(Extensions ext, Object thisArg, Object ...args); - public Object call(Extensions ext) { + public abstract Object call(Environment ext, Object thisArg, Object ...args); + public Object call(Environment ext) { return call(ext, null); } @Override - protected Object getField(Extensions ext, Object key) { + protected Object getField(Environment ext, Object key) { if ("name".equals(key)) return name; if ("length".equals(key)) return length; return super.getField(ext, key); } @Override - protected boolean setField(Extensions ext, Object key, Object val) { + protected boolean setField(Environment ext, Object key, Object val) { if ("name".equals(key)) name = Values.toString(ext, val); else if ("length".equals(key)) length = (int)Values.toNumber(ext, val); else return super.setField(ext, key, val); return true; } @Override - protected boolean hasField(Extensions ext, Object key) { + protected boolean hasField(Environment ext, Object key) { if ("name".equals(key)) return true; if ("length".equals(key)) return true; return super.hasField(ext, key); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java index e1145f9..3746568 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java @@ -1,7 +1,6 @@ package me.topchetoeu.jscript.runtime.values; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.utils.interop.Arguments; public class NativeFunction extends FunctionValue { @@ -12,8 +11,8 @@ public class NativeFunction extends FunctionValue { public final NativeFunctionRunner action; @Override - public Object call(Extensions ext, Object thisArg, Object ...args) { - return action.run(new Arguments(Context.of(ext), thisArg, args)); + public Object call(Environment env, Object thisArg, Object ...args) { + return action.run(new Arguments(env, thisArg, args)); } public NativeFunction(String name, NativeFunctionRunner action) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java b/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java index 938bc73..c2fbf55 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java @@ -2,8 +2,8 @@ package me.topchetoeu.jscript.runtime.values; import java.util.WeakHashMap; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; public class NativeWrapper extends ObjectValue { @@ -28,12 +28,12 @@ public class NativeWrapper extends ObjectValue { } } - private static final Key> WRAPPERS = new Key<>(); + private static final Key> WRAPPER_MAP = new Key<>(); private static final Object NATIVE_PROTO = new Object(); public final Object wrapped; @Override - public ObjectValue getPrototype(Extensions ext) { + public ObjectValue getPrototype(Environment ext) { if (ext != null && prototype == NATIVE_PROTO) { var clazz = wrapped.getClass(); var res = NativeWrapperProvider.get(ext).getProto(clazz); @@ -60,17 +60,13 @@ public class NativeWrapper extends ObjectValue { prototype = NATIVE_PROTO; } - public static NativeWrapper of(Extensions exts, Object wrapped) { - if (exts == null) return new NativeWrapper(wrapped); - var wrappers = exts.get(WRAPPERS); + public static NativeWrapper of(Environment env, Object wrapped) { + if (env == null) return new NativeWrapper(wrapped); - if (wrappers == null) { - wrappers = new WeakHashMap<>(); - exts.add(WRAPPERS, wrappers); - } + var wrappers = env.get(WRAPPER_MAP); + if (wrappers == null) return new NativeWrapper(wrapped); var key = new MapKey(wrapped); - if (wrappers.containsKey(key)) return wrappers.get(key); var res = new NativeWrapper(wrapped); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java index c1e0768..aabb1f1 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java @@ -6,8 +6,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.runtime.Environment; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; public class ObjectValue { public static enum PlaceholderProto { @@ -54,10 +53,10 @@ public class ObjectValue { public LinkedHashSet nonConfigurableSet = new LinkedHashSet<>(); public LinkedHashSet nonEnumerableSet = new LinkedHashSet<>(); - private Property getProperty(Extensions ext, Object key) { + private Property getProperty(Environment env, Object key) { if (properties.containsKey(key)) return properties.get(key); - var proto = getPrototype(ext); - if (proto != null) return proto.getProperty(ext, key); + var proto = getPrototype(env); + if (proto != null) return proto.getProperty(env, key); else return null; } @@ -86,8 +85,8 @@ public class ObjectValue { state = State.FROZEN; } - public final boolean defineProperty(Extensions ext, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { - key = Values.normalize(ext, key); val = Values.normalize(ext, val); + public final boolean defineProperty(Environment env, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { + key = Values.normalize(env, key); val = Values.normalize(env, val); boolean reconfigured = writable != memberWritable(key) || configurable != memberConfigurable(key) || @@ -125,11 +124,11 @@ public class ObjectValue { values.put(key, val); return true; } - public final boolean defineProperty(Extensions ext, Object key, Object val) { - return defineProperty(ext, key, val, true, true, true); + public final boolean defineProperty(Environment env, Object key, Object val) { + return defineProperty(env, key, val, true, true, true); } - public final boolean defineProperty(Extensions ext, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { - key = Values.normalize(ext, key); + public final boolean defineProperty(Environment env, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { + key = Values.normalize(env, key); if ( properties.containsKey(key) && properties.get(key).getter == getter && @@ -152,17 +151,17 @@ public class ObjectValue { return true; } - public ObjectValue getPrototype(Extensions ext) { + public ObjectValue getPrototype(Environment env) { if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype; try { - if (prototype == ARR_PROTO) return ext.get(Environment.ARRAY_PROTO); - if (prototype == FUNC_PROTO) return ext.get(Environment.FUNCTION_PROTO); - if (prototype == ERR_PROTO) return ext.get(Environment.ERROR_PROTO); - if (prototype == RANGE_ERR_PROTO) return ext.get(Environment.RANGE_ERR_PROTO); - if (prototype == SYNTAX_ERR_PROTO) return ext.get(Environment.SYNTAX_ERR_PROTO); - if (prototype == TYPE_ERR_PROTO) return ext.get(Environment.TYPE_ERR_PROTO); - return ext.get(Environment.OBJECT_PROTO); + if (prototype == ARR_PROTO) return env.get(Environment.ARRAY_PROTO); + if (prototype == FUNC_PROTO) return env.get(Environment.FUNCTION_PROTO); + if (prototype == ERR_PROTO) return env.get(Environment.ERROR_PROTO); + if (prototype == RANGE_ERR_PROTO) return env.get(Environment.RANGE_ERR_PROTO); + if (prototype == SYNTAX_ERR_PROTO) return env.get(Environment.SYNTAX_ERR_PROTO); + if (prototype == TYPE_ERR_PROTO) return env.get(Environment.TYPE_ERR_PROTO); + return env.get(Environment.OBJECT_PROTO); } catch (NullPointerException e) { return null; } } @@ -185,10 +184,10 @@ public class ObjectValue { * A method, used to get the value of a field. If a property is bound to * this key, but not a field, this method should return null. */ - protected Object getField(Extensions ext, Object key) { + protected Object getField(Environment env, Object key) { if (values.containsKey(key)) return values.get(key); - var proto = getPrototype(ext); - if (proto != null) return proto.getField(ext, key); + var proto = getPrototype(env); + if (proto != null) return proto.getField(env, key); else return null; } /** @@ -196,9 +195,9 @@ public class ObjectValue { * bound to this key, a new field should be created with the given value * @return Whether or not the operation was successful */ - protected boolean setField(Extensions ext, Object key, Object val) { + protected boolean setField(Environment env, Object key, Object val) { if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) { - ((FunctionValue)val).name = Values.toString(ext, key); + ((FunctionValue)val).name = Values.toString(env, key); } values.put(key, val); @@ -207,40 +206,40 @@ public class ObjectValue { /** * Deletes the field bound to the given key. */ - protected void deleteField(Extensions ext, Object key) { + protected void deleteField(Environment env, Object key) { values.remove(key); } /** * Returns whether or not there is a field bound to the given key. * This must ignore properties */ - protected boolean hasField(Extensions ext, Object key) { + protected boolean hasField(Environment env, Object key) { return values.containsKey(key); } - public final Object getMember(Extensions ext, Object key, Object thisArg) { - key = Values.normalize(ext, key); + public final Object getMember(Environment env, Object key, Object thisArg) { + key = Values.normalize(env, key); if ("__proto__".equals(key)) { - var res = getPrototype(ext); + var res = getPrototype(env); return res == null ? Values.NULL : res; } - var prop = getProperty(ext, key); + var prop = getProperty(env, key); if (prop != null) { if (prop.getter == null) return null; - else return prop.getter.call(ext, Values.normalize(ext, thisArg)); + else return prop.getter.call(env, Values.normalize(env, thisArg)); } - else return getField(ext, key); + else return getField(env, key); } - public final boolean setMember(Extensions ext, Object key, Object val, Object thisArg, boolean onlyProps) { - key = Values.normalize(ext, key); val = Values.normalize(ext, val); + public final boolean setMember(Environment env, Object key, Object val, Object thisArg, boolean onlyProps) { + key = Values.normalize(env, key); val = Values.normalize(env, val); - var prop = getProperty(ext, key); + var prop = getProperty(env, key); if (prop != null) { if (prop.setter == null) return false; - prop.setter.call(ext, Values.normalize(ext, thisArg), val); + prop.setter.call(env, Values.normalize(env, thisArg), val); return true; } else if (onlyProps) return false; @@ -249,32 +248,32 @@ public class ObjectValue { values.put(key, val); return true; } - else if ("__proto__".equals(key)) return setPrototype(ext, val); + else if ("__proto__".equals(key)) return setPrototype(env, val); else if (nonWritableSet.contains(key)) return false; - else return setField(ext, key, val); + else return setField(env, key, val); } - public final boolean hasMember(Extensions ext, Object key, boolean own) { - key = Values.normalize(ext, key); + public final boolean hasMember(Environment env, Object key, boolean own) { + key = Values.normalize(env, key); if (key != null && "__proto__".equals(key)) return true; - if (hasField(ext, key)) return true; + if (hasField(env, key)) return true; if (properties.containsKey(key)) return true; if (own) return false; - var proto = getPrototype(ext); - return proto != null && proto.hasMember(ext, key, own); + var proto = getPrototype(env); + return proto != null && proto.hasMember(env, key, own); } - public final boolean deleteMember(Extensions ext, Object key) { - key = Values.normalize(ext, key); + public final boolean deleteMember(Environment env, Object key) { + key = Values.normalize(env, key); if (!memberConfigurable(key)) return false; properties.remove(key); nonWritableSet.remove(key); nonEnumerableSet.remove(key); - deleteField(ext, key); + deleteField(env, key); return true; } - public final boolean setPrototype(Extensions ext, Object val) { - val = Values.normalize(ext, val); + public final boolean setPrototype(Environment env, Object val) { + val = Values.normalize(env, val); if (!extensible()) return false; if (val == null || val == Values.NULL) { @@ -284,14 +283,14 @@ public class ObjectValue { else if (val instanceof ObjectValue) { var obj = (ObjectValue)val; - if (ext != null) { - if (obj == ext.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO; - else if (obj == ext.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO; - else if (obj == ext.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO; - else if (obj == ext.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO; - else if (obj == ext.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO; - else if (obj == ext.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO; - else if (obj == ext.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO; + if (env != null) { + if (obj == env.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO; + else if (obj == env.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO; + else if (obj == env.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO; + else if (obj == env.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO; + else if (obj == env.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO; + else if (obj == env.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO; + else if (obj == env.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO; else prototype = obj; } else prototype = obj; @@ -301,22 +300,22 @@ public class ObjectValue { return false; } - public final ObjectValue getMemberDescriptor(Extensions ext, Object key) { - key = Values.normalize(ext, key); + public final ObjectValue getMemberDescriptor(Environment env, Object key) { + key = Values.normalize(env, key); var prop = properties.get(key); var res = new ObjectValue(); - res.defineProperty(ext, "configurable", memberConfigurable(key)); - res.defineProperty(ext, "enumerable", memberEnumerable(key)); + res.defineProperty(env, "configurable", memberConfigurable(key)); + res.defineProperty(env, "enumerable", memberEnumerable(key)); if (prop != null) { - res.defineProperty(ext, "get", prop.getter); - res.defineProperty(ext, "set", prop.setter); + res.defineProperty(env, "get", prop.getter); + res.defineProperty(env, "set", prop.setter); } - else if (hasField(ext, key)) { - res.defineProperty(ext, "value", values.get(key)); - res.defineProperty(ext, "writable", memberWritable(key)); + else if (hasField(env, key)) { + res.defineProperty(env, "value", values.get(key)); + res.defineProperty(env, "writable", memberWritable(key)); } else return null; return res; @@ -337,10 +336,10 @@ public class ObjectValue { return res; } - public ObjectValue(Extensions ext, Map values) { + public ObjectValue(Environment env, Map values) { this(PlaceholderProto.OBJECT); for (var el : values.entrySet()) { - defineProperty(ext, el.getKey(), el.getValue()); + defineProperty(env, el.getKey(), el.getValue()); } } public ObjectValue(PlaceholderProto proto) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java index a64f4c2..3abf2ee 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.runtime.values; import java.util.HashMap; import java.util.List; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; public class ScopeValue extends ObjectValue { @@ -11,12 +11,12 @@ public class ScopeValue extends ObjectValue { public final HashMap names = new HashMap<>(); @Override - protected Object getField(Extensions ext, Object key) { + protected Object getField(Environment ext, Object key) { if (names.containsKey(key)) return variables[names.get(key)].get(ext); return super.getField(ext, key); } @Override - protected boolean setField(Extensions ext, Object key, Object val) { + protected boolean setField(Environment ext, Object key, Object val) { if (names.containsKey(key)) { variables[names.get(key)].set(ext, val); return true; @@ -28,12 +28,12 @@ public class ScopeValue extends ObjectValue { return super.setField(ext, key, val); } @Override - protected void deleteField(Extensions ext, Object key) { + protected void deleteField(Environment ext, Object key) { if (names.containsKey(key)) return; super.deleteField(ext, key); } @Override - protected boolean hasField(Extensions ext, Object key) { + protected boolean hasField(Environment ext, Object key) { if (names.containsKey(key)) return true; return super.hasField(ext, key); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java new file mode 100644 index 0000000..a240ec0 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -0,0 +1,733 @@ +package me.topchetoeu.jscript.runtime.values; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import me.topchetoeu.jscript.common.Operation; +// import me.topchetoeu.jscript.lib.PromiseLib; +import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.ConvertException; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; +import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; + +public interface Value { + public static enum CompareResult { + NOT_EQUAL, + EQUAL, + LESS, + GREATER; + + public boolean less() { return this == LESS; } + public boolean greater() { return this == GREATER; } + public boolean lessOrEqual() { return this == LESS || this == EQUAL; } + public boolean greaterOrEqual() { return this == GREATER || this == EQUAL; } + + public static CompareResult from(int cmp) { + if (cmp < 0) return LESS; + if (cmp > 0) return GREATER; + return EQUAL; + } + } + + public static final Object NULL = new Object(); + public static final Object NO_RETURN = new Object(); + + public static double number(Object val) { + if (val instanceof Number) return ((Number)val).doubleValue(); + else return Double.NaN; + } + + @SuppressWarnings("unchecked") + public static T wrapper(Object val, Class clazz) { + if (isWrapper(val)) val = ((NativeWrapper)val).wrapped; + if (val != null && clazz.isInstance(val)) return (T)val; + else return null; + } + + public static String type(Object val) { + if (val == null) return "undefined"; + if (val instanceof String) return "string"; + if (val instanceof Number) return "number"; + if (val instanceof Boolean) return "boolean"; + if (val instanceof Symbol) return "symbol"; + if (val instanceof FunctionValue) return "function"; + return "object"; + } + + public boolean isPrimitive(); + public BooleanValue toBoolean(); + + public default Value call(Environment env, Value self, Value ...args) { + throw EngineException.ofType("Tried to call a non-function value."); + } + public default Value callNew(Environment env, Value ...args) { + var res = new ObjectValue(); + + try { + var proto = Values.getMember(env, this, "prototype"); + setPrototype(env, res, proto); + + var ret = this.call(env, res, args); + + if (!ret.isPrimitive()) return ret; + return res; + } + catch (IllegalArgumentException e) { + throw EngineException.ofType("Tried to call new on an invalid constructor."); + } + } + + public default Value toPrimitive(Environment env, Value val) { + if (val.isPrimitive()) return val; + + if (env != null) { + var valueOf = getMember(env, val, "valueOf"); + + if (valueOf instanceof FunctionValue) { + var res = valueOf.call(env, val); + if (res.isPrimitive()) return res; + } + + var toString = getMember(env, val, "toString"); + if (toString instanceof FunctionValue) { + var res = toString.call(env, val); + if (res.isPrimitive()) return res; + } + } + + throw EngineException.ofType("Value couldn't be converted to a primitive."); + } + public default NumberValue toNumber(Environment ext, Object obj) { + var val = this.toPrimitive(ext, obj, ConvertHint.VALUEOF); + + if (val instanceof NumberValue) return number(val); + if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0; + if (val instanceof String) { + try { return Double.parseDouble((String)val); } + catch (NumberFormatException e) { return Double.NaN; } + } + return Double.NaN; + } + public default StringValue toString(Environment ext, Object obj) { + var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); + + if (val == null) return "undefined"; + if (val == NULL) return "null"; + + if (val instanceof Number) { + var d = number(val); + if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; + if (d == Double.POSITIVE_INFINITY) return "Infinity"; + if (Double.isNaN(d)) return "NaN"; + return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); + } + if (val instanceof Boolean) return (Boolean)val ? "true" : "false"; + if (val instanceof String) return (String)val; + if (val instanceof Symbol) return val.toString(); + + return "Unknown value"; + } + + public static Object add(Environment ext, Object a, Object b) { + if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b); + else return toNumber(ext, a) + toNumber(ext, b); + } + public static double subtract(Environment ext, Object a, Object b) { + return toNumber(ext, a) - toNumber(ext, b); + } + public static double multiply(Environment ext, Object a, Object b) { + return toNumber(ext, a) * toNumber(ext, b); + } + public static double divide(Environment ext, Object a, Object b) { + return toNumber(ext, a) / toNumber(ext, b); + } + public static double modulo(Environment ext, Object a, Object b) { + return toNumber(ext, a) % toNumber(ext, b); + } + + public static double negative(Environment ext, Object obj) { + return -toNumber(ext, obj); + } + + public static int and(Environment ext, Object a, Object b) { + return (int)toNumber(ext, a) & (int)toNumber(ext, b); + } + public static int or(Environment ext, Object a, Object b) { + return (int)toNumber(ext, a) | (int)toNumber(ext, b); + } + public static int xor(Environment ext, Object a, Object b) { + return (int)toNumber(ext, a) ^ (int)toNumber(ext, b); + } + public static int bitwiseNot(Environment ext, Object obj) { + return ~(int)toNumber(ext, obj); + } + + public static int shiftLeft(Environment ext, Object a, Object b) { + return (int)toNumber(ext, a) << (int)toNumber(ext, b); + } + public static int shiftRight(Environment ext, Object a, Object b) { + return (int)toNumber(ext, a) >> (int)toNumber(ext, b); + } + public static long unsignedShiftRight(Environment ext, Object a, Object b) { + long _a = (long)toNumber(ext, a); + long _b = (long)toNumber(ext, b); + + if (_a < 0) _a += 0x100000000l; + if (_b < 0) _b += 0x100000000l; + return _a >>> _b; + } + + public static CompareResult compare(Environment ext, Object a, Object b) { + a = toPrimitive(ext, a, ConvertHint.VALUEOF); + b = toPrimitive(ext, b, ConvertHint.VALUEOF); + + if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b)); + + var _a = toNumber(ext, a); + var _b = toNumber(ext, b); + + if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL; + + return CompareResult.from(Double.compare(_a, _b)); + } + + public static boolean not(Object obj) { + return !toBoolean(obj); + } + + public static boolean isInstanceOf(Environment ext, Object obj, Object proto) { + if (obj == null || obj == NULL || proto == null || proto == NULL) return false; + var val = getPrototype(ext, obj); + + while (val != null) { + if (val.equals(proto)) return true; + val = val.getPrototype(ext); + } + + return false; + } + + public static Object operation(Environment ext, Operation op, Object ...args) { + switch (op) { + case ADD: return add(ext, args[0], args[1]); + case SUBTRACT: return subtract(ext, args[0], args[1]); + case DIVIDE: return divide(ext, args[0], args[1]); + case MULTIPLY: return multiply(ext, args[0], args[1]); + case MODULO: return modulo(ext, args[0], args[1]); + + case AND: return and(ext, args[0], args[1]); + case OR: return or(ext, args[0], args[1]); + case XOR: return xor(ext, args[0], args[1]); + + case EQUALS: return strictEquals(ext, args[0], args[1]); + case NOT_EQUALS: return !strictEquals(ext, args[0], args[1]); + case LOOSE_EQUALS: return looseEqual(ext, args[0], args[1]); + case LOOSE_NOT_EQUALS: return !looseEqual(ext, args[0], args[1]); + + case GREATER: return compare(ext, args[0], args[1]).greater(); + case GREATER_EQUALS: return compare(ext, args[0], args[1]).greaterOrEqual(); + case LESS: return compare(ext, args[0], args[1]).less(); + case LESS_EQUALS: return compare(ext, args[0], args[1]).lessOrEqual(); + + case INVERSE: return bitwiseNot(ext, args[0]); + case NOT: return not(args[0]); + case POS: return toNumber(ext, args[0]); + case NEG: return negative(ext, args[0]); + + case SHIFT_LEFT: return shiftLeft(ext, args[0], args[1]); + case SHIFT_RIGHT: return shiftRight(ext, args[0], args[1]); + case USHIFT_RIGHT: return unsignedShiftRight(ext, args[0], args[1]); + + case IN: return hasMember(ext, args[1], args[0], false); + case INSTANCEOF: { + var proto = getMember(ext, args[1], "prototype"); + return isInstanceOf(ext, args[0], proto); + } + + default: return null; + } + } + + public static Object getMember(Environment ctx, Object obj, Object key) { + obj = normalize(ctx, obj); key = normalize(ctx, key); + if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); + if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); + if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj); + + if (obj instanceof String && key instanceof Number) { + var i = number(key); + var s = (String)obj; + if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) { + return s.charAt((int)i) + ""; + } + } + + var proto = getPrototype(ctx, obj); + + if (proto == null) return "__proto__".equals(key) ? NULL : null; + else if (key != null && "__proto__".equals(key)) return proto; + else return proto.getMember(ctx, key, obj); + } + public static Object getMemberPath(Environment ctx, Object obj, Object ...path) { + var res = obj; + for (var key : path) res = getMember(ctx, res, key); + return res; + } + public static boolean setMember(Environment ctx, Object obj, Object key, Object val) { + obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); + if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); + if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); + if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val); + if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false); + + var proto = getPrototype(ctx, obj); + return proto.setMember(ctx, key, val, obj, true); + } + public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) { + if (obj == null || obj == NULL) return false; + obj = normalize(ctx, obj); key = normalize(ctx, key); + + if ("__proto__".equals(key)) return true; + if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own); + + if (obj instanceof String && key instanceof Number) { + var i = number(key); + var s = (String)obj; + if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true; + } + + if (own) return false; + + var proto = getPrototype(ctx, obj); + return proto != null && proto.hasMember(ctx, key, own); + } + public static boolean deleteMember(Environment ext, Object obj, Object key) { + if (obj == null || obj == NULL) return false; + obj = normalize(ext, obj); key = normalize(ext, key); + + if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key); + else return false; + } + public static ObjectValue getPrototype(Environment ext, Object obj) { + if (obj == null || obj == NULL) return null; + obj = normalize(ext, obj); + if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext); + if (ext == null) return null; + + if (obj instanceof String) return ext.get(Environment.STRING_PROTO); + else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO); + else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO); + else if (obj instanceof Symbol) return ext.get(Environment.SYMBOL_PROTO); + + return null; + } + public static boolean setPrototype(Environment ext, Object obj, Object proto) { + obj = normalize(ext, obj); + return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto); + } + public static void makePrototypeChain(Environment ext, Object... chain) { + for(var i = 1; i < chain.length; i++) { + setPrototype(ext, chain[i], chain[i - 1]); + } + } + public static List getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) { + List res = new ArrayList<>(); + + if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable); + if (obj instanceof String) { + for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i); + } + + if (!own) { + var proto = getPrototype(ext, obj); + + while (proto != null) { + res.addAll(proto.keys(includeNonEnumerable)); + proto = getPrototype(ext, proto); + } + } + + + return res; + } + public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) { + if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key); + else if (obj instanceof String && key instanceof Number) { + var i = ((Number)key).intValue(); + var _i = ((Number)key).doubleValue(); + if (i - _i != 0) return null; + if (i < 0 || i >= ((String)obj).length()) return null; + + return new ObjectValue(ext, Map.of( + "value", ((String)obj).charAt(i) + "", + "writable", false, + "enumerable", true, + "configurable", false + )); + } + else return null; + } + + public static boolean strictEquals(Environment ext, Object a, Object b) { + a = normalize(ext, a); + b = normalize(ext, b); + + if (a == null || b == null) return a == null && b == null; + if (isNan(a) || isNan(b)) return false; + if (a instanceof Number && number(a) == -0.) a = 0.; + if (b instanceof Number && number(b) == -0.) b = 0.; + + return a == b || a.equals(b); + } + public static boolean looseEqual(Environment ext, Object a, Object b) { + a = normalize(ext, a); b = normalize(ext, b); + + // In loose equality, null is equivalent to undefined + if (a == NULL) a = null; + if (b == NULL) b = null; + + if (a == null || b == null) return a == null && b == null; + // If both are objects, just compare their references + if (!isPrimitive(a) && !isPrimitive(b)) return a == b; + + // Convert values to primitives + a = toPrimitive(ext, a, ConvertHint.VALUEOF); + b = toPrimitive(ext, b, ConvertHint.VALUEOF); + + // Compare symbols by reference + if (a instanceof Symbol || b instanceof Symbol) return a == b; + if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); + if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); + + // Default to strings + return toString(ext, a).equals(toString(ext, b)); + } + + public static Object normalize(Environment ext, Object val) { + if (val instanceof Number) return number(val); + if (isPrimitive(val) || val instanceof ObjectValue) return val; + if (val instanceof Character) return val + ""; + + if (val instanceof Map) { + var res = new ObjectValue(); + + for (var entry : ((Map)val).entrySet()) { + res.defineProperty(ext, entry.getKey(), entry.getValue()); + } + + return res; + } + + if (val instanceof Iterable) { + var res = new ArrayValue(); + + for (var entry : ((Iterable)val)) { + res.set(ext, res.size(), entry); + } + + return res; + } + + if (val instanceof Class) { + if (ext == null) return null; + else return NativeWrapperProvider.get(ext).getConstr((Class)val); + } + + return NativeWrapper.of(ext, val); + } + + @SuppressWarnings("unchecked") + public static T convert(Environment ext, Object obj, Class clazz) { + if (clazz == Void.class) return null; + + if (obj instanceof NativeWrapper) { + var res = ((NativeWrapper)obj).wrapped; + if (clazz.isInstance(res)) return (T)res; + } + + if (clazz == null || clazz == Object.class) return (T)obj; + + if (obj instanceof ArrayValue) { + if (clazz.isAssignableFrom(ArrayList.class)) { + var raw = ((ArrayValue)obj).toArray(); + var res = new ArrayList<>(); + for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); + return (T)new ArrayList<>(res); + } + if (clazz.isAssignableFrom(HashSet.class)) { + var raw = ((ArrayValue)obj).toArray(); + var res = new HashSet<>(); + for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); + return (T)new HashSet<>(res); + } + if (clazz.isArray()) { + var raw = ((ArrayValue)obj).toArray(); + Object res = Array.newInstance(clazz.getComponentType(), raw.length); + for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); + return (T)res; + } + } + + if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { + var res = new HashMap<>(); + for (var el : ((ObjectValue)obj).values.entrySet()) res.put( + convert(ext, el.getKey(), null), + convert(ext, el.getValue(), null) + ); + return (T)res; + } + + if (clazz == String.class) return (T)toString(ext, obj); + if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj); + if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, obj); + if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, obj); + if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, obj); + if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, obj); + if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, obj); + if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, obj); + + if (clazz == Character.class || clazz == char.class) { + if (obj instanceof Number) return (T)(Character)(char)number(obj); + else { + var res = toString(ext, obj); + if (res.length() == 0) throw new ConvertException("\"\"", "Character"); + else return (T)(Character)res.charAt(0); + } + } + + if (obj == null) return null; + if (clazz.isInstance(obj)) return (T)obj; + if (clazz.isAssignableFrom(NativeWrapper.class)) { + return (T)NativeWrapper.of(ext, obj); + } + + throw new ConvertException(type(obj), clazz.getSimpleName()); + } + + public static Iterable fromJSIterator(Environment ext, Object obj) { + return () -> { + try { + var symbol = Symbol.get("Symbol.iterator"); + + var iteratorFunc = getMember(ext, obj, symbol); + if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); + var iterator = iteratorFunc instanceof FunctionValue ? + ((FunctionValue)iteratorFunc).call(ext, obj, obj) : + iteratorFunc; + var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next"); + + if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator(); + + return new Iterator() { + private Object value = null; + public boolean consumed = true; + private FunctionValue next = (FunctionValue)nextFunc; + + private void loadNext() { + if (next == null) value = null; + else if (consumed) { + var curr = next.call(ext, iterator); + if (curr == null) { next = null; value = null; } + if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; } + else { + this.value = Values.getMember(ext, curr, "value"); + consumed = false; + } + } + } + + @Override + public boolean hasNext() { + loadNext(); + return next != null; + } + @Override + public Object next() { + loadNext(); + var res = value; + value = null; + consumed = true; + return res; + } + }; + } + catch (IllegalArgumentException | NullPointerException e) { + return Collections.emptyIterator(); + } + }; + } + + public static ObjectValue toJSIterator(Environment ext, Iterator it) { + var res = new ObjectValue(); + + try { + var key = getMember(ext, getMember(ext, ext.get(Environment.SYMBOL_PROTO), "constructor"), "iterator"); + res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); + } + catch (IllegalArgumentException | NullPointerException e) { } + + res.defineProperty(ext, "next", new NativeFunction("", args -> { + if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); + else { + var obj = new ObjectValue(); + obj.defineProperty(args.env, "value", it.next()); + return obj; + } + })); + + return res; + } + + public static ObjectValue toJSIterator(Environment ext, Iterable it) { + return toJSIterator(ext, it.iterator()); + } + + public static ObjectValue toJSAsyncIterator(Environment ext, Iterator it) { + var res = new ObjectValue(); + + try { + var key = getMemberPath(ext, ext.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator"); + res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); + } + catch (IllegalArgumentException | NullPointerException e) { } + + res.defineProperty(ext, "next", new NativeFunction("", args -> { + return PromiseLib.await(args.env, () -> { + if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); + else { + var obj = new ObjectValue(); + obj.defineProperty(args.env, "value", it.next()); + return obj; + } + }); + })); + + return res; + } + + private static boolean isEmptyFunc(ObjectValue val) { + if (!(val instanceof FunctionValue)) return false; + if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false; + var proto = val.values.get("prototype"); + if (!(proto instanceof ObjectValue)) return false; + var protoObj = (ObjectValue)proto; + if (protoObj.values.get("constructor") != val) return false; + if (protoObj.values.size() + protoObj.properties.size() != 1) return false; + return true; + } + private static String toReadable(Environment ext, Object val, HashSet passed, int tab) { + if (tab == 0 && val instanceof String) return (String)val; + + if (passed.contains(val)) return "[circular]"; + + var printed = true; + var res = new StringBuilder(); + var dbg = DebugContext.get(ext); + + if (val instanceof FunctionValue) { + res.append(val.toString()); + var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null; + + if (loc != null) res.append(" @ " + loc); + } + else if (val instanceof ArrayValue) { + res.append("["); + var obj = ((ArrayValue)val); + for (int i = 0; i < obj.size(); i++) { + if (i != 0) res.append(", "); + else res.append(" "); + if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab)); + else res.append(""); + } + res.append(" ] "); + } + else if (val instanceof NativeWrapper) { + var obj = ((NativeWrapper)val).wrapped; + res.append("Native " + obj.toString() + " "); + } + else printed = false; + + if (val instanceof ObjectValue) { + if (tab > 3) { + return "{...}"; + } + + passed.add(val); + + var obj = (ObjectValue)val; + if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) { + if (!printed) res.append("{}\n"); + } + else { + res.append("{\n"); + + for (var el : obj.values.entrySet()) { + for (int i = 0; i < tab + 1; i++) res.append(" "); + res.append(toReadable(ext, el.getKey(), passed, tab + 1)); + res.append(": "); + res.append(toReadable(ext, el.getValue(), passed, tab + 1)); + res.append(",\n"); + } + for (var el : obj.properties.entrySet()) { + for (int i = 0; i < tab + 1; i++) res.append(" "); + res.append(toReadable(ext, el.getKey(), passed, tab + 1)); + res.append(": [prop],\n"); + } + + for (int i = 0; i < tab; i++) res.append(" "); + res.append("}"); + } + + passed.remove(val); + } + else if (val == null) return "undefined"; + else if (val == Values.NULL) return "null"; + else if (val instanceof String) return "'" + val + "'"; + else return Values.toString(ext, val); + + return res.toString(); + } + + public static String toReadable(Environment ext, Object val) { + return toReadable(ext, val, new HashSet<>(), 0); + } + public static String errorToReadable(RuntimeException err, String prefix) { + prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix; + if (err instanceof EngineException) { + var ee = ((EngineException)err); + try { + return prefix + " " + ee.toString(ee.env); + } + catch (EngineException ex) { + return prefix + " " + toReadable(ee.env, ee.value); + } + } + else if (err instanceof SyntaxException) { + return prefix + " SyntaxError " + ((SyntaxException)err).msg; + } + else if (err.getCause() instanceof InterruptedException) return ""; + else { + var str = new ByteArrayOutputStream(); + err.printStackTrace(new PrintStream(str)); + + return prefix + " internal error " + str.toString(); + } + } + public static void printValue(Environment ext, Object val) { + System.out.print(toReadable(ext, val)); + } + public static void printError(RuntimeException err, String prefix) { + System.out.println(errorToReadable(err, prefix)); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Values.java b/src/java/me/topchetoeu/jscript/runtime/values/Values.java index db93cd8..0e527f6 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Values.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Values.java @@ -13,10 +13,9 @@ import java.util.List; import java.util.Map; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.lib.PromiseLib; -import me.topchetoeu.jscript.runtime.Environment; -import me.topchetoeu.jscript.runtime.Extensions; +// import me.topchetoeu.jscript.lib.PromiseLib; import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.ConvertException; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; @@ -74,7 +73,7 @@ public class Values { return "object"; } - private static Object tryCallConvertFunc(Extensions ext, Object obj, String name) { + private static Object tryCallConvertFunc(Environment ext, Object obj, String name) { var func = getMember(ext, obj, name); if (func instanceof FunctionValue) { @@ -95,7 +94,7 @@ public class Values { obj == NULL; } - public static Object toPrimitive(Extensions ext, Object obj, ConvertHint hint) { + public static Object toPrimitive(Environment ext, Object obj, ConvertHint hint) { obj = normalize(ext, obj); if (isPrimitive(obj)) return obj; @@ -116,7 +115,7 @@ public class Values { if (obj instanceof Boolean) return (Boolean)obj; return true; } - public static double toNumber(Extensions ext, Object obj) { + public static double toNumber(Environment ext, Object obj) { var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); if (val instanceof Number) return number(val); @@ -127,7 +126,7 @@ public class Values { } return Double.NaN; } - public static String toString(Extensions ext, Object obj) { + public static String toString(Environment ext, Object obj) { var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); if (val == null) return "undefined"; @@ -147,47 +146,47 @@ public class Values { return "Unknown value"; } - public static Object add(Extensions ext, Object a, Object b) { + public static Object add(Environment ext, Object a, Object b) { if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b); else return toNumber(ext, a) + toNumber(ext, b); } - public static double subtract(Extensions ext, Object a, Object b) { + public static double subtract(Environment ext, Object a, Object b) { return toNumber(ext, a) - toNumber(ext, b); } - public static double multiply(Extensions ext, Object a, Object b) { + public static double multiply(Environment ext, Object a, Object b) { return toNumber(ext, a) * toNumber(ext, b); } - public static double divide(Extensions ext, Object a, Object b) { + public static double divide(Environment ext, Object a, Object b) { return toNumber(ext, a) / toNumber(ext, b); } - public static double modulo(Extensions ext, Object a, Object b) { + public static double modulo(Environment ext, Object a, Object b) { return toNumber(ext, a) % toNumber(ext, b); } - public static double negative(Extensions ext, Object obj) { + public static double negative(Environment ext, Object obj) { return -toNumber(ext, obj); } - public static int and(Extensions ext, Object a, Object b) { + public static int and(Environment ext, Object a, Object b) { return (int)toNumber(ext, a) & (int)toNumber(ext, b); } - public static int or(Extensions ext, Object a, Object b) { + public static int or(Environment ext, Object a, Object b) { return (int)toNumber(ext, a) | (int)toNumber(ext, b); } - public static int xor(Extensions ext, Object a, Object b) { + public static int xor(Environment ext, Object a, Object b) { return (int)toNumber(ext, a) ^ (int)toNumber(ext, b); } - public static int bitwiseNot(Extensions ext, Object obj) { + public static int bitwiseNot(Environment ext, Object obj) { return ~(int)toNumber(ext, obj); } - public static int shiftLeft(Extensions ext, Object a, Object b) { + public static int shiftLeft(Environment ext, Object a, Object b) { return (int)toNumber(ext, a) << (int)toNumber(ext, b); } - public static int shiftRight(Extensions ext, Object a, Object b) { + public static int shiftRight(Environment ext, Object a, Object b) { return (int)toNumber(ext, a) >> (int)toNumber(ext, b); } - public static long unsignedShiftRight(Extensions ext, Object a, Object b) { + public static long unsignedShiftRight(Environment ext, Object a, Object b) { long _a = (long)toNumber(ext, a); long _b = (long)toNumber(ext, b); @@ -196,7 +195,7 @@ public class Values { return _a >>> _b; } - public static CompareResult compare(Extensions ext, Object a, Object b) { + public static CompareResult compare(Environment ext, Object a, Object b) { a = toPrimitive(ext, a, ConvertHint.VALUEOF); b = toPrimitive(ext, b, ConvertHint.VALUEOF); @@ -214,7 +213,7 @@ public class Values { return !toBoolean(obj); } - public static boolean isInstanceOf(Extensions ext, Object obj, Object proto) { + public static boolean isInstanceOf(Environment ext, Object obj, Object proto) { if (obj == null || obj == NULL || proto == null || proto == NULL) return false; var val = getPrototype(ext, obj); @@ -226,7 +225,7 @@ public class Values { return false; } - public static Object operation(Extensions ext, Operation op, Object ...args) { + public static Object operation(Environment ext, Operation op, Object ...args) { switch (op) { case ADD: return add(ext, args[0], args[1]); case SUBTRACT: return subtract(ext, args[0], args[1]); @@ -267,7 +266,7 @@ public class Values { } } - public static Object getMember(Extensions ctx, Object obj, Object key) { + public static Object getMember(Environment ctx, Object obj, Object key) { obj = normalize(ctx, obj); key = normalize(ctx, key); if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); @@ -287,12 +286,12 @@ public class Values { else if (key != null && "__proto__".equals(key)) return proto; else return proto.getMember(ctx, key, obj); } - public static Object getMemberPath(Extensions ctx, Object obj, Object ...path) { + public static Object getMemberPath(Environment ctx, Object obj, Object ...path) { var res = obj; for (var key : path) res = getMember(ctx, res, key); return res; } - public static boolean setMember(Extensions ctx, Object obj, Object key, Object val) { + public static boolean setMember(Environment ctx, Object obj, Object key, Object val) { obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); @@ -302,7 +301,7 @@ public class Values { var proto = getPrototype(ctx, obj); return proto.setMember(ctx, key, val, obj, true); } - public static boolean hasMember(Extensions ctx, Object obj, Object key, boolean own) { + public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) { if (obj == null || obj == NULL) return false; obj = normalize(ctx, obj); key = normalize(ctx, key); @@ -320,14 +319,14 @@ public class Values { var proto = getPrototype(ctx, obj); return proto != null && proto.hasMember(ctx, key, own); } - public static boolean deleteMember(Extensions ext, Object obj, Object key) { + public static boolean deleteMember(Environment ext, Object obj, Object key) { if (obj == null || obj == NULL) return false; obj = normalize(ext, obj); key = normalize(ext, key); if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key); else return false; } - public static ObjectValue getPrototype(Extensions ext, Object obj) { + public static ObjectValue getPrototype(Environment ext, Object obj) { if (obj == null || obj == NULL) return null; obj = normalize(ext, obj); if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext); @@ -340,16 +339,16 @@ public class Values { return null; } - public static boolean setPrototype(Extensions ext, Object obj, Object proto) { + public static boolean setPrototype(Environment ext, Object obj, Object proto) { obj = normalize(ext, obj); return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto); } - public static void makePrototypeChain(Extensions ext, Object... chain) { + public static void makePrototypeChain(Environment ext, Object... chain) { for(var i = 1; i < chain.length; i++) { setPrototype(ext, chain[i], chain[i - 1]); } } - public static List getMembers(Extensions ext, Object obj, boolean own, boolean includeNonEnumerable) { + public static List getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) { List res = new ArrayList<>(); if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable); @@ -369,7 +368,7 @@ public class Values { return res; } - public static ObjectValue getMemberDescriptor(Extensions ext, Object obj, Object key) { + public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) { if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key); else if (obj instanceof String && key instanceof Number) { var i = ((Number)key).intValue(); @@ -387,11 +386,11 @@ public class Values { else return null; } - public static Object call(Extensions ext, Object func, Object thisArg, Object ...args) { + public static Object call(Environment ext, Object func, Object thisArg, Object ...args) { if (!(func instanceof FunctionValue)) throw EngineException.ofType("Tried to call a non-function value."); return ((FunctionValue)func).call(ext, thisArg, args); } - public static Object callNew(Extensions ext, Object func, Object ...args) { + public static Object callNew(Environment ext, Object func, Object ...args) { var res = new ObjectValue(); try { var proto = Values.getMember(ext, func, "prototype"); @@ -407,7 +406,7 @@ public class Values { } } - public static boolean strictEquals(Extensions ext, Object a, Object b) { + public static boolean strictEquals(Environment ext, Object a, Object b) { a = normalize(ext, a); b = normalize(ext, b); @@ -418,7 +417,7 @@ public class Values { return a == b || a.equals(b); } - public static boolean looseEqual(Extensions ext, Object a, Object b) { + public static boolean looseEqual(Environment ext, Object a, Object b) { a = normalize(ext, a); b = normalize(ext, b); // In loose equality, null is equivalent to undefined @@ -442,7 +441,7 @@ public class Values { return toString(ext, a).equals(toString(ext, b)); } - public static Object normalize(Extensions ext, Object val) { + public static Object normalize(Environment ext, Object val) { if (val instanceof Number) return number(val); if (isPrimitive(val) || val instanceof ObjectValue) return val; if (val instanceof Character) return val + ""; @@ -476,7 +475,7 @@ public class Values { } @SuppressWarnings("unchecked") - public static T convert(Extensions ext, Object obj, Class clazz) { + public static T convert(Environment ext, Object obj, Class clazz) { if (clazz == Void.class) return null; if (obj instanceof NativeWrapper) { @@ -543,7 +542,7 @@ public class Values { throw new ConvertException(type(obj), clazz.getSimpleName()); } - public static Iterable fromJSIterator(Extensions ext, Object obj) { + public static Iterable fromJSIterator(Environment ext, Object obj) { return () -> { try { var symbol = Symbol.get("Symbol.iterator"); @@ -596,7 +595,7 @@ public class Values { }; } - public static ObjectValue toJSIterator(Extensions ext, Iterator it) { + public static ObjectValue toJSIterator(Environment ext, Iterator it) { var res = new ObjectValue(); try { @@ -609,7 +608,7 @@ public class Values { if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); else { var obj = new ObjectValue(); - obj.defineProperty(args.ctx, "value", it.next()); + obj.defineProperty(args.env, "value", it.next()); return obj; } })); @@ -617,11 +616,11 @@ public class Values { return res; } - public static ObjectValue toJSIterator(Extensions ext, Iterable it) { + public static ObjectValue toJSIterator(Environment ext, Iterable it) { return toJSIterator(ext, it.iterator()); } - public static ObjectValue toJSAsyncIterator(Extensions ext, Iterator it) { + public static ObjectValue toJSAsyncIterator(Environment ext, Iterator it) { var res = new ObjectValue(); try { @@ -631,11 +630,11 @@ public class Values { catch (IllegalArgumentException | NullPointerException e) { } res.defineProperty(ext, "next", new NativeFunction("", args -> { - return PromiseLib.await(args.ctx, () -> { + return PromiseLib.await(args.env, () -> { if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); else { var obj = new ObjectValue(); - obj.defineProperty(args.ctx, "value", it.next()); + obj.defineProperty(args.env, "value", it.next()); return obj; } }); @@ -654,7 +653,7 @@ public class Values { if (protoObj.values.size() + protoObj.properties.size() != 1) return false; return true; } - private static String toReadable(Extensions ext, Object val, HashSet passed, int tab) { + private static String toReadable(Environment ext, Object val, HashSet passed, int tab) { if (tab == 0 && val instanceof String) return (String)val; if (passed.contains(val)) return "[circular]"; @@ -727,7 +726,7 @@ public class Values { return res.toString(); } - public static String toReadable(Extensions ext, Object val) { + public static String toReadable(Environment ext, Object val) { return toReadable(ext, val, new HashSet<>(), 0); } public static String errorToReadable(RuntimeException err, String prefix) { @@ -735,10 +734,10 @@ public class Values { if (err instanceof EngineException) { var ee = ((EngineException)err); try { - return prefix + " " + ee.toString(ee.ext); + return prefix + " " + ee.toString(ee.env); } catch (EngineException ex) { - return prefix + " " + toReadable(ee.ext, ee.value); + return prefix + " " + toReadable(ee.env, ee.value); } } else if (err instanceof SyntaxException) { @@ -752,7 +751,7 @@ public class Values { return prefix + " internal error " + str.toString(); } } - public static void printValue(Extensions ext, Object val) { + public static void printValue(Environment ext, Object val) { System.out.print(toReadable(ext, val)); } public static void printError(RuntimeException err, String prefix) { diff --git a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java b/src/java/me/topchetoeu/jscript/utils/JSCompiler.java index 81e2c5a..78e66af 100644 --- a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java +++ b/src/java/me/topchetoeu/jscript/utils/JSCompiler.java @@ -5,11 +5,11 @@ import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; public class JSCompiler implements Compiler { - public final Extensions ext; + public final Environment ext; private void registerFunc(FunctionBody body, CompileResult res) { var map = res.map(); @@ -30,7 +30,7 @@ public class JSCompiler implements Compiler { return func; } - public JSCompiler(Extensions ext) { + public JSCompiler(Environment ext) { this.ext = ext; } } diff --git a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java index 5f55ed5..0fdaf73 100644 --- a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java +++ b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java @@ -10,11 +10,10 @@ import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Reading; import me.topchetoeu.jscript.lib.Internals; import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Engine; -import me.topchetoeu.jscript.runtime.Environment; import me.topchetoeu.jscript.runtime.EventLoop; import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; @@ -29,7 +28,6 @@ import me.topchetoeu.jscript.utils.filesystem.Mode; import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem; import me.topchetoeu.jscript.utils.filesystem.RootFilesystem; import me.topchetoeu.jscript.utils.filesystem.STDFilesystem; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; import me.topchetoeu.jscript.utils.modules.ModuleRepo; import me.topchetoeu.jscript.utils.permissions.PermissionsManager; import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; @@ -38,7 +36,7 @@ public class JScriptRepl { static Thread engineTask, debugTask; static Engine engine = new Engine(); static DebugServer debugServer = new DebugServer(); - static Environment environment = new Environment(); + static Environment environment = Environment.empty(); static int j = 0; static String[] args; @@ -97,8 +95,8 @@ public class JScriptRepl { glob.define(null, false, new NativeFunction("go", args -> { try { var f = Path.of("do.js"); - var func = args.ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); - return func.call(args.ctx); + var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); + return func.call(args.env); } catch (IOException e) { throw new EngineException("Couldn't open do.js"); @@ -106,7 +104,7 @@ public class JScriptRepl { })); glob.define(null, false, new NativeFunction("log", args -> { for (var el : args.args) { - Values.printValue(args.ctx, el); + Values.printValue(args.env, el); } return null; @@ -120,7 +118,7 @@ public class JScriptRepl { environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); environment.add(Filesystem.KEY, fs); environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); - environment.add(Compiler.KEY, new JSCompiler(new Context(environment))); + environment.add(Compiler.KEY, new JSCompiler(environment)); environment.add(EventLoop.KEY, engine); } private static void initEngine() { diff --git a/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java b/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java new file mode 100644 index 0000000..15fac42 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java @@ -0,0 +1,212 @@ +package me.topchetoeu.jscript.utils.debug; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.function.Supplier; + +import me.topchetoeu.jscript.common.json.JSON; +import me.topchetoeu.jscript.common.json.JSONMap; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.ArrayValue; +import me.topchetoeu.jscript.runtime.values.FunctionValue; +import me.topchetoeu.jscript.runtime.values.ObjectValue; +import me.topchetoeu.jscript.runtime.values.Symbol; +import me.topchetoeu.jscript.runtime.values.Values; + +class ObjectManager { + public static class ObjRef { + public final ObjectValue obj; + public final Environment ext; + public final HashSet heldGroups = new HashSet<>(); + public boolean held = true; + + public boolean shouldRelease() { + return !held && heldGroups.size() == 0; + } + + public ObjRef(Environment ext, ObjectValue obj) { + this.ext = ext; + this.obj = obj; + } + } + + private Supplier idSupplier; + private HashMap idToObject = new HashMap<>(); + private HashMap objectToId = new HashMap<>(); + private HashMap> objectGroups = new HashMap<>(); + + public JSONMap serialize(Environment env, Object val, boolean byValue) { + val = Values.normalize(null, val); + env = SimpleDebugger.sanitizeEnvironment(env); + + if (val == Values.NULL) { + return new JSONMap() + .set("type", "object") + .set("subtype", "null") + .setNull("value") + .set("description", "null"); + } + + if (val instanceof ObjectValue) { + var obj = (ObjectValue)val; + int id; + + if (objectToId.containsKey(obj)) id = objectToId.get(obj); + else { + id = idSupplier.get(); + var ref = new ObjRef(env, obj); + objectToId.put(obj, id); + idToObject.put(id, ref); + } + + var type = "object"; + String subtype = null; + String className = null; + + if (obj instanceof FunctionValue) type = "function"; + if (obj instanceof ArrayValue) subtype = "array"; + + try { className = Values.toString(env, Values.getMemberPath(env, obj, "constructor", "name")); } + catch (Exception e) { } + + var res = new JSONMap() + .set("type", type) + .set("objectId", id + ""); + + if (subtype != null) res.set("subtype", subtype); + if (className != null) { + res.set("className", className); + res.set("description", className); + } + + if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")"); + else if (obj instanceof FunctionValue) res.set("description", obj.toString()); + else { + var defaultToString = false; + + try { + defaultToString = + Values.getMember(env, obj, "toString") == + Values.getMember(env, env.get(Environment.OBJECT_PROTO), "toString"); + } + catch (Exception e) { } + + try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(env, obj) + " }")); } + catch (Exception e) { } + } + + + if (byValue) try { res.put("value", JSON.fromJs(env, obj)); } + catch (Exception e) { } + + return res; + } + + if (val == null) return new JSONMap().set("type", "undefined"); + if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val); + if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val); + if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString()); + if (val instanceof Number) { + var num = (double)(Number)val; + var res = new JSONMap().set("type", "number"); + + if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity"); + else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity"); + else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0"); + else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(0d)) res.set("unserializableValue", "0"); + else if (Double.isNaN(num)) res.set("unserializableValue", "NaN"); + else res.set("value", num); + + return res; + } + + throw new IllegalArgumentException("Unexpected JS object."); + } + public JSONMap serialize(Environment ext, Object val) { + return serialize(ext, val, false); + } + + public void addToGroup(String name, Object val) { + if (val instanceof ObjectValue) { + var obj = (ObjectValue)val; + var id = objectToId.getOrDefault(obj, -1); + if (id < 0) return; + + var ref = idToObject.get(id); + + if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref); + else objectGroups.put(name, new ArrayList<>(List.of(ref))); + + ref.heldGroups.add(name); + } + } + public void removeGroup(String name) { + var objs = objectGroups.remove(name); + + if (objs != null) { + for (var obj : objs) { + if (obj.heldGroups.remove(name) && obj.shouldRelease()) { + var id = objectToId.remove(obj.obj); + if (id != null) idToObject.remove(id); + } + } + } + } + + public ObjRef get(int id) { + return idToObject.get(id); + } + public void release(int id) { + var ref = idToObject.get(id); + ref.held = false; + + if (ref.shouldRelease()) { + objectToId.remove(ref.obj); + idToObject.remove(id); + } + } + + public Object deserializeArgument(JSONMap val) { + if (val.isString("objectId")) return get(Integer.parseInt(val.string("objectId"))).obj; + else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) { + case "NaN": return Double.NaN; + case "-Infinity": return Double.NEGATIVE_INFINITY; + case "Infinity": return Double.POSITIVE_INFINITY; + case "-0": return -0.; + } + + var res = val.get("value"); + + if (res == null) return null; + else return JSON.toJs(res); + } + + public JSONMap serializeException(Environment ext, EngineException err) { + String text = null; + + try { + text = Values.toString(ext, err.value); + } + catch (EngineException e) { + text = "[error while stringifying]"; + } + + return new JSONMap() + .set("exceptionId", idSupplier.get()) + .set("exception", serialize(ext, err.value)) + .set("text", text); + } + + public void clear() { + this.idToObject.clear(); + this.objectToId.clear(); + this.objectGroups.clear(); + } + + public ObjectManager(Supplier idSupplier) { + this.idSupplier = idSupplier; + } +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java index 9b89386..d248456 100644 --- a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java +++ b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java @@ -23,20 +23,17 @@ import me.topchetoeu.jscript.common.json.JSONList; import me.topchetoeu.jscript.common.json.JSONMap; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Engine; -import me.topchetoeu.jscript.runtime.Environment; import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.Extensions; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.values.ArrayValue; import me.topchetoeu.jscript.runtime.values.FunctionValue; import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; import me.topchetoeu.jscript.runtime.values.Values; // very simple indeed @@ -150,10 +147,10 @@ public class SimpleDebugger implements Debugger { this.frame = frame; this.id = id; - this.global = GlobalScope.get(frame.ctx).obj; + this.global = GlobalScope.get(frame.env).obj; this.local = frame.getLocalScope(); this.capture = frame.getCaptureScope(); - Values.makePrototypeChain(frame.ctx, global, capture, local); + Values.makePrototypeChain(frame.env, global, capture, local); this.valstack = frame.getValStackScope(); this.serialized = new JSONMap() @@ -163,48 +160,32 @@ public class SimpleDebugger implements Debugger { .add(new JSONMap() .set("type", "local") .set("name", "Local Scope") - .set("object", serializeObj(frame.ctx, local)) + .set("object", objects.serialize(frame.env, local)) ) .add(new JSONMap() .set("type", "closure") .set("name", "Closure") - .set("object", serializeObj(frame.ctx, capture)) + .set("object", objects.serialize(frame.env, capture)) ) .add(new JSONMap() .set("type", "global") .set("name", "Global Scope") - .set("object", serializeObj(frame.ctx.extensions, global)) + .set("object", objects.serialize(frame.env, global)) ) .add(new JSONMap() .set("type", "other") .set("name", "Value Stack") - .set("object", serializeObj(frame.ctx.extensions, valstack)) + .set("object", objects.serialize(frame.env, valstack)) ) ); } } - private class ObjRef { - public final ObjectValue obj; - public final Extensions ext; - public final HashSet heldGroups = new HashSet<>(); - public boolean held = true; - - public boolean shouldRelease() { - return !held && heldGroups.size() == 0; - } - - public ObjRef(Extensions ext, ObjectValue obj) { - this.ext = ext; - this.obj = obj; - } - } - private static class RunResult { - public final Extensions ext; + public final Environment ext; public final Object result; public final EngineException error; - public RunResult(Extensions ext, Object result, EngineException error) { + public RunResult(Environment ext, Object result, EngineException error) { this.ext = ext; this.result = result; this.error = error; @@ -232,15 +213,17 @@ public class SimpleDebugger implements Debugger { private HashMap idToFrame = new HashMap<>(); private HashMap codeFrameToFrame = new HashMap<>(); - private HashMap idToObject = new HashMap<>(); - private HashMap objectToId = new HashMap<>(); - private HashMap> objectGroups = new HashMap<>(); + private ObjectManager objects = new ObjectManager(this::nextId); + // private HashMap idToObject = new HashMap<>(); + // private HashMap objectToId = new HashMap<>(); + // private HashMap> objectGroups = new HashMap<>(); private Notifier updateNotifier = new Notifier(); private boolean pendingPause = false; private int nextId = 0; - private DebugFrame stepOutFrame = null, currFrame = null; + private DebugFrame stepOutFrame = null; + private List frames = new ArrayList<>(); private int stepOutPtr = 0; private boolean compare(String src, String target) { @@ -288,18 +271,14 @@ public class SimpleDebugger implements Debugger { } else return codeFrameToFrame.get(frame); } - private synchronized void updateFrames(Context ctx) { - var frame = ctx.frame; - if (frame == null) return; - currFrame = getFrame(frame); - } - private JSONList serializeFrames(Context ctx) { + private JSONList serializeFrames(Environment env) { var res = new JSONList(); - for (var el : ctx.frames()) { + for (var el : DebugContext.get(env).getStackFrames()) { var frame = getFrame(el); if (frame.location == null) continue; + frame.serialized.set("location", serializeLocation(frame.location)); if (frame.location != null) res.add(frame.serialized); } @@ -339,151 +318,6 @@ public class SimpleDebugger implements Debugger { .set("columnNumber", loc.start() - 1); } - private JSONMap serializeObj(Extensions env, Object val, boolean byValue) { - val = Values.normalize(null, val); - env = sanitizeEnvironment(env); - var ctx = Context.of(env); - - if (val == Values.NULL) { - return new JSONMap() - .set("type", "object") - .set("subtype", "null") - .setNull("value") - .set("description", "null"); - } - - if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - int id; - - if (objectToId.containsKey(obj)) id = objectToId.get(obj); - else { - id = nextId(); - var ref = new ObjRef(env, obj); - objectToId.put(obj, id); - idToObject.put(id, ref); - } - - var type = "object"; - String subtype = null; - String className = null; - - if (obj instanceof FunctionValue) type = "function"; - if (obj instanceof ArrayValue) subtype = "array"; - - try { className = Values.toString(ctx, Values.getMemberPath(ctx, obj, "constructor", "name")); } - catch (Exception e) { } - - var res = new JSONMap() - .set("type", type) - .set("objectId", id + ""); - - if (subtype != null) res.set("subtype", subtype); - if (className != null) { - res.set("className", className); - res.set("description", className); - } - - if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")"); - else if (obj instanceof FunctionValue) res.set("description", obj.toString()); - else { - var defaultToString = false; - - try { - defaultToString = - Values.getMember(ctx, obj, "toString") == - Values.getMember(ctx, env.get(Environment.OBJECT_PROTO), "toString"); - } - catch (Exception e) { } - - try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(ctx, obj) + " }")); } - catch (Exception e) { } - } - - - if (byValue) try { res.put("value", JSON.fromJs(env, obj)); } - catch (Exception e) { } - - return res; - } - - if (val == null) return new JSONMap().set("type", "undefined"); - if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val); - if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val); - if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString()); - if (val instanceof Number) { - var num = (double)(Number)val; - var res = new JSONMap().set("type", "number"); - - if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity"); - else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity"); - else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0"); - else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(0d)) res.set("unserializableValue", "0"); - else if (Double.isNaN(num)) res.set("unserializableValue", "NaN"); - else res.set("value", num); - - return res; - } - - throw new IllegalArgumentException("Unexpected JS object."); - } - private JSONMap serializeObj(Extensions ext, Object val) { - return serializeObj(ext, val, false); - } - private void addObjectGroup(String name, Object val) { - if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - var id = objectToId.getOrDefault(obj, -1); - if (id < 0) return; - - var ref = idToObject.get(id); - - if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref); - else objectGroups.put(name, new ArrayList<>(List.of(ref))); - - ref.heldGroups.add(name); - } - } - private void releaseGroup(String name) { - var objs = objectGroups.remove(name); - - if (objs != null) for (var obj : objs) { - if (obj.heldGroups.remove(name) && obj.shouldRelease()) { - var id = objectToId.remove(obj.obj); - if (id != null) idToObject.remove(id); - } - } - } - private Object deserializeArgument(JSONMap val) { - if (val.isString("objectId")) return idToObject.get(Integer.parseInt(val.string("objectId"))).obj; - else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) { - case "NaN": return Double.NaN; - case "-Infinity": return Double.NEGATIVE_INFINITY; - case "Infinity": return Double.POSITIVE_INFINITY; - case "-0": return -0.; - } - var res = val.get("value"); - if (res == null) return null; - else return JSON.toJs(res); - } - - private JSONMap serializeException(Extensions ext, EngineException err) { - String text = null; - - try { - text = Values.toString(Context.of(ext), err.value); - } - catch (EngineException e) { - text = "[error while stringifying]"; - } - - var res = new JSONMap() - .set("exceptionId", nextId()) - .set("exception", serializeObj(ext, err.value)) - .set("text", text); - - return res; - } private void resume(State state) { try { @@ -496,11 +330,11 @@ public class SimpleDebugger implements Debugger { close(); } } - private void pauseDebug(Context ctx, Breakpoint bp) { + private void pauseDebug(Environment env, Breakpoint bp) { try { state = State.PAUSED_NORMAL; var map = new JSONMap() - .set("callFrames", serializeFrames(ctx)) + .set("callFrames", serializeFrames(env)) .set("reason", "debugCommand"); if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + "")); @@ -511,11 +345,11 @@ public class SimpleDebugger implements Debugger { close(); } } - private void pauseException(Context ctx) { + private void pauseException(Environment env) { try { state = State.PAUSED_EXCEPTION; var map = new JSONMap() - .set("callFrames", serializeFrames(ctx)) + .set("callFrames", serializeFrames(env)) .set("reason", "exception"); ws.send(new V8Event("Debugger.paused", map)); @@ -540,26 +374,19 @@ public class SimpleDebugger implements Debugger { } } - private Extensions sanitizeEnvironment(Extensions ext) { - var res = ext.child(); - - res.remove(EventLoop.KEY); - res.remove(DebugContext.KEY); - res.add(DebugContext.IGNORE); - - return res; + static Environment sanitizeEnvironment(Environment env) { + return env.child().remove(EventLoop.KEY).remove(DebugContext.KEY).add(DebugContext.IGNORE); } private RunResult run(DebugFrame codeFrame, String code) { if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); var engine = new Engine(); - var env = codeFrame.frame.ctx.extensions.copy(); - env.remove(DebugContext.KEY); - env.remove(EventLoop.KEY); - env.remove(GlobalScope.KEY); - env.add(EventLoop.KEY, engine); - env.add(GlobalScope.KEY, new GlobalScope(codeFrame.local)); + var env = codeFrame.frame.env.child() + .remove(DebugContext.KEY) + .add(DebugContext.IGNORE) + .add(EventLoop.KEY, engine) + .add(GlobalScope.KEY, new GlobalScope(codeFrame.local)); var awaiter = engine.pushMsg(false, env, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); @@ -571,20 +398,19 @@ public class SimpleDebugger implements Debugger { catch (SyntaxException e) { return new RunResult(env, null, EngineException.ofSyntax(e.toString())); } } - private ObjectValue vscodeAutoSuggest(Extensions ext, Object target, String query, boolean variable) { + private ObjectValue vscodeAutoSuggest(Environment env, Object target, String query, boolean variable) { var res = new ArrayValue(); var passed = new HashSet(); var tildas = "~"; - var ctx = Context.of(ext); - if (target == null) target = GlobalScope.get(ext); + if (target == null) target = GlobalScope.get(env); - for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) { - for (var el : Values.getMembers(ctx, proto, true, true)) { - var strKey = Values.toString(ctx, el); + for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(env, proto)) { + for (var el : Values.getMembers(env, proto, true, true)) { + var strKey = Values.toString(env, el); if (passed.contains(strKey)) continue; passed.add(strKey); - var val = Values.getMember(ctx, Values.getMemberDescriptor(ctx, proto, el), "value"); + var val = Values.getMember(env, Values.getMemberDescriptor(env, proto, el), "value"); var desc = new ObjectValue(); var sortText = ""; if (strKey.startsWith(query)) sortText += "0@"; @@ -594,27 +420,27 @@ public class SimpleDebugger implements Debugger { else sortText += "4@"; sortText += tildas + strKey; - desc.defineProperty(ctx, "label", strKey); - desc.defineProperty(ctx, "sortText", sortText); + desc.defineProperty(env, "label", strKey); + desc.defineProperty(env, "sortText", sortText); if (val instanceof FunctionValue) { - if (strKey.equals("constructor")) desc.defineProperty(ctx, "type", "name"); - else desc.defineProperty(ctx, "type", variable ? "function" : "method"); + if (strKey.equals("constructor")) desc.defineProperty(env, "type", "name"); + else desc.defineProperty(env, "type", variable ? "function" : "method"); } - else desc.defineProperty(ctx, "type", variable ? "variable" : "property"); + else desc.defineProperty(env, "type", variable ? "variable" : "property"); switch (Values.type(val)) { case "number": case "boolean": - desc.defineProperty(ctx, "detail", Values.toString(ctx, val)); + desc.defineProperty(env, "detail", Values.toString(env, val)); break; case "object": - if (val == Values.NULL) desc.defineProperty(ctx, "detail", "null"); + if (val == Values.NULL) desc.defineProperty(env, "detail", "null"); else try { - desc.defineProperty(ctx, "detail", Values.getMemberPath(ctx, target, "constructor", "name")); + desc.defineProperty(env, "detail", Values.getMemberPath(env, target, "constructor", "name")); } catch (IllegalArgumentException e) { - desc.defineProperty(ctx, "detail", "object"); + desc.defineProperty(env, "detail", "object"); } break; case "function": { @@ -624,15 +450,15 @@ public class SimpleDebugger implements Debugger { type += "?"; } type += ")"; - desc.defineProperty(ctx, "detail", type); + desc.defineProperty(env, "detail", type); break; } default: - desc.defineProperty(ctx, "type", Values.type(val)); + desc.defineProperty(env, "type", Values.type(val)); break; } - res.set(ctx, res.size(), desc); + res.set(env, res.size(), desc); } tildas += "~"; @@ -640,8 +466,8 @@ public class SimpleDebugger implements Debugger { } var resObj = new ObjectValue(); - resObj.defineProperty(ctx, "result", res); - resObj.defineProperty(ctx, "isArray", target instanceof ArrayValue); + resObj.defineProperty(env, "result", res); + resObj.defineProperty(env, "isArray", target instanceof ArrayValue); return resObj; } @@ -679,13 +505,12 @@ public class SimpleDebugger implements Debugger { idToFrame.clear(); codeFrameToFrame.clear(); - idToObject.clear(); - objectToId.clear(); - objectGroups.clear(); + objects.clear(); pendingPause = false; - stepOutFrame = currFrame = null; + frames.clear(); + stepOutFrame = null; stepOutPtr = 0; for (var ctx : contexts.keySet()) ctx.detachDebugger(this); @@ -744,7 +569,6 @@ public class SimpleDebugger implements Debugger { var bpt = new Breakpoint(nextId(), regex, line, col, cond); idToBreakpoint.put(bpt.id, bpt); - for (var el : mappings.entrySet()) { bpt.addFunc(el.getKey(), el.getValue()); } @@ -794,8 +618,8 @@ public class SimpleDebugger implements Debugger { @Override public synchronized void stepInto(V8Message msg) throws IOException { if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); else { - stepOutFrame = currFrame; - stepOutPtr = currFrame.frame.codePtr; + stepOutFrame = frames.get(frames.size() - 1); + stepOutPtr = stepOutFrame.frame.codePtr; resume(State.STEPPING_IN); ws.send(msg.respond()); } @@ -803,8 +627,8 @@ public class SimpleDebugger implements Debugger { @Override public synchronized void stepOut(V8Message msg) throws IOException { if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); else { - stepOutFrame = currFrame; - stepOutPtr = currFrame.frame.codePtr; + stepOutFrame = frames.get(frames.size() - 1); + stepOutPtr = stepOutFrame.frame.codePtr; resume(State.STEPPING_OUT); ws.send(msg.respond()); } @@ -812,8 +636,8 @@ public class SimpleDebugger implements Debugger { @Override public synchronized void stepOver(V8Message msg) throws IOException { if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); else { - stepOutFrame = currFrame; - stepOutPtr = currFrame.frame.codePtr; + stepOutFrame = frames.get(frames.size() - 1); + stepOutPtr = stepOutFrame.frame.codePtr; resume(State.STEPPING_OVER); ws.send(msg.respond()); } @@ -827,34 +651,26 @@ public class SimpleDebugger implements Debugger { var cf = idToFrame.get(cfId); var res = run(cf, expr); - if (group != null) addObjectGroup(group, res.result); + if (group != null) objects.addToGroup(group, res.result); - if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(res.ext, res.error)))); - else ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ext, res.result)))); + if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(res.ext, res.error)))); + else ws.send(msg.respond(new JSONMap().set("result", objects.serialize(res.ext, res.result)))); } @Override public synchronized void releaseObjectGroup(V8Message msg) throws IOException { var group = msg.params.string("objectGroup"); - releaseGroup(group); + objects.removeGroup(group); ws.send(msg.respond()); } @Override public synchronized void releaseObject(V8Message msg) throws IOException { var id = Integer.parseInt(msg.params.string("objectId")); - var ref = idToObject.get(id); - ref.held = false; - - if (ref.shouldRelease()) { - objectToId.remove(ref.obj); - idToObject.remove(id); - } - + objects.release(id); ws.send(msg.respond()); } @Override public synchronized void getProperties(V8Message msg) throws IOException { - var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); + var ref = objects.get(Integer.parseInt(msg.params.string("objectId"))); var obj = ref.obj; - var ext = ref.ext; - var ctx = Context.of(ext); + var env = ref.ext; var res = new JSONList(); var own = true; @@ -866,17 +682,17 @@ public class SimpleDebugger implements Debugger { if (obj.properties.containsKey(key)) { var prop = obj.properties.get(key); - propDesc.set("name", Values.toString(ctx, key)); - if (prop.getter != null) propDesc.set("get", serializeObj(ext, prop.getter)); - if (prop.setter != null) propDesc.set("set", serializeObj(ext, prop.setter)); + propDesc.set("name", Values.toString(env, key)); + if (prop.getter != null) propDesc.set("get", objects.serialize(env, prop.getter)); + if (prop.setter != null) propDesc.set("set", objects.serialize(env, prop.setter)); propDesc.set("enumerable", obj.memberEnumerable(key)); propDesc.set("configurable", obj.memberConfigurable(key)); propDesc.set("isOwn", true); res.add(propDesc); } else { - propDesc.set("name", Values.toString(ctx, key)); - propDesc.set("value", serializeObj(ext, Values.getMember(ctx, obj, key))); + propDesc.set("name", Values.toString(env, key)); + propDesc.set("value", objects.serialize(env, Values.getMember(env, obj, key))); propDesc.set("writable", obj.memberWritable(key)); propDesc.set("enumerable", obj.memberEnumerable(key)); propDesc.set("configurable", obj.memberConfigurable(key)); @@ -885,12 +701,12 @@ public class SimpleDebugger implements Debugger { } } - var proto = Values.getPrototype(ctx, obj); + var proto = Values.getPrototype(env, obj); if (own) { var protoDesc = new JSONMap(); protoDesc.set("name", "__proto__"); - protoDesc.set("value", serializeObj(ext, proto == null ? Values.NULL : proto)); + protoDesc.set("value", objects.serialize(env, proto == null ? Values.NULL : proto)); protoDesc.set("writable", true); protoDesc.set("enumerable", false); protoDesc.set("configurable", false); @@ -911,14 +727,13 @@ public class SimpleDebugger implements Debugger { .list("arguments", new JSONList()) .stream() .map(v -> v.map()) - .map(this::deserializeArgument) + .map(objects::deserializeArgument) .collect(Collectors.toList()); var byValue = msg.params.bool("returnByValue", false); - var thisArgRef = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); + var thisArgRef = objects.get(Integer.parseInt(msg.params.string("objectId"))); var thisArg = thisArgRef.obj; - var ext = thisArgRef.ext; - var ctx = Context.of(ext); + var env = thisArgRef.ext; while (true) { var start = src.lastIndexOf("//# sourceURL="); @@ -934,27 +749,27 @@ public class SimpleDebugger implements Debugger { else if (compare(src, VSCODE_SELF)) res = thisArg; else if (compare(src, CHROME_GET_PROP_FUNC)) { res = thisArg; - for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(ctx, res, JSON.toJs(el)); + for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(env, res, JSON.toJs(el)); } else if (compare(src, CHROME_GET_PROP_FUNC_2)) { - res = Values.call(ctx, args.get(0), thisArg); + res = Values.call(env, args.get(0), thisArg); } else if (compare(src, VSCODE_CALL)) { var func = (FunctionValue)(args.size() < 1 ? null : args.get(0)); - ws.send(msg.respond(new JSONMap().set("result", serializeObj(ext, func.call(ctx, thisArg))))); + ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, func.call(env, thisArg))))); } else if (compare(src, VSCODE_AUTOCOMPLETE)) { var target = args.get(0); if (target == null) target = thisArg; - res = vscodeAutoSuggest(ext, target, Values.toString(ctx, args.get(1)), Values.toBoolean(args.get(2))); + res = vscodeAutoSuggest(env, target, Values.toString(env, args.get(1)), Values.toBoolean(args.get(2))); } else { ws.send(new V8Error("Please use well-known functions with callFunctionOn")); return; } - ws.send(msg.respond(new JSONMap().set("result", serializeObj(ext, res, byValue)))); + ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, res, byValue)))); } - catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ext, e)))); } + catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(env, e)))); } } @Override public synchronized void runtimeEnable(V8Message msg) throws IOException { @@ -977,7 +792,7 @@ public class SimpleDebugger implements Debugger { } mappings.put(body, map); } - @Override public boolean onInstruction(Context ctx, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) { + @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) { if (!enabled) return false; boolean isBreakpointable; @@ -988,7 +803,7 @@ public class SimpleDebugger implements Debugger { synchronized (this) { frame = getFrame(cf); - var map = DebugContext.get(ctx).getMap(frame.frame.function); + var map = DebugContext.get(env).getMap(frame.frame.function); frame.updateLoc(map.toLocation(frame.frame.codePtr)); loc = frame.location; @@ -996,27 +811,27 @@ public class SimpleDebugger implements Debugger { isBreakpointable = loc != null && (bptType.shouldStepIn()); if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { - pauseException(ctx); + pauseException(env); } else if ( loc != null && (state == State.STEPPING_IN || state == State.STEPPING_OVER) && returnVal != Values.NO_RETURN && stepOutFrame == frame ) { - pauseDebug(ctx, null); + pauseDebug(env, null); } else if (isBreakpointable && bpLocs.containsKey(loc)) { for (var bp : bpLocs.get(loc)) { - var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result); - if (ok) pauseDebug(ctx, bp); + var ok = bp.condition == null ? true : Values.toBoolean(run(frames.get(frames.size() - 1), bp.condition).result); + if (ok) pauseDebug(env, bp); } } // else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null); else if (isBreakpointable && pendingPause) { - pauseDebug(ctx, null); + pauseDebug(env, null); pendingPause = false; } - else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(ctx, null); + else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(env, null); } @@ -1039,11 +854,11 @@ public class SimpleDebugger implements Debugger { else if (stepOutPtr != frame.frame.codePtr) { if (state == State.STEPPING_IN && bptType.shouldStepIn()) { - pauseDebug(ctx, null); + pauseDebug(env, null); break; } else if (state == State.STEPPING_OVER && bptType.shouldStepOver()) { - pauseDebug(ctx, null); + pauseDebug(env, null); break; } } @@ -1056,28 +871,32 @@ public class SimpleDebugger implements Debugger { return false; } - @Override public void onFramePush(Context ctx, Frame frame) { - var prevFrame = currFrame; - updateFrames(ctx); + @Override public void onFramePush(Environment env, Frame frame) { + var prevFrame = frames.get(frames.size() - 1); + var newFrame = getFrame(frame); + frames.add(newFrame); if (stepOutFrame != null && stepOutFrame.frame == prevFrame.frame && state == State.STEPPING_IN) { - stepOutFrame = currFrame; + stepOutFrame = newFrame; } } - @Override public void onFramePop(Context ctx, Frame frame) { - updateFrames(ctx); + @Override public void onFramePop(Environment env, Frame frame) { + frames.remove(frames.size() - 1); try { idToFrame.remove(codeFrameToFrame.remove(frame).id); } catch (NullPointerException e) { } - if (ctx.stackSize == 0) { + if (frames.size() == 0) { if (state == State.PAUSED_EXCEPTION || state == State.PAUSED_NORMAL) resume(State.RESUMED); } else if (stepOutFrame != null && stepOutFrame.frame == frame && state == State.STEPPING_OUT) { state = State.STEPPING_IN; - stepOutFrame = currFrame; + stepOutFrame = frames.get(frames.size() - 1); } } + @Override public List getStackFrame() { + return frames.stream().map(v -> v.frame).collect(Collectors.toList()); + } public SimpleDebugger attach(DebugContext ctx) { ctx.attachDebugger(this); diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java index 6f477cb..283d633 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.utils.filesystem; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; public interface Filesystem { public static final Key KEY = new Key<>(); @@ -12,7 +12,7 @@ public interface Filesystem { FileStat stat(String path); void close(); - public static Filesystem get(Extensions exts) { + public static Filesystem get(Environment exts) { return exts.get(KEY); } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java b/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java index 4e8c2ff..2ced1aa 100644 --- a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java +++ b/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java @@ -2,32 +2,14 @@ package me.topchetoeu.jscript.utils.interop; import java.lang.reflect.Array; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.NativeWrapper; import me.topchetoeu.jscript.runtime.values.Values; -public class Arguments implements Extensions { +public class Arguments { public final Object self; public final Object[] args; - public final Context ctx; - - @Override public void add(Key key, T obj) { - ctx.add(key, obj); - } - @Override public T get(Key key) { - return ctx.get(key); - } - @Override public boolean has(Key key) { - return ctx.has(key); - } - @Override public boolean remove(Key key) { - return ctx.remove(key); - } - @Override public Iterable> keys() { - return ctx.keys(); - } + public final Environment env; public int n() { return args.length; @@ -41,7 +23,7 @@ public class Arguments implements Extensions { return convert(-1, type); } public T convert(int i, Class type) { - return Values.convert(ctx, get(i), type); + return Values.convert(env, get(i), type); } public Object get(int i, boolean unwrap) { Object res = null; @@ -63,7 +45,7 @@ public class Arguments implements Extensions { public Arguments slice(int start) { var res = new Object[Math.max(0, args.length - start)]; for (int j = start; j < args.length; j++) res[j - start] = get(j); - return new Arguments(ctx, args, res); + return new Arguments(env, args, res); } @SuppressWarnings("unchecked") @@ -113,26 +95,26 @@ public class Arguments implements Extensions { return res; } - public String getString(int i) { return Values.toString(ctx, get(i)); } + public String getString(int i) { return Values.toString(env, get(i)); } public boolean getBoolean(int i) { return Values.toBoolean(get(i)); } - public int getInt(int i) { return (int)Values.toNumber(ctx, get(i)); } - public long getLong(int i) { return (long)Values.toNumber(ctx, get(i)); } - public double getDouble(int i) { return Values.toNumber(ctx, get(i)); } - public float getFloat(int i) { return (float)Values.toNumber(ctx, get(i)); } + public int getInt(int i) { return (int)Values.toNumber(env, get(i)); } + public long getLong(int i) { return (long)Values.toNumber(env, get(i)); } + public double getDouble(int i) { return Values.toNumber(env, get(i)); } + public float getFloat(int i) { return (float)Values.toNumber(env, get(i)); } public int getInt(int i, int def) { var res = get(i); if (res == null) return def; - else return (int)Values.toNumber(ctx, res); + else return (int)Values.toNumber(env, res); } public String getString(int i, String def) { var res = get(i); if (res == null) return def; - else return Values.toString(ctx, res); + else return Values.toString(env, res); } - public Arguments(Context ctx, Object thisArg, Object... args) { - this.ctx = ctx; + public Arguments(Environment env, Object thisArg, Object... args) { + this.env = env; this.args = args; this.self = thisArg; } diff --git a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java b/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java index 3f7573a..1022ce4 100644 --- a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java +++ b/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java @@ -12,10 +12,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Copyable; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.values.FunctionValue; @@ -24,7 +22,7 @@ import me.topchetoeu.jscript.runtime.values.ObjectValue; import me.topchetoeu.jscript.runtime.values.Symbol; import me.topchetoeu.jscript.runtime.values.Values; -public class NativeWrapperProvider implements Copyable { +public class NativeWrapperProvider { public static final Key KEY = new Key<>(); private final HashMap, FunctionValue> constructors = new HashMap<>(); @@ -35,7 +33,7 @@ public class NativeWrapperProvider implements Copyable { private final HashMap, Class> interfaceToProxy = new HashMap<>(); private final HashSet> ignore = new HashSet<>(); - private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) { + private static Object call(Environment ctx, String name, Method method, Object thisArg, Object... args) { try { var realArgs = new Object[method.getParameterCount()]; System.arraycopy(args, 0, realArgs, 0, realArgs.length); @@ -60,7 +58,7 @@ public class NativeWrapperProvider implements Copyable { } } private static FunctionValue create(String name, Method method) { - return new NativeFunction(name, args -> call(args.ctx, name, method, args.self, args)); + return new NativeFunction(name, args -> call(args.env, name, method, args.self, args)); } private static void checkSignature(Method method, boolean forceStatic, Class ...params) { if (forceStatic && !Modifier.isStatic(method.getModifiers())) throw new IllegalArgumentException(String.format( @@ -335,8 +333,8 @@ public class NativeWrapperProvider implements Copyable { var parentConstr = getConstr(parent); if (parentProto != null && parentConstr != null) { - Values.setPrototype(Extensions.EMPTY, proto, parentProto); - Values.setPrototype(Extensions.EMPTY, constr, parentConstr); + Values.setPrototype(Environment.empty(), proto, parentProto); + Values.setPrototype(Environment.empty(), constr, parentConstr); return; } @@ -450,7 +448,7 @@ public class NativeWrapperProvider implements Copyable { public NativeWrapperProvider() { } - public static NativeWrapperProvider get(Extensions ext) { + public static NativeWrapperProvider get(Environment ext) { return ext.get(KEY); } } diff --git a/src/java/me/topchetoeu/jscript/utils/modules/Module.java b/src/java/me/topchetoeu/jscript/utils/modules/Module.java index ad84d6c..63b1164 100644 --- a/src/java/me/topchetoeu/jscript/utils/modules/Module.java +++ b/src/java/me/topchetoeu/jscript/utils/modules/Module.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.utils.modules; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; public abstract class Module { private Object value; @@ -9,9 +9,9 @@ public abstract class Module { public Object value() { return value; } public boolean loaded() { return loaded; } - protected abstract Object onLoad(Context ctx); + protected abstract Object onLoad(Environment ctx); - public void load(Context ctx) { + public void load(Environment ctx) { if (loaded) return; this.value = onLoad(ctx); this.loaded = true; diff --git a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java index 1138485..99cffdc 100644 --- a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java +++ b/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java @@ -3,9 +3,8 @@ package me.topchetoeu.jscript.utils.modules; import java.util.HashMap; import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.utils.filesystem.Filesystem; import me.topchetoeu.jscript.utils.filesystem.Mode; @@ -14,34 +13,34 @@ public interface ModuleRepo { public static final Key KEY = new Key<>(); public static final Key CWD = new Key<>(); - public Module getModule(Context ctx, String cwd, String name); + public Module getModule(Environment ctx, String cwd, String name); public static ModuleRepo ofFilesystem(Filesystem fs) { var modules = new HashMap(); - return (ctx, cwd, name) -> { + return (env, cwd, name) -> { name = fs.normalize(cwd, name); var filename = Filename.parse(name); var src = fs.open(name, Mode.READ).readToString(); if (modules.containsKey(name)) return modules.get(name); - var env = Context.clean(ctx.extensions).child(); - env.add(CWD, fs.normalize(name, "..")); - var glob = env.get(GlobalScope.KEY); - env.add(GlobalScope.KEY, glob.child()); + var moduleEnv = env.child() + .add(CWD, fs.normalize(name, "..")) + .add(GlobalScope.KEY, env.hasNotNull(GlobalScope.KEY) ? env.get(GlobalScope.KEY).child() : new GlobalScope()); - var mod = new SourceModule(filename, src, env); + var mod = new SourceModule(filename, src, moduleEnv); modules.put(name, mod); return mod; }; } - public static String cwd(Extensions exts) { - return exts.init(CWD, "/"); + public static String cwd(Environment exts) { + exts.init(CWD, "/"); + return "/"; } - public static ModuleRepo get(Extensions exts) { + public static ModuleRepo get(Environment exts) { return exts.get(KEY); } } diff --git a/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java index 6e9ca40..2644941 100644 --- a/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java +++ b/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java @@ -2,14 +2,14 @@ package me.topchetoeu.jscript.utils.modules; import java.util.HashMap; -import me.topchetoeu.jscript.runtime.Context; +import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; public class RootModuleRepo implements ModuleRepo { public final HashMap repos = new HashMap<>(); @Override - public Module getModule(Context ctx, String cwd, String name) { + public Module getModule(Environment env, String cwd, String name) { var i = name.indexOf(":"); String repoName, modName; @@ -25,6 +25,6 @@ public class RootModuleRepo implements ModuleRepo { var repo = repos.get(repoName); if (repo == null) throw EngineException.ofError("ModuleError", "Couldn't find module repo '" + repoName + "'."); - return repo.getModule(ctx, cwd, modName); + return repo.getModule(env, cwd, modName); } } diff --git a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java b/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java index 4efd72a..36aeb0d 100644 --- a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java +++ b/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java @@ -1,21 +1,20 @@ package me.topchetoeu.jscript.utils.modules; import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.Context; -import me.topchetoeu.jscript.runtime.Extensions; +import me.topchetoeu.jscript.runtime.Compiler; +import me.topchetoeu.jscript.runtime.environment.Environment; public class SourceModule extends Module { public final Filename filename; public final String source; - public final Extensions ext; + public final Environment ext; @Override - protected Object onLoad(Context ctx) { - var res = new Context(ext).compile(filename, source); - return res.call(ctx); + protected Object onLoad(Environment env) { + return Compiler.compile(env, filename, source).call(env); } - public SourceModule(Filename filename, String source, Extensions ext) { + public SourceModule(Filename filename, String source, Environment ext) { this.filename = filename; this.source = source; this.ext = ext; diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java index 53112b3..19f474b 100644 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java +++ b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.utils.permissions; -import me.topchetoeu.jscript.runtime.Extensions; -import me.topchetoeu.jscript.runtime.Key; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; public interface PermissionsProvider { public static final Key KEY = new Key<>(); @@ -20,7 +20,7 @@ public interface PermissionsProvider { return hasPermission(new Permission(perm, matcher)); } - public static PermissionsProvider get(Extensions exts) { + public static PermissionsProvider get(Environment exts) { return (perm, value) -> { if (exts.hasNotNull(KEY)) return exts.get(KEY).hasPermission(perm); else return true; -- 2.45.2 From bab59d454f9e3f465026f8e0bc10ab346281598e Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 25 Aug 2024 19:10:05 +0300 Subject: [PATCH 02/48] refactor: Transition to a Value class --- gradle.properties | 2 +- .../jscript/{runtime => common}/Compiler.java | 6 +- .../parsing => common}/ParseRes.java | 5 +- .../topchetoeu/jscript/common/json/JSON.java | 65 +- .../jscript/common/mapping/FunctionMap.java | 33 +- .../jscript/compilation/parsing/Parsing.java | 3 +- .../jscript/compilation/parsing/TestRes.java | 3 +- .../me/topchetoeu/jscript/lib/ArrayLib.java | 456 --------- .../jscript/lib/AsyncFunctionLib.java | 88 -- .../lib/AsyncGeneratorFunctionLib.java | 33 - .../jscript/lib/AsyncGeneratorLib.java | 111 --- .../me/topchetoeu/jscript/lib/BooleanLib.java | 37 - .../me/topchetoeu/jscript/lib/ConsoleLib.java | 38 - .../me/topchetoeu/jscript/lib/DateLib.java | 266 ----- .../topchetoeu/jscript/lib/EncodingLib.java | 89 -- .../me/topchetoeu/jscript/lib/ErrorLib.java | 54 - .../me/topchetoeu/jscript/lib/FileLib.java | 84 -- .../topchetoeu/jscript/lib/FilesystemLib.java | 193 ---- .../topchetoeu/jscript/lib/FunctionLib.java | 80 -- .../jscript/lib/GeneratorFunctionLib.java | 31 - .../topchetoeu/jscript/lib/GeneratorLib.java | 82 -- .../me/topchetoeu/jscript/lib/Internals.java | 229 ----- .../me/topchetoeu/jscript/lib/JSONLib.java | 24 - .../me/topchetoeu/jscript/lib/MapLib.java | 87 -- .../me/topchetoeu/jscript/lib/MathLib.java | 211 ---- .../me/topchetoeu/jscript/lib/NumberLib.java | 103 -- .../me/topchetoeu/jscript/lib/ObjectLib.java | 273 ----- .../me/topchetoeu/jscript/lib/PromiseLib.java | 403 -------- .../topchetoeu/jscript/lib/RangeErrorLib.java | 19 - .../me/topchetoeu/jscript/lib/RegExpLib.java | 354 ------- .../me/topchetoeu/jscript/lib/SetLib.java | 69 -- .../me/topchetoeu/jscript/lib/StringLib.java | 291 ------ .../me/topchetoeu/jscript/lib/SymbolLib.java | 81 -- .../jscript/lib/SyntaxErrorLib.java | 19 - .../topchetoeu/jscript/lib/ThrowableLib.java | 18 - .../topchetoeu/jscript/lib/TypeErrorLib.java | 19 - .../topchetoeu/jscript/runtime/EventLoop.java | 8 +- .../me/topchetoeu/jscript/runtime/Frame.java | 125 ++- .../jscript/runtime/InstructionRunner.java | 274 ++--- .../jscript/runtime/JSONConverter.java | 86 ++ .../jscript/runtime/SimpleRepl.java | 136 +++ .../jscript/runtime/WrapperProvider.java | 4 +- .../jscript/runtime/debug/DebugContext.java | 5 +- .../runtime/environment/Environment.java | 6 +- .../runtime/exceptions/EngineException.java | 40 +- .../jscript/runtime/scope/GlobalScope.java | 60 +- .../jscript/runtime/scope/ValueVariable.java | 25 +- .../jscript/runtime/scope/Variable.java | 19 +- .../jscript/runtime/values/ArrayValue.java | 227 ----- .../jscript/runtime/values/FunctionValue.java | 69 -- .../jscript/runtime/values/Member.java | 132 +++ .../jscript/runtime/values/NativeWrapper.java | 77 -- .../jscript/runtime/values/ObjectValue.java | 353 ------- .../jscript/runtime/values/ScopeValue.java | 54 - .../jscript/runtime/values/Symbol.java | 28 - .../jscript/runtime/values/Value.java | 932 +++++++----------- .../values/{Values.java => Values.java.old} | 21 +- .../runtime/values/functions/Arguments.java | 39 + .../values/{ => functions}/CodeFunction.java | 20 +- .../values/functions/FunctionValue.java | 77 ++ .../{ => functions}/NativeFunction.java | 11 +- .../runtime/values/objects/ArrayValue.java | 217 ++++ .../runtime/values/objects/ObjectValue.java | 134 +++ .../runtime/values/objects/ScopeValue.java | 35 + .../runtime/values/primitives/BoolValue.java | 38 + .../values/primitives/NumberValue.java | 80 ++ .../values/primitives/PrimitiveValue.java | 21 + .../values/primitives/StringValue.java | 34 + .../values/primitives/SymbolValue.java | 48 + .../runtime/values/primitives/VoidValue.java | 52 + .../topchetoeu/jscript/utils/JSCompiler.java | 36 - .../topchetoeu/jscript/utils/JScriptRepl.java | 150 --- .../jscript/utils/debug/DebugServer.java | 251 ----- .../jscript/utils/debug/Debugger.java | 37 - .../jscript/utils/debug/DebuggerProvider.java | 5 - .../jscript/utils/debug/HttpRequest.java | 102 -- .../jscript/utils/debug/ObjectManager.java | 212 ---- .../jscript/utils/debug/SimpleDebugger.java | 910 ----------------- .../jscript/utils/debug/V8Error.java | 19 - .../jscript/utils/debug/V8Event.java | 22 - .../jscript/utils/debug/V8Message.java | 50 - .../jscript/utils/debug/V8Result.java | 22 - .../jscript/utils/debug/WebSocket.java | 195 ---- .../jscript/utils/debug/WebSocketMessage.java | 29 - .../jscript/utils/filesystem/ActionType.java | 28 - .../jscript/utils/filesystem/BaseFile.java | 59 -- .../jscript/utils/filesystem/EntryType.java | 13 - .../jscript/utils/filesystem/ErrorReason.java | 23 - .../jscript/utils/filesystem/File.java | 169 ---- .../jscript/utils/filesystem/FileStat.java | 14 - .../jscript/utils/filesystem/Filesystem.java | 18 - .../utils/filesystem/FilesystemException.java | 86 -- .../utils/filesystem/HandleManager.java | 32 - .../jscript/utils/filesystem/LineReader.java | 16 - .../jscript/utils/filesystem/LineWriter.java | 7 - .../jscript/utils/filesystem/MemoryFile.java | 36 - .../utils/filesystem/MemoryFilesystem.java | 100 -- .../jscript/utils/filesystem/Mode.java | 41 - .../jscript/utils/filesystem/Paths.java | 52 - .../utils/filesystem/PhysicalFile.java | 35 - .../utils/filesystem/PhysicalFilesystem.java | 92 -- .../utils/filesystem/RootFilesystem.java | 99 -- .../utils/filesystem/STDFilesystem.java | 52 - .../jscript/utils/interop/Arguments.java | 121 --- .../jscript/utils/interop/Expose.java | 14 - .../utils/interop/ExposeConstructor.java | 10 - .../jscript/utils/interop/ExposeField.java | 13 - .../jscript/utils/interop/ExposeTarget.java | 28 - .../jscript/utils/interop/ExposeType.java | 7 - .../utils/interop/NativeWrapperProvider.java | 454 --------- .../jscript/utils/interop/OnWrapperInit.java | 12 - .../jscript/utils/interop/WrapperName.java | 12 - .../jscript/utils/mapping/SourceMap.java | 109 -- .../topchetoeu/jscript/utils/mapping/VLQ.java | 95 -- .../jscript/utils/modules/Module.java | 20 - .../jscript/utils/modules/ModuleRepo.java | 46 - .../jscript/utils/modules/RootModuleRepo.java | 30 - .../jscript/utils/modules/SourceModule.java | 22 - .../jscript/utils/permissions/Matcher.java | 69 -- .../jscript/utils/permissions/Permission.java | 19 - .../permissions/PermissionPredicate.java | 44 - .../utils/permissions/PermissionsManager.java | 59 -- .../permissions/PermissionsProvider.java | 29 - 123 files changed, 1863 insertions(+), 9808 deletions(-) rename src/java/me/topchetoeu/jscript/{runtime => common}/Compiler.java (82%) rename src/java/me/topchetoeu/jscript/{compilation/parsing => common}/ParseRes.java (95%) delete mode 100644 src/java/me/topchetoeu/jscript/lib/ArrayLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/BooleanLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/ConsoleLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/DateLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/EncodingLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/ErrorLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/FileLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/FilesystemLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/FunctionLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/GeneratorLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/Internals.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/JSONLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/MapLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/MathLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/NumberLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/ObjectLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/PromiseLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/RegExpLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/SetLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/StringLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/SymbolLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/ThrowableLib.java delete mode 100644 src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/JSONConverter.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/Member.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/Symbol.java rename src/java/me/topchetoeu/jscript/runtime/values/{Values.java => Values.java.old} (97%) create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java rename src/java/me/topchetoeu/jscript/runtime/values/{ => functions}/CodeFunction.java (58%) create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java rename src/java/me/topchetoeu/jscript/runtime/values/{ => functions}/NativeFunction.java (62%) create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/JSCompiler.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/JScriptRepl.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/Debugger.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/V8Error.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/V8Event.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/V8Message.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/V8Result.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/File.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/Arguments.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/Expose.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/modules/Module.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/permissions/Permission.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java delete mode 100644 src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java diff --git a/gradle.properties b/gradle.properties index a17b580..0181a94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ project_group = me.topchetoeu project_name = jscript project_version = 0.9.41-beta -main_class = me.topchetoeu.jscript.utils.JScriptRepl +main_class = me.topchetoeu.jscript.runtime.SimpleRepl diff --git a/src/java/me/topchetoeu/jscript/runtime/Compiler.java b/src/java/me/topchetoeu/jscript/common/Compiler.java similarity index 82% rename from src/java/me/topchetoeu/jscript/runtime/Compiler.java rename to src/java/me/topchetoeu/jscript/common/Compiler.java index ff87201..01d2bb0 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Compiler.java +++ b/src/java/me/topchetoeu/jscript/common/Compiler.java @@ -1,13 +1,11 @@ -package me.topchetoeu.jscript.runtime; +package me.topchetoeu.jscript.common; -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { public Key KEY = new Key<>(); diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java b/src/java/me/topchetoeu/jscript/common/ParseRes.java similarity index 95% rename from src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java rename to src/java/me/topchetoeu/jscript/common/ParseRes.java index 982c504..a373a5d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java +++ b/src/java/me/topchetoeu/jscript/common/ParseRes.java @@ -1,9 +1,10 @@ -package me.topchetoeu.jscript.compilation.parsing; +package me.topchetoeu.jscript.common; import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.compilation.parsing.TestRes; +import me.topchetoeu.jscript.compilation.parsing.Token; import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser; public class ParseRes { diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index d44270e..b81062b 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -1,79 +1,16 @@ package me.topchetoeu.jscript.common.json; -import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Operator; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; public class JSON { - public static Object toJs(JSONElement val) { - if (val.isBoolean()) return val.bool(); - if (val.isString()) return val.string(); - if (val.isNumber()) return val.number(); - if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList())); - if (val.isMap()) { - var res = new ObjectValue(); - for (var el : val.map().entrySet()) { - res.defineProperty(null, el.getKey(), toJs(el.getValue())); - } - return res; - } - if (val.isNull()) return Values.NULL; - return null; - } - private static JSONElement fromJs(Environment ext, Object val, HashSet prev) { - if (val instanceof Boolean) return JSONElement.bool((boolean)val); - if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue()); - if (val instanceof String) return JSONElement.string((String)val); - if (val == Values.NULL) return JSONElement.NULL; - if (val instanceof ArrayValue) { - if (prev.contains(val)) throw new EngineException("Circular dependency in JSON."); - prev.add(val); - - var res = new JSONList(); - - for (var el : ((ArrayValue)val).toArray()) { - var jsonEl = fromJs(ext, el, prev); - if (jsonEl == null) jsonEl = JSONElement.NULL; - res.add(jsonEl); - } - - prev.remove(val); - return JSONElement.of(res); - } - if (val instanceof ObjectValue) { - if (prev.contains(val)) throw new EngineException("Circular dependency in JSON."); - prev.add(val); - - var res = new JSONMap(); - - for (var el : Values.getMembers(ext, val, false, false)) { - var jsonEl = fromJs(ext, Values.getMember(ext, val, el), prev); - if (jsonEl == null) continue; - if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl); - } - - prev.remove(val); - return JSONElement.of(res); - } - if (val == null) return null; - return null; - } - public static JSONElement fromJs(Environment ext, Object val) { - return fromJs(ext, val, new HashSet<>()); - } - public static ParseRes parseIdentifier(List tokens, int i) { return Parsing.parseIdentifier(tokens, i); } diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index b6b992a..153c3a2 100644 --- a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -15,7 +15,6 @@ import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; -import me.topchetoeu.jscript.utils.mapping.SourceMap; public class FunctionMap { public static class FunctionMapBuilder { @@ -131,27 +130,27 @@ public class FunctionMap { return pcToLoc.lastEntry().getValue(); } - public FunctionMap apply(SourceMap map) { - var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames); + // public static FunctionMap apply(FunctionMap funcMap, SourceMap map) { + // var res = new FunctionMap(Map.of(), Map.of(), funcMap.localNames, funcMap.captureNames); - for (var el : pcToLoc.entrySet()) { - res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue())); - } + // for (var el : funcMap.pcToLoc.entrySet()) { + // res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue())); + // } - res.bps.putAll(bps); + // res.bps.putAll(bps); - for (var el : bpLocs.entrySet()) { - for (var loc : el.getValue()) { - loc = map.toCompiled(loc); - if (loc == null) continue; + // for (var el : bpLocs.entrySet()) { + // for (var loc : el.getValue()) { + // loc = map.toCompiled(loc); + // if (loc == null) continue; - if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>()); - res.bpLocs.get(loc.filename()).add(loc); - } - } + // if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>()); + // res.bpLocs.get(loc.filename()).add(loc); + // } + // } - return res; - } + // return res; + // } public FunctionMap clone() { var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames); diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java index 9ec962a..84a467f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java @@ -11,11 +11,12 @@ import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; +import me.topchetoeu.jscript.common.ParseRes.State; import me.topchetoeu.jscript.compilation.*; import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; -import me.topchetoeu.jscript.compilation.parsing.ParseRes.State; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; import me.topchetoeu.jscript.compilation.values.*; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java index c399a9e..ec234df 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java @@ -1,7 +1,8 @@ package me.topchetoeu.jscript.compilation.parsing; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.compilation.parsing.ParseRes.State; +import me.topchetoeu.jscript.common.ParseRes; +import me.topchetoeu.jscript.common.ParseRes.State; public class TestRes { public final State state; diff --git a/src/java/me/topchetoeu/jscript/lib/ArrayLib.java b/src/java/me/topchetoeu/jscript/lib/ArrayLib.java deleted file mode 100644 index ce6a5aa..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ArrayLib.java +++ /dev/null @@ -1,456 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.Iterator; -import java.util.Stack; - -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Array") -public class ArrayLib { - private static int normalizeI(int len, int i, boolean clamp) { - if (i < 0) i += len; - if (clamp) { - if (i < 0) i = 0; - if (i > len) i = len; - } - return i; - } - - @Expose(value = "length", type = ExposeType.GETTER) - public static int __getLength(Arguments args) { - return args.self(ArrayValue.class).size(); - } - @Expose(value = "length", type = ExposeType.SETTER) - public static void __setLength(Arguments args) { - args.self(ArrayValue.class).setSize(args.getInt(0)); - } - - @Expose public static ObjectValue __values(Arguments args) { - return __iterator(args); - } - @Expose public static ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.env, () -> new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < args.self(ArrayValue.class).size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return i++; - } - }); - } - @Expose public static ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.env, () -> new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < args.self(ArrayValue.class).size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return new ArrayValue(args.env, i, args.self(ArrayValue.class).get(i++)); - } - }); - } - - @Expose(value = "@@Symbol.iterator") - public static ObjectValue __iterator(Arguments args) { - return Values.toJSIterator(args.env, args.self(ArrayValue.class)); - } - @Expose(value = "@@Symbol.asyncIterator") - public static ObjectValue __asyncIterator(Arguments args) { - return Values.toJSAsyncIterator(args.env, args.self(ArrayValue.class).iterator()); - } - - @Expose public static ArrayValue __concat(Arguments args) { - // TODO: Fully implement with non-array spreadable objects - var arrs = args.slice(-1); - var size = 0; - - for (int i = 0; i < arrs.n(); i++) { - if (arrs.get(i) instanceof ArrayValue) size += arrs.convert(i, ArrayValue.class).size(); - else i++; - } - - var res = new ArrayValue(size); - - for (int i = 0, j = 0; i < arrs.n(); i++) { - if (arrs.get(i) instanceof ArrayValue) { - var arrEl = arrs.convert(i, ArrayValue.class); - int n = arrEl.size(); - arrEl.copyTo(res, 0, j, n); - j += n; - } - else { - res.set(args.env, j++, arrs.get(i)); - } - } - - return res; - } - @Expose public static ArrayValue __sort(Arguments args) { - var arr = args.self(ArrayValue.class); - var cmp = args.convert(0, FunctionValue.class); - - var defaultCmp = new NativeFunction("", _args -> { - return _args.getString(0).compareTo(_args.getString(1)); - }); - - arr.sort((a, b) -> { - var res = Values.toNumber(args.env, (cmp == null ? defaultCmp : cmp).call(args.env, null, a, b)); - if (res < 0) return -1; - if (res > 0) return 1; - return 0; - }); - return arr; - } - - @Expose public static ArrayValue __fill(Arguments args) { - var arr = args.self(ArrayValue.class); - var val = args.get(0); - var start = normalizeI(arr.size(), args.getInt(1, 0), true); - var end = normalizeI(arr.size(), args.getInt(2, arr.size()), true); - - for (; start < end; start++) arr.set(args.env, start, val); - - return arr; - } - @Expose public static boolean __every(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = 0; i < arr.size(); i++) { - if (arr.has(i) && !Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, arr - ))) return false; - } - - return true; - } - @Expose public static boolean __some(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, arr - ))) return true; - } - - return false; - } - @Expose public static ArrayValue __filter(Arguments args) { - var arr = args.self(ArrayValue.class); - var res = new ArrayValue(arr.size()); - - for (int i = 0, j = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, arr - ))) res.set(args.env, j++, arr.get(i)); - } - - return res; - } - @Expose public static ArrayValue __map(Arguments args) { - var arr = args.self(ArrayValue.class); - var res = new ArrayValue(arr.size()); - res.setSize(arr.size()); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i)) res.set(args.env, i, Values.call(args.env, args.get(0), args.get(1), arr.get(i), i, arr)); - } - return res; - } - @Expose public static void __forEach(Arguments args) { - var arr = args.self(ArrayValue.class); - var func = args.convert(0, FunctionValue.class); - var thisArg = args.get(1); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i)) func.call(args.env, thisArg, arr.get(i), i, arr); - } - } - - @Expose public static Object __reduce(Arguments args) { - var arr = args.self(ArrayValue.class); - var func = args.convert(0, FunctionValue.class); - var res = args.get(1); - var i = 0; - - if (args.n() < 2) { - for (; i < arr.size(); i++) { - if (arr.has(i)){ - res = arr.get(i++); - break; - } - } - } - - for (; i < arr.size(); i++) { - if (arr.has(i)) { - res = func.call(args.env, null, res, arr.get(i), i, arr); - } - } - - return res; - } - @Expose public static Object __reduceRight(Arguments args) { - var arr = args.self(ArrayValue.class); - var func = args.convert(0, FunctionValue.class); - var res = args.get(1); - var i = arr.size(); - - if (args.n() < 2) { - while (!arr.has(i--) && i >= 0) { - res = arr.get(i); - } - } - else i--; - - for (; i >= 0; i--) { - if (arr.has(i)) { - res = func.call(args.env, null, res, arr.get(i), i, arr); - } - } - - return res; - } - - @Expose public static ArrayValue __flat(Arguments args) { - var arr = args.self(ArrayValue.class); - var depth = args.getInt(0, 1); - var res = new ArrayValue(arr.size()); - var stack = new Stack(); - var depths = new Stack(); - - stack.push(arr); - depths.push(-1); - - while (!stack.empty()) { - var el = stack.pop(); - int d = depths.pop(); - - if ((d == -1 || d < depth) && el instanceof ArrayValue) { - var arrEl = (ArrayValue)el; - for (int i = arrEl.size() - 1; i >= 0; i--) { - if (!arrEl.has(i)) continue; - stack.push(arrEl.get(i)); - depths.push(d + 1); - } - } - else res.set(args.env, res.size(), el); - } - - return res; - } - @Expose public static ArrayValue __flatMap(Arguments args) { - return __flat(new Arguments(args.env, __map(args), 1)); - } - - @Expose public static Object __find(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return arr.get(i); - } - - return null; - } - @Expose public static Object __findLast(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = arr.size() - 1; i >= 0; i--) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return arr.get(i); - } - - return null; - } - - @Expose public static int __findIndex(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return i; - } - - return -1; - } - @Expose public static int __findLastIndex(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = arr.size() - 1; i >= 0; i--) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return i; - } - - return -1; - } - - @Expose public static int __indexOf(Arguments args) { - var arr = args.self(ArrayValue.class); - var val = args.get(0); - var start = normalizeI(arr.size(), args.getInt(1), true); - - for (int i = start; i < arr.size(); i++) { - if (Values.strictEquals(args.env, arr.get(i), val)) return i; - } - - return -1; - } - @Expose public static int __lastIndexOf(Arguments args) { - var arr = args.self(ArrayValue.class); - var val = args.get(0); - var start = normalizeI(arr.size(), args.getInt(1), true); - - for (int i = arr.size(); i >= start; i--) { - if (Values.strictEquals(args.env, arr.get(i), val)) return i; - } - - return -1; - } - - @Expose public static boolean __includes(Arguments args) { - return __indexOf(args) >= 0; - } - - @Expose public static Object __pop(Arguments args) { - var arr = args.self(ArrayValue.class); - if (arr.size() == 0) return null; - - var val = arr.get(arr.size() - 1); - arr.shrink(1); - return val; - } - @Expose public static int __push(Arguments args) { - var arr = args.self(ArrayValue.class); - var values = args.args; - - arr.copyFrom(args.env, values, 0, arr.size(), values.length); - return arr.size(); - } - - @Expose public static Object __shift(Arguments args) { - var arr = args.self(ArrayValue.class); - - if (arr.size() == 0) return null; - var val = arr.get(0); - - arr.move(1, 0, arr.size()); - arr.shrink(1); - return val; - } - @Expose public static int __unshift(Arguments args) { - var arr = args.self(ArrayValue.class); - var values = args.slice(0).args; - - arr.move(0, values.length, arr.size()); - arr.copyFrom(args.env, values, 0, 0, values.length); - return arr.size(); - } - - @Expose public static ArrayValue __slice(Arguments args) { - var arr = args.self(ArrayValue.class); - var start = normalizeI(arr.size(), args.getInt(0), true); - var end = normalizeI(arr.size(), args.getInt(1, arr.size()), true); - - var res = new ArrayValue(end - start); - arr.copyTo(res, start, 0, end - start); - return res; - } - - @Expose public static ArrayValue __splice(Arguments args) { - var arr = args.self(ArrayValue.class); - var start = normalizeI(arr.size(), args.getInt(0), true); - var deleteCount = normalizeI(arr.size(), args.getInt(1, arr.size()), true); - var items = args.slice(2).args; - - if (start + deleteCount >= arr.size()) deleteCount = arr.size() - start; - - var size = arr.size() - deleteCount + items.length; - var res = new ArrayValue(deleteCount); - arr.copyTo(res, start, 0, deleteCount); - arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount); - arr.copyFrom(args.env, items, 0, start, items.length); - arr.setSize(size); - - return res; - } - @Expose public static String __toString(Arguments args) { - return __join(new Arguments(args.env, args.self, ",")); - } - - @Expose public static String __join(Arguments args) { - var arr = args.self(ArrayValue.class); - var sep = args.getString(0, ", "); - var res = new StringBuilder(); - var comma = false; - - for (int i = 0; i < arr.size(); i++) { - if (!arr.has(i)) continue; - - if (comma) res.append(sep); - comma = true; - - var el = arr.get(i); - if (el == null || el == Values.NULL) continue; - - res.append(Values.toString(args.env, el)); - } - - return res.toString(); - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isArray(Arguments args) { - return args.get(0) instanceof ArrayValue; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __of(Arguments args) { - return new ArrayValue(args.env, args.slice(0).args); - } - - @ExposeConstructor public static ArrayValue __constructor(Arguments args) { - ArrayValue res; - - if (args.n() == 1 && args.get(0) instanceof Number) { - var len = args.getInt(0); - res = new ArrayValue(len); - res.setSize(len); - } - else { - var val = args.args; - res = new ArrayValue(val.length); - res.copyFrom(args.env, val, 0, 0, val.length); - } - - return res; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java deleted file mode 100644 index af1f178..0000000 --- a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.lib.PromiseLib.Handle; -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("AsyncFunction") -public class AsyncFunctionLib extends FunctionValue { - public final CodeFunction func; - - private static class AsyncHelper { - public PromiseLib promise = new PromiseLib(); - public Frame frame; - - private boolean awaiting = false; - - private void next(Environment env, Object inducedValue, EngineException inducedError) { - Object res = null; - - frame.onPush(); - awaiting = false; - while (!awaiting) { - try { - if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); - else if (inducedError != null) res = frame.induceError(inducedError); - else res = frame.next(); - - inducedValue = Values.NO_RETURN; - inducedError = null; - - if (res != Values.NO_RETURN) { - promise.fulfill(env, res); - break; - } - } - catch (EngineException e) { - promise.reject(env, e); - break; - } - } - frame.onPop(); - - if (awaiting) { - PromiseLib.handle(env, frame.pop(), new Handle() { - @Override - public void onFulfil(Object val) { - next(env, val, null); - } - @Override - public void onReject(EngineException err) { - next(env, Values.NO_RETURN, err); - } - }.defer(env)); - } - } - - public Object await(Arguments args) { - this.awaiting = true; - return args.get(0); - } - } - - @Override - public Object call(Environment env, Object thisArg, Object ...args) { - var handler = new AsyncHelper(); - - var newArgs = new Object[args.length + 1]; - newArgs[0] = new NativeFunction("await", handler::await); - System.arraycopy(args, 0, newArgs, 1, args.length); - - handler.frame = new Frame(env, thisArg, newArgs, (CodeFunction)func); - handler.next(env, Values.NO_RETURN, null); - return handler.promise; - } - - public AsyncFunctionLib(FunctionValue func) { - super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); - this.func = (CodeFunction)func; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java deleted file mode 100644 index f6bdbc7..0000000 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("AsyncGeneratorFunction") -public class AsyncGeneratorFunctionLib extends FunctionValue { - public final CodeFunction func; - - @Override - public Object call(Environment ext, Object thisArg, Object ...args) { - var handler = new AsyncGeneratorLib(); - - var newArgs = new Object[args.length + 2]; - newArgs[0] = new NativeFunction("await", handler::await); - newArgs[1] = new NativeFunction("yield", handler::yield); - System.arraycopy(args, 0, newArgs, 2, args.length); - - handler.frame = new Frame(ext, thisArg, newArgs, func); - return handler; - } - - public AsyncGeneratorFunctionLib(CodeFunction func) { - super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a code function."); - this.func = func; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java deleted file mode 100644 index cb1ad6a..0000000 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java +++ /dev/null @@ -1,111 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.Map; - -import me.topchetoeu.jscript.lib.PromiseLib.Handle; -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("AsyncGenerator") -public class AsyncGeneratorLib { - private int state = 0; - private boolean done = false; - private PromiseLib currPromise; - public Frame frame; - - private void next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) { - if (done) { - if (inducedError != null) throw inducedError; - currPromise.fulfill(env, new ObjectValue(env, Map.of( - "done", true, - "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn - ))); - return; - } - - Object res = null; - state = 0; - - frame.onPush(); - while (state == 0) { - try { - if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); - else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue); - else if (inducedError != null) res = frame.induceError(inducedError); - else res = frame.next(); - - inducedValue = inducedReturn = Values.NO_RETURN; - inducedError = null; - - if (res != Values.NO_RETURN) { - var obj = new ObjectValue(); - obj.defineProperty(env, "done", true); - obj.defineProperty(env, "value", res); - currPromise.fulfill(env, obj); - break; - } - } - catch (EngineException e) { - currPromise.reject(env, e); - break; - } - } - frame.onPop(); - - if (state == 1) { - PromiseLib.handle(env, frame.pop(), new Handle() { - @Override public void onFulfil(Object val) { - next(env, val, Values.NO_RETURN, null); - } - @Override public void onReject(EngineException err) { - next(env, Values.NO_RETURN, Values.NO_RETURN, err); - } - }.defer(env)); - } - else if (state == 2) { - var obj = new ObjectValue(); - obj.defineProperty(env, "done", false); - obj.defineProperty(env, "value", frame.pop()); - currPromise.fulfill(env, obj); - } - } - - @Override - public String toString() { - if (done) return "Generator [closed]"; - if (state != 0) return "Generator [suspended]"; - return "Generator [running]"; - } - - public Object await(Arguments args) { - this.state = 1; - return args.get(0); - } - public Object yield(Arguments args) { - this.state = 2; - return args.get(0); - } - - @Expose public PromiseLib __next(Arguments args) { - this.currPromise = new PromiseLib(); - if (args.has(0)) next(args.env, args.get(0), Values.NO_RETURN, null); - else next(args.env, Values.NO_RETURN, Values.NO_RETURN, null); - return this.currPromise; - } - @Expose public PromiseLib __return(Arguments args) { - this.currPromise = new PromiseLib(); - next(args.env, Values.NO_RETURN, args.get(0), null); - return this.currPromise; - } - @Expose public PromiseLib __throw(Arguments args) { - this.currPromise = new PromiseLib(); - next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env)); - return this.currPromise; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/BooleanLib.java b/src/java/me/topchetoeu/jscript/lib/BooleanLib.java deleted file mode 100644 index 019a061..0000000 --- a/src/java/me/topchetoeu/jscript/lib/BooleanLib.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Boolean") -public class BooleanLib { - public static final BooleanLib TRUE = new BooleanLib(true); - public static final BooleanLib FALSE = new BooleanLib(false); - - public final boolean value; - - @Override public String toString() { - return value + ""; - } - - public BooleanLib(boolean val) { - this.value = val; - } - - @ExposeConstructor public static Object __constructor(Arguments args) { - var val = args.getBoolean(0); - if (args.self instanceof ObjectValue) return val ? TRUE : FALSE; - else return val; - } - @Expose public static String __toString(Arguments args) { - return args.self(Boolean.class) ? "true" : "false"; - } - @Expose public static boolean __valueOf(Arguments args) { - if (Values.isWrapper(args.self, BooleanLib.class)) return Values.wrapper(args.self, BooleanLib.class).value; - return args.self(Boolean.class); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java b/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java deleted file mode 100644 index cd08dbf..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.io.IOException; - -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.File; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Console") -public class ConsoleLib { - public static interface Writer { - void writeLine(String val) throws IOException; - } - - private File file; - - @Expose - public void __log(Arguments args) { - var res = new StringBuilder(); - var first = true; - - for (var el : args.args) { - if (!first) res.append(" "); - first = false; - res.append(Values.toReadable(args.env, el).getBytes()); - } - - for (var line : res.toString().split("\n", -1)) { - file.write(line.getBytes()); - } - } - - public ConsoleLib(File file) { - this.file = file; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/DateLib.java b/src/java/me/topchetoeu/jscript/lib/DateLib.java deleted file mode 100644 index b4b5082..0000000 --- a/src/java/me/topchetoeu/jscript/lib/DateLib.java +++ /dev/null @@ -1,266 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Date") -public class DateLib { - private Calendar normal; - private Calendar utc; - - private void updateUTC() { - if (utc == null || normal == null) return; - utc.setTimeInMillis(normal.getTimeInMillis()); - } - private void updateNormal() { - if (utc == null || normal == null) return; - normal.setTimeInMillis(utc.getTimeInMillis()); - } - private void invalidate() { - normal = utc = null; - } - - @Expose public double __getYear() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.YEAR) - 1900; - } - @Expose public double __setYeard(Arguments args) { - var real = args.getDouble(0); - if (real >= 0 && real <= 99) real = real + 1900; - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.YEAR, (int)real); - updateUTC(); - return __getTime(); - } - - @Expose public double __getFullYear() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.YEAR); - } - @Expose public double __getMonth() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.MONTH); - } - @Expose public double __getDate() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.DAY_OF_MONTH); - } - @Expose public double __getDay() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.DAY_OF_WEEK); - } - @Expose public double __getHours() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.HOUR_OF_DAY); - } - @Expose public double __getMinutes() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.MINUTE); - } - @Expose public double __getSeconds() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.SECOND); - } - @Expose public double __getMilliseconds() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.MILLISECOND); - } - - @Expose public double __getUTCFullYear() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.YEAR); - } - @Expose public double __getUTCMonth() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.MONTH); - } - @Expose public double __getUTCDate() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.DAY_OF_MONTH); - } - @Expose public double __getUTCDay() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.DAY_OF_WEEK); - } - @Expose public double __getUTCHours() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.HOUR_OF_DAY); - } - @Expose public double __getUTCMinutes() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.MINUTE); - } - @Expose public double __getUTCSeconds() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.SECOND); - } - @Expose public double __getUTCMilliseconds() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.MILLISECOND); - } - - @Expose public double __setFullYear(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.YEAR, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setMonthd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.MONTH, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setDated(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.DAY_OF_MONTH, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setDayd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.DAY_OF_WEEK, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setHoursd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.HOUR_OF_DAY, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setMinutesd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.MINUTE, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setSecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.SECOND, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setMillisecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.MILLISECOND, (int)real); - updateUTC(); - return __getTime(); - } - - @Expose public double __setUTCFullYeard(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.YEAR, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCMonthd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.MONTH, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCDated(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.DAY_OF_MONTH, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCDayd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.DAY_OF_WEEK, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCHoursd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.HOUR_OF_DAY, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCMinutesd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.MINUTE, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCSecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.SECOND, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCMillisecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.MILLISECOND, (int)real); - updateNormal(); - return __getTime(); - } - - @Expose public double __getTime() { - if (utc == null) return Double.NaN; - return utc.getTimeInMillis(); - } - @Expose public double __getTimezoneOffset() { - if (normal == null) return Double.NaN; - return normal.getTimeZone().getRawOffset() / 60000; - } - - @Expose public double __valueOf() { - if (normal == null) return Double.NaN; - else return normal.getTimeInMillis(); - } - - @Expose public String __toString() { - return normal.getTime().toString(); - } - - @Override public String toString() { - return __toString(); - } - - public DateLib(long timestamp) { - normal = Calendar.getInstance(); - utc = Calendar.getInstance(); - normal.setTimeInMillis(timestamp); - utc.setTimeZone(TimeZone.getTimeZone("UTC")); - utc.setTimeInMillis(timestamp); - } - - public DateLib() { - this(new Date().getTime()); - } - - @ExposeConstructor public static DateLib init(Arguments args) { - if (args.has(0)) return new DateLib(args.getLong(0)); - else return new DateLib(); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __now() { - return new DateLib().__getTime(); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/EncodingLib.java b/src/java/me/topchetoeu/jscript/lib/EncodingLib.java deleted file mode 100644 index e164ef8..0000000 --- a/src/java/me/topchetoeu/jscript/lib/EncodingLib.java +++ /dev/null @@ -1,89 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; - -import me.topchetoeu.jscript.common.Buffer; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Encoding") -public class EncodingLib { - private static final String HEX = "0123456789ABCDEF"; - - public static String encodeUriAny(String str, String keepAlphabet) { - if (str == null) str = "undefined"; - - var bytes = str.getBytes(); - var sb = new StringBuilder(bytes.length); - - for (byte c : bytes) { - if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c); - else { - sb.append('%'); - sb.append(HEX.charAt(c / 16)); - sb.append(HEX.charAt(c % 16)); - } - } - - return sb.toString(); - } - public static String decodeUriAny(String str, String keepAlphabet) { - if (str == null) str = "undefined"; - - var res = new Buffer(); - var bytes = str.getBytes(); - - for (var i = 0; i < bytes.length; i++) { - var c = bytes[i]; - if (c == '%') { - if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed."); - var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]); - if (!Parsing.isAny((char)b, keepAlphabet)) { - i += 2; - res.append((byte)b); - continue; - } - } - res.append(c); - } - - return new String(res.data()); - } - - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __encode(Arguments args) { - var res = new ArrayValue(); - for (var el : args.getString(0).getBytes()) res.set(null, res.size(), (int)el); - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static String __decode(Arguments args) { - var raw = args.convert(0, ArrayList.class); - var res = new byte[raw.size()]; - for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.env, raw.get(i)); - return new String(res); - } - - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURIComponent(Arguments args) { - return EncodingLib.encodeUriAny(args.getString(0), ".-_!~*'()"); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURIComponent(Arguments args) { - return decodeUriAny(args.getString(0), ""); - } - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURI(Arguments args) { - return encodeUriAny(args.getString(0), ";,/?:@&=+$#.-_!~*'()"); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURI(Arguments args) { - return decodeUriAny(args.getString(0), ",/?:@&=+$#."); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/ErrorLib.java b/src/java/me/topchetoeu/jscript/lib/ErrorLib.java deleted file mode 100644 index 431f6bd..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ErrorLib.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.ConvertException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Error") -public class ErrorLib { - private static String toString(Environment ctx, Object name, Object message) { - if (name == null) name = ""; - else name = Values.toString(ctx, name).trim(); - if (message == null) message = ""; - else message = Values.toString(ctx, message).trim(); - StringBuilder res = new StringBuilder(); - - if (!name.equals("")) res.append(name); - if (!message.equals("") && !name.equals("")) res.append(": "); - if (!message.equals("")) res.append(message); - - return res.toString(); - } - - @ExposeField public static final String __name = "Error"; - - @Expose public static String __toString(Arguments args) { - if (args.self instanceof ObjectValue) return toString(args.env, - Values.getMember(args.env, args.self, "name"), - Values.getMember(args.env, args.self, "message") - ); - else return "[Invalid error]"; - } - - @ExposeConstructor public static ObjectValue __constructor(Arguments args) { - var target = new ObjectValue(); - var message = args.getString(0, ""); - - try { - target = args.self(ObjectValue.class); - } - catch (ConvertException e) {} - - target.setPrototype(PlaceholderProto.ERROR); - target.defineProperty(args.env, "message", Values.toString(args.env, message)); - - return target; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/FileLib.java b/src/java/me/topchetoeu/jscript/lib/FileLib.java deleted file mode 100644 index 6a8deb4..0000000 --- a/src/java/me/topchetoeu/jscript/lib/FileLib.java +++ /dev/null @@ -1,84 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.File; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("File") -public class FileLib { - public final File fd; - - @Expose public PromiseLib __pointer(Arguments args) { - return PromiseLib.await(args.env, () -> { - try { - return fd.seek(0, 1); - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose public PromiseLib __length(Arguments args) { - return PromiseLib.await(args.env, () -> { - try { - long curr = fd.seek(0, 1); - long res = fd.seek(0, 2); - fd.seek(curr, 0); - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - - @Expose public PromiseLib __read(Arguments args) { - return PromiseLib.await(args.env, () -> { - var n = args.getInt(0); - try { - var buff = new byte[n]; - var res = new ArrayValue(); - int resI = fd.read(buff); - - for (var i = resI - 1; i >= 0; i--) res.set(args.env, i, (int)buff[i]); - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose public PromiseLib __write(Arguments args) { - return PromiseLib.await(args.env, () -> { - var val = args.convert(0, ArrayValue.class); - try { - var res = new byte[val.size()]; - - for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.env, val.get(i)); - fd.write(res); - - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose public PromiseLib __close(Arguments args) { - return PromiseLib.await(args.env, () -> { - fd.close(); - return null; - }); - } - @Expose public PromiseLib __seek(Arguments args) { - return PromiseLib.await(args.env, () -> { - var ptr = args.getLong(0); - var whence = args.getInt(1); - - try { - return fd.seek(ptr, whence); - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - - public FileLib(File fd) { - this.fd = fd; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java b/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java deleted file mode 100644 index d2f9c3c..0000000 --- a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java +++ /dev/null @@ -1,193 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Stack; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.ActionType; -import me.topchetoeu.jscript.utils.filesystem.EntryType; -import me.topchetoeu.jscript.utils.filesystem.ErrorReason; -import me.topchetoeu.jscript.utils.filesystem.File; -import me.topchetoeu.jscript.utils.filesystem.FileStat; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException; -import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Filesystem") -public class FilesystemLib { - @ExposeField(target = ExposeTarget.STATIC) - public static final int __SEEK_SET = 0; - @ExposeField(target = ExposeTarget.STATIC) - public static final int __SEEK_CUR = 1; - @ExposeField(target = ExposeTarget.STATIC) - public static final int __SEEK_END = 2; - - private static Filesystem fs(Environment env) { - var fs = Filesystem.get(env); - if (fs != null) return fs; - throw EngineException.ofError("Current environment doesn't have a file system."); - } - - @Expose(target = ExposeTarget.STATIC) - public static String __normalize(Arguments args) { - return fs(args.env).normalize(args.convert(String.class)); - } - - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __open(Arguments args) { - return PromiseLib.await(args.env, () -> { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - var _mode = Mode.parse(args.getString(1)); - - try { - if (fs.stat(path).type != EntryType.FILE) { - throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a file").setAction(ActionType.OPEN).setPath(path); - } - - return new FileLib(fs.open(path, _mode)); - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __ls(Arguments args) { - - return Values.toJSAsyncIterator(args.env, new Iterator<>() { - private boolean failed, done; - private File file; - private String nextLine; - - private void update() { - if (done) return; - if (!failed) { - if (file == null) { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - - if (fs.stat(path).type != EntryType.FOLDER) { - throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a directory").setAction(ActionType.OPEN); - } - - file = fs.open(path, Mode.READ); - } - - if (nextLine == null) { - while (true) { - nextLine = file.readLine(); - if (nextLine == null) { - done = true; - return; - } - nextLine = nextLine.trim(); - if (!nextLine.equals("")) break; - } - } - } - } - - @Override - public boolean hasNext() { - try { - update(); - return !done && !failed; - } - catch (FilesystemException e) { throw e.toEngineException(); } - } - @Override - public String next() { - try { - update(); - var res = nextLine; - nextLine = null; - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __mkdir(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - fs(args.env).create(args.getString(0), EntryType.FOLDER); - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __mkfile(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - fs(args.env).create(args.getString(0), EntryType.FILE); - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __rm(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - var recursive = args.getBoolean(1); - - if (!recursive) fs.create(path, EntryType.NONE); - else { - var stack = new Stack(); - stack.push(path); - - while (!stack.empty()) { - var currPath = stack.pop(); - FileStat stat; - - try { stat = fs.stat(currPath); } - catch (FilesystemException e) { continue; } - - if (stat.type == EntryType.FOLDER) { - for (var el : fs.open(currPath, Mode.READ).readToString().split("\n")) stack.push(el); - } - else fs.create(currPath, EntryType.NONE); - } - } - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __stat(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - var stat = fs.stat(path); - var res = new ObjectValue(); - - res.defineProperty(args.env, "type", stat.type.name); - res.defineProperty(args.env, "mode", stat.mode.name); - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __exists(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { fs(args.env).stat(args.getString(0)); return true; } - catch (FilesystemException e) { return false; } - }); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/FunctionLib.java b/src/java/me/topchetoeu/jscript/lib/FunctionLib.java deleted file mode 100644 index 278bac3..0000000 --- a/src/java/me/topchetoeu/jscript/lib/FunctionLib.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Function") -public class FunctionLib { - private static int i; - - @Expose public static Object __apply(Arguments args) { - return args.self(FunctionValue.class).call(args.env, args.get(0), args.convert(1, ArrayValue.class).toArray()); - } - @Expose public static Object __call(Arguments args) { - return args.self(FunctionValue.class).call(args.env, args.get(0), args.slice(1).args); - } - @Expose public static FunctionValue __bind(Arguments args) { - var self = args.self(FunctionValue.class); - var thisArg = args.get(0); - var bindArgs = args.slice(1).args; - - return new NativeFunction(self.name + " (bound)", callArgs -> { - Object[] resArgs; - - if (args.n() == 0) resArgs = bindArgs; - else { - resArgs = new Object[bindArgs.length + callArgs.n()]; - System.arraycopy(bindArgs, 0, resArgs, 0, bindArgs.length); - System.arraycopy(callArgs.args, 0, resArgs, bindArgs.length, callArgs.n()); - } - - return self.call(callArgs.env, thisArg, resArgs); - }); - } - @Expose public static String __toString(Arguments args) { - return args.self.toString(); - } - - @Expose(target = ExposeTarget.STATIC) - public static FunctionValue __async(Arguments args) { - return new AsyncFunctionLib(args.convert(0, FunctionValue.class)); - } - @Expose(target = ExposeTarget.STATIC) - public static FunctionValue __asyncGenerator(Arguments args) { - return new AsyncGeneratorFunctionLib(args.convert(0, CodeFunction.class)); - } - @Expose(target = ExposeTarget.STATIC) - public static FunctionValue __generator(Arguments args) { - return new GeneratorFunctionLib(args.convert(0, CodeFunction.class)); - } - - @ExposeConstructor - public static Object __constructor(Arguments args) { - var parts = args.convert(String.class); - if (parts.length == 0) parts = new String[] { "" }; - - var src = "return function("; - - for (var i = 0; i < parts.length - 1; i++) { - if (i != 0) src += ","; - src += parts[i]; - } - - src += "){" + parts[parts.length - 1] + "}"; - - var body = Compiler.get(args.env).compile(new Filename("jscript", "func/" + i++), src); - var func = new CodeFunction(args.env, "", body, new ValueVariable[0]); - return Values.call(args.env, func, null); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java deleted file mode 100644 index 1316cfb..0000000 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java +++ /dev/null @@ -1,31 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("GeneratorFunction") -public class GeneratorFunctionLib extends FunctionValue { - public final CodeFunction func; - - @Override public Object call(Environment env, Object thisArg, Object ...args) { - var handler = new GeneratorLib(); - - var newArgs = new Object[args.length + 1]; - newArgs[0] = new NativeFunction("yield", handler::yield); - System.arraycopy(args, 0, newArgs, 1, args.length); - - handler.frame = new Frame(env, thisArg, newArgs, func); - return handler; - } - - public GeneratorFunctionLib(CodeFunction func) { - super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); - this.func = func; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java deleted file mode 100644 index be8b71c..0000000 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java +++ /dev/null @@ -1,82 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Generator") -public class GeneratorLib { - private boolean yielding = true; - private boolean done = false; - public Frame frame; - - private ObjectValue next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) { - if (done) { - if (inducedError != Values.NO_RETURN) throw inducedError; - var res = new ObjectValue(); - res.defineProperty(env, "done", true); - res.defineProperty(env, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn); - return res; - } - - Object res = null; - yielding = false; - - frame.onPush(); - while (!yielding) { - try { - if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); - else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue); - else if (inducedError != null) res = frame.induceError(inducedError); - else res = frame.next(); - - inducedReturn = Values.NO_RETURN; - inducedError = null; - if (res != Values.NO_RETURN) { - done = true; - break; - } - } - catch (EngineException e) { - done = true; - throw e; - } - } - frame.onPop(); - - if (done) frame = null; - else res = frame.pop(); - - var obj = new ObjectValue(); - obj.defineProperty(env, "done", done); - obj.defineProperty(env, "value", res); - return obj; - } - - @Expose public ObjectValue __next(Arguments args) { - if (args.n() == 0) return next(args.env, Values.NO_RETURN, Values.NO_RETURN, null); - else return next(args.env, args.get(0), Values.NO_RETURN, null); - } - @Expose public ObjectValue __throw(Arguments args) { - return next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env)); - } - @Expose public ObjectValue __return(Arguments args) { - return next(args.env, Values.NO_RETURN, args.get(0), null); - } - - @Override public String toString() { - if (done) return "Generator [closed]"; - if (yielding) return "Generator [suspended]"; - return "Generator [running]"; - } - - public Object yield(Arguments args) { - this.yielding = true; - return args.get(0); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/Internals.java b/src/java/me/topchetoeu/jscript/lib/Internals.java deleted file mode 100644 index e2633b6..0000000 --- a/src/java/me/topchetoeu/jscript/lib/Internals.java +++ /dev/null @@ -1,229 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.HashMap; - -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; -import me.topchetoeu.jscript.utils.modules.ModuleRepo; - -public class Internals { - private static final Key> THREADS = new Key<>(); - private static final Key I = new Key<>(); - - @Expose(target = ExposeTarget.STATIC) - public static Object __require(Arguments args) { - var repo = ModuleRepo.get(args.env); - - if (repo != null) { - var res = repo.getModule(args.env, ModuleRepo.cwd(args.env), args.getString(0)); - res.load(args.env); - return res.value(); - } - - else throw EngineException.ofError("Modules are not supported."); - } - - @Expose(target = ExposeTarget.STATIC) - public static Thread __setTimeout(Arguments args) { - var func = args.convert(0, FunctionValue.class); - var delay = args.getDouble(1); - var arguments = args.slice(2).args; - - if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - - var thread = new Thread(() -> { - var ms = (long)delay; - var ns = (int)((delay - ms) * 10000000); - - try { Thread.sleep(ms, ns); } - catch (InterruptedException e) { return; } - - args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false); - }); - - thread.start(); - - args.env.init(I, 1); - args.env.init(THREADS, new HashMap<>()); - var i = args.env.get(I); - - args.env.add(I, i + 1); - args.env.get(THREADS).put(i, thread); - - return thread; - } - @Expose(target = ExposeTarget.STATIC) - public static Thread __setInterval(Arguments args) { - var func = args.convert(0, FunctionValue.class); - var delay = args.getDouble(1); - var arguments = args.slice(2).args; - - if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - - var thread = new Thread(() -> { - var ms = (long)delay; - var ns = (int)((delay - ms) * 10000000); - - while (true) { - try { - Thread.sleep(ms, ns); - } - catch (InterruptedException e) { return; } - - args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false); - } - }); - - thread.start(); - - args.env.init(I, 1); - args.env.init(THREADS, new HashMap<>()); - var i = args.env.get(I); - - args.env.add(I, i + 1); - args.env.get(THREADS).put(i, thread); - - return thread; - } - - @Expose(target = ExposeTarget.STATIC) - public static void __clearTimeout(Arguments args) { - var i = args.getInt(0); - HashMap map = args.env.get(THREADS); - if (map == null) return; - - var thread = map.get(i); - if (thread == null) return; - - thread.interrupt(); - map.remove(i); - } - @Expose(target = ExposeTarget.STATIC) - public static void __clearInterval(Arguments args) { - __clearTimeout(args); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __parseInt(Arguments args) { - return NumberLib.__parseInt(args); - } - @Expose(target = ExposeTarget.STATIC) - public static double __parseFloat(Arguments args) { - return NumberLib.__parseFloat(args); - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isNaN(Arguments args) { - return NumberLib.__isNaN(args); - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isFinite(Arguments args) { - return NumberLib.__isFinite(args); - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isInfinite(Arguments args) { - return NumberLib.__isInfinite(args); - } - - @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) - public static FileLib __stdin(Arguments args) { - return new FileLib(Filesystem.get(args.env).open("std://in", Mode.READ)); - } - @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) - public static FileLib __stdout(Arguments args) { - return new FileLib(Filesystem.get(args.env).open("std://out", Mode.READ)); - } - @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) - public static FileLib __stderr(Arguments args) { - return new FileLib(Filesystem.get(args.env).open("std://err", Mode.READ)); - } - - @ExposeField(target = ExposeTarget.STATIC) - public static double __NaN = Double.NaN; - @ExposeField(target = ExposeTarget.STATIC) - public static double __Infinity = Double.POSITIVE_INFINITY; - - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURIComponent(Arguments args) { - return EncodingLib.__encodeURIComponent(args); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURIComponent(Arguments args) { - return EncodingLib.__decodeURIComponent(args); - } - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURI(Arguments args) { - return EncodingLib.__encodeURI(args); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURI(Arguments args) { - return EncodingLib.__decodeURI(args); - } - - public static Environment apply(Environment env) { - var wp = new NativeWrapperProvider(); - var glob = new GlobalScope(wp.getNamespace(Internals.class)); - - glob.define(null, "Math", false, wp.getNamespace(MathLib.class)); - glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class)); - glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class)); - glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class)); - - glob.define(null, false, wp.getConstr(FileLib.class)); - - glob.define(null, false, wp.getConstr(DateLib.class)); - glob.define(null, false, wp.getConstr(ObjectLib.class)); - glob.define(null, false, wp.getConstr(FunctionLib.class)); - glob.define(null, false, wp.getConstr(ArrayLib.class)); - - glob.define(null, false, wp.getConstr(BooleanLib.class)); - glob.define(null, false, wp.getConstr(NumberLib.class)); - glob.define(null, false, wp.getConstr(StringLib.class)); - glob.define(null, false, wp.getConstr(SymbolLib.class)); - - glob.define(null, false, wp.getConstr(PromiseLib.class)); - glob.define(null, false, wp.getConstr(RegExpLib.class)); - glob.define(null, false, wp.getConstr(MapLib.class)); - glob.define(null, false, wp.getConstr(SetLib.class)); - - glob.define(null, false, wp.getConstr(ErrorLib.class)); - glob.define(null, false, wp.getConstr(SyntaxErrorLib.class)); - glob.define(null, false, wp.getConstr(TypeErrorLib.class)); - glob.define(null, false, wp.getConstr(RangeErrorLib.class)); - - env.add(Environment.OBJECT_PROTO, wp.getProto(ObjectLib.class)); - env.add(Environment.FUNCTION_PROTO, wp.getProto(FunctionLib.class)); - env.add(Environment.ARRAY_PROTO, wp.getProto(ArrayLib.class)); - - env.add(Environment.BOOL_PROTO, wp.getProto(BooleanLib.class)); - env.add(Environment.NUMBER_PROTO, wp.getProto(NumberLib.class)); - env.add(Environment.STRING_PROTO, wp.getProto(StringLib.class)); - env.add(Environment.SYMBOL_PROTO, wp.getProto(SymbolLib.class)); - - env.add(Environment.ERROR_PROTO, wp.getProto(ErrorLib.class)); - env.add(Environment.SYNTAX_ERR_PROTO, wp.getProto(SyntaxErrorLib.class)); - env.add(Environment.TYPE_ERR_PROTO, wp.getProto(TypeErrorLib.class)); - env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class)); - - env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class)); - Values.setPrototype(Environment.empty(), wp.getProto(ObjectLib.class), null); - - env.add(NativeWrapperProvider.KEY, wp); - env.add(GlobalScope.KEY, glob); - - return env; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/JSONLib.java b/src/java/me/topchetoeu/jscript/lib/JSONLib.java deleted file mode 100644 index 11bbb55..0000000 --- a/src/java/me/topchetoeu/jscript/lib/JSONLib.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("JSON") -public class JSONLib { - @Expose(target = ExposeTarget.STATIC) - public static Object __parse(Arguments args) { - try { - return JSON.toJs(JSON.parse(null, args.getString(0))); - } - catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); } - } - @Expose(target = ExposeTarget.STATIC) - public static String __stringify(Arguments args) { - return JSON.stringify(JSON.fromJs(args.env, args.get(0))); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/MapLib.java b/src/java/me/topchetoeu/jscript/lib/MapLib.java deleted file mode 100644 index bfda4f7..0000000 --- a/src/java/me/topchetoeu/jscript/lib/MapLib.java +++ /dev/null @@ -1,87 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Map") -public class MapLib { - private LinkedHashMap map = new LinkedHashMap<>(); - - @Expose("@@Symbol.iterator") - public ObjectValue __iterator(Arguments args) { - return this.__entries(args); - } - - @Expose public void __clear() { - map.clear(); - } - @Expose public boolean __delete(Arguments args) { - var key = args.get(0); - if (map.containsKey(key)) { - map.remove(key); - return true; - } - return false; - } - - @Expose public ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.env, map - .entrySet() - .stream() - .map(v -> new ArrayValue(args.env, v.getKey(), v.getValue())) - .collect(Collectors.toList()) - ); - } - @Expose public ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.env, map.keySet()); - } - @Expose public ObjectValue __values(Arguments args) { - return Values.toJSIterator(args.env, map.values()); - } - - @Expose public Object __get(Arguments args) { - return map.get(args.get(0)); - } - @Expose public MapLib __set(Arguments args) { - map.put(args.get(0), args.get(1)); - return this; - } - @Expose public boolean __has(Arguments args) { - return map.containsKey(args.get(0)); - } - - @Expose(type = ExposeType.GETTER) - public int __size() { - return map.size(); - } - - @Expose public void __forEach(Arguments args) { - var keys = new ArrayList<>(map.keySet()); - - for (var el : keys) Values.call(args.env, args.get(0), args.get(1), map.get(el), el, args.self); - } - - public MapLib(Environment env, Object iterable) { - for (var el : Values.fromJSIterator(env, iterable)) { - try { - map.put(Values.getMember(env, el, 0), Values.getMember(env, el, 1)); - } - catch (IllegalArgumentException e) { } - } - } - - @ExposeConstructor public static MapLib __constructor(Arguments args) { - return new MapLib(args.env, args.get(0)); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/MathLib.java b/src/java/me/topchetoeu/jscript/lib/MathLib.java deleted file mode 100644 index a0b5d53..0000000 --- a/src/java/me/topchetoeu/jscript/lib/MathLib.java +++ /dev/null @@ -1,211 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Math") -public class MathLib { - @ExposeField(target = ExposeTarget.STATIC) - public static final double __E = Math.E; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __PI = Math.PI; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __SQRT2 = Math.sqrt(2); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __SQRT1_2 = Math.sqrt(.5); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LN2 = Math.log(2); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LN10 = Math.log(10); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LOG2E = Math.log(Math.E) / __LN2; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LOG10E = Math.log10(Math.E); - - @Expose(target = ExposeTarget.STATIC) - public static double __asin(Arguments args) { - return Math.asin(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __acos(Arguments args) { - return Math.acos(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __atan(Arguments args) { - return Math.atan(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __atan2(Arguments args) { - var x = args.getDouble(1); - var y = args.getDouble(0); - - if (x == 0) { - if (y == 0) return Double.NaN; - return Math.signum(y) * Math.PI / 2; - } - else { - var val = Math.atan(y / x); - if (x > 0) return val; - else if (y < 0) return val - Math.PI; - else return val + Math.PI; - } - - } - - @Expose(target = ExposeTarget.STATIC) - public static double __asinh(Arguments args) { - var x = args.getDouble(0); - return Math.log(x + Math.sqrt(x * x + 1)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __acosh(Arguments args) { - var x = args.getDouble(0); - return Math.log(x + Math.sqrt(x * x - 1)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __atanh(Arguments args) { - var x = args.getDouble(0); - - if (x <= -1 || x >= 1) return Double.NaN; - return .5 * Math.log((1 + x) / (1 - x)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sin(Arguments args) { - return Math.sin(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __cos(Arguments args) { - return Math.cos(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __tan(Arguments args) { - return Math.tan(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sinh(Arguments args) { - return Math.sinh(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __cosh(Arguments args) { - return Math.cosh(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __tanh(Arguments args) { - return Math.tanh(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sqrt(Arguments args) { - return Math.sqrt(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __cbrt(Arguments args) { - return Math.cbrt(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __hypot(Arguments args) { - var res = 0.; - for (var i = 0; i < args.n(); i++) { - var val = args.getDouble(i); - res += val * val; - } - return Math.sqrt(res); - } - @Expose(target = ExposeTarget.STATIC) - public static int __imul(Arguments args) { return args.getInt(0) * args.getInt(1); } - - @Expose(target = ExposeTarget.STATIC) - public static double __exp(Arguments args) { - return Math.exp(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __expm1(Arguments args) { - return Math.expm1(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __pow(Arguments args) { return Math.pow(args.getDouble(0), args.getDouble(1)); } - - @Expose(target = ExposeTarget.STATIC) - public static double __log(Arguments args) { - return Math.log(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __log10(Arguments args) { - return Math.log10(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __log1p(Arguments args) { - return Math.log1p(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __log2(Arguments args) { - return Math.log(args.getDouble(0)) / __LN2; - } - - @Expose(target = ExposeTarget.STATIC) - public static double __ceil(Arguments args) { - return Math.ceil(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __floor(Arguments args) { - return Math.floor(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __round(Arguments args) { - return Math.round(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static float __fround(Arguments args) { - return (float)args.getDouble(0); - } - @Expose(target = ExposeTarget.STATIC) - public static double __trunc(Arguments args) { - var x = args.getDouble(0); - return Math.floor(Math.abs(x)) * Math.signum(x); - } - @Expose(target = ExposeTarget.STATIC) - public static double __abs(Arguments args) { - return Math.abs(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __max(Arguments args) { - var res = Double.NEGATIVE_INFINITY; - - for (var i = 0; i < args.n(); i++) { - var el = args.getDouble(i); - if (el > res) res = el; - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static double __min(Arguments args) { - var res = Double.POSITIVE_INFINITY; - - for (var i = 0; i < args.n(); i++) { - var el = args.getDouble(i); - if (el < res) res = el; - } - - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sign(Arguments args) { - return Math.signum(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __random() { return Math.random(); } - @Expose(target = ExposeTarget.STATIC) - public static int __clz32(Arguments args) { - return Integer.numberOfLeadingZeros(args.getInt(0)); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/NumberLib.java b/src/java/me/topchetoeu/jscript/lib/NumberLib.java deleted file mode 100644 index 1a7aac1..0000000 --- a/src/java/me/topchetoeu/jscript/lib/NumberLib.java +++ /dev/null @@ -1,103 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.text.NumberFormat; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Number") -public class NumberLib { - @ExposeField(target = ExposeTarget.STATIC) - public static final double __EPSILON = Math.ulp(1.0); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MAX_SAFE_INTEGER = 9007199254740991.; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MIN_SAFE_INTEGER = -__MAX_SAFE_INTEGER; - // lmao big number go brrr - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MAX_VALUE = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MIN_VALUE = -__MAX_VALUE; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __NaN = 0. / 0; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __NEGATIVE_INFINITY = -1. / 0; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __POSITIVE_INFINITY = 1. / 0; - - public final double value; - - @Override public String toString() { return value + ""; } - - public NumberLib(double val) { - this.value = val; - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isFinite(Arguments args) { return Double.isFinite(args.getDouble(0)); } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isInfinite(Arguments args) { return Double.isInfinite(args.getDouble(0)); } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isNaN(Arguments args) { return Double.isNaN(args.getDouble(0)); } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isSafeInteger(Arguments args) { - return args.getDouble(0) > __MIN_SAFE_INTEGER && args.getDouble(0) < __MAX_SAFE_INTEGER; - } - - @Expose(target = ExposeTarget.STATIC) - public static double __parseFloat(Arguments args) { - return args.getDouble(0); - } - @Expose(target = ExposeTarget.STATIC) - public static double __parseInt(Arguments args) { - var radix = args.getInt(1, 10); - - if (radix < 2 || radix > 36) return Double.NaN; - else { - long res = 0; - - for (var c : args.getString(0).toCharArray()) { - var digit = 0; - - if (c >= '0' && c <= '9') digit = c - '0'; - else if (c >= 'a' && c <= 'z') digit = c - 'a' + 10; - else if (c >= 'A' && c <= 'Z') digit = c - 'A' + 10; - else break; - - if (digit > radix) break; - - res *= radix; - res += digit; - } - - return res; - } - } - - @ExposeConstructor public static Object __constructor(Arguments args) { - if (args.self instanceof ObjectValue) return new NumberLib(args.getDouble(0)); - else return args.getDouble(0); - } - @Expose public static String __toString(Arguments args) { - return Values.toString(args.env, args.self); - } - @Expose public static String __toFixed(Arguments args) { - var digits = args.getInt(0, 0); - - var nf = NumberFormat.getNumberInstance(); - nf.setMinimumFractionDigits(digits); - nf.setMaximumFractionDigits(digits); - - return nf.format(args.getDouble(-1)); - } - @Expose public static double __valueOf(Arguments args) { - if (Values.isWrapper(args.self, NumberLib.class)) return Values.wrapper(args.self, NumberLib.class).value; - else return Values.toNumber(args.env, args.self); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/ObjectLib.java b/src/java/me/topchetoeu/jscript/lib/ObjectLib.java deleted file mode 100644 index 2dfbafa..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ObjectLib.java +++ /dev/null @@ -1,273 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Object") -public class ObjectLib { - @Expose(target = ExposeTarget.STATIC) - public static Object __assign(Arguments args) { - for (var obj : args.slice(1).args) { - for (var key : Values.getMembers(args.env, obj, true, true)) { - Values.setMember(args.env, args.get(0), key, Values.getMember(args.env, obj, key)); - } - } - return args.get(0); - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __create(Arguments args) { - var obj = new ObjectValue(); - Values.setPrototype(args.env, obj, args.get(0)); - - if (args.n() >= 1) { - var newArgs = new Object[args.n()]; - System.arraycopy(args.args, 1, args, 1, args.n() - 1); - newArgs[0] = obj; - - __defineProperties(new Arguments(args.env, null, newArgs)); - } - - return obj; - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __defineProperty(Arguments args) { - var obj = args.convert(0, ObjectValue.class); - var key = args.get(1); - var attrib = args.convert(2, ObjectValue.class); - - var hasVal = Values.hasMember(args.env, attrib, "value", false); - var hasGet = Values.hasMember(args.env, attrib, "get", false); - var hasSet = Values.hasMember(args.env, attrib, "set", false); - - if (hasVal) { - if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property."); - if (!obj.defineProperty( - args.env, key, - Values.getMember(args.env, attrib, "value"), - Values.toBoolean(Values.getMember(args.env, attrib, "writable")), - Values.toBoolean(Values.getMember(args.env, attrib, "configurable")), - Values.toBoolean(Values.getMember(args.env, attrib, "enumerable")) - )) throw EngineException.ofType("Can't define property '" + key + "'."); - } - else { - var get = Values.getMember(args.env, attrib, "get"); - var set = Values.getMember(args.env, attrib, "set"); - if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function."); - if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function."); - - if (!obj.defineProperty( - args.env, key, - (FunctionValue)get, (FunctionValue)set, - Values.toBoolean(Values.getMember(args.env, attrib, "configurable")), - Values.toBoolean(Values.getMember(args.env, attrib, "enumerable")) - )) throw EngineException.ofType("Can't define property '" + key + "'."); - } - - return obj; - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __defineProperties(Arguments args) { - var obj = args.convert(0, ObjectValue.class); - var attrib = args.get(1); - - for (var key : Values.getMembers(null, attrib, false, false)) { - __defineProperty(new Arguments(args.env, null, obj, key, Values.getMember(args.env, attrib, key))); - } - - return obj; - } - - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __keys(Arguments args) { - var obj = args.get(0); - var all = args.getBoolean(1); - var res = new ArrayValue(); - - for (var key : Values.getMembers(args.env, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __entries(Arguments args) { - var res = new ArrayValue(); - var obj = args.get(0); - var all = args.getBoolean(1); - - for (var key : Values.getMembers(args.env, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), new ArrayValue(args.env, key, Values.getMember(args.env, obj, key))); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __values(Arguments args) { - var res = new ArrayValue(); - var obj = args.get(0); - var all = args.getBoolean(1); - - for (var key : Values.getMembers(args.env, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), Values.getMember(args.env, obj, key)); - } - - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __getOwnPropertyDescriptor(Arguments args) { - return Values.getMemberDescriptor(args.env, args.get(0), args.get(1)); - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __getOwnPropertyDescriptors(Arguments args) { - var res = new ObjectValue(); - var obj = args.get(0); - for (var key : Values.getMembers(args.env, obj, true, true)) { - res.defineProperty(args.env, key, Values.getMemberDescriptor(args.env, obj, key)); - } - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __getOwnPropertyNames(Arguments args) { - var res = new ArrayValue(); - var obj = args.get(0); - var all = args.getBoolean(1); - - for (var key : Values.getMembers(args.env, obj, true, true)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __getOwnPropertySymbols(Arguments args) { - var obj = args.get(0); - var res = new ArrayValue(); - - for (var key : Values.getMembers(args.env, obj, true, true)) { - if (key instanceof Symbol) res.set(args.env, res.size(), key); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __hasOwn(Arguments args) { - return Values.hasMember(args.env, args.get(0), args.get(1), true); - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __getPrototypeOf(Arguments args) { - return Values.getPrototype(args.env, args.get(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static Object __setPrototypeOf(Arguments args) { - Values.setPrototype(args.env, args.get(0), args.get(1)); - return args.get(0); - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __fromEntries(Arguments args) { - var res = new ObjectValue(); - - for (var el : Values.fromJSIterator(args.env, args.get(0))) { - if (el instanceof ArrayValue) { - res.defineProperty(args.env, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1)); - } - } - - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static Object __preventExtensions(Arguments args) { - if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).preventExtensions(); - return args.get(0); - } - @Expose(target = ExposeTarget.STATIC) - public static Object __seal(Arguments args) { - if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).seal(); - return args.get(0); - } - @Expose(target = ExposeTarget.STATIC) - public static Object __freeze(Arguments args) { - if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).freeze(); - return args.get(0); - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isExtensible(Arguments args) { - var obj = args.get(0); - if (!(obj instanceof ObjectValue)) return false; - return ((ObjectValue)obj).extensible(); - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isSealed(Arguments args) { - var obj = args.get(0); - - if (!(obj instanceof ObjectValue)) return true; - var _obj = (ObjectValue)obj; - - if (_obj.extensible()) return false; - - for (var key : _obj.keys(true)) { - if (_obj.memberConfigurable(key)) return false; - } - - return true; - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isFrozen(Arguments args) { - var obj = args.get(0); - - if (!(obj instanceof ObjectValue)) return true; - var _obj = (ObjectValue)obj; - - if (_obj.extensible()) return false; - - for (var key : _obj.keys(true)) { - if (_obj.memberConfigurable(key)) return false; - if (_obj.memberWritable(key)) return false; - } - - return true; - } - - @Expose - public static Object __valueOf(Arguments args) { - return args.self; - } - @Expose - public static String __toString(Arguments args) { - var name = Values.getMember(args.env, args.self, Symbol.get("Symbol.typeName")); - if (name == null) name = "Unknown"; - else name = Values.toString(args.env, name); - - return "[object " + name + "]"; - } - @Expose - public static boolean __hasOwnProperty(Arguments args) { - return Values.hasMember(args.env, args.self, args.get(0), true); - } - - @ExposeConstructor - public static Object __constructor(Arguments args) { - var arg = args.get(0); - if (arg == null || arg == Values.NULL) return new ObjectValue(); - else if (arg instanceof Boolean) return new BooleanLib((boolean)arg); - else if (arg instanceof Number) return new NumberLib(((Number)arg).doubleValue()); - else if (arg instanceof String) return new StringLib((String)arg); - else if (arg instanceof Symbol) return new SymbolLib((Symbol)arg); - else return arg; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java b/src/java/me/topchetoeu/jscript/lib/PromiseLib.java deleted file mode 100644 index 5bd95cb..0000000 --- a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java +++ /dev/null @@ -1,403 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.List; - -import me.topchetoeu.jscript.common.ResultRunnable; -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Promise") -public class PromiseLib { - public static interface Handle { - void onFulfil(Object val); - void onReject(EngineException err); - - default Handle defer(Environment loop) { - var self = this; - - return new Handle() { - @Override public void onFulfil(Object val) { - if (!loop.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - loop.get(EventLoop.KEY).pushMsg(() -> self.onFulfil(val), true); - } - @Override public void onReject(EngineException val) { - if (!loop.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - loop.get(EventLoop.KEY).pushMsg(() -> self.onReject(val), true); - } - }; - } - } - - private static final int STATE_PENDING = 0; - private static final int STATE_FULFILLED = 1; - private static final int STATE_REJECTED = 2; - - private List handles = new ArrayList<>(); - - private int state = STATE_PENDING; - private boolean handled = false; - private Object val; - - private void resolveSynchronized(Environment env, Object val, int newState) { - this.val = val; - this.state = newState; - - for (var handle : handles) { - if (newState == STATE_FULFILLED) handle.onFulfil(val); - if (newState == STATE_REJECTED) { - handle.onReject((EngineException)val); - handled = true; - } - } - - if (state == STATE_REJECTED && !handled) { - Values.printError(((EngineException)val).setEnvironment(env), "(in promise)"); - } - - handles = null; - - // ctx.get(EventLoop.KEY).pushMsg(() -> { - // if (!ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - - - // handles = null; - // }, true); - - } - private synchronized void resolve(Environment env, Object val, int newState) { - if (this.state != STATE_PENDING || newState == STATE_PENDING) return; - - handle(env, val, new Handle() { - @Override public void onFulfil(Object val) { - resolveSynchronized(env, val, newState); - } - @Override public void onReject(EngineException err) { - resolveSynchronized(env, val, STATE_REJECTED); - } - }); - } - - public synchronized void fulfill(Environment env, Object val) { - resolve(env, val, STATE_FULFILLED); - } - public synchronized void reject(Environment env, EngineException val) { - resolve(env, val, STATE_REJECTED); - } - - private void handle(Handle handle) { - if (state == STATE_FULFILLED) handle.onFulfil(val); - else if (state == STATE_REJECTED) { - handle.onReject((EngineException)val); - handled = true; - } - else handles.add(handle); - } - - @Override public String toString() { - if (state == STATE_PENDING) return "Promise (pending)"; - else if (state == STATE_FULFILLED) return "Promise (fulfilled)"; - else return "Promise (rejected)"; - } - - public PromiseLib() { - this.state = STATE_PENDING; - this.val = null; - } - - public static PromiseLib await(Environment env, ResultRunnable runner) { - var res = new PromiseLib(); - - new Thread(() -> { - try { - res.fulfill(env, runner.run()); - } - catch (EngineException e) { - res.reject(env, e); - } - catch (Exception e) { - if (e instanceof InterruptException) throw e; - else { - res.reject(env, EngineException.ofError("Native code failed with " + e.getMessage())); - } - } - }, "Promisifier").start(); - - return res; - } - public static PromiseLib await(Environment env, Runnable runner) { - return await(env, () -> { - runner.run(); - return null; - }); - } - - public static void handle(Environment env, Object obj, Handle handle) { - if (Values.isWrapper(obj, PromiseLib.class)) { - var promise = Values.wrapper(obj, PromiseLib.class); - handle(env, promise, handle); - return; - } - if (obj instanceof PromiseLib) { - ((PromiseLib)obj).handle(handle); - return; - } - - var rethrow = new boolean[1]; - - try { - var then = Values.getMember(env, obj, "then"); - - if (then instanceof FunctionValue) Values.call(env, then, obj, - new NativeFunction(args -> { - try { handle.onFulfil(args.get(0)); } - catch (Exception e) { - rethrow[0] = true; - throw e; - } - return null; - }), - new NativeFunction(args -> { - try { handle.onReject(new EngineException(args.get(0))); } - catch (Exception e) { - rethrow[0] = true; - throw e; - } - return null; - }) - ); - else handle.onFulfil(obj); - - return; - } - catch (Exception e) { - if (rethrow[0]) throw e; - } - - handle.onFulfil(obj); - } - - public static PromiseLib ofResolved(Environment ctx, Object value) { - var res = new PromiseLib(); - res.fulfill(ctx, value); - return res; - } - public static PromiseLib ofRejected(Environment ctx, EngineException value) { - var res = new PromiseLib(); - res.reject(ctx, value); - return res; - } - - @Expose(value = "resolve", target = ExposeTarget.STATIC) - public static PromiseLib __ofResolved(Arguments args) { - return ofResolved(args.env, args.get(0)); - } - @Expose(value = "reject", target = ExposeTarget.STATIC) - public static PromiseLib __ofRejected(Arguments args) { - return ofRejected(args.env, new EngineException(args.get(0)).setEnvironment(args.env)); - } - - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __any(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - - if (promises.size() == 0) return ofRejected(args.env, EngineException.ofError("No promises passed to 'Promise.any'.").setEnvironment(args.env)); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - var errors = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (res.state != STATE_PENDING) break; - - handle(args.env, val, new Handle() { - public void onFulfil(Object val) { res.fulfill(args.env, val); } - public void onReject(EngineException err) { - errors.set(args.env, index, err.value); - n[0]--; - if (n[0] <= 0) res.reject(args.env, new EngineException(errors).setEnvironment(args.env)); - } - }); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __race(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - var res = new PromiseLib(); - - for (var i = 0; i < promises.size(); i++) { - var val = promises.get(i); - if (res.state != STATE_PENDING) break; - - handle(args.env, val, new Handle() { - @Override public void onFulfil(Object val) { res.fulfill(args.env, val); } - @Override public void onReject(EngineException err) { res.reject(args.env, err); } - }); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __all(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - if (res.state != STATE_PENDING) break; - - var index = i; - var val = promises.get(i); - - handle(args.env, val, new Handle() { - @Override public void onFulfil(Object val) { - result.set(args.env, index, val); - n[0]--; - if (n[0] <= 0) res.fulfill(args.env, result); - } - @Override public void onReject(EngineException err) { - res.reject(args.env, err); - } - }); - } - - if (n[0] <= 0) res.fulfill(args.env, result); - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __allSettled(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - if (res.state != STATE_PENDING) break; - - var index = i; - - handle(args.env, promises.get(i), new Handle() { - @Override public void onFulfil(Object val) { - var desc = new ObjectValue(); - desc.defineProperty(args.env, "status", "fulfilled"); - desc.defineProperty(args.env, "value", val); - - result.set(args.env, index, desc); - - n[0]--; - if (n[0] <= 0) res.fulfill(args.env, res); - } - @Override public void onReject(EngineException err) { - var desc = new ObjectValue(); - desc.defineProperty(args.env, "status", "reject"); - desc.defineProperty(args.env, "value", err.value); - - result.set(args.env, index, desc); - - n[0]--; - if (n[0] <= 0) res.fulfill(args.env, res); - } - }); - } - - if (n[0] <= 0) res.fulfill(args.env, result); - - return res; - } - - @Expose - public static Object __then(Arguments args) { - var onFulfill = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null; - var onReject = args.get(1) instanceof FunctionValue ? args.convert(1, FunctionValue.class) : null; - - var res = new PromiseLib(); - - handle(args.env, args.self, new Handle() { - @Override public void onFulfil(Object val) { - try { res.fulfill(args.env, onFulfill.call(args.env, null, val)); } - catch (EngineException e) { res.reject(args.env, e); } - } - @Override public void onReject(EngineException err) { - try { res.fulfill(args.env, onReject.call(args.env, null, err.value)); } - catch (EngineException e) { res.reject(args.env, e); } - } - }.defer(args.env)); - - return res; - } - @Expose - public static Object __catch(Arguments args) { - return __then(new Arguments(args.env, args.self, null, args.get(0))); - } - @Expose - public static Object __finally(Arguments args) { - var func = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null; - - var res = new PromiseLib(); - - handle(args.env, args.self, new Handle() { - @Override public void onFulfil(Object val) { - try { - func.call(args.env); - res.fulfill(args.env, val); - } - catch (EngineException e) { res.reject(args.env, e); } - } - @Override public void onReject(EngineException err) { - try { - func.call(args.env); - res.reject(args.env, err); - } - catch (EngineException e) { res.reject(args.env, e); } - } - }.defer(args.env)); - - return res; - } - - @ExposeConstructor - public static PromiseLib __constructor(Arguments args) { - var func = args.convert(0, FunctionValue.class); - var res = new PromiseLib(); - - try { - func.call( - args.env, null, - new NativeFunction(null, _args -> { - res.fulfill(_args.env, _args.get(0)); - return null; - }), - new NativeFunction(null, _args -> { - res.reject(_args.env, new EngineException(_args.get(0)).setEnvironment(_args.env)); - return null; - }) - ); - } - catch (EngineException e) { - res.reject(args.env, e); - } - - return res; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java b/src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java deleted file mode 100644 index 78877ce..0000000 --- a/src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("RangeError") -public class RangeErrorLib extends ErrorLib { - @ExposeField public static final String __name = "RangeError"; - - @ExposeConstructor public static ObjectValue constructor(Arguments args) { - var target = ErrorLib.__constructor(args); - target.setPrototype(PlaceholderProto.RANGE_ERROR); - return target; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/RegExpLib.java b/src/java/me/topchetoeu/jscript/lib/RegExpLib.java deleted file mode 100644 index d4cf695..0000000 --- a/src/java/me/topchetoeu/jscript/lib/RegExpLib.java +++ /dev/null @@ -1,354 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.regex.Pattern; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeWrapper; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("RegExp") -public class RegExpLib { - // I used Regex to analyze Regex - private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL); - private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]"); - - private Pattern pattern; - private String[] namedGroups; - private int flags; - - public int lastI = 0; - public final String source; - public final boolean hasIndices; - public final boolean global; - public final boolean sticky; - - @Expose(type = ExposeType.GETTER) - public int __lastIndex() { return lastI; } - @Expose(type = ExposeType.SETTER) - public void __setLastIndex(Arguments args) { lastI = args.getInt(0); } - @Expose(type = ExposeType.GETTER) - public String __source() { return source; } - - @Expose(type = ExposeType.GETTER) - public boolean __ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __multiline() { return (flags & Pattern.MULTILINE) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __dotAll() { return (flags & Pattern.DOTALL) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __global() { return global; } - @Expose(type = ExposeType.GETTER) - public boolean __sticky() { return sticky; } - @Expose(type = ExposeType.GETTER) - public final String __flags() { - String res = ""; - if (hasIndices) res += 'd'; - if (global) res += 'g'; - if (__ignoreCase()) res += 'i'; - if (__multiline()) res += 'm'; - if (__dotAll()) res += 's'; - if (__unicode()) res += 'u'; - if (sticky) res += 'y'; - return res; - } - - @Expose public Object __exec(Arguments args) { - var str = args.getString(0); - var matcher = pattern.matcher(str); - if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) { - lastI = 0; - return Values.NULL; - } - if (sticky || global) { - lastI = matcher.end(); - if (matcher.end() == matcher.start()) lastI++; - } - - var obj = new ArrayValue(); - ObjectValue groups = null; - - for (var el : namedGroups) { - if (groups == null) groups = new ObjectValue(); - try { groups.defineProperty(null, el, matcher.group(el)); } - catch (IllegalArgumentException e) { } - } - - - for (int i = 0; i < matcher.groupCount() + 1; i++) { - obj.set(null, i, matcher.group(i)); - } - obj.defineProperty(null, "groups", groups); - obj.defineProperty(null, "index", matcher.start()); - obj.defineProperty(null, "input", str); - - if (hasIndices) { - var indices = new ArrayValue(); - for (int i = 0; i < matcher.groupCount() + 1; i++) { - indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i))); - } - var groupIndices = new ObjectValue(); - for (var el : namedGroups) { - groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el))); - } - indices.defineProperty(null, "groups", groupIndices); - obj.defineProperty(null, "indices", indices); - } - - return obj; - } - - @Expose public boolean __test(Arguments args) { - return this.__exec(args) != Values.NULL; - } - - @Expose("@@Symbol.match") public Object __match(Arguments args) { - if (this.global) { - var res = new ArrayValue(); - Object val; - while ((val = this.__exec(args)) != Values.NULL) { - res.set(args.env, res.size(), Values.getMember(args.env, val, 0)); - } - lastI = 0; - return res; - } - else { - var res = this.__exec(args); - if (!this.sticky) this.lastI = 0; - return res; - } - } - - @Expose("@@Symbol.matchAll") public Object __matchAll(Arguments args) { - var pattern = this.toGlobal(); - - return Values.toJSIterator(args.env, new Iterator() { - private Object val = null; - private boolean updated = false; - - private void update() { - if (!updated) val = pattern.__exec(args); - } - @Override public boolean hasNext() { - update(); - return val != Values.NULL; - } - @Override public Object next() { - update(); - updated = false; - return val; - } - }); - } - - @Expose("@@Symbol.split") public ArrayValue __split(Arguments args) { - var pattern = this.toGlobal(); - var target = args.getString(0); - var hasLimit = args.get(1) != null; - var lim = args.getInt(1); - var sensible = args.getBoolean(2); - - Object match; - int lastEnd = 0; - var res = new ArrayValue(); - - while ((match = pattern.__exec(args)) != Values.NULL) { - var added = new ArrayList(); - var arrMatch = (ArrayValue)match; - int index = (int)Values.toNumber(args.env, Values.getMember(args.env, match, "index")); - var matchVal = (String)arrMatch.get(0); - - if (index >= target.length()) break; - - if (matchVal.length() == 0 || index - lastEnd > 0) { - added.add(target.substring(lastEnd, pattern.lastI)); - if (pattern.lastI < target.length()) { - for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i)); - } - } - else { - for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i)); - } - - if (sensible) { - if (hasLimit && res.size() + added.size() >= lim) break; - else for (var i = 0; i < added.size(); i++) res.set(args.env, res.size(), added.get(i)); - } - else { - for (var i = 0; i < added.size(); i++) { - if (hasLimit && res.size() >= lim) return res; - else res.set(args.env, res.size(), added.get(i)); - } - } - lastEnd = pattern.lastI; - } - if (lastEnd < target.length()) { - res.set(args.env, res.size(), target.substring(lastEnd)); - } - return res; - } - - @Expose("@@Symbol.replace") public String __replace(Arguments args) { - var pattern = this.toIndexed(); - var target = args.getString(0); - var replacement = args.get(1); - Object match; - var lastEnd = 0; - var res = new StringBuilder(); - - while ((match = pattern.__exec(args)) != Values.NULL) { - var indices = (ArrayValue)((ArrayValue)Values.getMember(args.env, match, "indices")).get(0); - var arrMatch = (ArrayValue)match; - - var start = ((Number)indices.get(0)).intValue(); - var end = ((Number)indices.get(1)).intValue(); - - res.append(target.substring(lastEnd, start)); - if (replacement instanceof FunctionValue) { - var callArgs = new Object[arrMatch.size() + 2]; - callArgs[0] = target.substring(start, end); - arrMatch.copyTo(callArgs, 1, 1, arrMatch.size() - 1); - callArgs[callArgs.length - 2] = start; - callArgs[callArgs.length - 1] = target; - res.append(Values.toString(args.env, ((FunctionValue)replacement).call(args.env, null, callArgs))); - } - else { - res.append(Values.toString(args.env, replacement)); - } - lastEnd = end; - if (!pattern.global) break; - } - if (lastEnd < target.length()) { - res.append(target.substring(lastEnd)); - } - return res.toString(); - } - - // [Symbol.search](target, reverse, start) { - // const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; - // if (!reverse) { - // pattern.lastIndex = (start as any) | 0; - // const res = pattern.exec(target); - // if (res) return res.index; - // else return -1; - // } - // else { - // start ??= target.length; - // start |= 0; - // let res: RegExpResult | null = null; - // while (true) { - // const tmp = pattern.exec(target); - // if (tmp === null || tmp.index > start) break; - // res = tmp; - // } - // if (res && res.index <= start) return res.index; - // else return -1; - // } - // }, - - public RegExpLib toGlobal() { - return new RegExpLib(pattern, namedGroups, flags, source, hasIndices, true, sticky); - } - public RegExpLib toIndexed() { - return new RegExpLib(pattern, namedGroups, flags, source, true, global, sticky); - } - - public String toString() { - return "/" + source + "/" + __flags(); - } - - public RegExpLib(String pattern, String flags) { - if (pattern == null || pattern.equals("")) pattern = "(?:)"; - if (flags == null || flags.equals("")) flags = ""; - - this.flags = 0; - this.hasIndices = flags.contains("d"); - this.global = flags.contains("g"); - this.sticky = flags.contains("y"); - this.source = pattern; - - if (flags.contains("i")) this.flags |= Pattern.CASE_INSENSITIVE; - if (flags.contains("m")) this.flags |= Pattern.MULTILINE; - if (flags.contains("s")) this.flags |= Pattern.DOTALL; - if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS; - - if (pattern.equals("{(\\d+)}")) pattern = "\\{([0-9]+)\\}"; - this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags); - - var matcher = NAMED_PATTERN.matcher(source); - var groups = new ArrayList(); - - while (matcher.find()) { - if (!checkEscaped(source, matcher.start() - 1)) { - groups.add(matcher.group(1)); - } - } - - namedGroups = groups.toArray(String[]::new); - } - - private RegExpLib(Pattern pattern, String[] namedGroups, int flags, String source, boolean hasIndices, boolean global, boolean sticky) { - this.pattern = pattern; - this.namedGroups = namedGroups; - this.flags = flags; - this.source = source; - this.hasIndices = hasIndices; - this.global = global; - this.sticky = sticky; - } - public RegExpLib(String pattern) { this(pattern, null); } - public RegExpLib() { this(null, null); } - - @ExposeConstructor - public static RegExpLib __constructor(Arguments args) { - return new RegExpLib(cleanupPattern(args.env, args.get(0)), cleanupFlags(args.env, args.get(1))); - } - @Expose(target = ExposeTarget.STATIC) - public static RegExpLib __escape(Arguments args) { - return escape(Values.toString(args.env, args.get(0)), cleanupFlags(args.env, args.get(1))); - } - - private static String cleanupPattern(Environment env, Object val) { - if (val == null) return "(?:)"; - if (val instanceof RegExpLib) return ((RegExpLib)val).source; - if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) { - return ((RegExpLib)((NativeWrapper)val).wrapped).source; - } - var res = Values.toString(env, val); - if (res.equals("")) return "(?:)"; - return res; - } - private static String cleanupFlags(Environment env, Object val) { - if (val == null) return ""; - return Values.toString(env, val); - } - - private static boolean checkEscaped(String s, int pos) { - int n = 0; - - while (true) { - if (pos <= 0) break; - if (s.charAt(pos) != '\\') break; - n++; - pos--; - } - - return (n % 2) != 0; - } - - public static RegExpLib escape(String raw, String flags) { - return new RegExpLib(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/SetLib.java b/src/java/me/topchetoeu/jscript/lib/SetLib.java deleted file mode 100644 index d76113a..0000000 --- a/src/java/me/topchetoeu/jscript/lib/SetLib.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Set") -public class SetLib { - private LinkedHashSet set = new LinkedHashSet<>(); - - @Expose("@@Symbol.iterator") - public ObjectValue __iterator(Arguments args) { - return this.__values(args); - } - - @Expose public ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.env, set.stream().map(v -> new ArrayValue(args.env, v, v)).collect(Collectors.toList())); - } - @Expose public ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.env, set); - } - @Expose public ObjectValue __values(Arguments args) { - return Values.toJSIterator(args.env, set); - } - - @Expose public Object __add(Arguments args) { - return set.add(args.get(0)); - } - @Expose public boolean __delete(Arguments args) { - return set.remove(args.get(0)); - } - @Expose public boolean __has(Arguments args) { - return set.contains(args.get(0)); - } - - @Expose public void __clear() { - set.clear(); - } - - @Expose(type = ExposeType.GETTER) - public int __size() { - return set.size(); - } - - @Expose public void __forEach(Arguments args) { - var keys = new ArrayList<>(set); - - for (var el : keys) Values.call(args.env, args.get(0), args.get(1), el, el, args.self); - } - - public SetLib(Environment env, Object iterable) { - for (var el : Values.fromJSIterator(env, iterable)) set.add(el); - } - - @ExposeConstructor - public static SetLib __constructor(Arguments args) { - return new SetLib(args.env, args.get(0)); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/StringLib.java b/src/java/me/topchetoeu/jscript/lib/StringLib.java deleted file mode 100644 index dba6546..0000000 --- a/src/java/me/topchetoeu/jscript/lib/StringLib.java +++ /dev/null @@ -1,291 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.regex.Pattern; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -// TODO: implement index wrapping properly -@WrapperName("String") -public class StringLib { - public final String value; - - @Override public String toString() { return value; } - - public StringLib(String val) { - this.value = val; - } - - private static String passThis(Arguments args, String funcName) { - var val = args.self; - if (Values.isWrapper(val, StringLib.class)) return Values.wrapper(val, StringLib.class).value; - else if (val instanceof String) return (String)val; - else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve strings.", funcName)); - } - private static int normalizeI(int i, int len, boolean clamp) { - if (i < 0) i += len; - if (clamp) { - if (i < 0) i = 0; - if (i > len) i = len; - } - return i; - } - - @Expose(type = ExposeType.GETTER) - public static int __length(Arguments args) { - return passThis(args, "length").length(); - } - - @Expose public static String __substring(Arguments args) { - var val = passThis(args, "substring"); - var start = Math.max(0, Math.min(val.length(), args.getInt(0))); - var end = Math.max(0, Math.min(val.length(), args.getInt(1, val.length()))); - - if (end < start) { - var tmp = end; - end = start; - start = tmp; - } - - return val.substring(start, end); - } - @Expose public static String __substr(Arguments args) { - var val = passThis(args, "substr"); - var start = normalizeI(args.getInt(0), val.length(), true); - int end = normalizeI(args.getInt(1, val.length() - start) + start, val.length(), true); - return val.substring(start, end); - } - - @Expose public static String __toLowerCase(Arguments args) { - return passThis(args, "toLowerCase").toLowerCase(); - } - @Expose public static String __toUpperCase(Arguments args) { - return passThis(args, "toUpperCase").toUpperCase(); - } - - @Expose public static String __charAt(Arguments args) { - return passThis(args, "charAt").charAt(args.getInt(0)) + ""; - } - @Expose public static double __charCodeAt(Arguments args) { - var str = passThis(args, "charCodeAt"); - var i = args.getInt(0); - if (i < 0 || i >= str.length()) return Double.NaN; - else return str.charAt(i); - } - @Expose public static double __codePointAt(Arguments args) { - var str = passThis(args, "codePointAt"); - var i = args.getInt(0); - if (i < 0 || i >= str.length()) return Double.NaN; - else return str.codePointAt(i); - } - - @Expose public static boolean __startsWith(Arguments args) { - return passThis(args, "startsWith").startsWith(args.getString(0), args.getInt(1)); - } - @Expose public static boolean __endsWith(Arguments args) { - return passThis(args, "endsWith").lastIndexOf(args.getString(0), args.getInt(1)) >= 0; - } - - @Expose public static int __indexOf(Arguments args) { - var val = passThis(args, "indexOf"); - var term = args.get(0); - var start = args.getInt(1); - var search = Values.getMember(args.env, term, Symbol.get("Symbol.search")); - - if (search instanceof FunctionValue) { - return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, false, start)); - } - else return val.indexOf(Values.toString(args.env, term), start); - } - @Expose public static int __lastIndexOf(Arguments args) { - var val = passThis(args, "lastIndexOf"); - var term = args.get(0); - var start = args.getInt(1); - var search = Values.getMember(args.env, term, Symbol.get("Symbol.search")); - - if (search instanceof FunctionValue) { - return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, true, start)); - } - else return val.lastIndexOf(Values.toString(args.env, term), start); - } - - @Expose public static boolean __includes(Arguments args) { - return __indexOf(args) >= 0; - } - - @Expose public static String __replace(Arguments args) { - var val = passThis(args, "replace"); - var term = args.get(0); - var replacement = args.get(1); - var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); - - if (replace instanceof FunctionValue) { - return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement)); - } - else return val.replaceFirst(Pattern.quote(Values.toString(args.env, term)), Values.toString(args.env, replacement)); - } - @Expose public static String __replaceAll(Arguments args) { - var val = passThis(args, "replaceAll"); - var term = args.get(0); - var replacement = args.get(1); - var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); - - if (replace instanceof FunctionValue) { - return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement)); - } - else return val.replace(Values.toString(args.env, term), Values.toString(args.env, replacement)); - } - - @Expose public static ArrayValue __match(Arguments args) { - var val = passThis(args, "match"); - var term = args.get(0); - - FunctionValue match; - - try { - var _match = Values.getMember(args.env, term, Symbol.get("Symbol.match")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else if (args.env.hasNotNull(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), ""); - _match = Values.getMember(args.env, regex, Symbol.get("Symbol.match")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else throw EngineException.ofError("Regular expressions don't support matching."); - } - else throw EngineException.ofError("Regular expressions not supported."); - } - catch (IllegalArgumentException e) { return new ArrayValue(args.env, ""); } - - var res = match.call(args.env, term, val); - if (res instanceof ArrayValue) return (ArrayValue)res; - else return new ArrayValue(args.env, ""); - } - @Expose public static Object __matchAll(Arguments args) { - var val = passThis(args, "matchAll"); - var term = args.get(0); - - FunctionValue match = null; - - try { - var _match = Values.getMember(args.env, term, Symbol.get("Symbol.matchAll")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - } - catch (IllegalArgumentException e) { } - - if (match == null && args.env.hasNotNull(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), "g"); - var _match = Values.getMember(args.env, regex, Symbol.get("Symbol.matchAll")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else throw EngineException.ofError("Regular expressions don't support matching."); - } - else throw EngineException.ofError("Regular expressions not supported."); - - return match.call(args.env, term, val); - } - - @Expose public static ArrayValue __split(Arguments args) { - var val = passThis(args, "split"); - var term = args.get(0); - var lim = args.get(1); - var sensible = args.getBoolean(2); - - if (lim != null) lim = Values.toNumber(args.env, lim); - - if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); - if (replace instanceof FunctionValue) { - var tmp = ((FunctionValue)replace).call(args.env, term, val, lim, sensible); - - if (tmp instanceof ArrayValue) { - var parts = new ArrayValue(((ArrayValue)tmp).size()); - for (int i = 0; i < parts.size(); i++) parts.set(args.env, i, Values.toString(args.env, ((ArrayValue)tmp).get(i))); - return parts; - } - } - } - - String[] parts; - var pattern = Pattern.quote(Values.toString(args.env, term)); - - if (lim == null) parts = val.split(pattern); - else if ((double)lim < 1) return new ArrayValue(); - else if (sensible) parts = val.split(pattern, (int)(double)lim); - else { - var limit = (int)(double)lim; - parts = val.split(pattern, limit + 1); - ArrayValue res; - - if (parts.length > limit) res = new ArrayValue(limit); - else res = new ArrayValue(parts.length); - - for (var i = 0; i < parts.length && i < limit; i++) res.set(args.env, i, parts[i]); - - return res; - } - - var res = new ArrayValue(parts.length); - var i = 0; - - for (; i < parts.length; i++) { - if (lim != null && (double)lim <= i) break; - res.set(args.env, i, parts[i]); - } - - return res; - } - - @Expose public static String __slice(Arguments args) { - var self = passThis(args, "slice"); - var start = normalizeI(args.getInt(0), self.length(), false); - var end = normalizeI(args.getInt(1, self.length()), self.length(), false); - - return __substring(new Arguments(args.env, self, start, end)); - } - - @Expose public static String __concat(Arguments args) { - var res = new StringBuilder(passThis(args, "concat")); - - for (var el : args.convert(String.class)) res.append(el); - - return res.toString(); - } - @Expose public static String __trim(Arguments args) { - return passThis(args, "trim").trim(); - } - @Expose public static String __trimStart(Arguments args) { - return passThis(args, "trimStart").replaceAll("^\\s+", ""); - } - @Expose public static String __trimEnd(Arguments args) { - return passThis(args, "trimEnd").replaceAll("\\s+$", ""); - } - - @ExposeConstructor public static Object __constructor(Arguments args) { - var val = args.getString(0, ""); - if (args.self instanceof ObjectValue) return new StringLib(val); - else return val; - } - @Expose public static String __toString(Arguments args) { - return passThis(args, "toString"); - } - @Expose public static String __valueOf(Arguments args) { - return passThis(args, "valueOf"); - } - - @Expose(target = ExposeTarget.STATIC) - public static String __fromCharCode(Arguments args) { - var val = args.convertInt(); - char[] arr = new char[val.length]; - for (var i = 0; i < val.length; i++) arr[i] = (char)val[i]; - return new String(arr); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/SymbolLib.java b/src/java/me/topchetoeu/jscript/lib/SymbolLib.java deleted file mode 100644 index e6a553d..0000000 --- a/src/java/me/topchetoeu/jscript/lib/SymbolLib.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.HashMap; -import java.util.Map; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Symbol") -public class SymbolLib { - private static final Map symbols = new HashMap<>(); - - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __typeName = Symbol.get("Symbol.typeName"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __replace = Symbol.get("Symbol.replace"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __match = Symbol.get("Symbol.match"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __matchAll = Symbol.get("Symbol.matchAll"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __split = Symbol.get("Symbol.split"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __search = Symbol.get("Symbol.search"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __iterator = Symbol.get("Symbol.iterator"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __asyncIterator = Symbol.get("Symbol.asyncIterator"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __cause = Symbol.get("Symbol.cause"); - - public final Symbol value; - - private static Symbol passThis(Arguments args, String funcName) { - var val = args.self; - if (Values.isWrapper(val, SymbolLib.class)) return Values.wrapper(val, SymbolLib.class).value; - else if (val instanceof Symbol) return (Symbol)val; - else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve symbols.", funcName)); - } - - public SymbolLib(Symbol val) { - this.value = val; - } - - @Expose public static String __toString(Arguments args) { - return passThis(args, "toString").value; - } - @Expose public static Symbol __valueOf(Arguments args) { - return passThis(args, "valueOf"); - } - - @ExposeConstructor - public static Object __constructor(Arguments args) { - if (args.self instanceof ObjectValue) throw EngineException.ofType("Symbol constructor may not be called with new."); - if (args.get(0) == null) return new Symbol(""); - else return new Symbol(args.getString(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static Symbol __for(Arguments args) { - var key = args.getString(0); - if (symbols.containsKey(key)) return symbols.get(key); - else { - var sym = new Symbol(key); - symbols.put(key, sym); - return sym; - } - } - @Expose(target = ExposeTarget.STATIC) - public static String __keyFor(Arguments args) { - return passThis(new Arguments(args.env, args.get(0)), "keyFor").value; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java b/src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java deleted file mode 100644 index 1557729..0000000 --- a/src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("SyntaxError") -public class SyntaxErrorLib extends ErrorLib { - @ExposeField public static final String __name = "SyntaxError"; - - @ExposeConstructor public static ObjectValue __constructor(Arguments args) { - var target = ErrorLib.__constructor(args); - target.setPrototype(PlaceholderProto.SYNTAX_ERROR); - return target; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/ThrowableLib.java b/src/java/me/topchetoeu/jscript/lib/ThrowableLib.java deleted file mode 100644 index 960229f..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ThrowableLib.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; - -public class ThrowableLib { - @Expose public static String __message(Arguments args) { - if (args.self instanceof Throwable) return ((Throwable)args.self).getMessage(); - else return null; - } - @Expose public static String __name(Arguments args) { - return args.self.getClass().getSimpleName(); - } - - @Expose public static String __toString(Arguments args) { - return __name(args) + ": " + __message(args); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java b/src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java deleted file mode 100644 index 98107ae..0000000 --- a/src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("TypeError") -public class TypeErrorLib extends ErrorLib { - @ExposeField public static final String __name = "TypeError"; - - @ExposeConstructor public static ObjectValue __constructor(Arguments args) { - var target = ErrorLib.__constructor(args); - target.setPrototype(PlaceholderProto.TYPE_ERROR); - return target; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index 9502c7d..adbc6ff 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -1,12 +1,14 @@ package me.topchetoeu.jscript.runtime; +import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.ResultRunnable; import me.topchetoeu.jscript.common.events.DataNotifier; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; public interface EventLoop { public static final Key KEY = new Key<>(); @@ -25,10 +27,10 @@ public interface EventLoop { return pushMsg(() -> { runnable.run(); return null; }, micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) { + public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { return pushMsg(() -> func.call(env, thisArg, args), micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) { + public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { return pushMsg(() -> Compiler.compile(env, filename, raw).call(env, thisArg, args), micro); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 69e7c19..02f1360 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.runtime; -import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Stack; import me.topchetoeu.jscript.common.Instruction; @@ -11,11 +12,13 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.scope.LocalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ScopeValue; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; public class Frame { public static final Key KEY = new Key<>(); @@ -64,12 +67,12 @@ public class Frame { private static class PendingResult { public final boolean isReturn, isJump, isThrow; - public final Object value; + public final Value value; public final EngineException error; public final int ptr; public final Instruction instruction; - private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) { + private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Value value, EngineException error, int ptr) { this.instruction = instr; this.isReturn = isReturn; this.isJump = isJump; @@ -82,7 +85,7 @@ public class Frame { public static PendingResult ofNone() { return new PendingResult(null, false, false, false, null, null, 0); } - public static PendingResult ofReturn(Object value, Instruction instr) { + public static PendingResult ofReturn(Value value, Instruction instr) { return new PendingResult(instr, true, false, false, value, null, 0); } public static PendingResult ofThrow(EngineException error, Instruction instr) { @@ -100,7 +103,7 @@ public class Frame { public final CodeFunction function; public final Environment env; - public Object[] stack = new Object[32]; + public Value[] stack = new Value[32]; public int stackPtr = 0; public int codePtr = 0; public boolean jumpFlag = false; @@ -113,52 +116,54 @@ public class Frame { tryStack.add(res); } - public Object peek() { + public Value peek() { return peek(0); } - public Object peek(int offset) { + public Value peek(int offset) { if (stackPtr <= offset) return null; else return stack[stackPtr - 1 - offset]; } - public Object pop() { + public Value pop() { if (stackPtr == 0) return null; return stack[--stackPtr]; } - public Object[] take(int n) { + public Value[] take(int n) { int srcI = stackPtr - n; if (srcI < 0) srcI = 0; int dstI = n + srcI - stackPtr; int copyN = stackPtr - srcI; - Object[] res = new Object[n]; + Value[] res = new Value[n]; System.arraycopy(stack, srcI, res, dstI, copyN); stackPtr -= copyN; return res; } - public void push(Object val) { + public void push(Value val) { if (stack.length <= stackPtr) { - var newStack = new Object[stack.length * 2]; + var newStack = new Value[stack.length * 2]; System.arraycopy(stack, 0, newStack, 0, stack.length); stack = newStack; } - stack[stackPtr++] = Values.normalize(env, val); + + stack[stackPtr++] = val; } - private Object next(Object value, Object returnValue, EngineException error) { - if (value != Values.NO_RETURN) push(value); + // for the love of christ don't touch this + private Value next(Value value, Value returnValue, EngineException error) { + if (value != null) push(value); Instruction instr = null; if (codePtr >= 0 && codePtr < function.body.instructions.length) instr = function.body.instructions[codePtr]; - if (returnValue == Values.NO_RETURN && error == null) { + if (returnValue == null && error == null) { try { if (Thread.interrupted()) throw new InterruptException(); if (instr == null) returnValue = null; else { - DebugContext.get(env).onInstruction(env, this, instr, Values.NO_RETURN, null, false); + DebugContext.get(env).onInstruction(env, this, instr, null, null, false); try { this.jumpFlag = this.popTryFlag = false; @@ -181,7 +186,7 @@ public class Frame { if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error); else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr)); } - else if (returnValue != Values.NO_RETURN) { + else if (returnValue != null) { if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr)); } else if (jumpFlag && !tryCtx.inBounds(codePtr)) { @@ -209,7 +214,7 @@ public class Frame { } error = null; - returnValue = Values.NO_RETURN; + returnValue = null; break; } else { @@ -227,7 +232,7 @@ public class Frame { tryStack.pop(); codePtr = tryCtx.end; if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction; - if (!jumpFlag && returnValue == Values.NO_RETURN && error == null) { + if (!jumpFlag && returnValue == null && error == null) { if (tryCtx.result.isJump) { codePtr = tryCtx.result.ptr; jumpFlag = true; @@ -255,19 +260,19 @@ public class Frame { DebugContext.get(env).onInstruction(env, this, instr, null, error, caught); throw error; } - if (returnValue != Values.NO_RETURN) { + if (returnValue != null) { DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false); return returnValue; } - return Values.NO_RETURN; + return null; } /** * Executes the next instruction in the frame */ - public Object next() { - return next(Values.NO_RETURN, Values.NO_RETURN, null); + public Value next() { + return next(null, null, null); } /** * Induces a value on the stack (as if it were returned by the last function call) @@ -275,8 +280,8 @@ public class Frame { * * @param value The value to induce */ - public Object next(Object value) { - return next(value, Values.NO_RETURN, null); + public Value next(Value value) { + return next(value, null, null); } /** * Induces a thrown error and executes the next instruction. @@ -286,8 +291,8 @@ public class Frame { * * @param error The error to induce */ - public Object induceError(EngineException error) { - return next(Values.NO_RETURN, Values.NO_RETURN, error); + public Value induceError(EngineException error) { + return next(null, null, error); } /** * Induces a return, as if there was a return statement before @@ -298,8 +303,8 @@ public class Frame { * * @param value The retunr value to induce */ - public Object induceReturn(Object value) { - return next(Values.NO_RETURN, value, null); + public Value induceReturn(Value value) { + return next(null, value, null); } public void onPush() { @@ -348,35 +353,47 @@ public class Frame { */ public ObjectValue getValStackScope() { return new ObjectValue() { - @Override - protected Object getField(Environment ext, Object key) { - var i = (int)Values.toNumber(ext, key); + @Override public Member getOwnMember(Environment env, Value key) { + var res = super.getOwnMember(env, key); + if (res != null) return res; + + var f = key.toNumber(env).value; + var i = (int)f; + if (i < 0 || i >= stackPtr) return null; - else return stack[i]; + else return new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { return stack[i]; } + @Override public boolean set(Environment env, Value val, Value self) { + stack[i] = val; + return true; + } + }; } - @Override - protected boolean hasField(Environment ext, Object key) { - return true; - } - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - for (var i = 0; i < stackPtr; i++) res.add(i); + @Override public Map getOwnMembers(Environment env) { + var res = new LinkedHashMap(); + + for (var i = 0; i < stackPtr; i++) { + var _i = i; + res.put(i + "", new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { return stack[_i]; } + @Override public boolean set(Environment env, Value val, Value self) { + stack[_i] = val; + return true; + } + }); + } + return res; } }; } - public Frame(Environment env, Object thisArg, Object[] args, CodeFunction func) { + public Frame(Environment env, Value thisArg, Value[] args, CodeFunction func) { this.env = env; this.args = args.clone(); this.scope = new LocalScope(func.body.localsN, func.captures); - this.scope.get(0).set(null, thisArg); - var argsObj = new ArrayValue(); - for (var i = 0; i < args.length; i++) { - argsObj.set(env, i, args[i]); - } - this.scope.get(1).value = argsObj; + this.scope.get(0).set(thisArg); + this.scope.get(1).value = new ArrayValue(args); this.thisArg = thisArg; this.function = func; diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index c7bcfb8..bd25878 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.runtime; +import java.util.ArrayList; import java.util.Collections; import me.topchetoeu.jscript.common.Instruction; @@ -8,85 +9,96 @@ import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class InstructionRunner { - private static Object execReturn(Environment ext, Instruction instr, Frame frame) { + private static Value execReturn(Environment env, Instruction instr, Frame frame) { return frame.pop(); } - private static Object execThrow(Environment ext, Instruction instr, Frame frame) { + private static Value execThrow(Environment env, Instruction instr, Frame frame) { throw new EngineException(frame.pop()); } - private static Object execThrowSyntax(Environment ext, Instruction instr, Frame frame) { + private static Value execThrowSyntax(Environment env, Instruction instr, Frame frame) { throw EngineException.ofSyntax((String)instr.get(0)); } - private static Object execCall(Environment ext, Instruction instr, Frame frame) { + private static Value execCall(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); - frame.push(Values.call(ext, func, thisArg, callArgs)); + frame.push(func.call(env, thisArg, callArgs)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execCallNew(Environment ext, Instruction instr, Frame frame) { + private static Value execCallNew(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); - frame.push(Values.callNew(ext, funcObj, callArgs)); + frame.push(funcObj.callNew(env, callArgs)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execMakeVar(Environment ext, Instruction instr, Frame frame) { + private static Value execMakeVar(Environment env, Instruction instr, Frame frame) { var name = (String)instr.get(0); - GlobalScope.get(ext).define(ext, name); + GlobalScope.get(env).define(env, false, name); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execDefProp(Environment ext, Instruction instr, Frame frame) { - var setter = frame.pop(); - var getter = frame.pop(); + private static Value execDefProp(Environment env, Instruction instr, Frame frame) { + var setterVal = frame.pop(); + var getterVal = frame.pop(); var name = frame.pop(); var obj = frame.pop(); - if (getter != null && !(getter instanceof FunctionValue)) throw EngineException.ofType("Getter must be a function or undefined."); - if (setter != null && !(setter instanceof FunctionValue)) throw EngineException.ofType("Setter must be a function or undefined."); - if (!(obj instanceof ObjectValue)) throw EngineException.ofType("Property apply target must be an object."); - ((ObjectValue)obj).defineProperty(ext, name, (FunctionValue)getter, (FunctionValue)setter, false, false); + FunctionValue getter, setter; + + if (getterVal == VoidValue.UNDEFINED) getter = null; + else if (getterVal instanceof FunctionValue) getter = (FunctionValue)getterVal; + else throw EngineException.ofType("Getter must be a function or undefined."); + + if (setterVal == VoidValue.UNDEFINED) setter = null; + else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal; + else throw EngineException.ofType("Setter must be a function or undefined."); + + obj.defineOwnMember(env, name, new PropertyMember(getter, setter, true, true)); frame.push(obj); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execKeys(Environment ext, Instruction instr, Frame frame) { + private static Value execKeys(Environment env, Instruction instr, Frame frame) { var val = frame.pop(); - var members = Values.getMembers(ext, val, false, false); + var members = new ArrayList<>(val.getMembers(env, false, true).keySet()); Collections.reverse(members); frame.push(null); for (var el : members) { - if (el instanceof Symbol) continue; var obj = new ObjectValue(); - obj.defineProperty(ext, "value", el); + obj.defineOwnMember(env, new StringValue("value"), FieldMember.of(new StringValue(el))); frame.push(obj); } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execTryStart(Environment ext, Instruction instr, Frame frame) { + private static Value execTryStart(Environment env, Instruction instr, Frame frame) { int start = frame.codePtr + 1; int catchStart = (int)instr.get(0); int finallyStart = (int)instr.get(1); @@ -95,14 +107,14 @@ public class InstructionRunner { int end = (int)instr.get(2) + start; frame.addTry(start, end, catchStart, finallyStart); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execTryEnd(Environment ext, Instruction instr, Frame frame) { + private static Value execTryEnd(Environment env, Instruction instr, Frame frame) { frame.popTryFlag = true; - return Values.NO_RETURN; + return null; } - private static Object execDup(Environment ext, Instruction instr, Frame frame) { + private static Value execDup(Environment env, Instruction instr, Frame frame) { int count = instr.get(0); for (var i = 0; i < count; i++) { @@ -110,45 +122,48 @@ public class InstructionRunner { } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadValue(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadValue(Environment env, Instruction instr, Frame frame) { switch (instr.type) { - case PUSH_UNDEFINED: frame.push(null); break; - case PUSH_NULL: frame.push(Values.NULL); break; - default: frame.push(instr.get(0)); break; + case PUSH_UNDEFINED: frame.push(VoidValue.UNDEFINED); break; + case PUSH_NULL: frame.push(VoidValue.NULL); break; + case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); break; + case PUSH_NUMBER: frame.push(new NumberValue(instr.get(0))); break; + case PUSH_STRING: frame.push(new StringValue(instr.get(0))); break; + default: } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadVar(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadVar(Environment env, Instruction instr, Frame frame) { var i = instr.get(0); - if (i instanceof String) frame.push(GlobalScope.get(ext).get(ext, (String)i)); - else frame.push(frame.scope.get((int)i).get(ext)); + if (i instanceof String) frame.push(GlobalScope.get(env).get(env, (String)i)); + else frame.push(frame.scope.get((int)i).get(env)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadObj(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadObj(Environment env, Instruction instr, Frame frame) { frame.push(new ObjectValue()); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadGlob(Environment ext, Instruction instr, Frame frame) { - frame.push(GlobalScope.get(ext).obj); + private static Value execLoadGlob(Environment env, Instruction instr, Frame frame) { + frame.push(GlobalScope.get(env).object); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadArr(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadArr(Environment env, Instruction instr, Frame frame) { var res = new ArrayValue(); res.setSize(instr.get(0)); frame.push(res); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadFunc(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { int id = instr.get(0); var captures = new ValueVariable[instr.params.length - 1]; @@ -156,174 +171,175 @@ public class InstructionRunner { captures[i - 1] = frame.scope.get(instr.get(i)); } - var func = new CodeFunction(ext, "", frame.function.body.children[id], captures); + var func = new CodeFunction(env, "", frame.function.body.children[id], captures); frame.push(func); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadMember(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadMember(Environment env, Instruction instr, Frame frame) { var key = frame.pop(); var obj = frame.pop(); try { - frame.push(Values.getMember(ext, obj, key)); + frame.push(obj.getMember(env, key)); } catch (IllegalArgumentException e) { throw EngineException.ofType(e.getMessage()); } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadRegEx(Environment ext, Instruction instr, Frame frame) { - if (ext.hasNotNull(Environment.REGEX_CONSTR)) { - frame.push(Values.callNew(ext, ext.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1))); + private static Value execLoadRegEx(Environment env, Instruction instr, Frame frame) { + if (env.hasNotNull(Environment.REGEX_CONSTR)) { + frame.push(env.get(Environment.REGEX_CONSTR).callNew(env, instr.get(0), instr.get(1))); } else { throw EngineException.ofSyntax("Regex is not supported."); } + frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execDiscard(Environment ext, Instruction instr, Frame frame) { + private static Value execDiscard(Environment env, Instruction instr, Frame frame) { frame.pop(); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execStoreMember(Environment ext, Instruction instr, Frame frame) { + private static Value execStoreMember(Environment env, Instruction instr, Frame frame) { var val = frame.pop(); var key = frame.pop(); var obj = frame.pop(); - if (!Values.setMember(ext, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); + if (!obj.setMember(env, key, val)) throw EngineException.ofSyntax("Can't set member '" + key.toReadable(env) + "'."); if ((boolean)instr.get(0)) frame.push(val); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execStoreVar(Environment ext, Instruction instr, Frame frame) { + private static Value execStoreVar(Environment env, Instruction instr, Frame frame) { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); - if (i instanceof String) GlobalScope.get(ext).set(ext, (String)i, val); - else frame.scope.get((int)i).set(ext, val); + if (i instanceof String) GlobalScope.get(env).set(env, (String)i, val); + else frame.scope.get((int)i).set(env, val); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execStoreSelfFunc(Environment ext, Instruction instr, Frame frame) { - frame.scope.locals[(int)instr.get(0)].set(ext, frame.function); + private static Value execStoreSelfFunc(Environment env, Instruction instr, Frame frame) { + frame.scope.locals[(int)instr.get(0)].set(env, frame.function); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execJmp(Environment ext, Instruction instr, Frame frame) { + private static Value execJmp(Environment env, Instruction instr, Frame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; - return Values.NO_RETURN; + return null; } - private static Object execJmpIf(Environment ext, Instruction instr, Frame frame) { - if (Values.toBoolean(frame.pop())) { + private static Value execJmpIf(Environment env, Instruction instr, Frame frame) { + if (frame.pop().toBoolean().value) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; } else frame.codePtr ++; - return Values.NO_RETURN; + return null; } - private static Object execJmpIfNot(Environment ext, Instruction instr, Frame frame) { - if (Values.not(frame.pop())) { + private static Value execJmpIfNot(Environment env, Instruction instr, Frame frame) { + if (frame.pop().not().value) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; } else frame.codePtr ++; - return Values.NO_RETURN; + return null; } - private static Object execTypeof(Environment ext, Instruction instr, Frame frame) { + private static Value execTypeof(Environment env, Instruction instr, Frame frame) { String name = instr.get(0); - Object obj; + Value obj; if (name != null) { - if (GlobalScope.get(ext).has(ext, name)) { - obj = GlobalScope.get(ext).get(ext, name); + if (GlobalScope.get(env).has(env, name)) { + obj = GlobalScope.get(env).get(env, name); } else obj = null; } else obj = frame.pop(); - frame.push(Values.type(obj)); + frame.push(obj.type()); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execNop(Environment ext, Instruction instr, Frame frame) { + private static Value execNop(Environment env, Instruction instr, Frame frame) { frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execDelete(Environment ext, Instruction instr, Frame frame) { + private static Value execDelete(Environment env, Instruction instr, Frame frame) { var key = frame.pop(); var val = frame.pop(); - if (!Values.deleteMember(ext, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); + if (!val.deleteMember(env, key)) throw EngineException.ofSyntax("Can't delete member '" + key.toReadable(env) + "'."); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execOperation(Environment ext, Instruction instr, Frame frame) { + private static Value execOperation(Environment env, Instruction instr, Frame frame) { Operation op = instr.get(0); - var args = new Object[op.operands]; + var args = new Value[op.operands]; for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); - frame.push(Values.operation(ext, op, args)); + frame.push(Value.operation(env, op, args)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - public static Object exec(Environment ext, Instruction instr, Frame frame) { + public static Value exec(Environment env, Instruction instr, Frame frame) { switch (instr.type) { - case NOP: return execNop(ext, instr, frame); - case RETURN: return execReturn(ext, instr, frame); - case THROW: return execThrow(ext, instr, frame); - case THROW_SYNTAX: return execThrowSyntax(ext, instr, frame); - case CALL: return execCall(ext, instr, frame); - case CALL_NEW: return execCallNew(ext, instr, frame); - case TRY_START: return execTryStart(ext, instr, frame); - case TRY_END: return execTryEnd(ext, instr, frame); + case NOP: return execNop(env, instr, frame); + case RETURN: return execReturn(env, instr, frame); + case THROW: return execThrow(env, instr, frame); + case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); + case CALL: return execCall(env, instr, frame); + case CALL_NEW: return execCallNew(env, instr, frame); + case TRY_START: return execTryStart(env, instr, frame); + case TRY_END: return execTryEnd(env, instr, frame); - case DUP: return execDup(ext, instr, frame); + case DUP: return execDup(env, instr, frame); case PUSH_UNDEFINED: case PUSH_NULL: case PUSH_STRING: case PUSH_NUMBER: case PUSH_BOOL: - return execLoadValue(ext, instr, frame); - case LOAD_VAR: return execLoadVar(ext, instr, frame); - case LOAD_OBJ: return execLoadObj(ext, instr, frame); - case LOAD_ARR: return execLoadArr(ext, instr, frame); - case LOAD_FUNC: return execLoadFunc(ext, instr, frame); - case LOAD_MEMBER: return execLoadMember(ext, instr, frame); - case LOAD_REGEX: return execLoadRegEx(ext, instr, frame); - case LOAD_GLOB: return execLoadGlob(ext, instr, frame); + return execLoadValue(env, instr, frame); + case LOAD_VAR: return execLoadVar(env, instr, frame); + case LOAD_OBJ: return execLoadObj(env, instr, frame); + case LOAD_ARR: return execLoadArr(env, instr, frame); + case LOAD_FUNC: return execLoadFunc(env, instr, frame); + case LOAD_MEMBER: return execLoadMember(env, instr, frame); + case LOAD_REGEX: return execLoadRegEx(env, instr, frame); + case LOAD_GLOB: return execLoadGlob(env, instr, frame); - case DISCARD: return execDiscard(ext, instr, frame); - case STORE_MEMBER: return execStoreMember(ext, instr, frame); - case STORE_VAR: return execStoreVar(ext, instr, frame); - case STORE_SELF_FUNC: return execStoreSelfFunc(ext, instr, frame); - case MAKE_VAR: return execMakeVar(ext, instr, frame); + case DISCARD: return execDiscard(env, instr, frame); + case STORE_MEMBER: return execStoreMember(env, instr, frame); + case STORE_VAR: return execStoreVar(env, instr, frame); + case STORE_SELF_FUNC: return execStoreSelfFunc(env, instr, frame); + case MAKE_VAR: return execMakeVar(env, instr, frame); - case KEYS: return execKeys(ext, instr, frame); - case DEF_PROP: return execDefProp(ext, instr, frame); - case TYPEOF: return execTypeof(ext, instr, frame); - case DELETE: return execDelete(ext, instr, frame); + case KEYS: return execKeys(env, instr, frame); + case DEF_PROP: return execDefProp(env, instr, frame); + case TYPEOF: return execTypeof(env, instr, frame); + case DELETE: return execDelete(env, instr, frame); - case JMP: return execJmp(ext, instr, frame); - case JMP_IF: return execJmpIf(ext, instr, frame); - case JMP_IFN: return execJmpIfNot(ext, instr, frame); + case JMP: return execJmp(env, instr, frame); + case JMP_IF: return execJmpIf(env, instr, frame); + case JMP_IFN: return execJmpIfNot(env, instr, frame); - case OPERATION: return execOperation(ext, instr, frame); + case OPERATION: return execOperation(env, instr, frame); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } diff --git a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java new file mode 100644 index 0000000..84b390a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java @@ -0,0 +1,86 @@ +package me.topchetoeu.jscript.runtime; + +import java.util.HashSet; +import java.util.stream.Collectors; + +import me.topchetoeu.jscript.common.json.JSONElement; +import me.topchetoeu.jscript.common.json.JSONList; +import me.topchetoeu.jscript.common.json.JSONMap; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +public class JSONConverter { + public static Value toJs(JSONElement val) { + if (val.isBoolean()) return BoolValue.of(val.bool()); + if (val.isString()) return new StringValue(val.string()); + if (val.isNumber()) return new NumberValue(val.number()); + if (val.isList()) return ArrayValue.of(val.list().stream().map(JSONConverter::toJs).collect(Collectors.toList())); + if (val.isMap()) { + var res = new ObjectValue(); + + for (var el : val.map().entrySet()) { + res.defineOwnMember(null, new StringValue(el.getKey()), FieldMember.of(toJs(el.getValue()))); + } + + return res; + } + if (val.isNull()) return VoidValue.NULL; + return VoidValue.UNDEFINED; + } + + public static JSONElement fromJs(Environment ext, Value val) { + var res = JSONConverter.fromJs(ext, val, new HashSet<>()); + if (res == null) return JSONElement.NULL; + else return res; + } + + public static JSONElement fromJs(Environment env, Value val, HashSet prev) { + if (val instanceof BoolValue) return JSONElement.bool(((BoolValue)val).value); + if (val instanceof NumberValue) return JSONElement.number(((NumberValue)val).value); + if (val instanceof StringValue) return JSONElement.string(((StringValue)val).value); + if (val == VoidValue.NULL) return JSONElement.NULL; + if (val instanceof VoidValue) return null; + + if (val instanceof ArrayValue) { + if (prev.contains(val)) throw EngineException.ofError("Circular dependency in JSON."); + prev.add(val); + + var res = new JSONList(); + + for (var el : ((ArrayValue)val).toArray()) { + var jsonEl = fromJs(env, el, prev); + if (jsonEl == null) continue; + res.add(jsonEl); + } + + prev.remove(val); + return JSONElement.of(res); + } + if (val instanceof ObjectValue) { + if (prev.contains(val)) throw EngineException.ofError("Circular dependency in JSON."); + prev.add(val); + + var res = new JSONMap(); + + for (var el : val.getMembers(env, true, true).entrySet()) { + var jsonEl = fromJs(env, el.getValue().get(env, val), prev); + if (jsonEl == null) continue; + res.put(el.getKey(), jsonEl); + } + + prev.remove(val); + return JSONElement.of(res); + } + if (val == null) return null; + return null; + } + +} diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java new file mode 100644 index 0000000..6cf802b --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -0,0 +1,136 @@ +package me.topchetoeu.jscript.runtime; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import me.topchetoeu.jscript.common.Compiler; +import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.Metadata; +import me.topchetoeu.jscript.common.Reading; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.exceptions.InterruptException; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; +import me.topchetoeu.jscript.runtime.scope.GlobalScope; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; + +public class SimpleRepl { + static Thread engineTask, debugTask; + static Engine engine = new Engine(); + static Environment environment = Environment.empty(); + + static int j = 0; + static String[] args; + + private static void reader() { + try { + for (var arg : args) { + try { + var file = Path.of(arg); + var raw = Files.readString(file); + var res = engine.pushMsg( + false, environment, + Filename.fromFile(file.toFile()), + raw, null + ).await(); + + System.err.println(res.toReadable(environment)); + } + catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } + } + + for (var i = 0; ; i++) { + try { + var raw = Reading.readline(); + + if (raw == null) break; + var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); + var res = engine.pushMsg(false, environment, func, null).await(); + System.err.println(res.toReadable(environment)); + } + catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } + } + } + catch (IOException e) { + System.out.println(e.toString()); + engine.thread().interrupt(); + } + catch (RuntimeException ex) { + if (ex instanceof InterruptException) return; + else { + System.out.println("Internal error ocurred:"); + ex.printStackTrace(); + } + } + } + + private static void initEnv() { + var glob = GlobalScope.get(environment); + + glob.define(null, false, new NativeFunction("exit", args -> { + throw new InterruptException(); + })); + // glob.define(null, false, new NativeFunction("go", args -> { + // try { + // var f = Path.of("do.js"); + // var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); + // return func.call(args.env); + // } + // catch (IOException e) { + // throw new EngineException("Couldn't open do.js"); + // } + // })); + glob.define(null, false, new NativeFunction("log", args -> { + for (var el : args.args) { + System.out.print(el.toReadable(args.env)); + } + + return null; + })); + + // var fs = new RootFilesystem(PermissionsProvider.get(environment)); + // fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); + // fs.protocols.put("file", new PhysicalFilesystem(".")); + // fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err)); + + // environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); + // environment.add(Filesystem.KEY, fs); + // environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); + // environment.add(Compiler.KEY, new JSCompiler(environment)); + environment.add(EventLoop.KEY, engine); + environment.add(GlobalScope.KEY, new GlobalScope()); + // environment.add(EventLoop.KEY, engine); + environment.add(Compiler.KEY, (filename, source) -> { + return Parsing.compile(filename, source).body(); + }); + } + private static void initEngine() { + // var ctx = new DebugContext(); + // environment.add(DebugContext.KEY, ctx); + + // debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx)); + engineTask = engine.start(); + // debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); + } + + public static void main(String args[]) throws InterruptedException { + System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); + + SimpleRepl.args = args; + var reader = new Thread(SimpleRepl::reader); + + initEnv(); + initEngine(); + + reader.setDaemon(true); + reader.setName("STD Reader"); + reader.start(); + + engine.thread().join(); + debugTask.interrupt(); + engineTask.interrupt(); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java index ac0555b..f97fa15 100644 --- a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java +++ b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java @@ -1,8 +1,8 @@ package me.topchetoeu.jscript.runtime; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public interface WrapperProvider { public ObjectValue getProto(Class obj); diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java index bb9be07..a1c5e26 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java @@ -14,8 +14,8 @@ import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; public class DebugContext { public static final Key KEY = new Key<>(); @@ -71,6 +71,7 @@ public class DebugContext { return getMapOrEmpty(((CodeFunction)func).body); } public List getStackFrames() { + if (debugger == null) return List.of(); return this.debugger.getStackFrame(); } diff --git a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java index 24c246e..11e10e6 100644 --- a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java +++ b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java @@ -7,9 +7,9 @@ import java.util.Random; import java.util.Set; import java.util.function.Supplier; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; +import me.topchetoeu.jscript.common.Compiler; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public class Environment { public static final Key COMPILE_FUNC = new Key<>(); diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 15f0aed..975db01 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -5,9 +5,11 @@ import java.util.List; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; public class EngineException extends RuntimeException { public static class StackElement { @@ -40,7 +42,7 @@ public class EngineException extends RuntimeException { } } - public final Object value; + public final Value value; public EngineException cause; public Environment env = null; public final List stackTrace = new ArrayList<>(); @@ -61,10 +63,10 @@ public class EngineException extends RuntimeException { return this; } - public String toString(Environment ext) { + public String toString(Environment env) { var ss = new StringBuilder(); try { - ss.append(Values.toString(ext, value)).append('\n'); + ss.append(value.toString(env)).append('\n'); } catch (EngineException e) { ss.append("[Error while stringifying]\n"); @@ -72,38 +74,40 @@ public class EngineException extends RuntimeException { for (var line : stackTrace) { if (line.visible()) ss.append(" ").append(line.toString()).append("\n"); } - if (cause != null) ss.append("Caused by ").append(cause.toString(ext)).append('\n'); + if (cause != null) ss.append("Caused by ").append(cause.toString(env)).append('\n'); ss.deleteCharAt(ss.length() - 1); return ss.toString(); } - private static Object err(String name, String msg, PlaceholderProto proto) { - var res = new ObjectValue(proto); - if (name != null) res.defineProperty(null, "name", name); - res.defineProperty(null, "message", msg); + private static ObjectValue err(String name, String msg, PrototypeProvider proto) { + var res = new ObjectValue(); + res.setPrototype(proto); + + if (name != null) res.defineOwnMember(Environment.empty(), new StringValue("name"), FieldMember.of(new StringValue(name))); + res.defineOwnMember(Environment.empty(), new StringValue("message"), FieldMember.of(new StringValue(msg))); return res; } - public EngineException(Object error) { - super(error == null ? "null" : error.toString()); + public EngineException(Value error) { + super(error.toReadable(Environment.empty())); this.value = error; this.cause = null; } public static EngineException ofError(String name, String msg) { - return new EngineException(err(name, msg, PlaceholderProto.ERROR)); + return new EngineException(err(name, msg, env -> env.get(Environment.ERROR_PROTO))); } public static EngineException ofError(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.ERROR_PROTO))); } public static EngineException ofSyntax(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.SYNTAX_ERR_PROTO))); } public static EngineException ofType(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.TYPE_ERR_PROTO))); } public static EngineException ofRange(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.RANGE_ERR_PROTO))); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java index eb26e61..a571bac 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java @@ -6,55 +6,48 @@ import java.util.Set; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class GlobalScope { public static final Key KEY = new Key<>(); - public final ObjectValue obj; + public final ObjectValue object; public boolean has(Environment ext, String name) { - return Values.hasMember(ext, obj, name, false); + return object.hasMember(ext, new StringValue(name), false); } public GlobalScope child() { - var obj = new ObjectValue(); - Values.setPrototype(null, obj, this.obj); - return new GlobalScope(obj); + var res = new GlobalScope(); + res.object.setPrototype(null, this.object); + return res; } - public Object define(Environment ext, String name) { - if (Values.hasMember(ext, obj, name, false)) return name; - obj.defineProperty(ext, name, null); - return name; + public void define(Environment ext, String name, Variable variable) { + object.defineOwnMember(ext, new StringValue(name), variable.toField(true, true)); } - public void define(Environment ext, String name, Variable val) { - obj.defineProperty(ext, name, - new NativeFunction("get " + name, args -> val.get(args.env)), - new NativeFunction("set " + name, args -> { val.set(args.env, args.get(0)); return null; }), - true, true - ); + public void define(Environment ext, boolean readonly, String name, Value val) { + object.defineOwnMember(ext, new StringValue(name), FieldMember.of(val, !readonly)); } - public void define(Environment ext, String name, boolean readonly, Object val) { - obj.defineProperty(ext, name, val, readonly, true, true); - } - public void define(Environment ext, String ...names) { - for (var n : names) define(ext, n); + public void define(Environment ext, boolean readonly, String ...names) { + for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED)); } public void define(Environment ext, boolean readonly, FunctionValue val) { - define(ext, val.name, readonly, val); + define(ext, readonly, val.name, val); } - public Object get(Environment ext, String name) { - if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - else return Values.getMember(ext, obj, name); + public Value get(Environment env, String name) { + if (!object.hasMember(env, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); + else return object.getMember(env, new StringValue(name)); } - public void set(Environment ext, String name, Object val) { - if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - if (!Values.setMember(ext, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); + public void set(Environment ext, String name, Value val) { + if (!object.hasMember(ext, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); + if (!object.setMember(ext, new StringValue(name), val)) throw EngineException.ofSyntax("The global '" + name + "' is read-only."); } public Set keys() { @@ -68,10 +61,11 @@ public class GlobalScope { } public GlobalScope() { - this.obj = new ObjectValue(); + this.object = new ObjectValue(); + this.object.setPrototype(null, null); } public GlobalScope(ObjectValue val) { - this.obj = val; + this.object = val; } public static GlobalScope get(Environment ext) { diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java index 60b30fb..a4b4a43 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java @@ -1,27 +1,24 @@ package me.topchetoeu.jscript.runtime.scope; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Value; public class ValueVariable implements Variable { public boolean readonly; - public Object value; + public Value value; - @Override - public boolean readonly() { return readonly; } + @Override public boolean readonly() { return readonly; } + @Override public final Value get(Environment env) { return get(); } + @Override public final boolean set(Environment env, Value val) { return set(val); } - @Override - public Object get(Environment ext) { - return value; + public Value get() { return value; } + public boolean set(Value val) { + if (readonly) return false; + this.value = val; + return true; } - @Override - public void set(Environment ext, Object val) { - if (readonly) return; - this.value = Values.normalize(ext, val); - } - - public ValueVariable(boolean readonly, Object val) { + public ValueVariable(boolean readonly, Value val) { this.readonly = readonly; this.value = val; } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java index db5fb89..85f5f5b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java @@ -1,9 +1,24 @@ package me.topchetoeu.jscript.runtime.scope; import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; public interface Variable { - Object get(Environment ext); + Value get(Environment env); default boolean readonly() { return true; } - default void set(Environment ext, Object val) { } + default boolean set(Environment env, Value val) { return false; } + + default FieldMember toField(boolean configurable, boolean enumerable) { + var self = this; + + return new FieldMember(!readonly(), configurable, enumerable) { + @Override public Value get(Environment env, Value _self) { + return self.get(env); + } + @Override public boolean set(Environment env, Value val, Value _self) { + return self.set(env, val); + } + }; + } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java deleted file mode 100644 index 0214505..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java +++ /dev/null @@ -1,227 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -// TODO: Make methods generic -public class ArrayValue extends ObjectValue implements Iterable { - private static final Object UNDEFINED = new Object(); - private Object[] values; - private int size; - - private Object[] alloc(int index) { - index++; - if (index < values.length) return values; - if (index < values.length * 2) index = values.length * 2; - - var arr = new Object[index]; - System.arraycopy(values, 0, arr, 0, values.length); - return arr; - } - - public int size() { return size; } - public boolean setSize(int val) { - if (val < 0) return false; - if (size > val) shrink(size - val); - else { - values = alloc(val); - size = val; - } - return true; - } - - public Object get(int i) { - if (i < 0 || i >= size) return null; - var res = values[i]; - if (res == UNDEFINED) return null; - else return res; - } - public void set(Environment ext, int i, Object val) { - if (i < 0) return; - - values = alloc(i); - - val = Values.normalize(ext, val); - if (val == null) val = UNDEFINED; - values[i] = val; - if (i >= size) size = i + 1; - } - public boolean has(int i) { - return i >= 0 && i < size && values[i] != null; - } - public void remove(int i) { - if (i < 0 || i >= values.length) return; - values[i] = null; - } - public void shrink(int n) { - if (n >= values.length) { - values = new Object[16]; - size = 0; - } - else { - for (int i = 0; i < n; i++) { - values[--size] = null; - } - } - } - - public Object[] toArray() { - Object[] res = new Object[size]; - copyTo(res, 0, 0, size); - return res; - } - public void copyTo(Object[] arr, int sourceStart, int destStart, int count) { - for (var i = 0; i < count; i++) { - if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null; - if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null; - else arr[i + sourceStart] = values[i + destStart]; - } - } - public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) { - if (arr == this) { - move(sourceStart, destStart, count); - return; - } - - // Iterate in reverse to reallocate at most once - if (destStart + count > arr.size) arr.size = destStart + count; - - for (var i = count - 1; i >= 0; i--) { - if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart); - if (values[i + sourceStart] == UNDEFINED) arr.set(null, i + destStart, null); - else if (values[i + sourceStart] == null) arr.remove(i + destStart); - else arr.set(null, i + destStart, values[i + sourceStart]); - } - } - - public void copyFrom(Environment ext, Object[] arr, int sourceStart, int destStart, int count) { - for (var i = 0; i < count; i++) { - set(ext, i + destStart, arr[i + sourceStart]); - } - } - - public void move(int srcI, int dstI, int n) { - values = alloc(dstI + n); - - System.arraycopy(values, srcI, values, dstI, n); - - if (dstI + n >= size) size = dstI + n; - } - - public void sort(Comparator comparator) { - Arrays.sort(values, 0, size, (a, b) -> { - var _a = 0; - var _b = 0; - - if (a == UNDEFINED) _a = 1; - if (a == null) _a = 2; - - if (b == UNDEFINED) _b = 1; - if (b == null) _b = 2; - - if (_a != 0 || _b != 0) return Integer.compare(_a, _b); - - return comparator.compare(a, b); - }); - } - - @Override - protected Object getField(Environment ext, Object key) { - if (key instanceof Number) { - var i = ((Number)key).doubleValue(); - if (i >= 0 && i - Math.floor(i) == 0) { - return get((int)i); - } - } - - return super.getField(ext, key); - } - @Override - protected boolean setField(Environment ext, Object key, Object val) { - if (key instanceof Number) { - var i = Values.number(key); - if (i >= 0 && i - Math.floor(i) == 0) { - set(ext, (int)i, val); - return true; - } - } - - return super.setField(ext, key, val); - } - @Override - protected boolean hasField(Environment ext, Object key) { - if (key instanceof Number) { - var i = Values.number(key); - if (i >= 0 && i - Math.floor(i) == 0) { - return has((int)i); - } - } - - return super.hasField(ext, key); - } - @Override - protected void deleteField(Environment ext, Object key) { - if (key instanceof Number) { - var i = Values.number(key); - if (i >= 0 && i - Math.floor(i) == 0) { - remove((int)i); - return; - } - } - - super.deleteField(ext, key); - } - - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - for (var i = 0; i < size(); i++) { - if (has(i)) res.add(i); - } - return res; - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return get(i++); - } - }; - } - - public ArrayValue() { - super(PlaceholderProto.ARRAY); - values = new Object[16]; - size = 0; - } - public ArrayValue(int cap) { - super(PlaceholderProto.ARRAY); - values = new Object[cap]; - size = 0; - } - public ArrayValue(Environment ext, Object ...values) { - this(); - this.values = new Object[values.length]; - size = values.length; - - for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ext, values[i]); - } - - public static ArrayValue of(Environment ext, Collection values) { - return new ArrayValue(ext, values.toArray(Object[]::new)); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java deleted file mode 100644 index 115f855..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.List; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -public abstract class FunctionValue extends ObjectValue { - public String name = ""; - public int length; - - @Override - public String toString() { - return String.format("function %s(...)", name); - } - - public abstract Object call(Environment ext, Object thisArg, Object ...args); - public Object call(Environment ext) { - return call(ext, null); - } - - @Override - protected Object getField(Environment ext, Object key) { - if ("name".equals(key)) return name; - if ("length".equals(key)) return length; - return super.getField(ext, key); - } - @Override - protected boolean setField(Environment ext, Object key, Object val) { - if ("name".equals(key)) name = Values.toString(ext, val); - else if ("length".equals(key)) length = (int)Values.toNumber(ext, val); - else return super.setField(ext, key, val); - return true; - } - @Override - protected boolean hasField(Environment ext, Object key) { - if ("name".equals(key)) return true; - if ("length".equals(key)) return true; - return super.hasField(ext, key); - } - - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - if (includeNonEnumerable) { - res.add("name"); - res.add("length"); - } - return res; - } - - public FunctionValue(String name, int length) { - super(PlaceholderProto.FUNCTION); - - if (name == null) name = ""; - this.length = length; - this.name = name; - - nonConfigurableSet.add("name"); - nonEnumerableSet.add("name"); - nonWritableSet.add("length"); - nonConfigurableSet.add("length"); - nonEnumerableSet.add("length"); - - var proto = new ObjectValue(); - proto.defineProperty(null, "constructor", this, true, false, false); - this.defineProperty(null, "prototype", proto, true, false, false); - } -} - diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/java/me/topchetoeu/jscript/runtime/values/Member.java new file mode 100644 index 0000000..91de993 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/Member.java @@ -0,0 +1,132 @@ +package me.topchetoeu.jscript.runtime.values; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +public interface Member { + public static final class PropertyMember implements Member { + public final FunctionValue getter; + public final FunctionValue setter; + public final boolean configurable; + public final boolean enumerable; + + @Override public Value get(Environment env, Value self) { + if (getter != null) return getter.call(env, self); + else return VoidValue.UNDEFINED; + } + @Override public boolean set(Environment env, Value val, Value self) { + if (setter == null) return false; + setter.call(env, self, val); + return true; + } + + @Override public boolean configurable() { return configurable; } + @Override public boolean enumerable() { return enumerable; } + + @Override public boolean configure(Environment env, Member newMember, Value self) { + if (!(newMember instanceof PropertyMember)) return false; + var prop = (PropertyMember)newMember; + + if (prop.configurable != configurable) return false; + if (prop.enumerable != enumerable) return false; + + if (prop.getter == getter) return true; + if (prop.setter == setter) return true; + return false; + } + + @Override public ObjectValue descriptor(Environment env, Value self) { + var res = new ObjectValue(); + + if (getter == null) res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(VoidValue.UNDEFINED)); + else res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(getter)); + + if (setter == null) res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(VoidValue.UNDEFINED)); + else res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(setter)); + + res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); + res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); + return res; + } + + public PropertyMember(FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { + this.getter = getter; + this.setter = setter; + this.configurable = configurable; + this.enumerable = enumerable; + } + } + + public static abstract class FieldMember implements Member { + private static class SimpleFieldMember extends FieldMember { + public Value value; + + @Override public Value get(Environment env, Value self) { return value; } + @Override public boolean set(Environment env, Value val, Value self) { + if (!writable) return false; + value = val; + return true; + } + public SimpleFieldMember(Value value, boolean configurable, boolean enumerable, boolean writable) { + super(configurable, enumerable, writable); + this.value = value; + } + } + + public boolean configurable; + public boolean enumerable; + public boolean writable; + + @Override public final boolean configurable() { return configurable; } + @Override public final boolean enumerable() { return enumerable; } + @Override public final boolean configure(Environment env, Member newMember, Value self) { + if (!(newMember instanceof FieldMember)) return false; + var field = (FieldMember)newMember; + + if (field.configurable != configurable) return false; + if (field.enumerable != enumerable) return false; + if (!writable) return field.get(env, self).strictEquals(env, get(env, self)); + + set(env, field.get(env, self), self); + writable = field.writable; + return true; + } + + @Override public ObjectValue descriptor(Environment env, Value self) { + var res = new ObjectValue(); + res.defineOwnMember(env, new StringValue("value"), FieldMember.of(get(env, self))); + res.defineOwnMember(env, new StringValue("writable"), FieldMember.of(BoolValue.of(writable))); + res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); + res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); + return res; + } + + public FieldMember(boolean configurable, boolean enumerable, boolean writable) { + this.configurable = configurable; + this.enumerable = enumerable; + this.writable = writable; + } + + public static FieldMember of(Value value) { + return new SimpleFieldMember(value, true, true, true); + } + public static FieldMember of(Value value, boolean writable) { + return new SimpleFieldMember(value, true, true, writable); + } + public static FieldMember of(Value value, boolean configurable, boolean enumerable, boolean writable) { + return new SimpleFieldMember(value, configurable, enumerable, writable); + } + } + + public boolean configurable(); + public boolean enumerable(); + public boolean configure(Environment env, Member newMember, Value self); + public ObjectValue descriptor(Environment env, Value self); + + public Value get(Environment env, Value self); + public boolean set(Environment env, Value val, Value self); +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java b/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java deleted file mode 100644 index c2fbf55..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java +++ /dev/null @@ -1,77 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.WeakHashMap; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; - -public class NativeWrapper extends ObjectValue { - private static class MapKey { - public final Object key; - - @Override - public int hashCode() { - return System.identityHashCode(key); - } - @Override - public boolean equals(Object obj) { - if (this == null || obj == null) return this == null && obj == null; - if (!(obj instanceof MapKey)) return false; - var other = (MapKey)obj; - - return other.key == key; - } - - public MapKey(Object key) { - this.key = key; - } - } - - private static final Key> WRAPPER_MAP = new Key<>(); - private static final Object NATIVE_PROTO = new Object(); - public final Object wrapped; - - @Override - public ObjectValue getPrototype(Environment ext) { - if (ext != null && prototype == NATIVE_PROTO) { - var clazz = wrapped.getClass(); - var res = NativeWrapperProvider.get(ext).getProto(clazz); - if (res != null) return res; - } - return super.getPrototype(ext); - } - - @Override - public String toString() { - return wrapped.toString(); - } - @Override - public boolean equals(Object obj) { - return wrapped.equals(obj); - } - @Override - public int hashCode() { - return wrapped.hashCode(); - } - - private NativeWrapper(Object wrapped) { - this.wrapped = wrapped; - prototype = NATIVE_PROTO; - } - - public static NativeWrapper of(Environment env, Object wrapped) { - if (env == null) return new NativeWrapper(wrapped); - - var wrappers = env.get(WRAPPER_MAP); - if (wrappers == null) return new NativeWrapper(wrapped); - - var key = new MapKey(wrapped); - if (wrappers.containsKey(key)) return wrappers.get(key); - - var res = new NativeWrapper(wrapped); - wrappers.put(key, res); - - return res; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java deleted file mode 100644 index aabb1f1..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java +++ /dev/null @@ -1,353 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -public class ObjectValue { - public static enum PlaceholderProto { - NONE, - OBJECT, - ARRAY, - FUNCTION, - ERROR, - SYNTAX_ERROR, - TYPE_ERROR, - RANGE_ERROR, - } - public static enum State { - NORMAL, - NO_EXTENSIONS, - SEALED, - FROZEN, - } - - public static class Property { - public final FunctionValue getter; - public final FunctionValue setter; - - public Property(FunctionValue getter, FunctionValue setter) { - this.getter = getter; - this.setter = setter; - } - } - - private static final Object OBJ_PROTO = new Object(); - private static final Object ARR_PROTO = new Object(); - private static final Object FUNC_PROTO = new Object(); - private static final Object ERR_PROTO = new Object(); - private static final Object SYNTAX_ERR_PROTO = new Object(); - private static final Object TYPE_ERR_PROTO = new Object(); - private static final Object RANGE_ERR_PROTO = new Object(); - - protected Object prototype; - - public State state = State.NORMAL; - public LinkedHashMap values = new LinkedHashMap<>(); - public LinkedHashMap properties = new LinkedHashMap<>(); - public LinkedHashSet nonWritableSet = new LinkedHashSet<>(); - public LinkedHashSet nonConfigurableSet = new LinkedHashSet<>(); - public LinkedHashSet nonEnumerableSet = new LinkedHashSet<>(); - - private Property getProperty(Environment env, Object key) { - if (properties.containsKey(key)) return properties.get(key); - var proto = getPrototype(env); - if (proto != null) return proto.getProperty(env, key); - else return null; - } - - public final boolean memberWritable(Object key) { - if (state == State.FROZEN) return false; - return !values.containsKey(key) || !nonWritableSet.contains(key); - } - public final boolean memberConfigurable(Object key) { - if (state == State.SEALED || state == State.FROZEN) return false; - return !nonConfigurableSet.contains(key); - } - public final boolean memberEnumerable(Object key) { - return !nonEnumerableSet.contains(key); - } - public final boolean extensible() { - return state == State.NORMAL; - } - - public final void preventExtensions() { - if (state == State.NORMAL) state = State.NO_EXTENSIONS; - } - public final void seal() { - if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED; - } - public final void freeze() { - state = State.FROZEN; - } - - public final boolean defineProperty(Environment env, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { - key = Values.normalize(env, key); val = Values.normalize(env, val); - boolean reconfigured = - writable != memberWritable(key) || - configurable != memberConfigurable(key) || - enumerable != memberEnumerable(key); - - if (!reconfigured) { - if (!memberWritable(key)) { - var a = values.get(key); - var b = val; - if (a == null || b == null) return a == null && b == null; - return a == b || a.equals(b); - } - values.put(key, val); - return true; - } - - if ( - properties.containsKey(key) && - values.get(key) == val && - !reconfigured - ) return true; - - if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false; - if (!memberConfigurable(key)) return false; - - nonWritableSet.remove(key); - nonEnumerableSet.remove(key); - properties.remove(key); - values.remove(key); - - if (!writable) nonWritableSet.add(key); - if (!configurable) nonConfigurableSet.add(key); - if (!enumerable) nonEnumerableSet.add(key); - - values.put(key, val); - return true; - } - public final boolean defineProperty(Environment env, Object key, Object val) { - return defineProperty(env, key, val, true, true, true); - } - public final boolean defineProperty(Environment env, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { - key = Values.normalize(env, key); - if ( - properties.containsKey(key) && - properties.get(key).getter == getter && - properties.get(key).setter == setter && - !configurable == nonConfigurableSet.contains(key) && - !enumerable == nonEnumerableSet.contains(key) - ) return true; - if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false; - if (!memberConfigurable(key)) return false; - - nonWritableSet.remove(key); - nonEnumerableSet.remove(key); - properties.remove(key); - values.remove(key); - - if (!configurable) nonConfigurableSet.add(key); - if (!enumerable) nonEnumerableSet.add(key); - - properties.put(key, new Property(getter, setter)); - return true; - } - - public ObjectValue getPrototype(Environment env) { - if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype; - - try { - if (prototype == ARR_PROTO) return env.get(Environment.ARRAY_PROTO); - if (prototype == FUNC_PROTO) return env.get(Environment.FUNCTION_PROTO); - if (prototype == ERR_PROTO) return env.get(Environment.ERROR_PROTO); - if (prototype == RANGE_ERR_PROTO) return env.get(Environment.RANGE_ERR_PROTO); - if (prototype == SYNTAX_ERR_PROTO) return env.get(Environment.SYNTAX_ERR_PROTO); - if (prototype == TYPE_ERR_PROTO) return env.get(Environment.TYPE_ERR_PROTO); - return env.get(Environment.OBJECT_PROTO); - } - catch (NullPointerException e) { return null; } - } - public final boolean setPrototype(PlaceholderProto val) { - if (!extensible()) return false; - switch (val) { - case OBJECT: prototype = OBJ_PROTO; break; - case FUNCTION: prototype = FUNC_PROTO; break; - case ARRAY: prototype = ARR_PROTO; break; - case ERROR: prototype = ERR_PROTO; break; - case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break; - case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break; - case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break; - case NONE: prototype = null; break; - } - return true; - } - - /** - * A method, used to get the value of a field. If a property is bound to - * this key, but not a field, this method should return null. - */ - protected Object getField(Environment env, Object key) { - if (values.containsKey(key)) return values.get(key); - var proto = getPrototype(env); - if (proto != null) return proto.getField(env, key); - else return null; - } - /** - * Changes the value of a field, that is bound to the given key. If no field is - * bound to this key, a new field should be created with the given value - * @return Whether or not the operation was successful - */ - protected boolean setField(Environment env, Object key, Object val) { - if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) { - ((FunctionValue)val).name = Values.toString(env, key); - } - - values.put(key, val); - return true; - } - /** - * Deletes the field bound to the given key. - */ - protected void deleteField(Environment env, Object key) { - values.remove(key); - } - /** - * Returns whether or not there is a field bound to the given key. - * This must ignore properties - */ - protected boolean hasField(Environment env, Object key) { - return values.containsKey(key); - } - - public final Object getMember(Environment env, Object key, Object thisArg) { - key = Values.normalize(env, key); - - if ("__proto__".equals(key)) { - var res = getPrototype(env); - return res == null ? Values.NULL : res; - } - - var prop = getProperty(env, key); - - if (prop != null) { - if (prop.getter == null) return null; - else return prop.getter.call(env, Values.normalize(env, thisArg)); - } - else return getField(env, key); - } - public final boolean setMember(Environment env, Object key, Object val, Object thisArg, boolean onlyProps) { - key = Values.normalize(env, key); val = Values.normalize(env, val); - - var prop = getProperty(env, key); - if (prop != null) { - if (prop.setter == null) return false; - prop.setter.call(env, Values.normalize(env, thisArg), val); - return true; - } - else if (onlyProps) return false; - else if (!extensible() && !values.containsKey(key)) return false; - else if (key == null) { - values.put(key, val); - return true; - } - else if ("__proto__".equals(key)) return setPrototype(env, val); - else if (nonWritableSet.contains(key)) return false; - else return setField(env, key, val); - } - public final boolean hasMember(Environment env, Object key, boolean own) { - key = Values.normalize(env, key); - - if (key != null && "__proto__".equals(key)) return true; - if (hasField(env, key)) return true; - if (properties.containsKey(key)) return true; - if (own) return false; - var proto = getPrototype(env); - return proto != null && proto.hasMember(env, key, own); - } - public final boolean deleteMember(Environment env, Object key) { - key = Values.normalize(env, key); - - if (!memberConfigurable(key)) return false; - properties.remove(key); - nonWritableSet.remove(key); - nonEnumerableSet.remove(key); - deleteField(env, key); - return true; - } - public final boolean setPrototype(Environment env, Object val) { - val = Values.normalize(env, val); - - if (!extensible()) return false; - if (val == null || val == Values.NULL) { - prototype = null; - return true; - } - else if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - - if (env != null) { - if (obj == env.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO; - else if (obj == env.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO; - else if (obj == env.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO; - else if (obj == env.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO; - else if (obj == env.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO; - else if (obj == env.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO; - else if (obj == env.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO; - else prototype = obj; - } - else prototype = obj; - - return true; - } - return false; - } - - public final ObjectValue getMemberDescriptor(Environment env, Object key) { - key = Values.normalize(env, key); - - var prop = properties.get(key); - var res = new ObjectValue(); - - res.defineProperty(env, "configurable", memberConfigurable(key)); - res.defineProperty(env, "enumerable", memberEnumerable(key)); - - if (prop != null) { - res.defineProperty(env, "get", prop.getter); - res.defineProperty(env, "set", prop.setter); - } - else if (hasField(env, key)) { - res.defineProperty(env, "value", values.get(key)); - res.defineProperty(env, "writable", memberWritable(key)); - } - else return null; - return res; - } - - public List keys(boolean includeNonEnumerable) { - var res = new ArrayList(); - - for (var key : values.keySet()) { - if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue; - res.add(key); - } - for (var key : properties.keySet()) { - if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue; - res.add(key); - } - - return res; - } - - public ObjectValue(Environment env, Map values) { - this(PlaceholderProto.OBJECT); - for (var el : values.entrySet()) { - defineProperty(env, el.getKey(), el.getValue()); - } - } - public ObjectValue(PlaceholderProto proto) { - nonConfigurableSet.add("__proto__"); - nonEnumerableSet.add("__proto__"); - setPrototype(proto); - } - public ObjectValue() { - this(PlaceholderProto.OBJECT); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java deleted file mode 100644 index 3abf2ee..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.HashMap; -import java.util.List; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; - -public class ScopeValue extends ObjectValue { - public final ValueVariable[] variables; - public final HashMap names = new HashMap<>(); - - @Override - protected Object getField(Environment ext, Object key) { - if (names.containsKey(key)) return variables[names.get(key)].get(ext); - return super.getField(ext, key); - } - @Override - protected boolean setField(Environment ext, Object key, Object val) { - if (names.containsKey(key)) { - variables[names.get(key)].set(ext, val); - return true; - } - - var proto = getPrototype(ext); - if (proto != null && proto.hasMember(ext, key, false) && proto.setField(ext, key, val)) return true; - - return super.setField(ext, key, val); - } - @Override - protected void deleteField(Environment ext, Object key) { - if (names.containsKey(key)) return; - super.deleteField(ext, key); - } - @Override - protected boolean hasField(Environment ext, Object key) { - if (names.containsKey(key)) return true; - return super.hasField(ext, key); - } - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - res.addAll(names.keySet()); - return res; - } - - public ScopeValue(ValueVariable[] variables, String[] names) { - this.variables = variables; - for (var i = 0; i < names.length && i < variables.length; i++) { - this.names.put(names[i], i); - this.nonConfigurableSet.add(names[i]); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Symbol.java b/src/java/me/topchetoeu/jscript/runtime/values/Symbol.java deleted file mode 100644 index efb8f32..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/Symbol.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.HashMap; - -public final class Symbol { - private static final HashMap registry = new HashMap<>(); - - public final String value; - - public Symbol(String value) { - this.value = value; - } - - @Override - public String toString() { - if (value == null) return "Symbol"; - else return "@@" + value; - } - - public static Symbol get(String name) { - if (registry.containsKey(name)) return registry.get(name); - else { - var res = new Symbol(name); - registry.put(name, res); - return res; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index a240ec0..480fd1e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -2,26 +2,34 @@ package me.topchetoeu.jscript.runtime.values; import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.lang.reflect.Array; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; import me.topchetoeu.jscript.common.Operation; -// import me.topchetoeu.jscript.lib.PromiseLib; +import me.topchetoeu.jscript.common.json.JSON; +import me.topchetoeu.jscript.common.json.JSONElement; +import me.topchetoeu.jscript.runtime.EventLoop; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.ConvertException; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; -public interface Value { +public abstract class Value { public static enum CompareResult { NOT_EQUAL, EQUAL, @@ -43,666 +51,490 @@ public interface Value { public static final Object NULL = new Object(); public static final Object NO_RETURN = new Object(); - public static double number(Object val) { - if (val instanceof Number) return ((Number)val).doubleValue(); - else return Double.NaN; + public abstract StringValue type(); + public abstract boolean isPrimitive(); + public abstract BoolValue toBoolean(); + + public final boolean isNaN() { + return this instanceof NumberValue && Double.isNaN(((NumberValue)this).value); } - @SuppressWarnings("unchecked") - public static T wrapper(Object val, Class clazz) { - if (isWrapper(val)) val = ((NativeWrapper)val).wrapped; - if (val != null && clazz.isInstance(val)) return (T)val; - else return null; - } - - public static String type(Object val) { - if (val == null) return "undefined"; - if (val instanceof String) return "string"; - if (val instanceof Number) return "number"; - if (val instanceof Boolean) return "boolean"; - if (val instanceof Symbol) return "symbol"; - if (val instanceof FunctionValue) return "function"; - return "object"; - } - - public boolean isPrimitive(); - public BooleanValue toBoolean(); - - public default Value call(Environment env, Value self, Value ...args) { + public Value call(Environment env, Value self, Value ...args) { throw EngineException.ofType("Tried to call a non-function value."); } - public default Value callNew(Environment env, Value ...args) { + public final Value callNew(Environment env, Value ...args) { var res = new ObjectValue(); + var proto = getMember(env, new StringValue("prototype")); - try { - var proto = Values.getMember(env, this, "prototype"); - setPrototype(env, res, proto); + if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); + else res.setPrototype(env, null); - var ret = this.call(env, res, args); + var ret = this.call(env, res, args); - if (!ret.isPrimitive()) return ret; - return res; - } - catch (IllegalArgumentException e) { - throw EngineException.ofType("Tried to call new on an invalid constructor."); + if (!ret.isPrimitive()) return ret; + return res; + } + + public abstract Value toPrimitive(Environment env); + public abstract NumberValue toNumber(Environment env); + public abstract StringValue toString(Environment env); + + public final int toInt(Environment env) { return (int)toNumber(env).value; } + public final long toLong(Environment env) { return (long)toNumber(env).value; } + + public Value add(Environment env, Value other) { + if (this instanceof StringValue || other instanceof StringValue) { + return new StringValue(this.toString(env).value + other.toString(env).value); } + else return new NumberValue(this.toNumber(env).value + other.toNumber(env).value); + } + public NumberValue subtract(Environment env, Value other) { + return new NumberValue(toNumber(env).value - other.toNumber(env).value); + } + public NumberValue multiply(Environment env, Value other) { + return new NumberValue(toNumber(env).value - other.toNumber(env).value); + } + public NumberValue divide(Environment env, Value other) { + return new NumberValue(toNumber(env).value / other.toNumber(env).value); + } + public NumberValue modulo(Environment env, Value other) { + return new NumberValue(toNumber(env).value % other.toNumber(env).value); + } + public NumberValue negative(Environment env) { + return new NumberValue(-toNumber(env).value); } - public default Value toPrimitive(Environment env, Value val) { - if (val.isPrimitive()) return val; - - if (env != null) { - var valueOf = getMember(env, val, "valueOf"); - - if (valueOf instanceof FunctionValue) { - var res = valueOf.call(env, val); - if (res.isPrimitive()) return res; - } - - var toString = getMember(env, val, "toString"); - if (toString instanceof FunctionValue) { - var res = toString.call(env, val); - if (res.isPrimitive()) return res; - } - } - - throw EngineException.ofType("Value couldn't be converted to a primitive."); + public NumberValue and(Environment env, Value other) { + return new NumberValue(this.toInt(env) & other.toInt(env)); } - public default NumberValue toNumber(Environment ext, Object obj) { - var val = this.toPrimitive(ext, obj, ConvertHint.VALUEOF); - - if (val instanceof NumberValue) return number(val); - if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0; - if (val instanceof String) { - try { return Double.parseDouble((String)val); } - catch (NumberFormatException e) { return Double.NaN; } - } - return Double.NaN; + public NumberValue or(Environment env, Value other) { + return new NumberValue(this.toInt(env) | other.toInt(env)); } - public default StringValue toString(Environment ext, Object obj) { - var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); - - if (val == null) return "undefined"; - if (val == NULL) return "null"; - - if (val instanceof Number) { - var d = number(val); - if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; - if (d == Double.POSITIVE_INFINITY) return "Infinity"; - if (Double.isNaN(d)) return "NaN"; - return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); - } - if (val instanceof Boolean) return (Boolean)val ? "true" : "false"; - if (val instanceof String) return (String)val; - if (val instanceof Symbol) return val.toString(); - - return "Unknown value"; + public NumberValue xor(Environment env, Value other) { + return new NumberValue(this.toInt(env) ^ other.toInt(env)); + } + public NumberValue bitwiseNot(Environment env) { + return new NumberValue(~this.toInt(env)); } - public static Object add(Environment ext, Object a, Object b) { - if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b); - else return toNumber(ext, a) + toNumber(ext, b); + public NumberValue shiftLeft(Environment env, Value other) { + return new NumberValue(this.toInt(env) << other.toInt(env)); } - public static double subtract(Environment ext, Object a, Object b) { - return toNumber(ext, a) - toNumber(ext, b); + public NumberValue shiftRight(Environment env, Value other) { + return new NumberValue(this.toInt(env) >> other.toInt(env)); } - public static double multiply(Environment ext, Object a, Object b) { - return toNumber(ext, a) * toNumber(ext, b); - } - public static double divide(Environment ext, Object a, Object b) { - return toNumber(ext, a) / toNumber(ext, b); - } - public static double modulo(Environment ext, Object a, Object b) { - return toNumber(ext, a) % toNumber(ext, b); - } - - public static double negative(Environment ext, Object obj) { - return -toNumber(ext, obj); + public NumberValue unsignedShiftRight(Environment env, Value other) { + long a = this.toInt(env); + long b = other.toInt(env); + + if (a < 0) a += 0x100000000l; + if (b < 0) b += 0x100000000l; + + return new NumberValue(a >>> b); } - public static int and(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) & (int)toNumber(ext, b); - } - public static int or(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) | (int)toNumber(ext, b); - } - public static int xor(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) ^ (int)toNumber(ext, b); - } - public static int bitwiseNot(Environment ext, Object obj) { - return ~(int)toNumber(ext, obj); + public CompareResult compare(Environment env, Value other) { + return toPrimitive(env).compare(env, other); } - public static int shiftLeft(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) << (int)toNumber(ext, b); - } - public static int shiftRight(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) >> (int)toNumber(ext, b); - } - public static long unsignedShiftRight(Environment ext, Object a, Object b) { - long _a = (long)toNumber(ext, a); - long _b = (long)toNumber(ext, b); - - if (_a < 0) _a += 0x100000000l; - if (_b < 0) _b += 0x100000000l; - return _a >>> _b; + public final BoolValue not() { + return toBoolean().value ? BoolValue.FALSE : BoolValue.TRUE; } - public static CompareResult compare(Environment ext, Object a, Object b) { - a = toPrimitive(ext, a, ConvertHint.VALUEOF); - b = toPrimitive(ext, b, ConvertHint.VALUEOF); - - if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b)); - - var _a = toNumber(ext, a); - var _b = toNumber(ext, b); - - if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL; - - return CompareResult.from(Double.compare(_a, _b)); - } - - public static boolean not(Object obj) { - return !toBoolean(obj); - } - - public static boolean isInstanceOf(Environment ext, Object obj, Object proto) { - if (obj == null || obj == NULL || proto == null || proto == NULL) return false; - var val = getPrototype(ext, obj); - - while (val != null) { + public final boolean isInstanceOf(Environment env, Value proto) { + for (var val = getPrototype(env); val != null; val = getPrototype(env)) { if (val.equals(proto)) return true; - val = val.getPrototype(ext); } return false; } - public static Object operation(Environment ext, Operation op, Object ...args) { + public static Value operation(Environment env, Operation op, Value ...args) { switch (op) { - case ADD: return add(ext, args[0], args[1]); - case SUBTRACT: return subtract(ext, args[0], args[1]); - case DIVIDE: return divide(ext, args[0], args[1]); - case MULTIPLY: return multiply(ext, args[0], args[1]); - case MODULO: return modulo(ext, args[0], args[1]); + case ADD: return args[0].add(env, args[1]); + case SUBTRACT: return args[0].subtract(env, args[1]); + case DIVIDE: return args[0].divide(env, args[1]); + case MULTIPLY: return args[0].multiply(env, args[1]); + case MODULO: return args[0].modulo(env, args[1]); - case AND: return and(ext, args[0], args[1]); - case OR: return or(ext, args[0], args[1]); - case XOR: return xor(ext, args[0], args[1]); + case AND: return args[0].and(env, args[1]); + case OR: return args[0].or(env, args[1]); + case XOR: return args[0].xor(env, args[1]); - case EQUALS: return strictEquals(ext, args[0], args[1]); - case NOT_EQUALS: return !strictEquals(ext, args[0], args[1]); - case LOOSE_EQUALS: return looseEqual(ext, args[0], args[1]); - case LOOSE_NOT_EQUALS: return !looseEqual(ext, args[0], args[1]); + case EQUALS: return BoolValue.of(args[0].strictEquals(env, args[1])); + case NOT_EQUALS: return BoolValue.of(!args[0].strictEquals(env, args[1])); + case LOOSE_EQUALS: return BoolValue.of(args[0].looseEqual(env, args[1])); + case LOOSE_NOT_EQUALS: return BoolValue.of(!args[0].looseEqual(env, args[1])); - case GREATER: return compare(ext, args[0], args[1]).greater(); - case GREATER_EQUALS: return compare(ext, args[0], args[1]).greaterOrEqual(); - case LESS: return compare(ext, args[0], args[1]).less(); - case LESS_EQUALS: return compare(ext, args[0], args[1]).lessOrEqual(); + case GREATER: return BoolValue.of(args[0].compare(env, args[1]).greater()); + case GREATER_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).greaterOrEqual()); + case LESS: return BoolValue.of(args[0].compare(env, args[1]).less()); + case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual()); - case INVERSE: return bitwiseNot(ext, args[0]); - case NOT: return not(args[0]); - case POS: return toNumber(ext, args[0]); - case NEG: return negative(ext, args[0]); + case INVERSE: return args[0].bitwiseNot(env); + case NOT: return args[0].not(); + case POS: return args[0].toNumber(env); + case NEG: return args[0].negative(env); - case SHIFT_LEFT: return shiftLeft(ext, args[0], args[1]); - case SHIFT_RIGHT: return shiftRight(ext, args[0], args[1]); - case USHIFT_RIGHT: return unsignedShiftRight(ext, args[0], args[1]); + case SHIFT_LEFT: return args[0].shiftLeft(env, args[1]); + case SHIFT_RIGHT: return args[0].shiftRight(env, args[1]); + case USHIFT_RIGHT: return args[0].unsignedShiftRight(env, args[1]); - case IN: return hasMember(ext, args[1], args[0], false); - case INSTANCEOF: { - var proto = getMember(ext, args[1], "prototype"); - return isInstanceOf(ext, args[0], proto); - } + case IN: return BoolValue.of(args[0].hasMember(env, args[1], false)); + case INSTANCEOF: return BoolValue.of(args[0].isInstanceOf(env, args[1].getMember(env, new StringValue("prototype")))); default: return null; } } - public static Object getMember(Environment ctx, Object obj, Object key) { - obj = normalize(ctx, obj); key = normalize(ctx, key); - if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); - if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj); + public abstract Member getOwnMember(Environment env, Value key); + public abstract Map getOwnMembers(Environment env); + public abstract Map getOwnSymbolMembers(Environment env); + public abstract boolean defineOwnMember(Environment env, Value key, Member member); + public abstract boolean deleteOwnMember(Environment env, Value key); - if (obj instanceof String && key instanceof Number) { - var i = number(key); - var s = (String)obj; - if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) { - return s.charAt((int)i) + ""; + public abstract ObjectValue getPrototype(Environment env); + public abstract boolean setPrototype(Environment env, ObjectValue val); + + public final Value getMember(Environment env, Value key) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + var member = obj.getOwnMember(env, key); + if (member != null) return member.get(env, obj); + } + + return VoidValue.UNDEFINED; + } + public final boolean setMember(Environment env, Value key, Value val) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + var member = obj.getOwnMember(env, key); + if (member != null) return member.set(env, val, obj); + } + + return defineOwnMember(env, key, FieldMember.of(val)); + } + public final boolean hasMember(Environment env, Value key, boolean own) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + if (obj.getOwnMember(env, key) != null) return true; + if (own) return false; + } + + return false; + } + public final boolean deleteMember(Environment env, Value key) { + if (!hasMember(env, key, true)) return true; + return deleteOwnMember(env, key); + } + public final Map getMembers(Environment env, boolean own, boolean onlyEnumerable) { + var res = new LinkedHashMap(); + var protos = new ArrayList(); + + for (var proto = this; proto != null; proto = proto.getPrototype(env)) { + protos.add(proto); + if (own) break; + } + + Collections.reverse(protos); + + for (var proto : protos) { + if (onlyEnumerable) { + for (var el : proto.getOwnMembers(env).entrySet()) { + if (!el.getValue().enumerable()) continue; + res.put(el.getKey(), el.getValue()); + } } + else res.putAll(proto.getOwnMembers(env)); } - var proto = getPrototype(ctx, obj); - - if (proto == null) return "__proto__".equals(key) ? NULL : null; - else if (key != null && "__proto__".equals(key)) return proto; - else return proto.getMember(ctx, key, obj); - } - public static Object getMemberPath(Environment ctx, Object obj, Object ...path) { - var res = obj; - for (var key : path) res = getMember(ctx, res, key); - return res; - } - public static boolean setMember(Environment ctx, Object obj, Object key, Object val) { - obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); - if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); - if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); - if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false); - - var proto = getPrototype(ctx, obj); - return proto.setMember(ctx, key, val, obj, true); - } - public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) { - if (obj == null || obj == NULL) return false; - obj = normalize(ctx, obj); key = normalize(ctx, key); - - if ("__proto__".equals(key)) return true; - if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own); - - if (obj instanceof String && key instanceof Number) { - var i = number(key); - var s = (String)obj; - if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true; - } - - if (own) return false; - - var proto = getPrototype(ctx, obj); - return proto != null && proto.hasMember(ctx, key, own); - } - public static boolean deleteMember(Environment ext, Object obj, Object key) { - if (obj == null || obj == NULL) return false; - obj = normalize(ext, obj); key = normalize(ext, key); - - if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key); - else return false; - } - public static ObjectValue getPrototype(Environment ext, Object obj) { - if (obj == null || obj == NULL) return null; - obj = normalize(ext, obj); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext); - if (ext == null) return null; - - if (obj instanceof String) return ext.get(Environment.STRING_PROTO); - else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO); - else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO); - else if (obj instanceof Symbol) return ext.get(Environment.SYMBOL_PROTO); - - return null; - } - public static boolean setPrototype(Environment ext, Object obj, Object proto) { - obj = normalize(ext, obj); - return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto); - } - public static void makePrototypeChain(Environment ext, Object... chain) { - for(var i = 1; i < chain.length; i++) { - setPrototype(ext, chain[i], chain[i - 1]); - } - } - public static List getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) { - List res = new ArrayList<>(); - - if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable); - if (obj instanceof String) { - for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i); - } - - if (!own) { - var proto = getPrototype(ext, obj); - - while (proto != null) { - res.addAll(proto.keys(includeNonEnumerable)); - proto = getPrototype(ext, proto); - } - } - - return res; } - public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) { - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key); - else if (obj instanceof String && key instanceof Number) { - var i = ((Number)key).intValue(); - var _i = ((Number)key).doubleValue(); - if (i - _i != 0) return null; - if (i < 0 || i >= ((String)obj).length()) return null; + public final Map getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) { + var res = new LinkedHashMap(); + var protos = new ArrayList(); - return new ObjectValue(ext, Map.of( - "value", ((String)obj).charAt(i) + "", - "writable", false, - "enumerable", true, - "configurable", false - )); + for (var proto = this; proto != null; proto = proto.getPrototype(env)) { + protos.add(proto); + if (own) break; } + + Collections.reverse(protos); + + for (var proto : protos) { + if (onlyEnumerable) { + for (var el : proto.getOwnSymbolMembers(env).entrySet()) { + if (!el.getValue().enumerable()) continue; + res.put(el.getKey(), el.getValue()); + } + } + else res.putAll(proto.getOwnSymbolMembers(env)); + } + + return res; + } + + public final Value getMemberPath(Environment env, Value ...path) { + var res = this; + for (var key : path) res = res.getMember(env, key); + return res; + } + public final ObjectValue getMemberDescriptor(Environment env, Value key) { + var member = getOwnMember(env, key); + + if (member != null) return member.descriptor(env, this); else return null; } - public static boolean strictEquals(Environment ext, Object a, Object b) { - a = normalize(ext, a); - b = normalize(ext, b); + public abstract boolean strictEquals(Environment env, Value other); - if (a == null || b == null) return a == null && b == null; - if (isNan(a) || isNan(b)) return false; - if (a instanceof Number && number(a) == -0.) a = 0.; - if (b instanceof Number && number(b) == -0.) b = 0.; - - return a == b || a.equals(b); - } - public static boolean looseEqual(Environment ext, Object a, Object b) { - a = normalize(ext, a); b = normalize(ext, b); + public final boolean looseEqual(Environment env, Value other) { + var a = this; + var b = other; // In loose equality, null is equivalent to undefined - if (a == NULL) a = null; - if (b == NULL) b = null; + if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue; - if (a == null || b == null) return a == null && b == null; // If both are objects, just compare their references - if (!isPrimitive(a) && !isPrimitive(b)) return a == b; + if (!a.isPrimitive() && !b.isPrimitive()) return a.strictEquals(env, b); // Convert values to primitives - a = toPrimitive(ext, a, ConvertHint.VALUEOF); - b = toPrimitive(ext, b, ConvertHint.VALUEOF); + a = a.toPrimitive(env); + b = b.toPrimitive(env); // Compare symbols by reference - if (a instanceof Symbol || b instanceof Symbol) return a == b; - if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); - if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); + if (a instanceof SymbolValue || b instanceof SymbolValue) return a.strictEquals(env, b); + // Compare booleans as numbers + if (a instanceof BoolValue || b instanceof BoolValue) return a.toNumber(env).strictEquals(env, b.toNumber(env)); + // Comparse numbers as numbers + if (a instanceof NumberValue || b instanceof NumberValue) return a.toNumber(env).strictEquals(env, b.toNumber(env)); // Default to strings - return toString(ext, a).equals(toString(ext, b)); + return a.toString(env).strictEquals(env, b.toString(env)); } - public static Object normalize(Environment ext, Object val) { - if (val instanceof Number) return number(val); - if (isPrimitive(val) || val instanceof ObjectValue) return val; - if (val instanceof Character) return val + ""; + // @SuppressWarnings("unchecked") + // public static T convert(Environment ext, Class clazz) { + // if (clazz == Void.class) return null; - if (val instanceof Map) { - var res = new ObjectValue(); + // if (this instanceof NativeWrapper) { + // var res = ((NativeWrapper)this).wrapped; + // if (clazz.isInstance(res)) return (T)res; + // } - for (var entry : ((Map)val).entrySet()) { - res.defineProperty(ext, entry.getKey(), entry.getValue()); - } + // if (clazz == null || clazz == Object.class) return (T)this; - return res; - } + // if (this instanceof ArrayValue) { + // if (clazz.isAssignableFrom(ArrayList.class)) { + // var raw = ((ArrayValue)this).toArray(); + // var res = new ArrayList<>(); + // for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); + // return (T)new ArrayList<>(res); + // } + // if (clazz.isAssignableFrom(HashSet.class)) { + // var raw = ((ArrayValue)this).toArray(); + // var res = new HashSet<>(); + // for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); + // return (T)new HashSet<>(res); + // } + // if (clazz.isArray()) { + // var raw = ((ArrayValue)this).toArray(); + // Object res = Array.newInstance(clazz.getComponentType(), raw.length); + // for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); + // return (T)res; + // } + // } - if (val instanceof Iterable) { - var res = new ArrayValue(); + // if (this instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { + // var res = new HashMap<>(); + // for (var el : ((ObjectValue)this).values.entrySet()) res.put( + // convert(ext, el.getKey(), null), + // convert(ext, el.getValue(), null) + // ); + // return (T)res; + // } - for (var entry : ((Iterable)val)) { - res.set(ext, res.size(), entry); - } + // if (clazz == String.class) return (T)toString(ext, this); + // if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(this); + // if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, this); + // if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, this); + // if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, this); + // if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, this); + // if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, this); + // if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, this); - return res; - } + // if (clazz == Character.class || clazz == char.class) { + // if (this instanceof Number) return (T)(Character)(char)number(this); + // else { + // var res = toString(ext, this); + // if (res.length() == 0) throw new ConvertException("\"\"", "Character"); + // else return (T)(Character)res.charAt(0); + // } + // } - if (val instanceof Class) { - if (ext == null) return null; - else return NativeWrapperProvider.get(ext).getConstr((Class)val); - } + // if (this == null) return null; + // if (clazz.isInstance(this)) return (T)this; + // if (clazz.isAssignableFrom(NativeWrapper.class)) { + // return (T)NativeWrapper.of(ext, this); + // } - return NativeWrapper.of(ext, val); - } + // throw new ConvertException(type(this), clazz.getSimpleName()); + // } - @SuppressWarnings("unchecked") - public static T convert(Environment ext, Object obj, Class clazz) { - if (clazz == Void.class) return null; - - if (obj instanceof NativeWrapper) { - var res = ((NativeWrapper)obj).wrapped; - if (clazz.isInstance(res)) return (T)res; - } - - if (clazz == null || clazz == Object.class) return (T)obj; - - if (obj instanceof ArrayValue) { - if (clazz.isAssignableFrom(ArrayList.class)) { - var raw = ((ArrayValue)obj).toArray(); - var res = new ArrayList<>(); - for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - return (T)new ArrayList<>(res); - } - if (clazz.isAssignableFrom(HashSet.class)) { - var raw = ((ArrayValue)obj).toArray(); - var res = new HashSet<>(); - for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - return (T)new HashSet<>(res); - } - if (clazz.isArray()) { - var raw = ((ArrayValue)obj).toArray(); - Object res = Array.newInstance(clazz.getComponentType(), raw.length); - for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); - return (T)res; - } - } - - if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { - var res = new HashMap<>(); - for (var el : ((ObjectValue)obj).values.entrySet()) res.put( - convert(ext, el.getKey(), null), - convert(ext, el.getValue(), null) - ); - return (T)res; - } - - if (clazz == String.class) return (T)toString(ext, obj); - if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj); - if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, obj); - if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, obj); - if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, obj); - if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, obj); - if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, obj); - if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, obj); - - if (clazz == Character.class || clazz == char.class) { - if (obj instanceof Number) return (T)(Character)(char)number(obj); - else { - var res = toString(ext, obj); - if (res.length() == 0) throw new ConvertException("\"\"", "Character"); - else return (T)(Character)res.charAt(0); - } - } - - if (obj == null) return null; - if (clazz.isInstance(obj)) return (T)obj; - if (clazz.isAssignableFrom(NativeWrapper.class)) { - return (T)NativeWrapper.of(ext, obj); - } - - throw new ConvertException(type(obj), clazz.getSimpleName()); - } - - public static Iterable fromJSIterator(Environment ext, Object obj) { + public Iterable toIterable(Environment env) { return () -> { - try { - var symbol = Symbol.get("Symbol.iterator"); + if (!(this instanceof FunctionValue)) return Collections.emptyIterator(); + var func = (FunctionValue)this; - var iteratorFunc = getMember(ext, obj, symbol); - if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); - var iterator = iteratorFunc instanceof FunctionValue ? - ((FunctionValue)iteratorFunc).call(ext, obj, obj) : - iteratorFunc; - var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next"); + return new Iterator() { + private Object value = null; + public boolean consumed = true; + private FunctionValue supplier = func; - if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator(); + private void loadNext() { + if (supplier == null) value = null; + else if (consumed) { + var curr = supplier.call(env, VoidValue.UNDEFINED); - return new Iterator() { - private Object value = null; - public boolean consumed = true; - private FunctionValue next = (FunctionValue)nextFunc; - - private void loadNext() { - if (next == null) value = null; - else if (consumed) { - var curr = next.call(ext, iterator); - if (curr == null) { next = null; value = null; } - if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; } - else { - this.value = Values.getMember(ext, curr, "value"); - consumed = false; - } + if (curr == null) { supplier = null; value = null; } + if (curr.getMember(env, new StringValue("done")).toBoolean().value) { supplier = null; value = null; } + else { + this.value = curr.getMember(env, new StringValue("value")); + consumed = false; } } + } - @Override - public boolean hasNext() { - loadNext(); - return next != null; - } - @Override - public Object next() { - loadNext(); - var res = value; - value = null; - consumed = true; - return res; - } - }; - } - catch (IllegalArgumentException | NullPointerException e) { - return Collections.emptyIterator(); - } + @Override + public boolean hasNext() { + loadNext(); + return supplier != null; + } + @Override + public Object next() { + loadNext(); + var res = value; + value = null; + consumed = true; + return res; + } + }; }; } - public static ObjectValue toJSIterator(Environment ext, Iterator it) { - var res = new ObjectValue(); + public static FunctionValue fromIterator(Environment ext, Iterable iterable) { + var it = iterable.iterator(); - try { - var key = getMember(ext, getMember(ext, ext.get(Environment.SYMBOL_PROTO), "constructor"), "iterator"); - res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); + return new NativeFunction("", args -> { + var obj = new ObjectValue(); + + if (!it.hasNext()) obj.defineOwnMember(args.env, new StringValue("done"), FieldMember.of(BoolValue.TRUE)); + else obj.defineOwnMember(args.env, new StringValue("value"), FieldMember.of(it.next())); + + return obj; + }); + } + + public void callWith(Environment env, Iterable it) { + for (var el : it) { + this.call(env, VoidValue.UNDEFINED, el); } - catch (IllegalArgumentException | NullPointerException e) { } - - res.defineProperty(ext, "next", new NativeFunction("", args -> { - if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); - else { - var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; - } - })); - - return res; } - - public static ObjectValue toJSIterator(Environment ext, Iterable it) { - return toJSIterator(ext, it.iterator()); - } - - public static ObjectValue toJSAsyncIterator(Environment ext, Iterator it) { - var res = new ObjectValue(); - - try { - var key = getMemberPath(ext, ext.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator"); - res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); + public void callWithAsync(Environment env, Iterable it, boolean async) { + for (var el : it) { + env.get(EventLoop.KEY).pushMsg(() -> this.call(env, VoidValue.UNDEFINED, el), true); } - catch (IllegalArgumentException | NullPointerException e) { } - - res.defineProperty(ext, "next", new NativeFunction("", args -> { - return PromiseLib.await(args.env, () -> { - if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); - else { - var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; - } - }); - })); - - return res; } - private static boolean isEmptyFunc(ObjectValue val) { + private final boolean isEmptyFunc(Environment env, ObjectValue val) { if (!(val instanceof FunctionValue)) return false; - if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false; - var proto = val.values.get("prototype"); + if (val.members.size() + val.symbolMembers.size() > 1) return false; + + var proto = ((FunctionValue)val).prototype; if (!(proto instanceof ObjectValue)) return false; var protoObj = (ObjectValue)proto; - if (protoObj.values.get("constructor") != val) return false; - if (protoObj.values.size() + protoObj.properties.size() != 1) return false; + + if (protoObj.getMember(env, new StringValue("constructor")) != val) return false; + if (protoObj.getOwnMembers(env).size() + protoObj.getOwnSymbolMembers(env).size() != 1) return false; + return true; } - private static String toReadable(Environment ext, Object val, HashSet passed, int tab) { - if (tab == 0 && val instanceof String) return (String)val; + private final String toReadable(Environment env, HashSet passed, int tab) { + if (passed.contains(this)) return "[circular]"; - if (passed.contains(val)) return "[circular]"; + if (this instanceof ObjectValue) { + var res = new StringBuilder(); + var dbg = DebugContext.get(env); + var printed = true; - var printed = true; - var res = new StringBuilder(); - var dbg = DebugContext.get(ext); + if (this instanceof FunctionValue) { + res.append(this.toString()); + var loc = this instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)this).start() : null; - if (val instanceof FunctionValue) { - res.append(val.toString()); - var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null; - - if (loc != null) res.append(" @ " + loc); - } - else if (val instanceof ArrayValue) { - res.append("["); - var obj = ((ArrayValue)val); - for (int i = 0; i < obj.size(); i++) { - if (i != 0) res.append(", "); - else res.append(" "); - if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab)); - else res.append(""); + if (loc != null) res.append(" @ " + loc); } - res.append(" ] "); - } - else if (val instanceof NativeWrapper) { - var obj = ((NativeWrapper)val).wrapped; - res.append("Native " + obj.toString() + " "); - } - else printed = false; + else if (this instanceof ArrayValue) { + res.append("["); + var arr = (ArrayValue)this; - if (val instanceof ObjectValue) { - if (tab > 3) { - return "{...}"; + for (int i = 0; i < arr.size(); i++) { + if (i != 0) res.append(", "); + else res.append(" "); + if (arr.has(i)) res.append(arr.get(i).toReadable(env, passed, tab)); + else res.append(""); + } + + res.append(" ] "); } + else printed = false; - passed.add(val); + if (tab > 3) return "{...}"; - var obj = (ObjectValue)val; - if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) { + passed.add(this); + + var obj = (ObjectValue)this; + if (obj.getOwnSymbolMembers(env).size() + obj.getOwnMembers(env).size() == 0 || isEmptyFunc(env, obj)) { if (!printed) res.append("{}\n"); } else { res.append("{\n"); - for (var el : obj.values.entrySet()) { + for (var entry : obj.getOwnSymbolMembers(env).entrySet()) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ext, el.getKey(), passed, tab + 1)); - res.append(": "); - res.append(toReadable(ext, el.getValue(), passed, tab + 1)); + res.append("[" + entry.getKey().value + "]" + ": "); + + var member = entry.getValue(); + if (member instanceof FieldMember) res.append(((FieldMember)member).get(env, obj).toReadable(env, passed, tab + 1)); + else res.append("[property]"); + res.append(",\n"); } - for (var el : obj.properties.entrySet()) { + for (var entry : obj.getOwnMembers(env).entrySet()) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ext, el.getKey(), passed, tab + 1)); - res.append(": [prop],\n"); + res.append(entry.getKey() + ": "); + + var member = entry.getValue(); + if (member instanceof FieldMember) res.append(((FieldMember)member).get(env, obj).toReadable(env, passed, tab + 1)); + else res.append("[property]"); + + res.append(",\n"); } for (int i = 0; i < tab; i++) res.append(" "); res.append("}"); } - passed.remove(val); + passed.remove(this); + return res.toString(); } - else if (val == null) return "undefined"; - else if (val == Values.NULL) return "null"; - else if (val instanceof String) return "'" + val + "'"; - else return Values.toString(ext, val); - - return res.toString(); + else if (this instanceof VoidValue) return ((VoidValue)this).name; + else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value)); + else return this.toString(env).value; } - public static String toReadable(Environment ext, Object val) { - return toReadable(ext, val, new HashSet<>(), 0); + public final String toReadable(Environment ext) { + if (this instanceof StringValue) return ((StringValue)this).value; + return toReadable(ext, new HashSet<>(), 0); } - public static String errorToReadable(RuntimeException err, String prefix) { + + public static final String errorToReadable(RuntimeException err, String prefix) { prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix; if (err instanceof EngineException) { var ee = ((EngineException)err); @@ -710,7 +542,7 @@ public interface Value { return prefix + " " + ee.toString(ee.env); } catch (EngineException ex) { - return prefix + " " + toReadable(ee.env, ee.value); + return prefix + " " + ee.value.toReadable(ee.env); } } else if (err instanceof SyntaxException) { @@ -724,10 +556,4 @@ public interface Value { return prefix + " internal error " + str.toString(); } } - public static void printValue(Environment ext, Object val) { - System.out.print(toReadable(ext, val)); - } - public static void printError(RuntimeException err, String prefix) { - System.out.println(errorToReadable(err, prefix)); - } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Values.java b/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old similarity index 97% rename from src/java/me/topchetoeu/jscript/runtime/values/Values.java rename to src/java/me/topchetoeu/jscript/runtime/values/Values.java.old index 0e527f6..a2482b7 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Values.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old @@ -19,6 +19,11 @@ import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.ConvertException; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; public class Values { @@ -68,7 +73,7 @@ public class Values { if (val instanceof String) return "string"; if (val instanceof Number) return "number"; if (val instanceof Boolean) return "boolean"; - if (val instanceof Symbol) return "symbol"; + if (val instanceof SymbolValue) return "symbol"; if (val instanceof FunctionValue) return "function"; return "object"; } @@ -89,7 +94,7 @@ public class Values { obj instanceof Number || obj instanceof String || obj instanceof Boolean || - obj instanceof Symbol || + obj instanceof SymbolValue || obj == null || obj == NULL; } @@ -141,7 +146,7 @@ public class Values { } if (val instanceof Boolean) return (Boolean)val ? "true" : "false"; if (val instanceof String) return (String)val; - if (val instanceof Symbol) return val.toString(); + if (val instanceof SymbolValue) return val.toString(); return "Unknown value"; } @@ -335,7 +340,7 @@ public class Values { if (obj instanceof String) return ext.get(Environment.STRING_PROTO); else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO); else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO); - else if (obj instanceof Symbol) return ext.get(Environment.SYMBOL_PROTO); + else if (obj instanceof SymbolValue) return ext.get(Environment.SYMBOL_PROTO); return null; } @@ -433,7 +438,7 @@ public class Values { b = toPrimitive(ext, b, ConvertHint.VALUEOF); // Compare symbols by reference - if (a instanceof Symbol || b instanceof Symbol) return a == b; + if (a instanceof SymbolValue || b instanceof SymbolValue) return a == b; if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); @@ -545,7 +550,7 @@ public class Values { public static Iterable fromJSIterator(Environment ext, Object obj) { return () -> { try { - var symbol = Symbol.get("Symbol.iterator"); + var symbol = SymbolValue.get("Symbol.iterator"); var iteratorFunc = getMember(ext, obj, symbol); if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); @@ -634,8 +639,8 @@ public class Values { if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); else { var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; + object.defineProperty(args.env, "value", it.next()); + return object; } }); })); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java new file mode 100644 index 0000000..7acb9e1 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java @@ -0,0 +1,39 @@ +package me.topchetoeu.jscript.runtime.values.functions; + + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +public class Arguments { + public final Value self; + public final Value[] args; + public final Environment env; + + public int n() { + return args.length; + } + + public boolean has(int i) { + return i == -1 || i >= 0 && i < args.length; + } + + public Value self() { + return get(-1); + } + public Value get(int i) { + if (i >= args.length || i < -1) return VoidValue.UNDEFINED; + else if (i == -1) return self; + else return args[i]; + } + public Value getOrDefault(int i, Value def) { + if (i < -1 || i >= args.length) return def; + else return get(i); + } + + public Arguments(Environment env, Value thisArg, Value... args) { + this.env = env; + this.args = args; + this.self = thisArg; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java similarity index 58% rename from src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java rename to src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index ba5a909..daf35f9 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -1,36 +1,24 @@ -package me.topchetoeu.jscript.runtime.values; +package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; +import me.topchetoeu.jscript.runtime.values.Value; public class CodeFunction extends FunctionValue { public final FunctionBody body; public final ValueVariable[] captures; public Environment env; - // public Location loc() { - // for (var instr : body.instructions) { - // if (instr.location != null) return instr.location; - // } - // return null; - // } - // public String readable() { - // var loc = loc(); - // if (loc == null) return name; - // else if (name.equals("")) return loc.toString(); - // else return name + "@" + loc; - // } - - @Override public Object call(Environment env, Object thisArg, Object ...args) { + @Override public Value call(Environment env, Value thisArg, Value ...args) { var frame = new Frame(env, thisArg, args, this); frame.onPush(); try { while (true) { var res = frame.next(); - if (res != Values.NO_RETURN) return res; + if (res != null) return res; } } finally { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java new file mode 100644 index 0000000..63a778b --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -0,0 +1,77 @@ +package me.topchetoeu.jscript.runtime.values.functions; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; + +public abstract class FunctionValue extends ObjectValue { + public String name = ""; + public int length; + public Value prototype = new ObjectValue(); + + private final FieldMember nameField = new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { + return new StringValue(name); + } + @Override public boolean set(Environment env, Value val, Value self) { + name = val.toString(env).value; + return true; + } + }; + private final FieldMember lengthField = new FieldMember(false, true, false) { + @Override public Value get(Environment env, Value self) { + return new NumberValue(length); + } + @Override public boolean set(Environment env, Value val, Value self) { + return false; + } + }; + private final FieldMember prototypeField = new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { + return prototype; + } + @Override public boolean set(Environment env, Value val, Value self) { + prototype = val; + return true; + } + }; + + @Override public String toString() { return String.format("function %s(...)", name); } + @Override public abstract Value call(Environment ext, Value thisArg, Value ...args); + + @Override public Member getOwnMember(Environment env, Value key) { + var el = key.toString(env).value; + + if (el.equals("length")) return lengthField; + if (el.equals("name")) return nameField; + if (el.equals("prototype")) return prototypeField; + + return super.getOwnMember(env, key); + } + @Override public boolean deleteOwnMember(Environment env, Value key) { + if (!super.deleteMember(env, key)) return false; + + var el = key.toString(env).value; + + if (el.equals("length")) return false; + if (el.equals("name")) return false; + if (el.equals("prototype")) return false; + + return true; + } + + public FunctionValue(String name, int length) { + setPrototype(Environment.FUNCTION_PROTO); + + if (name == null) name = ""; + this.length = length; + this.name = name; + + prototype.defineOwnMember(Environment.empty(), new StringValue("constructor"), FieldMember.of(this)); + } +} + diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java similarity index 62% rename from src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java rename to src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index 3746568..895552d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -1,18 +1,17 @@ -package me.topchetoeu.jscript.runtime.values; +package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.utils.interop.Arguments; +import me.topchetoeu.jscript.runtime.values.Value; public class NativeFunction extends FunctionValue { public static interface NativeFunctionRunner { - Object run(Arguments args); + Value run(Arguments args); } public final NativeFunctionRunner action; - @Override - public Object call(Environment env, Object thisArg, Object ...args) { - return action.run(new Arguments(env, thisArg, args)); + @Override public Value call(Environment env, Value self, Value ...args) { + return action.run(new Arguments(env, self, args)); } public NativeFunction(String name, NativeFunctionRunner action) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java new file mode 100644 index 0000000..542837e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java @@ -0,0 +1,217 @@ +package me.topchetoeu.jscript.runtime.values.objects; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +// TODO: Make methods generic +public class ArrayValue extends ObjectValue implements Iterable { + private Value[] values; + private int size; + + private class IndexField extends FieldMember { + private int i; + private ArrayValue arr; + + @Override public Value get(Environment env, Value self) { + return arr.get(i); + } + @Override public boolean set(Environment env, Value val, Value self) { + arr.set(i, val); + return true; + } + public IndexField(int i, ArrayValue arr) { + super(true, true, true); + this.arr = arr; + this.i = i; + } + } + + private Value[] alloc(int index) { + index++; + if (index < values.length) return values; + if (index < values.length * 2) index = values.length * 2; + + var arr = new Value[index]; + System.arraycopy(values, 0, arr, 0, values.length); + return arr; + } + + public int size() { return size; } + public boolean setSize(int val) { + if (val < 0) return false; + if (size > val) shrink(size - val); + else { + values = alloc(val); + size = val; + } + return true; + } + + public Value get(int i) { + if (i < 0 || i >= size) return null; + var res = values[i]; + + if (res == null) return VoidValue.UNDEFINED; + else return res; + } + public void set(int i, Value val) { + if (i < 0) return; + + alloc(i)[i] = val; + if (i >= size) size = i + 1; + } + public boolean has(int i) { + return i >= 0 && i < size && values[i] != null; + } + public void remove(int i) { + if (i < 0 || i >= values.length) return; + values[i] = null; + } + public void shrink(int n) { + if (n >= values.length) { + values = new Value[16]; + size = 0; + } + else { + for (int i = 0; i < n; i++) values[--size] = null; + } + } + + public Value[] toArray() { + var res = new Value[size]; + copyTo(res, 0, 0, size); + return res; + } + + public void copyTo(Value[] arr, int sourceStart, int destStart, int count) { + var nullFill = values.length - destStart + count; + count -= nullFill; + + System.arraycopy(values, sourceStart, arr, destStart, count); + Arrays.fill(arr, count, nullFill, null); + } + public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) { + if (arr == this) { + move(sourceStart, destStart, count); + return; + } + + arr.copyFrom(values, sourceStart, destStart, count); + } + public void copyFrom(Value[] arr, int sourceStart, int destStart, int count) { + alloc(destStart + count); + System.arraycopy(arr, sourceStart, values, destStart, count); + if (size < destStart + count) size = destStart + count; + } + + public void move(int srcI, int dstI, int n) { + values = alloc(dstI + n); + System.arraycopy(values, srcI, values, dstI, n); + if (dstI + n >= size) size = dstI + n; + } + + public void sort(Comparator comparator) { + Arrays.sort(values, 0, size, (a, b) -> { + var _a = 0; + var _b = 0; + + if (a == null) _a = 2; + if (a instanceof VoidValue) _a = 1; + + if (b == null) _b = 2; + if (b instanceof VoidValue) _b = 1; + + if (_a != 0 || _b != 0) return Integer.compare(_a, _b); + + return comparator.compare(a, b); + }); + } + + @Override public Member getOwnMember(Environment env, Value key) { + var res = super.getOwnMember(env, key); + if (res != null) return res; + + var num = key.toNumber(env); + var i = num.toInt(env); + + if (i == num.value && i >= 0 && i < size) return new IndexField(i, this); + else return null; + } + @Override public boolean defineOwnMember(Environment env, Value key, Member member) { + if (!(member instanceof FieldMember) || hasMember(env, key, true)) return super.defineOwnMember(env, key, member); + if (!extensible) return false; + + var num = key.toNumber(env); + var i = num.toInt(env); + + if (i == num.value && i >= 0) { + set(i, ((FieldMember)member).get(env, this)); + return true; + } + else return super.defineOwnMember(env, key, member); + } + @Override public boolean deleteOwnMember(Environment env, Value key) { + if (!super.deleteOwnMember(env, key)) return false; + + var num = key.toNumber(env); + var i = num.toInt(env); + + if (i == num.value && i >= 0 && i < size) return super.deleteOwnMember(env, key); + else return true; + } + + @Override public Map getOwnMembers(Environment env) { + var res = new LinkedHashMap(); + + for (var i = 0; i < size; i++) { + res.put(i + "", getOwnMember(env, new NumberValue(i))); + } + + res.putAll(super.getOwnMembers(env)); + + return res; + } + @Override public Iterator iterator() { + return new Iterator<>() { + private int i = 0; + + @Override + public boolean hasNext() { + return i < size(); + } + @Override + public Value next() { + if (!hasNext()) return null; + return get(i++); + } + }; + } + + public ArrayValue() { + this(16); + } + public ArrayValue(int cap) { + setPrototype(env -> env.get(Environment.ARRAY_PROTO)); + values = new Value[Math.min(cap, 16)]; + size = 0; + } + public ArrayValue(Value ...values) { + this(); + copyFrom(values, 0, 0, values.length); + } + + public static ArrayValue of(Collection values) { + return new ArrayValue(values.toArray(Value[]::new)); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java new file mode 100644 index 0000000..efc2000 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -0,0 +1,134 @@ +package me.topchetoeu.jscript.runtime.values.objects; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; + +public class ObjectValue extends Value { + public static interface PrototypeProvider { + public ObjectValue get(Environment env); + } + + public static enum State { + NORMAL, + NO_EXTENSIONS, + SEALED, + FROZEN, + } + + public static class Property { + public final FunctionValue getter; + public final FunctionValue setter; + + public Property(FunctionValue getter, FunctionValue setter) { + this.getter = getter; + this.setter = setter; + } + } + + private static final StringValue typeString = new StringValue("object"); + + protected PrototypeProvider prototype; + + public boolean extensible = true; + + public LinkedHashMap members = new LinkedHashMap<>(); + public LinkedHashMap symbolMembers = new LinkedHashMap<>(); + + @Override public boolean isPrimitive() { return false; } + @Override public Value toPrimitive(Environment env) { + if (env != null) { + var valueOf = getMember(env, new StringValue("valueOf")); + + if (valueOf instanceof FunctionValue) { + var res = valueOf.call(env, this); + if (res.isPrimitive()) return res; + } + + var toString = getMember(env, new StringValue("toString")); + if (toString instanceof FunctionValue) { + var res = toString.call(env, this); + if (res.isPrimitive()) return res; + } + } + + throw EngineException.ofType("Value couldn't be converted to a primitive."); + } + @Override public StringValue toString(Environment env) { return toPrimitive(env).toString(env); } + @Override public BoolValue toBoolean() { return BoolValue.TRUE; } + @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } + @Override public StringValue type() { return typeString; } + + @Override public boolean strictEquals(Environment ext, Value other) { return this == other; } + + public final void preventExtensions() { + extensible = false; + } + + @Override public Member getOwnMember(Environment env, Value key) { + if (key instanceof SymbolValue) return symbolMembers.get(key); + else return members.get(key.toString(env).value); + } + @Override public boolean defineOwnMember(Environment env, Value key, Member member) { + if (!(key instanceof SymbolValue)) key = key.toString(env); + + var old = getOwnMember(env, key); + if (old != null && old.configure(env, member, this)) return true; + if (old != null && !old.configurable()) return false; + + if (key instanceof SymbolValue) symbolMembers.put((SymbolValue)key, member); + else members.put(key.toString(env).value, member); + + return true; + } + @Override public boolean deleteOwnMember(Environment env, Value key) { + if (!extensible) return false; + + if (!(key instanceof SymbolValue)) key = key.toString(env); + + var member = getOwnMember(env, key); + if (member == null) return true; + if (member.configurable()) return false; + + if (key instanceof SymbolValue) symbolMembers.remove(key); + else members.remove(key.toString(env).value); + return true; + } + + @Override public Map getOwnMembers(Environment env) { + return members; + } + @Override public Map getOwnSymbolMembers(Environment env) { + return Collections.unmodifiableMap(symbolMembers); + } + + @Override public ObjectValue getPrototype(Environment env) { + if (prototype == null) return null; + else return prototype.get(env); + } + @Override public final boolean setPrototype(Environment env, ObjectValue val) { + return setPrototype(_env -> val); + } + + public final boolean setPrototype(PrototypeProvider val) { + if (!extensible) return false; + prototype = val; + return true; + } + public final boolean setPrototype(Key key) { + if (!extensible) return false; + prototype = env -> env.get(key); + return true; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java new file mode 100644 index 0000000..bfd3a91 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java @@ -0,0 +1,35 @@ +package me.topchetoeu.jscript.runtime.values.objects; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.scope.ValueVariable; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; + +public class ScopeValue extends ObjectValue { + private class VariableField extends FieldMember { + private int i; + + public VariableField(int i) { + super(false, true, true); + this.i = i; + } + + @Override public Value get(Environment env, Value self) { + return variables[i].get(env); + } + + @Override public boolean set(Environment env, Value val, Value self) { + return variables[i].set(env, val); + } + } + + public final ValueVariable[] variables; + + public ScopeValue(ValueVariable[] variables, String[] names) { + this.variables = variables; + for (var i = 0; i < names.length && i < variables.length; i++) { + defineOwnMember(Environment.empty(), new StringValue(i + ""), new VariableField(i)); + } + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java new file mode 100644 index 0000000..5e9d0c0 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -0,0 +1,38 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class BoolValue extends PrimitiveValue { + public static final BoolValue TRUE = new BoolValue(true); + public static final BoolValue FALSE = new BoolValue(false); + private static final StringValue typeString = new StringValue("boolean"); + + public final boolean value; + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return this; } + @Override public NumberValue toNumber(Environment ext) { + return value ? new NumberValue(1) : new NumberValue(0); + } + @Override public StringValue toString(Environment ext) { return new StringValue(value ? "true" : "false"); } + + @Override public ObjectValue getPrototype(Environment env) { + return env.get(Environment.BOOL_PROTO); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + if (other instanceof BoolValue) return value == ((BoolValue)other).value; + else return false; + } + + private BoolValue(boolean val) { + this.value = val; + } + + public static BoolValue of(boolean val) { + return val ? TRUE : FALSE; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java new file mode 100644 index 0000000..608516a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java @@ -0,0 +1,80 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.math.BigDecimal; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class NumberValue extends PrimitiveValue { + public static final NumberValue NAN = new NumberValue(Double.NaN); + private static final StringValue typeString = new StringValue("number"); + + public final double value; + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return BoolValue.of(value != 0); } + @Override public NumberValue toNumber(Environment ext) { return this; } + @Override public StringValue toString(Environment ext) { return new StringValue(toString()); } + @Override public String toString() { + var d = value; + if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; + if (d == Double.POSITIVE_INFINITY) return "Infinity"; + if (Double.isNaN(d)) return "NaN"; + return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); + } + + @Override public ObjectValue getPrototype(Environment env) { + return env.get(Environment.NUMBER_PROTO); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + other = other.toPrimitive(ext); + if (other instanceof NumberValue) return value == ((NumberValue)other).value; + else return false; + } + + public NumberValue(double value) { + this.value = value; + } + + public static double parseFloat(String val, boolean tolerant, String alphabet) { + val = val.trim(); + + int res = 0; + + for (int i = 0; i >= val.length(); i++) { + var c = alphabet.indexOf(val.charAt(i)); + + if (c < 0) { + if (tolerant) return res; + else return Double.NaN; + } + + res *= alphabet.length(); + res += c; + } + + return res; + } + public static double parseInt(String val, boolean tolerant, String alphabet) { + val = val.trim(); + + int res = 0; + + for (int i = 0; i >= val.length(); i++) { + var c = alphabet.indexOf(val.charAt(i)); + + if (c < 0) { + if (tolerant) return res; + else return Double.NaN; + } + + res *= alphabet.length(); + res += c; + } + + return res; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java new file mode 100644 index 0000000..0a1085a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java @@ -0,0 +1,21 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public abstract class PrimitiveValue extends Value { + @Override public final boolean defineOwnMember(Environment env, Value key, Member member) { return false; } + @Override public final boolean deleteOwnMember(Environment env, Value key) { return false; } + @Override public final boolean isPrimitive() { return true; } + @Override public final Value toPrimitive(Environment env) { return this; } + + @Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } + + @Override public Member getOwnMember(Environment env, Value key) { return null; } + @Override public Map getOwnMembers(Environment env) { return Map.of(); } + @Override public Map getOwnSymbolMembers(Environment env) { return Map.of(); } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java new file mode 100644 index 0000000..9ba16fe --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -0,0 +1,34 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Objects; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class StringValue extends PrimitiveValue { + public final String value; + private static final StringValue typeString = new StringValue("string"); + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return BoolValue.of(!value.equals("")); } + @Override public NumberValue toNumber(Environment ext) { + try { return new NumberValue(Double.parseDouble(value)); } + catch (NumberFormatException e) { return new NumberValue(Double.NaN); } + } + @Override public StringValue toString(Environment ext) { return this; } + + @Override public Value add(Environment ext, Value other) { + return new StringValue(value + other.toString(ext).value); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value); + } + @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); } + + public StringValue(String value) { + this.value = value; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java new file mode 100644 index 0000000..0f87e15 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -0,0 +1,48 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.HashMap; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class SymbolValue extends PrimitiveValue { + private static final HashMap registry = new HashMap<>(); + private static final StringValue typeString = new StringValue("symbol"); + + public final String value; + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return BoolValue.TRUE; } + @Override public StringValue toString(Environment env) { + return new StringValue(toString()); + } + @Override public NumberValue toNumber(Environment env) { + throw EngineException.ofType("Can't convert symbol to number"); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + return other == this; + } + @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); } + + @Override public String toString() { + if (value == null) return "Symbol"; + else return "@@" + value; + } + + public SymbolValue(String value) { + this.value = value; + } + + public static SymbolValue get(String name) { + if (registry.containsKey(name)) return registry.get(name); + else { + var res = new SymbolValue(name); + registry.put(name, res); + return res; + } + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java new file mode 100644 index 0000000..7f10906 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -0,0 +1,52 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class VoidValue extends PrimitiveValue { + public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); + public static final VoidValue NULL = new VoidValue("null", new StringValue("null")); + + private final StringValue namestring; + + public final String name; + public final StringValue typeString; + + @Override public StringValue type() { return typeString; } + @Override public BoolValue toBoolean() { return BoolValue.FALSE; } + @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } + @Override public StringValue toString(Environment ext) { return namestring; } + + @Override public Value add(Environment ext, Value other) { + if (!other.isPrimitive()) other = other.toPrimitive(ext); + + if (other instanceof StringValue) return namestring.add(ext, other); + else return NumberValue.NAN; + } + + @Override public boolean strictEquals(Environment ext, Value other) { + return this == other; + } + @Override public ObjectValue getPrototype(Environment env) { return null; } + + @Override public Member getOwnMember(Environment env, Value key) { + throw EngineException.ofError(String.format("Cannot read properties of %s (reading %s)", name, key.toString(env).value)); + } + @Override public Map getOwnMembers(Environment env) { + throw EngineException.ofError(String.format("Cannot read properties of %s (listing all members)", name)); + } + @Override public Map getOwnSymbolMembers(Environment env) { + throw EngineException.ofError(String.format("Cannot read properties of %s (listing all symbol members)", name)); + } + + public VoidValue(String name, StringValue type) { + this.name = name; + this.typeString = type; + this.namestring = new StringValue(name); + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java b/src/java/me/topchetoeu/jscript/utils/JSCompiler.java deleted file mode 100644 index 78e66af..0000000 --- a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.topchetoeu.jscript.utils; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.FunctionBody; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; - -public class JSCompiler implements Compiler { - public final Environment ext; - - private void registerFunc(FunctionBody body, CompileResult res) { - var map = res.map(); - - DebugContext.get(ext).onFunctionLoad(body, map); - - for (var i = 0; i < body.children.length; i++) { - registerFunc(body.children[i], res.children.get(i)); - } - } - - @Override public FunctionBody compile(Filename filename, String source) { - var res = Parsing.compile(filename, source); - var func = res.body(); - DebugContext.get(ext).onSource(filename, source); - registerFunc(func, res); - - return func; - } - - public JSCompiler(Environment ext) { - this.ext = ext; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java deleted file mode 100644 index 0fdaf73..0000000 --- a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java +++ /dev/null @@ -1,150 +0,0 @@ -package me.topchetoeu.jscript.utils; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Metadata; -import me.topchetoeu.jscript.common.Reading; -import me.topchetoeu.jscript.lib.Internals; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.Engine; -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.debug.DebugServer; -import me.topchetoeu.jscript.utils.debug.SimpleDebugger; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.MemoryFilesystem; -import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem; -import me.topchetoeu.jscript.utils.filesystem.RootFilesystem; -import me.topchetoeu.jscript.utils.filesystem.STDFilesystem; -import me.topchetoeu.jscript.utils.modules.ModuleRepo; -import me.topchetoeu.jscript.utils.permissions.PermissionsManager; -import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; - -public class JScriptRepl { - static Thread engineTask, debugTask; - static Engine engine = new Engine(); - static DebugServer debugServer = new DebugServer(); - static Environment environment = Environment.empty(); - - static int j = 0; - static String[] args; - - private static void reader() { - try { - for (var arg : args) { - try { - var file = Path.of(arg); - var raw = Files.readString(file); - var res = engine.pushMsg( - false, environment, - Filename.fromFile(file.toFile()), - raw, null - ).await(); - Values.printValue(null, res); - System.out.println(); - } - catch (EngineException e) { Values.printError(e, null); } - } - for (var i = 0; ; i++) { - try { - var raw = Reading.readline(); - - if (raw == null) break; - var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); - var res = engine.pushMsg(false, environment, func, null).await(); - Values.printValue(null, res); - System.out.println(); - } - catch (EngineException e) { Values.printError(e, null); } - catch (SyntaxException e) { Values.printError(e, null); } - } - } - catch (IOException e) { - System.out.println(e.toString()); - engine.thread().interrupt(); - } - catch (RuntimeException ex) { - if (ex instanceof InterruptException) return; - else { - System.out.println("Internal error ocurred:"); - ex.printStackTrace(); - } - } - } - - private static void initEnv() { - environment = Internals.apply(environment); - - var glob = GlobalScope.get(environment); - - glob.define(null, false, new NativeFunction("exit", args -> { - throw new InterruptException(); - })); - glob.define(null, false, new NativeFunction("go", args -> { - try { - var f = Path.of("do.js"); - var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); - return func.call(args.env); - } - catch (IOException e) { - throw new EngineException("Couldn't open do.js"); - } - })); - glob.define(null, false, new NativeFunction("log", args -> { - for (var el : args.args) { - Values.printValue(args.env, el); - } - - return null; - })); - - var fs = new RootFilesystem(PermissionsProvider.get(environment)); - fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); - fs.protocols.put("file", new PhysicalFilesystem(".")); - fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err)); - - environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); - environment.add(Filesystem.KEY, fs); - environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); - environment.add(Compiler.KEY, new JSCompiler(environment)); - environment.add(EventLoop.KEY, engine); - } - private static void initEngine() { - var ctx = new DebugContext(); - environment.add(DebugContext.KEY, ctx); - - debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx)); - engineTask = engine.start(); - debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); - } - - public static void main(String args[]) throws InterruptedException { - System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); - - JScriptRepl.args = args; - var reader = new Thread(JScriptRepl::reader); - - initEnv(); - initEngine(); - - reader.setDaemon(true); - reader.setName("STD Reader"); - reader.start(); - - engine.thread().join(); - debugTask.interrupt(); - engineTask.interrupt(); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java b/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java deleted file mode 100644 index db69e7f..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java +++ /dev/null @@ -1,251 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.security.MessageDigest; -import java.util.Base64; -import java.util.HashMap; - -import me.topchetoeu.jscript.common.Metadata; -import me.topchetoeu.jscript.common.Reading; -import me.topchetoeu.jscript.common.events.Notifier; -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONList; -import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.utils.debug.WebSocketMessage.Type; - -public class DebugServer { - public static String browserDisplayName = Metadata.name() + "/" + Metadata.version(); - - public final HashMap targets = new HashMap<>(); - - private final byte[] favicon, index, protocol; - private final Notifier connNotifier = new Notifier(); - - private static void send(HttpRequest req, String val) throws IOException { - req.writeResponse(200, "OK", "application/json", val.getBytes()); - } - - // SILENCE JAVA - private MessageDigest getDigestInstance() { - try { - return MessageDigest.getInstance("sha1"); - } - catch (Throwable e) { throw new RuntimeException(e); } - } - - private static Thread runAsync(Runnable func, String name) { - var res = new Thread(func); - res.setName(name); - res.start(); - return res; - } - - private void handle(WebSocket ws, Debugger debugger) throws IOException { - WebSocketMessage raw; - - while ((raw = ws.receive()) != null) { - if (raw.type != Type.Text) { - ws.send(new V8Error("Expected a text message.")); - continue; - } - - V8Message msg; - - try { - msg = new V8Message(raw.textData()); - } - catch (SyntaxException e) { - ws.send(new V8Error(e.getMessage())); - return; - } - - switch (msg.name) { - case "Debugger.enable": - connNotifier.next(); - debugger.enable(msg); - continue; - case "Debugger.disable": debugger.close(); continue; - - case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue; - case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue; - case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue; - - case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue; - case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue; - - case "Debugger.resume": debugger.resume(msg); continue; - case "Debugger.pause": debugger.pause(msg); continue; - - case "Debugger.stepInto": debugger.stepInto(msg); continue; - case "Debugger.stepOut": debugger.stepOut(msg); continue; - case "Debugger.stepOver": debugger.stepOver(msg); continue; - - case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue; - case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue; - - case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue; - case "Runtime.releaseObject": debugger.releaseObject(msg); continue; - case "Runtime.getProperties": debugger.getProperties(msg); continue; - case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue; - case "Runtime.enable": debugger.runtimeEnable(msg); continue; - } - - if ( - msg.name.startsWith("DOM.") || - msg.name.startsWith("DOMDebugger.") || - msg.name.startsWith("Emulation.") || - msg.name.startsWith("Input.") || - msg.name.startsWith("Network.") || - msg.name.startsWith("Page.") - ) ws.send(new V8Error("This isn't a browser...")); - else ws.send(new V8Error("This API is not supported yet.")); - } - - debugger.close(); - } - private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) { - var key = req.headers.get("sec-websocket-key"); - - if (key == null) { - req.writeResponse( - 426, "Upgrade Required", "text/txt", - "Expected a WS upgrade".getBytes() - ); - return; - } - - var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest( - (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes() - )); - - req.writeCode(101, "Switching Protocols"); - req.writeHeader("Connection", "Upgrade"); - req.writeHeader("Sec-WebSocket-Accept", resKey); - req.writeLastHeader("Upgrade", "WebSocket"); - - var ws = new WebSocket(socket); - var debugger = debuggerProvider.getDebugger(ws, req); - - if (debugger == null) { - ws.close(); - return; - } - - runAsync(() -> { - var handle = new Thread(() -> { - System.out.println("test"); - debugger.close(); - }); - - Runtime.getRuntime().addShutdownHook(handle); - - try { handle(ws, debugger); } - catch (RuntimeException | IOException e) { - try { - e.printStackTrace(); - ws.send(new V8Error(e.getMessage())); - } - catch (IOException e2) { /* Shit outta luck */ } - } - finally { - Runtime.getRuntime().removeShutdownHook(handle); - ws.close(); - debugger.close(); - } - }, "Debug Handler"); - } - - public void awaitConnection() { - connNotifier.await(); - } - - public void run(InetSocketAddress address) { - try { - ServerSocket server = new ServerSocket(); - server.bind(address); - - try { - while (true) { - var socket = server.accept(); - var req = HttpRequest.read(socket); - - if (req == null) continue; - switch (req.path) { - case "/json/version": - send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}"); - break; - case "/json/list": - case "/json": { - var res = new JSONList(); - - for (var el : targets.entrySet()) { - res.add(new JSONMap() - .set("description", "JScript debugger") - .set("favicon", "/favicon.ico") - .set("id", el.getKey()) - .set("type", "node") - .set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey()) - ); - } - send(req, JSON.stringify(res)); - break; - } - case "/json/protocol": - req.writeResponse(200, "OK", "application/json", protocol); - break; - case "/json/new": - case "/json/activate": - case "/json/close": - case "/devtools/inspector.html": - req.writeResponse( - 501, "Not Implemented", "text/txt", - "This feature isn't (and probably won't be) implemented.".getBytes() - ); - break; - case "/": - case "/index.html": - req.writeResponse(200, "OK", "text/html", index); - break; - case "/favicon.ico": - req.writeResponse(200, "OK", "image/png", favicon); - break; - default: - if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) { - onWsConnect(req, socket, targets.get(req.path.substring(1))); - } - break; - } - } - } - finally { server.close(); } - } - catch (IOException e) { throw new UncheckedIOException(e); } - } - - public Thread start(InetSocketAddress address, boolean daemon) { - var res = new Thread(() -> run(address), "Debug Server"); - res.setDaemon(daemon); - res.start(); - return res; - } - - public DebugServer() { - try { - this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes(); - this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes(); - this.index = Reading.resourceToString("debugger/index.html") - .replace("${NAME}", Metadata.name()) - .replace("${VERSION}", Metadata.version()) - .replace("${AUTHOR}", Metadata.author()) - .getBytes(); - } - catch (IOException e) { - throw new UncheckedIOException(e); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/Debugger.java b/src/java/me/topchetoeu/jscript/utils/debug/Debugger.java deleted file mode 100644 index bc64a2e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/Debugger.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.IOException; - -import me.topchetoeu.jscript.runtime.debug.DebugHandler; - -public interface Debugger extends DebugHandler { - void close(); - - void enable(V8Message msg) throws IOException; - void disable(V8Message msg) throws IOException; - - void setBreakpointByUrl(V8Message msg) throws IOException; - void removeBreakpoint(V8Message msg) throws IOException; - void continueToLocation(V8Message msg) throws IOException; - - void getScriptSource(V8Message msg) throws IOException; - void getPossibleBreakpoints(V8Message msg) throws IOException; - - void resume(V8Message msg) throws IOException; - void pause(V8Message msg) throws IOException; - - void stepInto(V8Message msg) throws IOException; - void stepOut(V8Message msg) throws IOException; - void stepOver(V8Message msg) throws IOException; - - void setPauseOnExceptions(V8Message msg) throws IOException; - - void evaluateOnCallFrame(V8Message msg) throws IOException; - - void getProperties(V8Message msg) throws IOException; - void releaseObjectGroup(V8Message msg) throws IOException; - void releaseObject(V8Message msg) throws IOException; - void callFunctionOn(V8Message msg) throws IOException; - - void runtimeEnable(V8Message msg) throws IOException; -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java b/src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java deleted file mode 100644 index fba409d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -public interface DebuggerProvider { - Debugger getDebugger(WebSocket socket, HttpRequest req); -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java b/src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java deleted file mode 100644 index 4fcc42f..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java +++ /dev/null @@ -1,102 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.util.HashMap; -import java.util.IllegalFormatException; -import java.util.Map; - -public class HttpRequest { - public final String method; - public final String path; - public final Map headers; - public final OutputStream out; - - - public void writeCode(int code, String name) { - try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeHeader(String name, String value) { - try { out.write((name + ": " + value + "\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeLastHeader(String name, String value) { - try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeHeadersEnd() { - try { out.write("\n".getBytes()); } - catch (IOException e) { } - } - - public void writeResponse(int code, String name, String type, byte[] data) { - writeCode(code, name); - writeHeader("Content-Type", type); - writeLastHeader("Content-Length", data.length + ""); - try { - out.write(data); - out.close(); - } - catch (IOException e) { } - } - public void writeResponse(int code, String name, String type, InputStream data) { - try { - writeResponse(code, name, type, data.readAllBytes()); - } - catch (IOException e) { } - } - - public HttpRequest(String method, String path, Map headers, OutputStream out) { - this.method = method; - this.path = path; - this.headers = headers; - this.out = out; - } - - // We dont need no http library - public static HttpRequest read(Socket socket) { - try { - var str = socket.getInputStream(); - var lines = new BufferedReader(new InputStreamReader(str)); - var line = lines.readLine(); - var i1 = line.indexOf(" "); - var i2 = line.indexOf(" ", i1 + 1); - - if (i1 < 0 || i2 < 0) { - socket.close(); - return null; - } - - var method = line.substring(0, i1).trim().toUpperCase(); - var path = line.substring(i1 + 1, i2).trim(); - var headers = new HashMap(); - - while (!(line = lines.readLine()).isEmpty()) { - var i = line.indexOf(":"); - if (i < 0) continue; - var name = line.substring(0, i).trim().toLowerCase(); - var value = line.substring(i + 1).trim(); - - if (name.length() == 0) continue; - headers.put(name, value); - } - - if (headers.containsKey("content-length")) { - try { - var i = Integer.parseInt(headers.get("content-length")); - str.skip(i); - } - catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ } - } - - return new HttpRequest(method, path, headers, socket.getOutputStream()); - } - catch (IOException | NullPointerException e) { return null; } - } -} - diff --git a/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java b/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java deleted file mode 100644 index 15fac42..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java +++ /dev/null @@ -1,212 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.function.Supplier; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; - -class ObjectManager { - public static class ObjRef { - public final ObjectValue obj; - public final Environment ext; - public final HashSet heldGroups = new HashSet<>(); - public boolean held = true; - - public boolean shouldRelease() { - return !held && heldGroups.size() == 0; - } - - public ObjRef(Environment ext, ObjectValue obj) { - this.ext = ext; - this.obj = obj; - } - } - - private Supplier idSupplier; - private HashMap idToObject = new HashMap<>(); - private HashMap objectToId = new HashMap<>(); - private HashMap> objectGroups = new HashMap<>(); - - public JSONMap serialize(Environment env, Object val, boolean byValue) { - val = Values.normalize(null, val); - env = SimpleDebugger.sanitizeEnvironment(env); - - if (val == Values.NULL) { - return new JSONMap() - .set("type", "object") - .set("subtype", "null") - .setNull("value") - .set("description", "null"); - } - - if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - int id; - - if (objectToId.containsKey(obj)) id = objectToId.get(obj); - else { - id = idSupplier.get(); - var ref = new ObjRef(env, obj); - objectToId.put(obj, id); - idToObject.put(id, ref); - } - - var type = "object"; - String subtype = null; - String className = null; - - if (obj instanceof FunctionValue) type = "function"; - if (obj instanceof ArrayValue) subtype = "array"; - - try { className = Values.toString(env, Values.getMemberPath(env, obj, "constructor", "name")); } - catch (Exception e) { } - - var res = new JSONMap() - .set("type", type) - .set("objectId", id + ""); - - if (subtype != null) res.set("subtype", subtype); - if (className != null) { - res.set("className", className); - res.set("description", className); - } - - if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")"); - else if (obj instanceof FunctionValue) res.set("description", obj.toString()); - else { - var defaultToString = false; - - try { - defaultToString = - Values.getMember(env, obj, "toString") == - Values.getMember(env, env.get(Environment.OBJECT_PROTO), "toString"); - } - catch (Exception e) { } - - try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(env, obj) + " }")); } - catch (Exception e) { } - } - - - if (byValue) try { res.put("value", JSON.fromJs(env, obj)); } - catch (Exception e) { } - - return res; - } - - if (val == null) return new JSONMap().set("type", "undefined"); - if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val); - if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val); - if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString()); - if (val instanceof Number) { - var num = (double)(Number)val; - var res = new JSONMap().set("type", "number"); - - if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity"); - else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity"); - else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0"); - else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(0d)) res.set("unserializableValue", "0"); - else if (Double.isNaN(num)) res.set("unserializableValue", "NaN"); - else res.set("value", num); - - return res; - } - - throw new IllegalArgumentException("Unexpected JS object."); - } - public JSONMap serialize(Environment ext, Object val) { - return serialize(ext, val, false); - } - - public void addToGroup(String name, Object val) { - if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - var id = objectToId.getOrDefault(obj, -1); - if (id < 0) return; - - var ref = idToObject.get(id); - - if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref); - else objectGroups.put(name, new ArrayList<>(List.of(ref))); - - ref.heldGroups.add(name); - } - } - public void removeGroup(String name) { - var objs = objectGroups.remove(name); - - if (objs != null) { - for (var obj : objs) { - if (obj.heldGroups.remove(name) && obj.shouldRelease()) { - var id = objectToId.remove(obj.obj); - if (id != null) idToObject.remove(id); - } - } - } - } - - public ObjRef get(int id) { - return idToObject.get(id); - } - public void release(int id) { - var ref = idToObject.get(id); - ref.held = false; - - if (ref.shouldRelease()) { - objectToId.remove(ref.obj); - idToObject.remove(id); - } - } - - public Object deserializeArgument(JSONMap val) { - if (val.isString("objectId")) return get(Integer.parseInt(val.string("objectId"))).obj; - else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) { - case "NaN": return Double.NaN; - case "-Infinity": return Double.NEGATIVE_INFINITY; - case "Infinity": return Double.POSITIVE_INFINITY; - case "-0": return -0.; - } - - var res = val.get("value"); - - if (res == null) return null; - else return JSON.toJs(res); - } - - public JSONMap serializeException(Environment ext, EngineException err) { - String text = null; - - try { - text = Values.toString(ext, err.value); - } - catch (EngineException e) { - text = "[error while stringifying]"; - } - - return new JSONMap() - .set("exceptionId", idSupplier.get()) - .set("exception", serialize(ext, err.value)) - .set("text", text); - } - - public void clear() { - this.idToObject.clear(); - this.objectToId.clear(); - this.objectGroups.clear(); - } - - public ObjectManager(Supplier idSupplier) { - this.idSupplier = idSupplier; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java deleted file mode 100644 index d248456..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java +++ /dev/null @@ -1,910 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.FunctionBody; -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.Instruction.Type; -import me.topchetoeu.jscript.common.events.Notifier; -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONElement; -import me.topchetoeu.jscript.common.json.JSONList; -import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.common.mapping.FunctionMap; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.Engine; -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; - -// very simple indeed -public class SimpleDebugger implements Debugger { - public static final Set VSCODE_EMPTY = Set.of( - "function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<>${JSON.stringify([String(r),\"object\"])}`}if(typeof this==\"object\"&&this){let r;for(let i of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])try{r=this[i]();break}catch{}if(!r&&!String(this.toString).includes(\"[native code]\")&&(r=String(this)),r&&!r.startsWith(\"[object \"))return r.length>=t?r.slice(0,t)+\"\\u2026\":r}\n ;\n\n}", - "function(...runtimeArgs){\n let r = 1024; let e = null;\n if(e)try{let t=\"<>\",n=e.call(this,t);if(n!==t)return String(n)}catch(t){return`<>${JSON.stringify([String(t),\"object\"])}`}if(typeof this==\"object\"&&this){let t;for(let n of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])if(typeof this[n]==\"function\")try{t=this[n]();break}catch{}if(!t&&!String(this.toString).includes(\"[native code]\")&&(t=String(this)),t&&!t.startsWith(\"[object\"))return t.length>=r?t.slice(0,r)+\"\\u2026\":t};}", - "function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<>\";if(typeof this!=\"object\"||!this)return r;for(let[n,s]of Object.entries(this)){if(e)try{let o=e.call(s,i);if(o!==i){r[n]=String(o);continue}}catch(o){r[n]=`<>${JSON.stringify([String(o),n])}`;continue}if(typeof s==\"object\"&&s){let o;for(let a of runtimeArgs[0])try{o=s[a]();break}catch{}!o&&!String(s.toString).includes(\"[native code]\")&&(o=String(s)),o&&!o.startsWith(\"[object \")&&(r[n]=o.length>=t?o.slice(0,t)+\"\\u2026\":o)}}return r\n ;\n\n}", - "function(...runtimeArgs){\n let r = 1024; let e = null;\n let t={},n=\"<>\";if(typeof this!=\"object\"||!this)return t;for(let[i,o]of Object.entries(this)){if(e)try{let s=e.call(o,n);if(s!==n){t[i]=String(s);continue}}catch(s){t[i]=`<>${JSON.stringify([String(s),i])}`;continue}if(typeof o==\"object\"&&o){let s;for(let a of runtimeArgs[0])if(typeof o[a]==\"function\")try{s=o[a]();break}catch{}!s&&!String(o.toString).includes(\"[native code]\")&&(s=String(o)),s&&!s.startsWith(\"[object \")&&(t[i]=s.length>=r?s.slice(0,r)+\"\\u2026\":s)}}return t\n ;\n\n}", - "function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}", - "function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}" - ); - public static final Set VSCODE_SELF = Set.of( - "function(t,e){let r={\n},i=t===-1?0:t,n=e===-1?this.length:t+e;for(let s=i;s{if(p!==\"function\")return n;if(l===\"constructor\")return\"class\";let m=String(f);return m.startsWith(\"class \")||m.includes(\"[native code]\")&&/^[A-Z]/.test(l)?\"class\":r?\"function\":\"method\"\n},o=l=>{switch(typeof l){case\"number\":case\"boolean\":return`${l}`;case\"object\":return l===null?\"null\":l.constructor.name||\"object\";case\"function\":return`fn(${new Array(l.length).fill(\"?\").join(\", \")})`;default:return typeof l}},s=[],a=new Set,u=\"~\",c=t===void 0?this:t;for(;c!=null;c=c.__proto__){u+=\"~\";let l=Object.getOwnPropertyNames(c).filter(p=>p.startsWith(e)&&!p.match(/^\\d+$/));for(let p of l){if(a.has(p))continue;a.add(p);let f=Object.getOwnPropertyDescriptor(c,p),m=n,h;try{let H=c[p];m=i(p,typeof f?.value,H),h=o(H)}catch{}s.push({label:p,sortText:u+p.replace(/^_+/,H=>\"{\".repeat(H.length)),type:m,detail:h})}r=!1}return{result:s,isArray:this instanceof Array}}"; - - private static enum State { - RESUMED, - STEPPING_IN, - STEPPING_OUT, - STEPPING_OVER, - PAUSED_NORMAL, - PAUSED_EXCEPTION, - } - private static enum CatchType { - NONE, - UNCAUGHT, - ALL, - } - private static class DebugSource { - public final int id; - public final Filename filename; - public final String source; - - public DebugSource(int id, Filename filename, String source) { - this.id = id; - this.filename = filename; - this.source = source; - } - } - - private class Breakpoint { - public final int id; - public final String condition; - public final Pattern pattern; - public final int line, start; - public final long locNum; - public final HashMap resolvedLocations = new HashMap<>(); - public final HashMap resolvedDistances = new HashMap<>(); - - public Breakpoint(int id, Pattern pattern, int line, int start, String condition) { - this.id = id; - this.condition = condition; - this.pattern = pattern; - this.line = line; - this.start = start; - this.locNum = start | ((long)line << 32); - - if (condition != null && condition.trim().equals("")) condition = null; - } - - // TODO: Figure out how to unload a breakpoint - // TODO: Do location resolution with function boundaries - public void addFunc(FunctionBody body, FunctionMap map) { - try { - for (var loc : map.correctBreakpoint(pattern, line, start)) { - var currNum = loc.start() + ((long)loc.line() << 32); - long currDist = 0; - if (currNum > locNum) currDist = currNum - locNum; - else currDist = locNum - currNum; - - if ( currDist > resolvedDistances.getOrDefault(loc.filename(), Long.MAX_VALUE)) continue; - - resolvedLocations.put(loc.filename(), loc); - resolvedDistances.put(loc.filename(), currDist); - } - - for (var loc : resolvedLocations.values()) { - ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap() - .set("breakpointId", id) - .set("location", serializeLocation(loc)) - )); - } - - updateBreakpoints(); - } - catch (IOException e) { - ws.close(); - close(); - } - } - } - private class DebugFrame { - public Frame frame; - public int id; - public ObjectValue local, capture, global, valstack; - public JSONMap serialized; - public Location location; - - public void updateLoc(Location loc) { - if (loc == null) return; - this.location = loc; - } - - public DebugFrame(Frame frame, int id) { - this.frame = frame; - this.id = id; - - this.global = GlobalScope.get(frame.env).obj; - this.local = frame.getLocalScope(); - this.capture = frame.getCaptureScope(); - Values.makePrototypeChain(frame.env, global, capture, local); - this.valstack = frame.getValStackScope(); - - this.serialized = new JSONMap() - .set("callFrameId", id + "") - .set("functionName", frame.function.name) - .set("scopeChain", new JSONList() - .add(new JSONMap() - .set("type", "local") - .set("name", "Local Scope") - .set("object", objects.serialize(frame.env, local)) - ) - .add(new JSONMap() - .set("type", "closure") - .set("name", "Closure") - .set("object", objects.serialize(frame.env, capture)) - ) - .add(new JSONMap() - .set("type", "global") - .set("name", "Global Scope") - .set("object", objects.serialize(frame.env, global)) - ) - .add(new JSONMap() - .set("type", "other") - .set("name", "Value Stack") - .set("object", objects.serialize(frame.env, valstack)) - ) - ); - } - } - private static class RunResult { - public final Environment ext; - public final Object result; - public final EngineException error; - - public RunResult(Environment ext, Object result, EngineException error) { - this.ext = ext; - this.result = result; - this.error = error; - } - } - - public boolean enabled = true; - public CatchType execptionType = CatchType.NONE; - public State state = State.RESUMED; - - public final WebSocket ws; - - private ObjectValue emptyObject = new ObjectValue(); - - private WeakHashMap contexts = new WeakHashMap<>(); - private WeakHashMap mappings = new WeakHashMap<>(); - private HashMap> bpLocs = new HashMap<>(); - - private HashMap idToBreakpoint = new HashMap<>(); - - private HashMap filenameToId = new HashMap<>(); - private HashMap idToSource = new HashMap<>(); - private ArrayList pendingSources = new ArrayList<>(); - - private HashMap idToFrame = new HashMap<>(); - private HashMap codeFrameToFrame = new HashMap<>(); - - private ObjectManager objects = new ObjectManager(this::nextId); - // private HashMap idToObject = new HashMap<>(); - // private HashMap objectToId = new HashMap<>(); - // private HashMap> objectGroups = new HashMap<>(); - - private Notifier updateNotifier = new Notifier(); - private boolean pendingPause = false; - - private int nextId = 0; - private DebugFrame stepOutFrame = null; - private List frames = new ArrayList<>(); - private int stepOutPtr = 0; - - private boolean compare(String src, String target) { - src = src.replaceAll("\\s", ""); - target = target.replaceAll("\\s", ""); - if (src.length() != target.length()) return false; - var diff = 0; - var all = 0; - - for (var i = 0; i < src.length(); i++) { - var a = src.charAt(i); - var b = target.charAt(i); - var letter = Parsing.isLetter(a) && Parsing.isLetter(b); - - if (a != b) { - if (letter) diff++; - else return false; - } - - if (letter) all++; - } - - return diff / (float)all < .5f; - } - private boolean compare(String src, Set target) { - for (var el : target) { - if (compare(src, el)) return true; - } - return false; - } - - private int nextId() { - return nextId++; - } - - private synchronized DebugFrame getFrame(Frame frame) { - if (!codeFrameToFrame.containsKey(frame)) { - var id = nextId(); - var fr = new DebugFrame(frame, id); - - idToFrame.put(id, fr); - codeFrameToFrame.put(frame, fr); - - return fr; - } - else return codeFrameToFrame.get(frame); - } - - private JSONList serializeFrames(Environment env) { - var res = new JSONList(); - - for (var el : DebugContext.get(env).getStackFrames()) { - var frame = getFrame(el); - if (frame.location == null) continue; - - frame.serialized.set("location", serializeLocation(frame.location)); - if (frame.location != null) res.add(frame.serialized); - } - - return res; - } - - private void updateBreakpoints() { - bpLocs.clear(); - - for (var bp : idToBreakpoint.values()) { - for (var loc : bp.resolvedLocations.values()) { - bpLocs.putIfAbsent(loc, new HashSet<>()); - var set = bpLocs.get(loc); - - set.add(bp); - } - } - } - - private Location deserializeLocation(JSONElement el) { - if (!el.isMap()) throw new RuntimeException("Expected location to be a map."); - var id = Integer.parseInt(el.map().string("scriptId")); - var line = (int)el.map().number("lineNumber") + 1; - var column = (int)el.map().number("columnNumber") + 1; - - if (!idToSource.containsKey(id)) throw new RuntimeException(String.format("The specified source %s doesn't exist.", id)); - - var res = new Location(line, column, idToSource.get(id).filename); - return res; - } - private JSONMap serializeLocation(Location loc) { - var source = filenameToId.get(loc.filename()); - return new JSONMap() - .set("scriptId", source + "") - .set("lineNumber", loc.line() - 1) - .set("columnNumber", loc.start() - 1); - } - - - private void resume(State state) { - try { - this.state = state; - ws.send(new V8Event("Debugger.resumed", new JSONMap())); - updateNotifier.next(); - } - catch (IOException e) { - ws.close(); - close(); - } - } - private void pauseDebug(Environment env, Breakpoint bp) { - try { - state = State.PAUSED_NORMAL; - var map = new JSONMap() - .set("callFrames", serializeFrames(env)) - .set("reason", "debugCommand"); - - if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + "")); - ws.send(new V8Event("Debugger.paused", map)); - } - catch (IOException e) { - ws.close(); - close(); - } - } - private void pauseException(Environment env) { - try { - state = State.PAUSED_EXCEPTION; - var map = new JSONMap() - .set("callFrames", serializeFrames(env)) - .set("reason", "exception"); - - ws.send(new V8Event("Debugger.paused", map)); - } - catch (IOException e) { - ws.close(); - close(); - } - } - - private void sendSource(DebugSource src){ - try { - ws.send(new V8Event("Debugger.scriptParsed", new JSONMap() - .set("scriptId", src.id + "") - .set("hash", src.source.hashCode()) - .set("url", src.filename + "") - )); - } - catch (IOException e) { - ws.close(); - close(); - } - } - - static Environment sanitizeEnvironment(Environment env) { - return env.child().remove(EventLoop.KEY).remove(DebugContext.KEY).add(DebugContext.IGNORE); - } - - private RunResult run(DebugFrame codeFrame, String code) { - if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); - var engine = new Engine(); - - var env = codeFrame.frame.env.child() - .remove(DebugContext.KEY) - .add(DebugContext.IGNORE) - .add(EventLoop.KEY, engine) - .add(GlobalScope.KEY, new GlobalScope(codeFrame.local)); - - var awaiter = engine.pushMsg(false, env, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); - - try { - engine.run(true); - return new RunResult(env, awaiter.await(), null); - } - catch (EngineException e) { return new RunResult(env, null, e); } - catch (SyntaxException e) { return new RunResult(env, null, EngineException.ofSyntax(e.toString())); } - } - - private ObjectValue vscodeAutoSuggest(Environment env, Object target, String query, boolean variable) { - var res = new ArrayValue(); - var passed = new HashSet(); - var tildas = "~"; - if (target == null) target = GlobalScope.get(env); - - for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(env, proto)) { - for (var el : Values.getMembers(env, proto, true, true)) { - var strKey = Values.toString(env, el); - if (passed.contains(strKey)) continue; - passed.add(strKey); - - var val = Values.getMember(env, Values.getMemberDescriptor(env, proto, el), "value"); - var desc = new ObjectValue(); - var sortText = ""; - if (strKey.startsWith(query)) sortText += "0@"; - else if (strKey.toLowerCase().startsWith(query.toLowerCase())) sortText += "1@"; - else if (strKey.contains(query)) sortText += "2@"; - else if (strKey.toLowerCase().contains(query.toLowerCase())) sortText += "3@"; - else sortText += "4@"; - sortText += tildas + strKey; - - desc.defineProperty(env, "label", strKey); - desc.defineProperty(env, "sortText", sortText); - - if (val instanceof FunctionValue) { - if (strKey.equals("constructor")) desc.defineProperty(env, "type", "name"); - else desc.defineProperty(env, "type", variable ? "function" : "method"); - } - else desc.defineProperty(env, "type", variable ? "variable" : "property"); - - switch (Values.type(val)) { - case "number": - case "boolean": - desc.defineProperty(env, "detail", Values.toString(env, val)); - break; - case "object": - if (val == Values.NULL) desc.defineProperty(env, "detail", "null"); - else try { - desc.defineProperty(env, "detail", Values.getMemberPath(env, target, "constructor", "name")); - } - catch (IllegalArgumentException e) { - desc.defineProperty(env, "detail", "object"); - } - break; - case "function": { - var type = "fn("; - for (var i = 0; i < ((FunctionValue)val).length; i++) { - if (i != 0) type += ","; - type += "?"; - } - type += ")"; - desc.defineProperty(env, "detail", type); - break; - } - default: - desc.defineProperty(env, "type", Values.type(val)); - break; - } - - res.set(env, res.size(), desc); - } - - tildas += "~"; - variable = true; - } - - var resObj = new ObjectValue(); - resObj.defineProperty(env, "result", res); - resObj.defineProperty(env, "isArray", target instanceof ArrayValue); - return resObj; - } - - @Override public synchronized void enable(V8Message msg) throws IOException { - enabled = true; - ws.send(msg.respond()); - - for (var el : pendingSources) sendSource(el); - pendingSources.clear(); - - updateNotifier.next(); - } - @Override public synchronized void disable(V8Message msg) throws IOException { - close(); - ws.send(msg.respond()); - } - @Override public synchronized void close() { - if (state != State.RESUMED) { - resume(State.RESUMED); - } - - enabled = false; - execptionType = CatchType.NONE; - state = State.RESUMED; - - mappings.clear(); - bpLocs.clear(); - - idToBreakpoint.clear(); - - filenameToId.clear(); - idToSource.clear(); - pendingSources.clear(); - - idToFrame.clear(); - codeFrameToFrame.clear(); - - objects.clear(); - - pendingPause = false; - - frames.clear(); - stepOutFrame = null; - stepOutPtr = 0; - - for (var ctx : contexts.keySet()) ctx.detachDebugger(this); - contexts.clear(); - - updateNotifier.next(); - } - - @Override public synchronized void getScriptSource(V8Message msg) throws IOException { - int id = Integer.parseInt(msg.params.string("scriptId")); - ws.send(msg.respond(new JSONMap().set("scriptSource", idToSource.get(id).source))); - } - @Override public synchronized void getPossibleBreakpoints(V8Message msg) throws IOException { - var start = deserializeLocation(msg.params.get("start")); - var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end")) : null; - var res = new JSONList(); - - for (var el : mappings.values()) { - for (var bp : el.breakpoints(start, end)) { - res.add(serializeLocation(bp)); - } - } - - ws.send(msg.respond(new JSONMap().set("locations", res))); - } - - @Override public synchronized void pause(V8Message msg) throws IOException { - pendingPause = true; - ws.send(msg.respond()); - } - @Override public synchronized void resume(V8Message msg) throws IOException { - resume(State.RESUMED); - ws.send(msg.respond(new JSONMap())); - } - - @Override public synchronized void setBreakpointByUrl(V8Message msg) throws IOException { - var line = (int)msg.params.number("lineNumber") + 1; - var col = (int)msg.params.number("columnNumber", 0) + 1; - var cond = msg.params.string("condition", "").trim(); - - if (cond.equals("")) cond = null; - if (cond != null) cond = "(" + cond + ")"; - - Pattern regex; - - if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url"))); - else if (msg.params.isString("urlRegex")) regex = Pattern.compile(msg.params.string("urlRegex")); - else { - ws.send(msg.respond(new JSONMap() - .set("breakpointId", "john-doe") - .set("locations", new JSONList()) - )); - return; - } - - var bpt = new Breakpoint(nextId(), regex, line, col, cond); - idToBreakpoint.put(bpt.id, bpt); - - for (var el : mappings.entrySet()) { - bpt.addFunc(el.getKey(), el.getValue()); - } - - var locs = new JSONList(); - - for (var loc : bpt.resolvedLocations.values()) { - locs.add(serializeLocation(loc)); - } - - ws.send(msg.respond(new JSONMap() - .set("breakpointId", bpt.id + "") - .set("locations", locs) - )); - } - @Override public synchronized void removeBreakpoint(V8Message msg) throws IOException { - var id = Integer.parseInt(msg.params.string("breakpointId")); - - idToBreakpoint.remove(id); - updateBreakpoints(); - ws.send(msg.respond()); - } - @Override public synchronized void continueToLocation(V8Message msg) throws IOException { - // TODO: Figure out if we need this - - // var loc = correctLocation(deserializeLocation(msg.params.get("location"))); - - // tmpBreakpts.add(loc); - - // resume(State.RESUMED); - // ws.send(msg.respond()); - } - - @Override public synchronized void setPauseOnExceptions(V8Message msg) throws IOException { - switch (msg.params.string("state")) { - case "none": execptionType = CatchType.NONE; break; - case "all": execptionType = CatchType.ALL; break; - case "uncaught": execptionType = CatchType.UNCAUGHT; break; - default: - ws.send(new V8Error("Invalid exception pause type.")); - return; - } - - ws.send(msg.respond()); - } - - @Override public synchronized void stepInto(V8Message msg) throws IOException { - if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); - else { - stepOutFrame = frames.get(frames.size() - 1); - stepOutPtr = stepOutFrame.frame.codePtr; - resume(State.STEPPING_IN); - ws.send(msg.respond()); - } - } - @Override public synchronized void stepOut(V8Message msg) throws IOException { - if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); - else { - stepOutFrame = frames.get(frames.size() - 1); - stepOutPtr = stepOutFrame.frame.codePtr; - resume(State.STEPPING_OUT); - ws.send(msg.respond()); - } - } - @Override public synchronized void stepOver(V8Message msg) throws IOException { - if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); - else { - stepOutFrame = frames.get(frames.size() - 1); - stepOutPtr = stepOutFrame.frame.codePtr; - resume(State.STEPPING_OVER); - ws.send(msg.respond()); - } - } - - @Override public synchronized void evaluateOnCallFrame(V8Message msg) throws IOException { - var cfId = Integer.parseInt(msg.params.string("callFrameId")); - var expr = msg.params.string("expression"); - var group = msg.params.string("objectGroup", null); - - var cf = idToFrame.get(cfId); - var res = run(cf, expr); - - if (group != null) objects.addToGroup(group, res.result); - - if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(res.ext, res.error)))); - else ws.send(msg.respond(new JSONMap().set("result", objects.serialize(res.ext, res.result)))); - } - - @Override public synchronized void releaseObjectGroup(V8Message msg) throws IOException { - var group = msg.params.string("objectGroup"); - objects.removeGroup(group); - ws.send(msg.respond()); - } - @Override public synchronized void releaseObject(V8Message msg) throws IOException { - var id = Integer.parseInt(msg.params.string("objectId")); - objects.release(id); - ws.send(msg.respond()); - } - @Override public synchronized void getProperties(V8Message msg) throws IOException { - var ref = objects.get(Integer.parseInt(msg.params.string("objectId"))); - var obj = ref.obj; - var env = ref.ext; - var res = new JSONList(); - var own = true; - - if (obj != emptyObject && obj != null) { - while (obj != null) { - for (var key : obj.keys(true)) { - var propDesc = new JSONMap(); - - if (obj.properties.containsKey(key)) { - var prop = obj.properties.get(key); - - propDesc.set("name", Values.toString(env, key)); - if (prop.getter != null) propDesc.set("get", objects.serialize(env, prop.getter)); - if (prop.setter != null) propDesc.set("set", objects.serialize(env, prop.setter)); - propDesc.set("enumerable", obj.memberEnumerable(key)); - propDesc.set("configurable", obj.memberConfigurable(key)); - propDesc.set("isOwn", true); - res.add(propDesc); - } - else { - propDesc.set("name", Values.toString(env, key)); - propDesc.set("value", objects.serialize(env, Values.getMember(env, obj, key))); - propDesc.set("writable", obj.memberWritable(key)); - propDesc.set("enumerable", obj.memberEnumerable(key)); - propDesc.set("configurable", obj.memberConfigurable(key)); - propDesc.set("isOwn", own); - res.add(propDesc); - } - } - - var proto = Values.getPrototype(env, obj); - - if (own) { - var protoDesc = new JSONMap(); - protoDesc.set("name", "__proto__"); - protoDesc.set("value", objects.serialize(env, proto == null ? Values.NULL : proto)); - protoDesc.set("writable", true); - protoDesc.set("enumerable", false); - protoDesc.set("configurable", false); - protoDesc.set("isOwn", own); - res.add(protoDesc); - } - - obj = proto; - own = false; - } - } - - ws.send(msg.respond(new JSONMap().set("result", res))); - } - @Override public synchronized void callFunctionOn(V8Message msg) throws IOException { - var src = msg.params.string("functionDeclaration"); - var args = msg.params - .list("arguments", new JSONList()) - .stream() - .map(v -> v.map()) - .map(objects::deserializeArgument) - .collect(Collectors.toList()); - var byValue = msg.params.bool("returnByValue", false); - - var thisArgRef = objects.get(Integer.parseInt(msg.params.string("objectId"))); - var thisArg = thisArgRef.obj; - var env = thisArgRef.ext; - - while (true) { - var start = src.lastIndexOf("//# sourceURL="); - if (start < 0) break; - var end = src.indexOf("\n", start); - if (end < 0) src = src.substring(0, start); - else src = src.substring(0, start) + src.substring(end + 1); - } - - try { - Object res = null; - if (compare(src, VSCODE_EMPTY)) res = emptyObject; - else if (compare(src, VSCODE_SELF)) res = thisArg; - else if (compare(src, CHROME_GET_PROP_FUNC)) { - res = thisArg; - for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(env, res, JSON.toJs(el)); - } - else if (compare(src, CHROME_GET_PROP_FUNC_2)) { - res = Values.call(env, args.get(0), thisArg); - } - else if (compare(src, VSCODE_CALL)) { - var func = (FunctionValue)(args.size() < 1 ? null : args.get(0)); - ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, func.call(env, thisArg))))); - } - else if (compare(src, VSCODE_AUTOCOMPLETE)) { - var target = args.get(0); - if (target == null) target = thisArg; - res = vscodeAutoSuggest(env, target, Values.toString(env, args.get(1)), Values.toBoolean(args.get(2))); - } - else { - ws.send(new V8Error("Please use well-known functions with callFunctionOn")); - return; - } - ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, res, byValue)))); - } - catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(env, e)))); } - } - - @Override public synchronized void runtimeEnable(V8Message msg) throws IOException { - ws.send(msg.respond()); - } - - @Override public void onSourceLoad(Filename filename, String source) { - int id = nextId(); - var src = new DebugSource(id, filename, source); - - idToSource.put(id, src); - filenameToId.put(filename, id); - - if (!enabled) pendingSources.add(src); - else sendSource(src); - } - @Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { - for (var bpt : idToBreakpoint.values()) { - bpt.addFunc(body, map); - } - mappings.put(body, map); - } - @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) { - if (!enabled) return false; - - boolean isBreakpointable; - Location loc; - DebugFrame frame; - BreakpointType bptType; - - synchronized (this) { - frame = getFrame(cf); - - var map = DebugContext.get(env).getMap(frame.frame.function); - - frame.updateLoc(map.toLocation(frame.frame.codePtr)); - loc = frame.location; - bptType = map.getBreakpoint(frame.frame.codePtr); - isBreakpointable = loc != null && (bptType.shouldStepIn()); - - if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { - pauseException(env); - } - else if ( - loc != null && - (state == State.STEPPING_IN || state == State.STEPPING_OVER) && - returnVal != Values.NO_RETURN && stepOutFrame == frame - ) { - pauseDebug(env, null); - } - else if (isBreakpointable && bpLocs.containsKey(loc)) { - for (var bp : bpLocs.get(loc)) { - var ok = bp.condition == null ? true : Values.toBoolean(run(frames.get(frames.size() - 1), bp.condition).result); - if (ok) pauseDebug(env, bp); - } - } - // else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null); - else if (isBreakpointable && pendingPause) { - pauseDebug(env, null); - pendingPause = false; - } - else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(env, null); - } - - - while (enabled) { - synchronized (this) { - switch (state) { - case PAUSED_EXCEPTION: - case PAUSED_NORMAL: break; - - case STEPPING_OUT: - case RESUMED: return false; - - case STEPPING_IN: - case STEPPING_OVER: - if (stepOutFrame.frame == frame.frame) { - if (returnVal != Values.NO_RETURN || error != null) { - state = State.STEPPING_OUT; - continue; - } - else if (stepOutPtr != frame.frame.codePtr) { - - if (state == State.STEPPING_IN && bptType.shouldStepIn()) { - pauseDebug(env, null); - break; - } - else if (state == State.STEPPING_OVER && bptType.shouldStepOver()) { - pauseDebug(env, null); - break; - } - } - } - return false; - } - } - updateNotifier.await(); - } - - return false; - } - @Override public void onFramePush(Environment env, Frame frame) { - var prevFrame = frames.get(frames.size() - 1); - var newFrame = getFrame(frame); - frames.add(newFrame); - - if (stepOutFrame != null && stepOutFrame.frame == prevFrame.frame && state == State.STEPPING_IN) { - stepOutFrame = newFrame; - } - } - @Override public void onFramePop(Environment env, Frame frame) { - frames.remove(frames.size() - 1); - - try { idToFrame.remove(codeFrameToFrame.remove(frame).id); } - catch (NullPointerException e) { } - - if (frames.size() == 0) { - if (state == State.PAUSED_EXCEPTION || state == State.PAUSED_NORMAL) resume(State.RESUMED); - } - else if (stepOutFrame != null && stepOutFrame.frame == frame && state == State.STEPPING_OUT) { - state = State.STEPPING_IN; - stepOutFrame = frames.get(frames.size() - 1); - } - } - @Override public List getStackFrame() { - return frames.stream().map(v -> v.frame).collect(Collectors.toList()); - } - - public SimpleDebugger attach(DebugContext ctx) { - ctx.attachDebugger(this); - contexts.put(ctx, ctx); - return this; - } - - public SimpleDebugger(WebSocket ws) { - this.ws = ws; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Error.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Error.java deleted file mode 100644 index 352dc70..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Error.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Error { - public final String message; - - public V8Error(String message) { - this.message = message; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap().set("error", new JSONMap() - .set("message", message) - )); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Event.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Event.java deleted file mode 100644 index 4ce0a36..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Event.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Event { - public final String name; - public final JSONMap params; - - public V8Event(String name, JSONMap params) { - this.name = name; - this.params = params; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - ); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Message.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Message.java deleted file mode 100644 index 6d31e0d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Message.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.util.Map; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONElement; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Message { - public final String name; - public final int id; - public final JSONMap params; - - public V8Message(String name, int id, Map params) { - this.name = name; - this.params = new JSONMap(params); - this.id = id; - } - public V8Result respond(JSONMap result) { - return new V8Result(id, result); - } - public V8Result respond() { - return new V8Result(id, new JSONMap()); - } - - public V8Message(JSONMap raw) { - if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'."); - if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'."); - - this.name = raw.string("method"); - this.id = (int)raw.number("id"); - this.params = raw.contains("params") ? raw.map("params") : new JSONMap(); - } - public V8Message(String raw) { - this(JSON.parse(null, raw).map()); - } - - public JSONMap toMap() { - var res = new JSONMap(); - return res; - } - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - .set("id", id) - ); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Result.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Result.java deleted file mode 100644 index d28d33a..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Result.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Result { - public final int id; - public final JSONMap result; - - public V8Result(int id, JSONMap result) { - this.id = id; - this.result = result; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("id", id) - .set("result", result) - ); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java b/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java deleted file mode 100644 index bb80125..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java +++ /dev/null @@ -1,195 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -import me.topchetoeu.jscript.utils.debug.WebSocketMessage.Type; - -public class WebSocket implements AutoCloseable { - public long maxLength = 1 << 20; - - private Socket socket; - private boolean closed = false; - - private OutputStream out() throws IOException { - return socket.getOutputStream(); - } - private InputStream in() throws IOException { - return socket.getInputStream(); - } - - private long readLen(int byteLen) throws IOException { - long res = 0; - - if (byteLen == 126) { - res |= in().read() << 8; - res |= in().read(); - return res; - } - else if (byteLen == 127) { - res |= in().read() << 56; - res |= in().read() << 48; - res |= in().read() << 40; - res |= in().read() << 32; - res |= in().read() << 24; - res |= in().read() << 16; - res |= in().read() << 8; - res |= in().read(); - return res; - } - else return byteLen; - } - private byte[] readMask(boolean has) throws IOException { - if (has) { - return new byte[] { - (byte)in().read(), - (byte)in().read(), - (byte)in().read(), - (byte)in().read() - }; - } - else return new byte[4]; - } - - private void writeLength(int len) throws IOException { - if (len < 126) { - out().write((int)len); - } - else if (len <= 0xFFFF) { - out().write(126); - out().write((int)(len >> 8) & 0xFF); - out().write((int)len & 0xFF); - } - else { - out().write(127); - out().write((len >> 56) & 0xFF); - out().write((len >> 48) & 0xFF); - out().write((len >> 40) & 0xFF); - out().write((len >> 32) & 0xFF); - out().write((len >> 24) & 0xFF); - out().write((len >> 16) & 0xFF); - out().write((len >> 8) & 0xFF); - out().write(len & 0xFF); - } - } - private synchronized void write(int type, byte[] data) throws IOException { - int i; - - for (i = 0; i < data.length / 0xFFFF; i++) { - out().write(type); - writeLength(0xFFFF); - out().write(data, i * 0xFFFF, 0xFFFF); - type = 0; - } - - out().write(type | 0x80); - writeLength(data.length % 0xFFFF); - out().write(data, i * 0xFFFF, data.length % 0xFFFF); - } - - public void send(String data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(1, data.getBytes()); - } - public void send(byte[] data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(2, data); - } - public void send(WebSocketMessage msg) throws IOException { - if (msg.type == Type.Binary) send(msg.binaryData()); - else send(msg.textData()); - } - public void send(Object data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(1, data.toString().getBytes()); - } - - public void close(String reason) { - if (socket != null) { - try { - write(8, reason.getBytes()); - socket.close(); - } - catch (Throwable e) { } - } - - socket = null; - closed = true; - } - public void close() { - close(""); - } - - private WebSocketMessage fail(String reason) { - System.out.println("WebSocket Error: " + reason); - close(reason); - return null; - } - - private byte[] readData() throws IOException { - var maskLen = in().read(); - var hasMask = (maskLen & 0x80) != 0; - var len = (int)readLen(maskLen & 0x7F); - var mask = readMask(hasMask); - - if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size"); - else { - var buff = new byte[len]; - - if (in().read(buff) < len) fail("WebSocket Error: payload too short"); - else { - for (int i = 0; i < len; i++) { - buff[i] ^= mask[(int)(i % 4)]; - } - return buff; - } - } - - return null; - } - - public WebSocketMessage receive() throws IOException { - var data = new ByteArrayOutputStream(); - var type = 0; - - while (socket != null && !closed) { - var finId = in().read(); - if (finId < 0) break; - var fin = (finId & 0x80) != 0; - int id = finId & 0x0F; - - if (id == 0x8) { close(); return null; } - if (id >= 0x8) { - if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented"); - if (id == 0x9) write(0xA, data.toByteArray()); - continue; - } - - if (type == 0) type = id; - if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment"); - - var buff = readData(); - if (buff == null) break; - - if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size"); - data.write(buff); - - if (!fin) continue; - var raw = data.toByteArray(); - - if (type == 1) { - return new WebSocketMessage(new String(raw)); - } - else return new WebSocketMessage(raw); - } - - return null; - } - - public WebSocket(Socket socket) { - this.socket = socket; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java b/src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java deleted file mode 100644 index 10d3959..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -public class WebSocketMessage { - public static enum Type { - Text, - Binary, - } - - public final Type type; - private final Object data; - - public final String textData() { - if (type != Type.Text) throw new IllegalStateException("Message is not text."); - return (String)data; - } - public final byte[] binaryData() { - if (type != Type.Binary) throw new IllegalStateException("Message is not binary."); - return (byte[])data; - } - - public WebSocketMessage(String data) { - this.type = Type.Text; - this.data = data; - } - public WebSocketMessage(byte[] data) { - this.type = Type.Binary; - this.data = data; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java deleted file mode 100644 index 323fadb..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum ActionType { - UNKNOWN(0, "An operation performed upon", "An operation was performed upon"), - READ(1, "Reading from", "Read from"), - WRITE(2, "Writting to", "Wrote to"), - SEEK(3, "Seeking in", "Sought in"), - CLOSE(4, "Closing", "Closed"), - STAT(5, "Stat of", "Statted"), - OPEN(6, "Opening", "Opened"), - CREATE(7, "Creating", "Created"), - DELETE(8, "Deleting", "Deleted"), - CLOSE_FS(9, "Closing filesystem", "Closed filesystem"); - - public final int code; - public final String continuous, past; - - public String readable(boolean usePast) { - if (usePast) return past; - else return continuous; - } - - private ActionType(int code, String continuous, String past) { - this.code = code; - this.continuous = continuous; - this.past = past; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java deleted file mode 100644 index f552bba..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public abstract class BaseFile implements File { - private T handle; - private Mode mode; - - protected final T handle() { - return handle; - } - - protected abstract int onRead(byte[] buff); - protected abstract void onWrite(byte[] buff); - protected abstract long onSeek(long offset, int pos); - protected abstract boolean onClose(); - - @Override public synchronized int read(byte[] buff) { - try { - if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); - if (!mode.readable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for reading."); - return onRead(buff); - } - catch (FilesystemException e) { throw e.setAction(ActionType.READ); } - } - @Override public synchronized void write(byte[] buff) { - try { - if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); - if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for writting."); - onWrite(buff); - } - catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } - } - @Override public synchronized long seek(long offset, int pos) { - try { - if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); - if (mode == Mode.NONE) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for seeking."); - return onSeek(offset, pos); - } - catch (FilesystemException e) { throw e.setAction(ActionType.SEEK); } - } - @Override public synchronized boolean close() { - if (handle != null) { - try { - var res = onClose(); - handle = null; - mode = Mode.NONE; - return res; - } - catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE); } - } - else return false; - } - - public BaseFile(T handle, Mode mode) { - this.mode = mode; - this.handle = handle; - - if (mode == Mode.NONE) this.handle = null; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java b/src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java deleted file mode 100644 index 09bd373..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum EntryType { - NONE("none"), - FILE("file"), - FOLDER("folder"); - - public final String name; - - private EntryType(String name) { - this.name = name; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java deleted file mode 100644 index 91cdc2b..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum ErrorReason { - UNKNOWN(0, "failed", false), - NO_PERMISSION(1, "is not allowed", false), - CLOSED(1, "that was closed", true), - UNSUPPORTED(2, "is not supported", false), - ILLEGAL_ARGS(3, "with illegal arguments", true), - DOESNT_EXIST(4, "that doesn't exist", true), - ALREADY_EXISTS(5, "that already exists", true), - ILLEGAL_PATH(6, "with illegal path", true), - NO_PARENT(7, "with a missing parent folder", true); - - public final int code; - public final boolean usePast; - public final String readable; - - private ErrorReason(int code, String readable, boolean usePast) { - this.code = code; - this.readable = readable; - this.usePast = usePast; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/File.java b/src/java/me/topchetoeu/jscript/utils/filesystem/File.java deleted file mode 100644 index f98c566..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/File.java +++ /dev/null @@ -1,169 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Iterator; -import java.util.LinkedList; - -import me.topchetoeu.jscript.common.Buffer; -public interface File { - default int read(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.READ); } - default void write(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.WRITE); } - default long seek(long offset, int pos) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.SEEK); } - default boolean close() { return false; } - - default byte[] readAll() { - var parts = new LinkedList(); - var sizes = new LinkedList(); - var buff = new byte[1024]; - var size = 0; - - while (true) { - var n = read(buff); - if (n < 0) break; - else if (n == 0) continue; - - parts.add(buff); - sizes.add(n); - size += n; - buff = new byte[1024]; - } - - buff = new byte[size]; - - var i = 0; - var j = 0; - - for (var part : parts) { - var currSize = sizes.get(j++); - - System.arraycopy(part, 0, buff, i, currSize); - i += currSize; - } - - return buff; - } - default String readToString() { - return new String(readAll()); - } - default String readLine() { - var res = new Buffer(); - var buff = new byte[1]; - - while (true) { - if (read(buff) == 0) { - if (res.length() == 0) return null; - else break; - } - - if (buff[0] == '\n') break; - - res.write(res.length(), buff); - } - return new String(res.data()); - } - - public static File ofStream(InputStream str) { - return new File() { - @Override public synchronized int read(byte[] buff) { - try { - try { return str.read(buff); } - catch (NullPointerException e) { throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); } - catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.READ); } - } - }; - } - public static File ofStream(OutputStream str) { - return new File() { - @Override public synchronized void write(byte[] buff) { - try { - try { str.write(buff); } - catch (NullPointerException e) {throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); } - catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } - } - }; - } - public static File ofLineWriter(LineWriter writer) { - var buff = new Buffer(); - return new File() { - @Override public synchronized void write(byte[] val) { - try { - if (val == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null."); - - for (var b : val) { - if (b == '\n') { - try { - writer.writeLine(new String(buff.data())); - buff.clear(); - } - catch (IOException e) { - throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); - } - } - else buff.append(b); - } - } - catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } - } - }; - } - public static File ofLineReader(LineReader reader) { - return new File() { - private int offset = 0; - private byte[] prev = new byte[0]; - - @Override - public synchronized int read(byte[] buff) { - try { - if (buff == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null."); - var ptr = 0; - - while (true) { - if (prev == null) break; - if (offset >= prev.length) { - try { - var line = reader.readLine(); - - if (line == null) { - prev = null; - break; - } - else prev = (line + "\n").getBytes(); - - offset = 0; - } - catch (IOException e) { - throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); - } - } - - if (ptr + prev.length - offset > buff.length) { - var n = buff.length - ptr; - System.arraycopy(prev, offset, buff, ptr, buff.length - ptr); - offset += n; - ptr += n; - break; - } - else { - var n = prev.length - offset; - System.arraycopy(prev, offset, buff, ptr, n); - offset += n; - ptr += n; - } - } - - return ptr; - } - catch (FilesystemException e) { throw e.setAction(ActionType.READ); } - } - }; - } - public static File ofIterator(Iterator it) { - return ofLineReader(LineReader.ofIterator(it)); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java b/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java deleted file mode 100644 index 903bde1..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public class FileStat { - public final Mode mode; - public final EntryType type; - - public FileStat(Mode mode, EntryType type) { - if (mode == Mode.NONE) type = EntryType.NONE; - if (type == EntryType.NONE) mode = Mode.NONE; - - this.mode = mode; - this.type = type; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java deleted file mode 100644 index 283d633..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; - -public interface Filesystem { - public static final Key KEY = new Key<>(); - - default String normalize(String... path) { return Paths.normalize(path); } - default boolean create(String path, EntryType type) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.CREATE); } - File open(String path, Mode mode); - FileStat stat(String path); - void close(); - - public static Filesystem get(Environment exts) { - return exts.get(KEY); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java b/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java deleted file mode 100644 index 802179a..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.ArrayList; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.Values; - -public class FilesystemException extends RuntimeException { - public final ErrorReason reason; - public final String details; - private ActionType action; - private EntryType entry = EntryType.FILE; - private String path; - - public FilesystemException setPath(String path) { - this.path = path; - return this; - } - public FilesystemException setAction(ActionType action) { - if (action == null) action = ActionType.UNKNOWN; - - this.action = action; - return this; - } - public FilesystemException setEntry(EntryType entry) { - if (entry == null) entry = EntryType.NONE; - - this.entry = entry; - return this; - } - - public ActionType action() { - return action; - } - public String path() { - return path; - } - public EntryType entry() { - return entry; - } - - public EngineException toEngineException() { - var res = EngineException.ofError("IOError", getMessage()); - - Values.setMember(null, res.value, "action", action.code); - Values.setMember(null, res.value, "reason", reason.code); - Values.setMember(null, res.value, "path", path); - Values.setMember(null, res.value, "entry", entry.name); - if (details != null) Values.setMember(null, res.value, "details", details); - - return res; - } - - @Override public String getMessage() { - var parts = new ArrayList(10); - - parts.add(action == null ? "An action performed upon " : action.readable(reason.usePast)); - - if (entry == EntryType.FILE) parts.add("file"); - if (entry == EntryType.FOLDER) parts.add("folder"); - - if (path != null && !path.isBlank()) parts.add(path.trim()); - - parts.add(reason.readable); - - var msg = String.join(" ", parts); - if (details != null) msg += ": " + details; - - return msg; - } - - public FilesystemException(ErrorReason type, String details) { - super(); - if (type == null) type = ErrorReason.UNKNOWN; - - this.details = details; - this.reason = type; - } - public FilesystemException(ErrorReason type) { - this(type, null); - } - public FilesystemException() { - this(null); - } - -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java b/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java deleted file mode 100644 index c6dbacb..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.HashSet; -import java.util.Set; - -public class HandleManager { - private Set files = new HashSet<>(); - - public File put(File val) { - var handle = new File() { - @Override public int read(byte[] buff) { - return val.read(buff); - } - @Override public void write(byte[] buff) { - val.write(buff); - } - @Override public long seek(long offset, int pos) { - return val.seek(offset, pos); - } - @Override public boolean close() { - return files.remove(this) && val.close(); - } - }; - files.add(handle); - return handle; - } - public void close() { - while (!files.isEmpty()) { - files.stream().findFirst().get().close(); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java b/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java deleted file mode 100644 index 6d84450..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; -import java.util.Iterator; - - -public interface LineReader { - String readLine() throws IOException; - - public static LineReader ofIterator(Iterator it) { - return () -> { - if (it.hasNext()) return it.next(); - else return null; - }; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java b/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java deleted file mode 100644 index 501631a..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; - -public interface LineWriter { - void writeLine(String value) throws IOException; -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java deleted file mode 100644 index cb89d1d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import me.topchetoeu.jscript.common.Buffer; - -class MemoryFile extends BaseFile { - private int ptr; - - @Override protected int onRead(byte[] buff) { - if (ptr >= handle().length()) return -1; - var res = handle().read(ptr, buff); - ptr += res; - return res; - } - @Override protected void onWrite(byte[] buff) { - handle().write(ptr, buff); - ptr += buff.length; - } - @Override protected long onSeek(long offset, int pos) { - if (pos == 0) ptr = (int)offset; - else if (pos == 1) ptr += (int)offset; - else if (pos == 2) ptr = handle().length() - (int)offset; - - if (ptr < 0) ptr = 0; - if (ptr > handle().length()) ptr = handle().length(); - - return pos; - } - @Override protected boolean onClose() { - ptr = 0; - return true; - } - - public MemoryFile(Buffer buff, Mode mode) { - super(buff, mode); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java deleted file mode 100644 index 68b1e4c..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java +++ /dev/null @@ -1,100 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.nio.file.Path; -import java.util.HashMap; -import java.util.HashSet; - -import me.topchetoeu.jscript.common.Buffer; -import me.topchetoeu.jscript.common.Filename; - -public class MemoryFilesystem implements Filesystem { - public final Mode mode; - private HashMap files = new HashMap<>(); - private HashSet folders = new HashSet<>(); - private HandleManager handles = new HandleManager(); - - private Path realPath(String path) { - return Filename.normalize(path); - } - - @Override public String normalize(String... path) { - return Paths.normalize(path); - } - @Override public synchronized File open(String _path, Mode perms) { - try { - var path = realPath(_path); - var pcount = path.getNameCount(); - - if (files.containsKey(path)) return handles.put(new MemoryFile(files.get(path), perms)); - else if (folders.contains(path)) { - var res = new StringBuilder(); - - for (var folder : folders) { - if (pcount + 1 != folder.getNameCount()) continue; - if (!folder.startsWith(path)) continue; - res.append(folder.toFile().getName()).append('\n'); - } - - for (var file : files.keySet()) { - if (pcount + 1 != file.getNameCount()) continue; - if (!file.startsWith(path)) continue; - res.append(file.toFile().getName()).append('\n'); - } - - return handles.put(new MemoryFile(new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ))); - } - else throw new FilesystemException(ErrorReason.DOESNT_EXIST); - } - catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.OPEN); } - } - @Override public synchronized boolean create(String _path, EntryType type) { - try { - var path = realPath(_path); - - switch (type) { - case FILE: - if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT); - if (folders.contains(path) || files.containsKey(path)) return false; - files.put(path, new Buffer()); - return true; - case FOLDER: - if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT); - if (folders.contains(path) || files.containsKey(path)) return false; - folders.add(path); - return true; - default: - case NONE: - return folders.remove(path) || files.remove(path) != null; - } - } - catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.CREATE); } - } - @Override public synchronized FileStat stat(String _path) { - var path = realPath(_path); - - if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE); - else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER); - else return new FileStat(Mode.NONE, EntryType.NONE); - } - @Override public synchronized void close() throws FilesystemException { - handles.close(); - } - - public MemoryFilesystem put(String path, byte[] data) { - var _path = realPath(path); - var _curr = "/"; - - for (var seg : _path) { - create(_curr, EntryType.FOLDER); - _curr += seg + "/"; - } - - files.put(_path, new Buffer(data)); - return this; - } - - public MemoryFilesystem(Mode mode) { - this.mode = mode; - folders.add(Path.of("/")); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java deleted file mode 100644 index b64d288..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum Mode { - NONE("", false, false), - READ("r", true, false), - WRITE("rw", false, true), - READ_WRITE("rw", true, true); - - public final String name; - public final boolean readable; - public final boolean writable; - - public Mode intersect(Mode other) { - return of(readable && other.readable, writable && other.writable); - } - - private Mode(String mode, boolean r, boolean w) { - this.name = mode; - this.readable = r; - this.writable = w; - } - - public static Mode of(boolean read, boolean write) { - if (read && write) return READ_WRITE; - if (read) return READ; - if (write) return WRITE; - return NONE; - } - - public static Mode parse(String mode) { - switch (mode.toLowerCase()) { - case "r": return READ; - case "w": return WRITE; - case "r+": - case "w+": - case "wr": - case "rw": return READ_WRITE; - default: return NONE; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java deleted file mode 100644 index 0bc3967..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.ArrayList; - -public class Paths { - public static String normalize(String... path) { - var parts = String.join("/", path).split("[\\\\/]"); - var res = new ArrayList(); - - for (var part : parts) { - if (part.equals("...")) res.clear(); - else if (part.equals("..")) { - if (res.size() > 0) res.remove(res.size() - 1); - } - else if (!part.equals(".") && !part.isEmpty()) res.add(part); - } - - var sb = new StringBuilder(); - - for (var el : res) sb.append("/").append(el); - - if (sb.length() == 0) return "/"; - else return sb.toString(); - } - - public static String chroot(String root, String path) { - return normalize(root) + normalize(path); - } - - public static String cwd(String cwd, String path) { - return normalize(cwd + "/" + path); - } - - public static String filename(String path) { - var i = path.lastIndexOf('/'); - if (i < 0) i = path.lastIndexOf('\\'); - - if (i < 0) return path; - else return path.substring(i + 1); - } - - public static String extension(String path) { - var i = path.lastIndexOf('.'); - - if (i < 0) return ""; - else return path.substring(i + 1); - } - - public static String dir(String path) { - return normalize(path + "/.."); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java deleted file mode 100644 index 67efb80..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.file.Path; - -public class PhysicalFile extends BaseFile { - @Override protected int onRead(byte[] buff) { - try { return handle().read(buff); } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.READ); } - } - @Override protected void onWrite(byte[] buff) { - try { handle().write(buff); } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.WRITE); } - } - @Override protected long onSeek(long offset, int pos) { - try { - if (pos == 1) offset += handle().getFilePointer(); - else if (pos == 2) offset += handle().length(); - handle().seek(offset); - return offset; - } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.SEEK); } - } - @Override protected boolean onClose() { - try { handle().close(); } - catch (IOException e) {} // SHUT - return true; - } - - public PhysicalFile(Path path, Mode mode) throws FileNotFoundException { - super(new RandomAccessFile(path.toFile(), mode.name), mode); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java deleted file mode 100644 index 9ff0c0e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java +++ /dev/null @@ -1,92 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; - -public class PhysicalFilesystem implements Filesystem { - public final String root; - private HandleManager handles = new HandleManager(); - - private void checkMode(Path path, Mode mode) { - if (!path.startsWith(root)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "Tried to jailbreak the sandbox."); - - if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions"); - if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No write permissions"); - } - - private Path realPath(String path) { - return Path.of(Paths.chroot(root, path)); - } - - @Override public String normalize(String... paths) { - return Paths.normalize(paths); - } - @Override public synchronized File open(String _path, Mode perms) { - try { - var path = realPath(normalize(_path)); - checkMode(path, perms); - - try { - if (Files.isDirectory(path)) return handles.put(File.ofIterator( - Files.list(path).map(v -> v.getFileName().toString()).iterator() - )); - else return handles.put(new PhysicalFile(path, perms)); - } - catch (FileNotFoundException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.OPEN).setPath(_path); } - } - @Override public synchronized boolean create(String _path, EntryType type) { - try { - var path = realPath(_path); - - try { - switch (type) { - case FILE: - Files.createFile(path); - break; - case FOLDER: - Files.createDirectories(path); - break; - case NONE: - default: - Files.delete(path); - } - } - catch (FileAlreadyExistsException | NoSuchFileException e) { return false; } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PARENT); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.CREATE).setPath(_path); } - - return true; - } - @Override public synchronized FileStat stat(String _path) { - var path = realPath(_path); - - if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE); - - var perms = Mode.of(Files.isReadable(path), Files.isWritable(path)); - - if (perms == Mode.NONE) return new FileStat(Mode.NONE, EntryType.NONE); - - return new FileStat( - perms, - Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE - ); - } - @Override public synchronized void close() throws FilesystemException { - try { - handles.close(); - } - catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); } - } - - public PhysicalFilesystem(String root) { - this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString()); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java deleted file mode 100644 index 1db7355..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java +++ /dev/null @@ -1,99 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.HashMap; -import java.util.Map; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.utils.permissions.Matcher; -import me.topchetoeu.jscript.utils.permissions.Permission; -import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; - -public class RootFilesystem implements Filesystem { - public final Map protocols = new HashMap<>(); - public final PermissionsProvider perms; - - public static final Permission PERM_READ = new Permission("jscript.file.read", Matcher.fileWildcard()); - public static final Permission PERM_WRITE = new Permission("jscript.file.read", Matcher.fileWildcard()); - - private boolean canRead(String _path) { - return perms.hasPermission(PERM_READ, _path); - } - private boolean canWrite(String _path) { - return perms.hasPermission(PERM_WRITE, _path); - } - - private void modeAllowed(String _path, Mode mode) throws FilesystemException { - if (mode.readable && perms != null && !canRead(_path)) { - throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions").setPath(_path); - } - if (mode.writable && perms != null && !canWrite(_path)) { - throw new FilesystemException(ErrorReason.NO_PERMISSION, "No wtrite permissions").setPath(_path); - } - } - - private Filesystem getProtocol(Filename filename) { - var protocol = protocols.get(filename.protocol); - - if (protocol == null) { - throw new FilesystemException(ErrorReason.DOESNT_EXIST, "The protocol '" + filename.protocol + "' doesn't exist."); - } - - return protocol; - } - - @Override public String normalize(String... paths) { - if (paths.length == 0) return "file://"; - else { - var filename = Filename.parse(paths[0]); - var protocol = protocols.get(filename.protocol); - paths[0] = filename.path; - - - if (protocol == null) return Paths.normalize(paths); - else return filename.protocol + "://" + protocol.normalize(paths); - } - } - @Override public synchronized File open(String path, Mode perms) throws FilesystemException { - try { - var filename = Filename.parse(path); - var protocol = getProtocol(filename); - - modeAllowed(filename.toString(), perms); - return protocol.open(filename.path, perms); - } - catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.OPEN); } - } - @Override public synchronized boolean create(String path, EntryType type) throws FilesystemException { - try { - var filename = Filename.parse(path); - var protocol = getProtocol(filename); - - modeAllowed(filename.toString(), Mode.WRITE); - return protocol.create(filename.path, type); - } - catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.CREATE); } - } - @Override public synchronized FileStat stat(String path) throws FilesystemException { - try { - var filename = Filename.parse(path); - var protocol = getProtocol(filename); - - return protocol.stat(filename.path); - } - catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.STAT); } - } - @Override public synchronized void close() throws FilesystemException { - try { - for (var protocol : protocols.values()) { - protocol.close(); - } - - protocols.clear(); - } - catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); } - } - - public RootFilesystem(PermissionsProvider perms) { - this.perms = perms; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java deleted file mode 100644 index 5b82c0e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.InputStream; -import java.io.OutputStream; - -public class STDFilesystem implements Filesystem { - private File in; - private File out; - private File err; - - @Override - public String normalize(String... path) { - var res = Paths.normalize(path); - while (res.startsWith("/")) res = res.substring(1); - while (res.endsWith("/")) res = res.substring(0, res.length() - 1); - return res; - } - - @Override public synchronized File open(String path, Mode mode) { - path = normalize(path); - if (in != null && path.equals("in")) return in; - else if (out != null && path.equals("out")) return out; - else if (err != null && path.equals("err")) return err; - else throw new FilesystemException(ErrorReason.DOESNT_EXIST).setAction(ActionType.OPEN).setPath(path); - } - @Override public synchronized FileStat stat(String path) { - path = normalize(path); - if (path.equals("in") || path.equals("out") || path.equals("err")) return new FileStat(Mode.READ_WRITE, EntryType.FILE); - else return new FileStat(Mode.NONE, EntryType.NONE); - } - @Override public synchronized void close() { - in = out = err = null; - } - - public STDFilesystem(File in, File out, File err) { - this.in = in; - this.out = out; - this.err = err; - } - public STDFilesystem(InputStream in, OutputStream out, OutputStream err) { - if (in != null) this.in = File.ofStream(in); - if (out != null) this.out = File.ofStream(out); - if (err != null) this.err = File.ofStream(err); - } - public STDFilesystem(LineReader in, LineWriter out) { - if (in != null) this.in = File.ofLineReader(in); - if (out != null) { - this.out = File.ofLineWriter(out); - this.err = File.ofLineWriter(out); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java b/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java deleted file mode 100644 index 2ced1aa..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java +++ /dev/null @@ -1,121 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.reflect.Array; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.NativeWrapper; -import me.topchetoeu.jscript.runtime.values.Values; - -public class Arguments { - public final Object self; - public final Object[] args; - public final Environment env; - - public int n() { - return args.length; - } - - public boolean has(int i) { - return i == -1 || i >= 0 && i < args.length; - } - - public T self(Class type) { - return convert(-1, type); - } - public T convert(int i, Class type) { - return Values.convert(env, get(i), type); - } - public Object get(int i, boolean unwrap) { - Object res = null; - - if (i == -1) res = self; - if (i >= 0 && i < args.length) res = args[i]; - if (unwrap && res instanceof NativeWrapper) res = ((NativeWrapper)res).wrapped; - - return res; - } - public Object get(int i) { - return get(i, false); - } - public Object getOrDefault(int i, Object def) { - if (i < 0 || i >= args.length) return def; - else return get(i); - } - - public Arguments slice(int start) { - var res = new Object[Math.max(0, args.length - start)]; - for (int j = start; j < args.length; j++) res[j - start] = get(j); - return new Arguments(env, args, res); - } - - @SuppressWarnings("unchecked") - public T[] convert(Class type) { - var res = Array.newInstance(type, args.length); - for (int i = 0; i < args.length; i++) Array.set(res, i, convert(i, type)); - return (T[])res; - } - public int[] convertInt() { - var res = new int[args.length]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Integer.class); - return res; - } - public long[] convertLong() { - var res = new long[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Long.class); - return res; - } - public short[] sliceShort() { - var res = new short[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Short.class); - return res; - } - public float[] sliceFloat() { - var res = new float[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Float.class); - return res; - } - public double[] sliceDouble() { - var res = new double[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Double.class); - return res; - } - public byte[] sliceByte() { - var res = new byte[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Byte.class); - return res; - } - public char[] sliceChar() { - var res = new char[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Character.class); - return res; - } - public boolean[] sliceBool() { - var res = new boolean[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Boolean.class); - return res; - } - - public String getString(int i) { return Values.toString(env, get(i)); } - public boolean getBoolean(int i) { return Values.toBoolean(get(i)); } - public int getInt(int i) { return (int)Values.toNumber(env, get(i)); } - public long getLong(int i) { return (long)Values.toNumber(env, get(i)); } - public double getDouble(int i) { return Values.toNumber(env, get(i)); } - public float getFloat(int i) { return (float)Values.toNumber(env, get(i)); } - - public int getInt(int i, int def) { - var res = get(i); - if (res == null) return def; - else return (int)Values.toNumber(env, res); - } - public String getString(int i, String def) { - var res = get(i); - if (res == null) return def; - else return Values.toString(env, res); - } - - public Arguments(Environment env, Object thisArg, Object... args) { - this.env = env; - this.args = args; - this.self = thisArg; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/Expose.java b/src/java/me/topchetoeu/jscript/utils/interop/Expose.java deleted file mode 100644 index 5ef6815..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/Expose.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Expose { - public String value() default ""; - public ExposeType type() default ExposeType.METHOD; - public ExposeTarget target() default ExposeTarget.MEMBER; -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java deleted file mode 100644 index f5b97e6..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ExposeConstructor { } diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java deleted file mode 100644 index a66330e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD, ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ExposeField { - public String value() default ""; - public ExposeTarget target() default ExposeTarget.MEMBER; -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java deleted file mode 100644 index 311a627..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -public enum ExposeTarget { - STATIC(true, true, false), - MEMBER(false, false, true), - NAMESPACE(false, true, false), - CONSTRUCTOR(true, false, false), - PROTOTYPE(false, false, true), - ALL(true, true, true); - - public final boolean constructor; - public final boolean namespace; - public final boolean prototype; - - public boolean shouldApply(ExposeTarget other) { - if (other.constructor && !constructor) return false; - if (other.namespace && !namespace) return false; - if (other.prototype && !prototype) return false; - - return true; - } - - private ExposeTarget(boolean constructor, boolean namespace, boolean prototype) { - this.constructor = constructor; - this.namespace = namespace; - this.prototype = prototype; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java deleted file mode 100644 index 15e91a9..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -public enum ExposeType { - METHOD, - GETTER, - SETTER, -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java b/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java deleted file mode 100644 index 1022ce4..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java +++ /dev/null @@ -1,454 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; - -public class NativeWrapperProvider { - public static final Key KEY = new Key<>(); - - private final HashMap, FunctionValue> constructors = new HashMap<>(); - private final HashMap, ObjectValue> prototypes = new HashMap<>(); - private final HashMap, ObjectValue> namespaces = new HashMap<>(); - private final HashMap, Class> classToProxy = new HashMap<>(); - private final HashMap, Class> proxyToClass = new HashMap<>(); - private final HashMap, Class> interfaceToProxy = new HashMap<>(); - private final HashSet> ignore = new HashSet<>(); - - private static Object call(Environment ctx, String name, Method method, Object thisArg, Object... args) { - try { - var realArgs = new Object[method.getParameterCount()]; - System.arraycopy(args, 0, realArgs, 0, realArgs.length); - if (Modifier.isStatic(method.getModifiers())) thisArg = null; - return Values.normalize(ctx, method.invoke(Values.convert(ctx, thisArg, method.getDeclaringClass()), realArgs)); - } - catch (InvocationTargetException e) { - if (e.getTargetException() instanceof EngineException) { - throw ((EngineException)e.getTargetException()).add(ctx, name, Location.INTERNAL); - } - else if (e.getTargetException() instanceof NullPointerException) { - // e.getTargetException().printStackTrace(); - throw EngineException.ofType("Unexpected value of 'undefined'.").add(ctx, name, Location.INTERNAL); - } - else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) { - throw new InterruptException(); - } - else throw new EngineException(e.getTargetException()).add(ctx, name, Location.INTERNAL); - } - catch (ReflectiveOperationException e) { - throw EngineException.ofError(e.getMessage()).add(ctx, name, Location.INTERNAL); - } - } - private static FunctionValue create(String name, Method method) { - return new NativeFunction(name, args -> call(args.env, name, method, args.self, args)); - } - private static void checkSignature(Method method, boolean forceStatic, Class ...params) { - if (forceStatic && !Modifier.isStatic(method.getModifiers())) throw new IllegalArgumentException(String.format( - "Method %s must be static.", - method.getDeclaringClass().getName() + "." + method.getName() - )); - - var actual = method.getParameterTypes(); - - boolean failed = actual.length > params.length; - - if (!failed) for (var i = 0; i < actual.length; i++) { - if (!actual[i].isAssignableFrom(params[i])) { - failed = true; - break; - } - } - - if (failed) throw new IllegalArgumentException(String.format( - "Method %s was expected to have a signature of '%s', found '%s' instead.", - method.getDeclaringClass().getName() + "." + method.getName(), - String.join(", ", Arrays.stream(params).map(v -> v.getName()).collect(Collectors.toList())), - String.join(", ", Arrays.stream(actual).map(v -> v.getName()).collect(Collectors.toList())) - )); - } - private static String getName(Class ...classes) { - String last = null; - - for (var clazz : classes) { - var classNat = clazz.getAnnotation(WrapperName.class); - if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim(); - else if (last != null) last = clazz.getSimpleName(); - } - - return last; - } - - private static void checkUnderscore(Member member) { - if (!member.getName().startsWith("__")) { - System.out.println(String.format("WARNING: The name of the exposed member '%s.%s' doesn't start with '__'.", - member.getDeclaringClass().getName(), - member.getName() - )); - } - } - private static String getName(Member member, String overrideName) { - if (overrideName == null) overrideName = ""; - if (overrideName.isBlank()) { - var res = member.getName(); - if (res.startsWith("__")) res = res.substring(2); - return res; - } - else return overrideName.trim(); - } - private static Object getKey(String name) { - if (name.startsWith("@@")) return Symbol.get(name.substring(2)); - else return name; - } - - private static boolean apply(ObjectValue obj, ExposeTarget target, Class clazz) { - var getters = new HashMap(); - var setters = new HashMap(); - var props = new HashSet(); - var nonProps = new HashSet(); - var any = false; - - for (var method : clazz.getDeclaredMethods()) { - for (var annotation : method.getAnnotationsByType(Expose.class)) { - any = true; - if (!annotation.target().shouldApply(target)) continue; - - checkUnderscore(method); - var name = getName(method, annotation.value()); - var key = getKey(name); - var repeat = false; - - switch (annotation.type()) { - case METHOD: - if (props.contains(key) || nonProps.contains(key)) repeat = true; - else { - checkSignature(method, false, Arguments.class); - obj.defineProperty(null, key, create(name, method), true, true, false); - nonProps.add(key); - } - break; - case GETTER: - if (nonProps.contains(key) || getters.containsKey(key)) repeat = true; - else { - checkSignature(method, false, Arguments.class); - getters.put(key, create(name, method)); - props.add(key); - } - break; - case SETTER: - if (nonProps.contains(key) || setters.containsKey(key)) repeat = true; - else { - checkSignature(method, false, Arguments.class); - setters.put(key, create(name, method)); - props.add(key); - } - break; - } - - if (repeat) - throw new IllegalArgumentException(String.format( - "A member '%s' in the wrapper for '%s' of type '%s' is already present.", - name, clazz.getName(), target.toString() - )); - } - for (var annotation : method.getAnnotationsByType(ExposeField.class)) { - any = true; - if (!annotation.target().shouldApply(target)) continue; - - checkUnderscore(method); - var name = getName(method, annotation.value()); - var key = getKey(name); - var repeat = false; - - if (props.contains(key) || nonProps.contains(key)) repeat = true; - else { - checkSignature(method, true); - try { - obj.defineProperty(null, key, method.invoke(null), true, true, false); - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new RuntimeException(e); - } - nonProps.add(key); - } - - if (repeat) - throw new IllegalArgumentException(String.format( - "A member '%s' in the wrapper for '%s' of type '%s' is already present.", - name, clazz.getName(), target.toString() - )); - } - } - for (var field : clazz.getDeclaredFields()) { - for (var annotation : field.getAnnotationsByType(ExposeField.class)) { - any = true; - if (!annotation.target().shouldApply(target)) continue; - - checkUnderscore(field); - var name = getName(field, annotation.value()); - var key = getKey(name); - var repeat = false; - - if (props.contains(key) || nonProps.contains(key)) repeat = true; - else { - try { - if (Modifier.isStatic(field.getModifiers())) { - obj.defineProperty(null, key, Values.normalize(null, field.get(null)), true, true, false); - } - else { - obj.defineProperty( - null, key, - new NativeFunction("get " + key, args -> { - try { return field.get(args.self(clazz)); } - catch (IllegalAccessException e) { e.printStackTrace(); return null; } - }), - Modifier.isFinal(field.getModifiers()) ? null : new NativeFunction("get " + key, args -> { - try { field.set(args.self(clazz), args.convert(0, field.getType())); } - catch (IllegalAccessException e) { e.printStackTrace(); } - return null; - }), - true, false - ); - } - nonProps.add(key); - } - catch (IllegalArgumentException | IllegalAccessException e) { } - } - - if (repeat) - throw new IllegalArgumentException(String.format( - "A member '%s' in the wrapper for '%s' of type '%s' is already present.", - name, clazz.getName(), target.toString() - )); - } - } - - for (var key : props) obj.defineProperty(null, key, getters.get(key), setters.get(key), true, false); - - return any; - } - private static boolean apply(ObjectValue obj, ExposeTarget target, Class ...appliers) { - var res = false; - - for (var i = appliers.length - 1; i >= 0; i--) { - res |= apply(obj, target, appliers[i]); - } - - return res; - } - - private static Method getConstructor(Class ...appliers) { - for (var clazz : appliers) { - for (var method : clazz.getDeclaredMethods()) { - if (!method.isAnnotationPresent(ExposeConstructor.class)) continue; - checkSignature(method, true, Arguments.class); - return method; - } - } - - return null; - } - - /** - * Generates a prototype for the given class. - * The returned object will have appropriate wrappers for all instance members. - * All accessors and methods will expect the this argument to be a native wrapper of the given class type. - * @param clazz The class for which a prototype should be generated - */ - public static ObjectValue makeProto(Class ...appliers) { - var res = new ObjectValue(); - res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(appliers)); - if (!apply(res, ExposeTarget.PROTOTYPE, appliers)) return null; - return res; - } - /** - * Generates a constructor for the given class. - * The returned function will have appropriate wrappers for all static members. - * When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible. - * @param clazz The class for which a constructor should be generated - */ - public static FunctionValue makeConstructor(Class ...appliers) { - var constr = getConstructor(appliers); - - FunctionValue res = constr == null ? - new NativeFunction(getName(appliers), args -> { throw EngineException.ofError("This constructor is not invokable."); }) : - create(getName(appliers), constr); - - if (!apply(res, ExposeTarget.CONSTRUCTOR, appliers) && constr == null) return null; - - return res; - } - /** - * Generates a namespace for the given class. - * The returned function will have appropriate wrappers for all static members. - * This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead. - * @param clazz The class for which a constructor should be generated - */ - public static ObjectValue makeNamespace(Class ...appliers) { - var res = new ObjectValue(); - - if (!apply(res, ExposeTarget.NAMESPACE, appliers)) return null; - - return res; - } - - private Class[] getAppliers(Class clazz) { - var res = new ArrayList>(); - - res.add(clazz); - - if (classToProxy.containsKey(clazz)) res.add(classToProxy.get(clazz)); - for (var intf : interfaceToProxy.keySet()) { - if (intf.isAssignableFrom(clazz)) res.add(interfaceToProxy.get(intf)); - } - - return res.toArray(Class[]::new); - } - - private void updateProtoChain(Class clazz, ObjectValue proto, FunctionValue constr) { - var parent = clazz; - - while (true) { - parent = parent.getSuperclass(); - if (parent == null) break; - - var parentProto = getProto(parent); - var parentConstr = getConstr(parent); - - if (parentProto != null && parentConstr != null) { - Values.setPrototype(Environment.empty(), proto, parentProto); - Values.setPrototype(Environment.empty(), constr, parentConstr); - - return; - } - } - } - - private void initType(Class clazz, FunctionValue constr, ObjectValue proto) { - if (constr != null && proto != null || ignore.contains(clazz)) return; - // i vomit - if ( - clazz == Object.class || - clazz == Void.class || - clazz == Number.class || clazz == Double.class || clazz == Float.class || - clazz == Long.class || clazz == Integer.class || clazz == Short.class || - clazz == Character.class || clazz == Byte.class || clazz == Boolean.class || - clazz.isPrimitive() || - clazz.isArray() || - clazz.isAnonymousClass() || - clazz.isEnum() || - clazz.isInterface() || - clazz.isSynthetic() - ) return; - - var appliers = getAppliers(clazz); - - if (constr == null) constr = makeConstructor(appliers); - if (proto == null) proto = makeProto(appliers); - - if (constr == null || proto == null) return; - - proto.defineProperty(null, "constructor", constr, true, false, false); - constr.defineProperty(null, "prototype", proto, true, false, false); - - prototypes.put(clazz, proto); - constructors.put(clazz, constr); - - updateProtoChain(clazz, proto, constr); - } - - public ObjectValue getProto(Class clazz) { - if (proxyToClass.containsKey(clazz)) return getProto(proxyToClass.get(clazz)); - - initType(clazz, constructors.get(clazz), prototypes.get(clazz)); - while (clazz != null) { - var res = prototypes.get(clazz); - if (res != null) return res; - clazz = clazz.getSuperclass(); - } - return null; - } - public ObjectValue getNamespace(Class clazz) { - if (proxyToClass.containsKey(clazz)) return getNamespace(proxyToClass.get(clazz)); - - if (!namespaces.containsKey(clazz)) namespaces.put(clazz, makeNamespace(clazz)); - while (clazz != null) { - var res = namespaces.get(clazz); - if (res != null) return res; - clazz = clazz.getSuperclass(); - } - return null; - } - public FunctionValue getConstr(Class clazz) { - if (proxyToClass.containsKey(clazz)) return getConstr(proxyToClass.get(clazz)); - - initType(clazz, constructors.get(clazz), prototypes.get(clazz)); - while (clazz != null) { - var res = constructors.get(clazz); - if (res != null) return res; - clazz = clazz.getSuperclass(); - } - return null; - } - - public NativeWrapperProvider copy() { - var res = new NativeWrapperProvider(); - - for (var pair : classToProxy.entrySet()) { - res.set(pair.getKey(), pair.getValue()); - } - - return this; - } - - public void set(Class clazz, Class wrapper) { - if (clazz == null) return; - - if (clazz.isInterface()) { - if (wrapper == null || wrapper == clazz) interfaceToProxy.remove(clazz); - else interfaceToProxy.put(clazz, wrapper); - } - else { - if (wrapper == null || wrapper == clazz) classToProxy.remove(clazz); - else classToProxy.put(clazz, wrapper); - } - - var classes = Stream.concat( - Stream.of(clazz), - prototypes.keySet().stream().filter(clazz::isAssignableFrom) - ).toArray(Class[]::new); - - for (var el : classes) { - prototypes.remove(el); - constructors.remove(el); - namespaces.remove(el); - } - - for (var el : classes) { - initType(el, null, null); - } - } - - public NativeWrapperProvider() { } - - public static NativeWrapperProvider get(Environment ext) { - return ext.get(KEY); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java b/src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java deleted file mode 100644 index 7b0e67d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java +++ /dev/null @@ -1,12 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface OnWrapperInit { - -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java b/src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java deleted file mode 100644 index daf078c..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java +++ /dev/null @@ -1,12 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface WrapperName { - String value(); -} diff --git a/src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java b/src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java deleted file mode 100644 index a7523b3..0000000 --- a/src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java +++ /dev/null @@ -1,109 +0,0 @@ -package me.topchetoeu.jscript.utils.mapping; - -import java.util.ArrayList; -import java.util.List; -import java.util.TreeMap; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.json.JSON; - -public class SourceMap { - private final TreeMap origToComp = new TreeMap<>(); - private final TreeMap compToOrig = new TreeMap<>(); - - public Location toCompiled(Location loc) { return convert(loc, origToComp); } - public Location toOriginal(Location loc) { return convert(loc, compToOrig); } - - private void add(long orig, long comp) { - var a = origToComp.remove(orig); - var b = compToOrig.remove(comp); - - if (b != null) origToComp.remove(b); - if (a != null) compToOrig.remove(a); - - origToComp.put(orig, comp); - compToOrig.put(comp, orig); - } - - public SourceMap apply(SourceMap map) { - var res = new SourceMap(); - - for (var el : new ArrayList<>(origToComp.entrySet())) { - var mapped = convert(el.getValue(), map.origToComp); - res.origToComp.put(el.getKey(), mapped); - } - for (var el : new ArrayList<>(compToOrig.entrySet())) { - var mapped = convert(el.getKey(), map.compToOrig); - res.compToOrig.put(mapped, el.getValue()); - res.add(el.getValue(), mapped); - } - - return res; - } - - public SourceMap clone() { - var res = new SourceMap(); - res.origToComp.putAll(this.origToComp); - res.compToOrig.putAll(this.compToOrig); - return res; - } - - public static SourceMap parse(String raw) { - var mapping = VLQ.decodeMapping(raw); - var res = new SourceMap(); - - var compRow = 0l; - var compCol = 0l; - - for (var origRow = 0; origRow < mapping.length; origRow++) { - var origCol = 0; - - for (var rawSeg : mapping[origRow]) { - if (rawSeg.length > 1 && rawSeg[1] != 0) throw new IllegalArgumentException("Source mapping is to more than one files."); - origCol += rawSeg.length > 0 ? rawSeg[0] : 0; - compRow += rawSeg.length > 2 ? rawSeg[2] : 0; - compCol += rawSeg.length > 3 ? rawSeg[3] : 0; - - var compPacked = ((long)compRow << 32) | compCol; - var origPacked = ((long)origRow << 32) | origCol; - - res.add(origPacked, compPacked); - } - } - - return res; - } - public static List getSources(String raw) { - var json = JSON.parse(null, raw).map(); - return json - .list("sourcesContent") - .stream() - .map(v -> v.string()) - .collect(Collectors.toList()); - } - - public static SourceMap chain(SourceMap ...maps) { - if (maps.length == 0) return null; - var res = maps[0]; - - for (var i = 1; i < maps.length; i++) res = res.apply(maps[i]); - - return res; - } - - private static Long convert(long packed, TreeMap map) { - if (map.containsKey(packed)) return map.get(packed); - var key = map.floorKey(packed); - if (key == null) return null; - else return map.get(key); - } - - private static Location convert(Location loc, TreeMap map) { - var packed = ((loc.line() - 1l) << 32) | (loc.start() - 1); - var resPacked = convert(packed, map); - - if (resPacked == null) return null; - else return new Location((int)(resPacked >> 32) + 1, (int)(resPacked & 0xFFFF) + 1, loc.filename()); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java b/src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java deleted file mode 100644 index 5f49aea..0000000 --- a/src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java +++ /dev/null @@ -1,95 +0,0 @@ -package me.topchetoeu.jscript.utils.mapping; - -import java.util.ArrayList; -import java.util.List; - -public class VLQ { - private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - private static long[] toArray(List list) { - var arr = new long[list.size()]; - for (var i = 0; i < list.size(); i++) arr[i] = list.get(i); - return arr; - } - - public static String encode(long... arr) { - var raw = new StringBuilder(); - - for (var data : arr) { - var b = data < 0 ? 1 : 0; - data = Math.abs(data); - b |= (int)(data & 0b1111) << 1; - data >>= 4; - b |= data > 0 ? 0x20 : 0;; - raw.append(ALPHABET.charAt(b)); - - while (data > 0) { - b = (int)(data & 0b11111); - data >>= 5; - b |= data > 0 ? 0x20 : 0; - raw.append(ALPHABET.charAt(b)); - } - } - - return raw.toString(); - } - public static long[] decode(String val) { - if (val.length() == 0) return new long[0]; - - var list = new ArrayList(); - - for (var i = 0; i < val.length();) { - var sign = 1; - var curr = ALPHABET.indexOf(val.charAt(i++)); - var cont = (curr & 0x20) == 0x20; - if ((curr & 1) == 1) sign = -1; - long res = (curr & 0b11110) >> 1; - var n = 4; - - for (; i < val.length() && cont;) { - curr = ALPHABET.indexOf(val.charAt(i++)); - cont = (curr & 0x20) == 0x20; - res |= (curr & 0b11111) << n; - n += 5; - if (!cont) break; - } - - list.add(res * sign); - } - - return toArray(list); - } - - public static String encodeMapping(long[][][] arr) { - var res = new StringBuilder(); - var semicolon = false; - - for (var line : arr) { - var comma = false; - - if (semicolon) res.append(";"); - semicolon = true; - - for (var el : line) { - if (comma) res.append(","); - comma = true; - res.append(encode(el)); - } - } - - return res.toString(); - } - public static long[][][] decodeMapping(String val) { - var lines = new ArrayList(); - - for (var line : val.split(";", -1)) { - var elements = new ArrayList(); - for (var el : line.split(",", -1)) { - elements.add(decode(el)); - } - lines.add(elements.toArray(long[][]::new)); - } - - return lines.toArray(long[][][]::new); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/modules/Module.java b/src/java/me/topchetoeu/jscript/utils/modules/Module.java deleted file mode 100644 index 63b1164..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/Module.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -public abstract class Module { - private Object value; - private boolean loaded; - - public Object value() { return value; } - public boolean loaded() { return loaded; } - - protected abstract Object onLoad(Environment ctx); - - public void load(Environment ctx) { - if (loaded) return; - this.value = onLoad(ctx); - this.loaded = true; - } -} - diff --git a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java deleted file mode 100644 index 99cffdc..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import java.util.HashMap; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.Mode; - -public interface ModuleRepo { - public static final Key KEY = new Key<>(); - public static final Key CWD = new Key<>(); - - public Module getModule(Environment ctx, String cwd, String name); - - public static ModuleRepo ofFilesystem(Filesystem fs) { - var modules = new HashMap(); - - return (env, cwd, name) -> { - name = fs.normalize(cwd, name); - var filename = Filename.parse(name); - var src = fs.open(name, Mode.READ).readToString(); - - if (modules.containsKey(name)) return modules.get(name); - - var moduleEnv = env.child() - .add(CWD, fs.normalize(name, "..")) - .add(GlobalScope.KEY, env.hasNotNull(GlobalScope.KEY) ? env.get(GlobalScope.KEY).child() : new GlobalScope()); - - var mod = new SourceModule(filename, src, moduleEnv); - modules.put(name, mod); - - return mod; - }; - } - - public static String cwd(Environment exts) { - exts.init(CWD, "/"); - return "/"; - } - public static ModuleRepo get(Environment exts) { - return exts.get(KEY); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java deleted file mode 100644 index 2644941..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import java.util.HashMap; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; - -public class RootModuleRepo implements ModuleRepo { - public final HashMap repos = new HashMap<>(); - - @Override - public Module getModule(Environment env, String cwd, String name) { - var i = name.indexOf(":"); - String repoName, modName; - - if (i < 0) { - repoName = "file"; - modName = name; - } - else { - repoName = name.substring(0, i); - modName = name.substring(i + 1); - } - - var repo = repos.get(repoName); - if (repo == null) throw EngineException.ofError("ModuleError", "Couldn't find module repo '" + repoName + "'."); - - return repo.getModule(env, cwd, modName); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java b/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java deleted file mode 100644 index 36aeb0d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.environment.Environment; - -public class SourceModule extends Module { - public final Filename filename; - public final String source; - public final Environment ext; - - @Override - protected Object onLoad(Environment env) { - return Compiler.compile(env, filename, source).call(env); - } - - public SourceModule(Filename filename, String source, Environment ext) { - this.filename = filename; - this.source = source; - this.ext = ext; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java b/src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java deleted file mode 100644 index fc1138c..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -import java.util.LinkedList; - -public interface Matcher { - static class State { - public final int predI, trgI, wildcardI; - public final boolean wildcard; - - @Override - public String toString() { - return String.format("State [pr=%s;trg=%s;wildN=%s;wild=%s]", predI, trgI, wildcardI, wildcard); - } - - public State(int predicateI, int targetI, int wildcardI, boolean wildcard) { - this.predI = predicateI; - this.trgI = targetI; - this.wildcardI = wildcardI; - this.wildcard = wildcard; - } - } - - boolean match(String predicate, String value); - - public static Matcher fileWildcard() { - return (predicate, value) -> execWildcard(predicate, value, '/'); - } - public static Matcher namespaceWildcard() { - return (predicate, value) -> execWildcard(predicate, value, '.'); - } - public static Matcher wildcard() { - return (predicate, value) -> execWildcard(predicate, value, '\0'); - } - - public static boolean execWildcard(String predicate, String target, char delim) { - if (predicate.equals("")) return target.equals(""); - - var queue = new LinkedList(); - queue.push(new State(0, 0, 0, false)); - - while (!queue.isEmpty()) { - var state = queue.poll(); - var predEnd = state.predI >= predicate.length(); - - if (state.trgI >= target.length()) return predEnd; - var predC = predEnd ? 0 : predicate.charAt(state.predI); - var trgC = target.charAt(state.trgI); - - if (state.wildcard) { - if (state.wildcardI == 2 || trgC != delim) { - queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true)); - } - queue.add(new State(state.predI, state.trgI, 0, false)); - } - else if (predC == '*') { - queue.add(new State(state.predI + 1, state.trgI, state.wildcardI + 1, false)); - } - else if (state.wildcardI > 0) { - if (state.wildcardI > 2) throw new IllegalArgumentException("Too many sequential stars."); - queue.add(new State(state.predI, state.trgI, state.wildcardI, true)); - } - else if (!predEnd && (predC == '?' || predC == trgC)) { - queue.add(new State(state.predI + 1, state.trgI + 1, 0, false)); - } - } - - return false; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/Permission.java b/src/java/me/topchetoeu/jscript/utils/permissions/Permission.java deleted file mode 100644 index f120500..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/Permission.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - - -public class Permission { - public final String namespace; - public final Matcher matcher; - - @Override public String toString() { - return namespace; - } - - public Permission(String namespace, Matcher matcher) { - this.namespace = namespace; - this.matcher = matcher; - } - public Permission(String raw) { - this(raw, null); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java deleted file mode 100644 index 5bedf8d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -public class PermissionPredicate { - public final String namespace; - public final String value; - public final boolean denies; - - public boolean match(Permission permission, String value) { - if (!match(permission)) return false; - if (this.value == null || value == null) return true; - if (permission.matcher == null) return true; - else return permission.matcher.match(this.value, value); - } - public boolean match(Permission permission) { - return Matcher.namespaceWildcard().match(namespace, permission.namespace); - } - - @Override - public String toString() { - if (value != null) return namespace + ":" + value; - else return namespace; - } - - public PermissionPredicate(String raw) { - raw = raw.trim(); - - if (raw.startsWith("!")) { - denies = true; - raw = raw.substring(1).trim(); - } - else denies = false; - - var i = raw.indexOf(':'); - - if (i > 0) { - value = raw.substring(i + 1); - namespace = raw.substring(0, i); - } - else { - value = null; - namespace = raw; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java deleted file mode 100644 index 72468cc..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; - -public class PermissionsManager implements PermissionsProvider { - public final ArrayList predicates = new ArrayList<>(); - - public PermissionsProvider add(PermissionPredicate perm) { - predicates.add(perm); - return this; - } - public PermissionsProvider add(String perm) { - predicates.add(new PermissionPredicate(perm)); - return this; - } - - @Override public boolean hasPermission(Permission perm, String value) { - for (var el : predicates) { - if (el.match(perm, value)) { - if (el.denies) return false; - else return true; - } - } - - return false; - } - @Override public boolean hasPermission(Permission perm) { - for (var el : predicates) { - if (el.match(perm)) { - if (el.denies) return false; - else return true; - } - } - - return false; - } - - public PermissionsProvider addFromStream(InputStream stream) throws IOException { - var reader = new BufferedReader(new InputStreamReader(stream)); - String line; - - while ((line = reader.readLine()) != null) { - var i = line.indexOf('#'); - if (i >= 0) line = line.substring(0, i); - - line = line.trim(); - - if (line.isEmpty()) continue; - - add(line); - } - - return this; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java deleted file mode 100644 index 19f474b..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; - -public interface PermissionsProvider { - public static final Key KEY = new Key<>(); - public static final PermissionsProvider ALL_PERMS = (perm, value) -> true; - - boolean hasPermission(Permission perm, String value); - - default boolean hasPermission(Permission perm) { - return hasPermission(perm, null); - } - - default boolean hasPermission(String perm, String value, Matcher matcher) { - return hasPermission(new Permission(perm, matcher), value); - } - default boolean hasPermission(String perm, Matcher matcher) { - return hasPermission(new Permission(perm, matcher)); - } - - public static PermissionsProvider get(Environment exts) { - return (perm, value) -> { - if (exts.hasNotNull(KEY)) return exts.get(KEY).hasPermission(perm); - else return true; - }; - } -} \ No newline at end of file -- 2.45.2 From ef0fc5a61d2128273b6c675a528992c1aee171dc Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 25 Aug 2024 21:53:29 +0300 Subject: [PATCH 03/48] refactor: distribute parse functions in node classes --- .../topchetoeu/jscript/common/json/JSON.java | 5 +- .../compilation/AssignableStatement.java | 46 + .../compilation/CompoundStatement.java | 48 + .../compilation/VariableDeclareStatement.java | 51 + .../compilation/control/BreakStatement.java | 29 +- .../control/ContinueStatement.java | 29 +- .../compilation/control/DebugStatement.java | 21 +- .../compilation/control/DeleteStatement.java | 28 + .../compilation/control/DoWhileStatement.java | 38 + .../compilation/control/ForInStatement.java | 69 +- .../compilation/control/ForOfStatement.java | 48 + .../compilation/control/ForStatement.java | 68 + .../compilation/control/IfStatement.java | 54 + .../compilation/control/ReturnStatement.java | 30 + .../compilation/control/SwitchStatement.java | 95 +- .../compilation/control/ThrowStatement.java | 25 + .../compilation/control/TryStatement.java | 54 +- .../compilation/control/WhileStatement.java | 38 + .../jscript/compilation/parsing/Parsing.java | 1159 +---------------- .../compilation/values/ArrayStatement.java | 46 + .../compilation/values/CallStatement.java | 62 +- .../compilation/values/ChangeStatement.java | 46 +- .../compilation/values/ConstantStatement.java | 27 + .../compilation/values/DiscardStatement.java | 18 + .../compilation/values/FunctionStatement.java | 52 + .../compilation/values/IndexStatement.java | 37 + .../compilation/values/ObjectStatement.java | 109 ++ .../values/OperationStatement.java | 83 +- .../compilation/values/RegexStatement.java | 21 + .../compilation/values/TypeofStatement.java | 18 + .../compilation/values/VariableStatement.java | 22 + .../me/topchetoeu/jscript/runtime/Frame.java | 5 +- .../jscript/runtime/SimpleRepl.java | 3 +- 33 files changed, 1346 insertions(+), 1138 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index b81062b..34704d6 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -8,6 +8,7 @@ import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Operator; import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.values.ConstantStatement; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class JSON { @@ -15,7 +16,7 @@ public class JSON { return Parsing.parseIdentifier(tokens, i); } public static ParseRes parseString(Filename filename, List tokens, int i) { - var res = Parsing.parseString(filename, tokens, i); + var res = ConstantStatement.parseString(filename, tokens, i); if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n); else return res.transform(); } @@ -23,7 +24,7 @@ public class JSON { var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT); if (minus) i++; - var res = Parsing.parseNumber(filename, tokens, i); + var res = ConstantStatement.parseNumber(filename, tokens, i); if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0)); else return res.transform(); } diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java index 9602c2e..c4c17d2 100644 --- a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java @@ -1,7 +1,15 @@ package me.topchetoeu.jscript.compilation; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; +import me.topchetoeu.jscript.common.ParseRes.State; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public abstract class AssignableStatement extends Statement { public abstract Statement toAssign(Statement val, Operation operation); @@ -9,4 +17,42 @@ public abstract class AssignableStatement extends Statement { protected AssignableStatement(Location loc) { super(loc); } + + public static ParseRes parse(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (precedence > 2) return ParseRes.failed(); + + var opRes = Parsing.parseOperator(tokens, i + n++); + if (opRes.state != State.SUCCESS) return ParseRes.failed(); + + var op = opRes.result; + if (!op.isAssign()) return ParseRes.failed(); + + if (!(prev instanceof AssignableStatement)) { + return ParseRes.error(loc, "Invalid expression on left hand side of assign operator."); + } + + var res = Parsing.parseValue(filename, tokens, i + n, 2); + if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res); + n += res.n; + + Operation operation = null; + + if (op == Operator.ASSIGN_ADD) operation = Operation.ADD; + if (op == Operator.ASSIGN_SUBTRACT) operation = Operation.SUBTRACT; + if (op == Operator.ASSIGN_MULTIPLY) operation = Operation.MULTIPLY; + if (op == Operator.ASSIGN_DIVIDE) operation = Operation.DIVIDE; + if (op == Operator.ASSIGN_MODULO) operation = Operation.MODULO; + if (op == Operator.ASSIGN_OR) operation = Operation.OR; + if (op == Operator.ASSIGN_XOR) operation = Operation.XOR; + if (op == Operator.ASSIGN_AND) operation = Operation.AND; + if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Operation.SHIFT_LEFT; + if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Operation.SHIFT_RIGHT; + if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Operation.USHIFT_RIGHT; + + return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n); + } + } diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java index 06eed66..62fa7a9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java @@ -1,11 +1,17 @@ package me.topchetoeu.jscript.compilation; +import java.util.ArrayList; import java.util.List; import java.util.Vector; +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class CompoundStatement extends Statement { @@ -61,4 +67,46 @@ public class CompoundStatement extends Statement { this.separateFuncs = separateFuncs; this.statements = statements; } + + public static ParseRes parseComma(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + + if (precedence > 1) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.COMMA)) return ParseRes.failed(); + + var res = Parsing.parseValue(filename, tokens, i + n, 2); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res); + n += res.n; + + return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n); + } + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); + + var statements = new ArrayList(); + + while (true) { + if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n++; + break; + } + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { + n++; + continue; + } + + var res = Parsing.parseStatement(filename, tokens, i + n); + if (!res.isSuccess()) { + return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a statement.", res); + } + n += res.n; + + statements.add(res.result); + } + + return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(Parsing.getLoc(filename, tokens, i + n - 1)), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java index 1f4b6a4..25d16f1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java @@ -1,10 +1,16 @@ package me.topchetoeu.jscript.compilation; +import java.util.ArrayList; import java.util.List; +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class VariableDeclareStatement extends Statement { @@ -49,4 +55,49 @@ public class VariableDeclareStatement extends Statement { super(loc); this.values = values; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "var")) return ParseRes.failed(); + + var res = new ArrayList(); + + if (Parsing.isStatementEnd(tokens, i + n)) { + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), 2); + else return ParseRes.res(new VariableDeclareStatement(loc, res), 1); + } + + while (true) { + var nameLoc = Parsing.getLoc(filename, tokens, i + n); + var nameRes = Parsing.parseIdentifier(tokens, i + n++); + if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name."); + + if (!Parsing.checkVarName(nameRes.result)) { + return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", nameRes.result)); + } + + Statement val = null; + + if (Parsing.isOperator(tokens, i + n, Operator.ASSIGN)) { + n++; + var valRes = Parsing.parseValue(filename, tokens, i + n, 2); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes); + n += valRes.n; + val = valRes.result; + } + + res.add(new Pair(nameRes.result, val, nameLoc)); + + if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + n++; + continue; + } + else if (Parsing.isStatementEnd(tokens, i + n)) { + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), n + 1); + else return ParseRes.res(new VariableDeclareStatement(loc, res), n); + } + else return ParseRes.error(loc, "Expected a comma or end of statement."); + } + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java index eafae48..4c39bc7 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java @@ -1,15 +1,21 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class BreakStatement extends Statement { public final String label; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.nop("break", label)); if (pollute) target.add(Instruction.pushUndefined()); } @@ -18,4 +24,23 @@ public class BreakStatement extends Statement { super(loc); this.label = label; } + + public static ParseRes parseBreak(Filename filename, List tokens, int i) { + if (!Parsing.isIdentifier(tokens, i, "break")) return ParseRes.failed(); + + if (Parsing.isStatementEnd(tokens, i + 1)) { + if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), null), 2); + else return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), null), 1); + } + + var labelRes = Parsing.parseIdentifier(tokens, i + 1); + if (labelRes.isFailed()) return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a label name or an end of statement."); + var label = labelRes.result; + + if (Parsing.isStatementEnd(tokens, i + 2)) { + if (Parsing.isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), label), 3); + else return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), label), 2); + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement."); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java index dfc3abd..2513892 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java @@ -1,15 +1,21 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ContinueStatement extends Statement { public final String label; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.nop("cont", label)); if (pollute) target.add(Instruction.pushUndefined()); } @@ -18,4 +24,23 @@ public class ContinueStatement extends Statement { super(loc); this.label = label; } + + public static ParseRes parseContinue(Filename filename, List tokens, int i) { + if (!Parsing.isIdentifier(tokens, i, "continue")) return ParseRes.failed(); + + if (Parsing.isStatementEnd(tokens, i + 1)) { + if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), null), 2); + else return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), null), 1); + } + + var labelRes = Parsing.parseIdentifier(tokens, i + 1); + if (labelRes.isFailed()) return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a label name or an end of statement."); + var label = labelRes.result; + + if (Parsing.isStatementEnd(tokens, i + 2)) { + if (Parsing.isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), label), 3); + else return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), label), 2); + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement."); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java index 20d23ef..bf1c352 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java @@ -1,13 +1,19 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class DebugStatement extends Statement { - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.debug()); if (pollute) target.add(Instruction.pushUndefined()); } @@ -15,4 +21,15 @@ public class DebugStatement extends Statement { public DebugStatement(Location loc) { super(loc); } + + public static ParseRes parse(Filename filename, List tokens, int i) { + if (!Parsing.isIdentifier(tokens, i, "debugger")) return ParseRes.failed(); + + if (Parsing.isStatementEnd(tokens, i + 1)) { + if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new DebugStatement(Parsing.getLoc(filename, tokens, i)), 2); + else return ParseRes.res(new DebugStatement(Parsing.getLoc(filename, tokens, i)), 1); + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement."); + } + } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java index 9355182..cdc35db 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java @@ -1,9 +1,18 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.values.ConstantStatement; +import me.topchetoeu.jscript.compilation.values.IndexStatement; +import me.topchetoeu.jscript.compilation.values.VariableStatement; public class DeleteStatement extends Statement { public final Statement key; @@ -18,6 +27,25 @@ public class DeleteStatement extends Statement { if (pollute) target.add(Instruction.pushValue(true)); } + public static ParseRes parseDelete(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "delete")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 15); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'delete'.", valRes); + n += valRes.n; + + if (valRes.result instanceof IndexStatement) { + var index = (IndexStatement)valRes.result; + return ParseRes.res(new DeleteStatement(loc, index.index, index.object), n); + } + else if (valRes.result instanceof VariableStatement) { + return ParseRes.error(loc, "A variable may not be deleted."); + } + else return ParseRes.res(new ConstantStatement(loc, true), n); + } + public DeleteStatement(Location loc, Statement key, Statement value) { super(loc); this.key = key; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java index 2417902..456b487 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java @@ -1,10 +1,17 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class DoWhileStatement extends Statement { public final Statement condition, body; @@ -33,4 +40,35 @@ public class DoWhileStatement extends Statement { this.condition = condition; this.body = body; } + + public static ParseRes parseDoWhile(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var labelRes = WhileStatement.parseLabel(tokens, i + n); + n += labelRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "do")) return ParseRes.failed(); + var bodyRes = Parsing.parseStatement(filename, tokens, i + n); + if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a do-while body.", bodyRes); + n += bodyRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "while")) return ParseRes.error(loc, "Expected 'while' keyword."); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'."); + + var condRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes); + n += condRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition."); + + var res = ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); + + if (Parsing.isStatementEnd(tokens, i + n)) { + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); + else return res; + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a semicolon."); + } + } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java index 216509a..d4d162f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java @@ -1,11 +1,18 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ForInStatement extends Statement { public final String varName; @@ -14,14 +21,12 @@ public class ForInStatement extends Statement { public final String label; public final Location varLocation; - @Override - public void declare(CompileResult target) { + @Override public void declare(CompileResult target) { body.declare(target); if (isDeclaration) target.scope.define(varName); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { var key = target.scope.getKey(varName); if (key instanceof String) target.add(Instruction.makeVar((String)key)); @@ -66,4 +71,60 @@ public class ForInStatement extends Statement { this.object = object; this.body = body; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var labelRes = WhileStatement.parseLabel(tokens, i + n); + var isDecl = false; + n += labelRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); + + if (Parsing.isIdentifier(tokens, i + n, "var")) { + isDecl = true; + n++; + } + + var nameRes = Parsing.parseIdentifier(tokens, i + n); + if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop."); + var nameLoc = Parsing.getLoc(filename, tokens, i + n); + n += nameRes.n; + + Statement varVal = null; + + if (Parsing.isOperator(tokens, i + n, Operator.ASSIGN)) { + n++; + + var valRes = Parsing.parseValue(filename, tokens, i + n, 2); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes); + n += nameRes.n; + + varVal = valRes.result; + } + + if (!Parsing.isIdentifier(tokens, i + n++, "in")) { + if (varVal == null) { + if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); + else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); + } + return ParseRes.error(loc, "Expected 'in' keyword after variable declaration."); + } + + var objRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes); + n += objRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); + + + var bodyRes = Parsing.parseStatement(filename, tokens, i + n); + if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes); + n += bodyRes.n; + + return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n); + } + } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java index fba5fc7..a304252 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java @@ -1,10 +1,17 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ForOfStatement extends Statement { public final String varName; @@ -70,4 +77,45 @@ public class ForOfStatement extends Statement { this.iterable = object; this.body = body; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var labelRes = WhileStatement.parseLabel(tokens, i + n); + var isDecl = false; + n += labelRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); + + if (Parsing.isIdentifier(tokens, i + n, "var")) { + isDecl = true; + n++; + } + + var nameRes = Parsing.parseIdentifier(tokens, i + n); + if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop."); + var nameLoc = Parsing.getLoc(filename, tokens, i + n); + n += nameRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "of")) { + if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); + else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); + else return ParseRes.error(loc, "Expected 'of' keyword after variable declaration."); + } + + var objRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes); + n += objRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); + + + var bodyRes = Parsing.parseStatement(filename, tokens, i + n); + if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes); + n += bodyRes.n; + + return ParseRes.res(new ForOfStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, objRes.result, bodyRes.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java index 9a59613..e97a073 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java @@ -1,10 +1,20 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.VariableDeclareStatement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.values.ConstantStatement; public class ForStatement extends Statement { public final Statement declaration, assignment, condition, body; @@ -42,4 +52,62 @@ public class ForStatement extends Statement { this.assignment = assignment; this.body = body; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var labelRes = WhileStatement.parseLabel(tokens, i + n); + n += labelRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); + + Statement decl, cond, inc; + + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { + n++; + decl = new CompoundStatement(loc, false); + } + else { + var declRes = ParseRes.any( + VariableDeclareStatement.parse(filename, tokens, i + n), + Parsing.parseValueStatement(filename, tokens, i + n) + ); + if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes); + n += declRes.n; + decl = declRes.result; + } + + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { + n++; + cond = new ConstantStatement(loc, 1); + } + else { + var condRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", condRes); + n += condRes.n; + if (!Parsing.isOperator(tokens, i + n++, Operator.SEMICOLON)) return ParseRes.error(loc, "Expected a semicolon.", condRes); + cond = condRes.result; + } + + if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { + n++; + inc = new CompoundStatement(loc, false); + } + else { + var incRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!incRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", incRes); + n += incRes.n; + inc = incRes.result; + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); + } + + + var res = Parsing.parseStatement(filename, tokens, i + n); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected a for body.", res); + n += res.n; + + return ParseRes.res(new ForStatement(loc, labelRes.result, decl, cond, inc, res.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java index ba37671..8dd2034 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java @@ -1,10 +1,17 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class IfStatement extends Statement { public final Statement condition, body, elseBody; @@ -45,4 +52,51 @@ public class IfStatement extends Statement { this.body = body; this.elseBody = elseBody; } + + public static ParseRes parseTernary(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + + if (precedence > 2) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.QUESTION)) return ParseRes.failed(); + + var a = Parsing.parseValue(filename, tokens, i + n, 2); + if (!a.isSuccess()) return ParseRes.error(loc, "Expected a value after the ternary operator.", a); + n += a.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed(); + + var b = Parsing.parseValue(filename, tokens, i + n, 2); + if (!b.isSuccess()) return ParseRes.error(loc, "Expected a second value after the ternary operator.", b); + n += b.n; + + return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n); + } + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (!Parsing.isIdentifier(tokens, i + n++, "if")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'if'."); + + var condRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes); + n += condRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition."); + + var res = Parsing.parseStatement(filename, tokens, i + n); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res); + n += res.n; + + if (!Parsing.isIdentifier(tokens, i + n, "else")) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); + n++; + + var elseRes = Parsing.parseStatement(filename, tokens, i + n); + if (!elseRes.isSuccess()) return ParseRes.error(loc, "Expected an else body.", elseRes); + n += elseRes.n; + + return ParseRes.res(new IfStatement(loc, condRes.result, res.result, elseRes.result), n); + } + } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java index 4e915fd..a9e0f5c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java @@ -1,9 +1,16 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ReturnStatement extends Statement { public final Statement value; @@ -19,4 +26,27 @@ public class ReturnStatement extends Statement { super(loc); this.value = value; } + + public static ParseRes parseReturn(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "return")) return ParseRes.failed(); + + if (Parsing.isStatementEnd(tokens, i + n)) { + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new ReturnStatement(loc, null), 2); + else return ParseRes.res(new ReturnStatement(loc, null), 1); + } + + var valRes = Parsing.parseValue(filename, tokens, i + n, 0); + n += valRes.n; + if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes); + + var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n); + + if (Parsing.isStatementEnd(tokens, i + n)) { + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); + else return res; + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.", valRes); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java index bf4988c..1883bd8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java @@ -1,14 +1,22 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class SwitchStatement extends Statement { public static class SwitchCase { @@ -26,13 +34,11 @@ public class SwitchStatement extends Statement { public final Statement[] body; public final int defaultI; - @Override - public void declare(CompileResult target) { + @Override public void declare(CompileResult target) { for (var stm : body) stm.declare(target); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { var caseToStatement = new HashMap(); var statementToIndex = new HashMap(); @@ -80,4 +86,85 @@ public class SwitchStatement extends Statement { this.cases = cases; this.body = body; } + + private static ParseRes parseSwitchCase(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (!Parsing.isIdentifier(tokens, i + n++, "case")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'case'.", valRes); + n += valRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected colons after 'case' value."); + + return ParseRes.res(valRes.result, n); + } + private static ParseRes parseDefaultCase(List tokens, int i) { + if (!Parsing.isIdentifier(tokens, i, "default")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + 1, Operator.COLON)) return ParseRes.error(Parsing.getLoc(null, tokens, i), "Expected colons after 'default'."); + + return ParseRes.res(null, 2); + } + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (!Parsing.isIdentifier(tokens, i + n++, "switch")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'switch'."); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a switch value.", valRes); + n += valRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after switch value."); + if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.error(loc, "Expected an opening brace after switch value."); + + var statements = new ArrayList(); + var cases = new ArrayList(); + var defaultI = -1; + + while (true) { + if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n++; + break; + } + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { + n++; + continue; + } + + var defaultRes = SwitchStatement.parseDefaultCase(tokens, i + n); + var caseRes = SwitchStatement.parseSwitchCase(filename, tokens, i + n); + + if (defaultRes.isSuccess()) { + defaultI = statements.size(); + n += defaultRes.n; + } + else if (caseRes.isSuccess()) { + cases.add(new SwitchCase(caseRes.result, statements.size())); + n += caseRes.n; + } + else if (defaultRes.isError()) return defaultRes.transform(); + else if (caseRes.isError()) return defaultRes.transform(); + else { + var res = ParseRes.any( + Parsing.parseStatement(filename, tokens, i + n), + CompoundStatement.parse(filename, tokens, i + n) + ); + if (!res.isSuccess()) { + return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a statement.", res); + } + n += res.n; + statements.add(res.result); + } + } + + return ParseRes.res(new SwitchStatement( + loc, valRes.result, defaultI, + cases.toArray(SwitchCase[]::new), + statements.toArray(Statement[]::new) + ), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java index 156f7ae..3e19c1f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java @@ -1,9 +1,16 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ThrowStatement extends Statement { public final Statement value; @@ -18,4 +25,22 @@ public class ThrowStatement extends Statement { super(loc); this.value = value; } + + public static ParseRes parseThrow(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "throw")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 0); + n += valRes.n; + if (valRes.isError()) return ParseRes.error(loc, "Expected a throw value.", valRes); + + var res = ParseRes.res(new ThrowStatement(loc, valRes.result), n); + + if (Parsing.isStatementEnd(tokens, i + n)) { + if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); + else return res; + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.", valRes); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java index 41ce48f..5854df2 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java @@ -1,10 +1,17 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class TryStatement extends Statement { public final Statement tryBody; @@ -12,15 +19,13 @@ public class TryStatement extends Statement { public final Statement finallyBody; public final String name; - @Override - public void declare(CompileResult target) { + @Override public void declare(CompileResult target) { tryBody.declare(target); if (catchBody != null) catchBody.declare(target); if (finallyBody != null) finallyBody.declare(target); } - @Override - public void compile(CompileResult target, boolean pollute, BreakpointType bpt) { + @Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) { int replace = target.temp(); int start = replace + 1, catchStart = -1, finallyStart = -1; @@ -55,4 +60,45 @@ public class TryStatement extends Statement { this.finallyBody = finallyBody; this.name = name; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (!Parsing.isIdentifier(tokens, i + n++, "try")) return ParseRes.failed(); + + var res = Parsing.parseStatement(filename, tokens, i + n); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res); + n += res.n; + + String name = null; + Statement catchBody = null, finallyBody = null; + + + if (Parsing.isIdentifier(tokens, i + n, "catch")) { + n++; + if (Parsing.isOperator(tokens, i + n, Operator.PAREN_OPEN)) { + n++; + var nameRes = Parsing.parseIdentifier(tokens, i + n++); + if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a catch variable name."); + name = nameRes.result; + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after catch variable name."); + } + + var catchRes = Parsing.parseStatement(filename, tokens, i + n); + if (!catchRes.isSuccess()) return ParseRes.error(loc, "Expected a catch body.", catchRes); + n += catchRes.n; + catchBody = catchRes.result; + } + + if (Parsing.isIdentifier(tokens, i + n, "finally")) { + n++; + var finallyRes = Parsing.parseStatement(filename, tokens, i + n); + if (!finallyRes.isSuccess()) return ParseRes.error(loc, "Expected a finally body.", finallyRes); + n += finallyRes.n; + finallyBody = finallyRes.result; + } + + return ParseRes.res(new TryStatement(loc, res.result, catchBody, finallyBody, name), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java index 459e4ef..ed9cc06 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java @@ -1,11 +1,18 @@ package me.topchetoeu.jscript.compilation.control; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class WhileStatement extends Statement { public final Statement condition, body; @@ -31,6 +38,14 @@ public class WhileStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } + public static ParseRes parseLabel(List tokens, int i) { + int n = 0; + + var nameRes = Parsing.parseIdentifier(tokens, i + n++); + if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed(); + + return ParseRes.res(nameRes.result, n); + } public WhileStatement(Location loc, String label, Statement condition, Statement body) { super(loc); this.label = label; @@ -49,4 +64,27 @@ public class WhileStatement extends Statement { } } } + + public static ParseRes parseWhile(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var labelRes = WhileStatement.parseLabel(tokens, i + n); + n += labelRes.n; + + if (!Parsing.isIdentifier(tokens, i + n++, "while")) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'."); + + var condRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes); + n += condRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition."); + + var res = Parsing.parseStatement(filename, tokens, i + n); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected a while body.", res); + n += res.n; + + return ParseRes.res(new WhileStatement(loc, labelRes.result, condRes.result, res.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java index 84a467f..321ec1a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java @@ -1,33 +1,30 @@ package me.topchetoeu.jscript.compilation.parsing; +import java.lang.invoke.CallSite; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.ParseRes; -import me.topchetoeu.jscript.common.ParseRes.State; import me.topchetoeu.jscript.compilation.*; -import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.control.*; -import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; import me.topchetoeu.jscript.compilation.values.*; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; // TODO: this has to be rewritten +// @SourceFile public class Parsing { public static interface Parser { ParseRes parse(Filename filename, List tokens, int i); } - private static class ObjProp { + public static class ObjProp { public final String name; public final String access; public final FunctionStatement func; @@ -139,6 +136,7 @@ public class Parsing { // This method is so long because we're tokenizing the string using an iterative approach // instead of a recursive descent parser. This is mainly done for performance reasons. + // preformance reasons my ass private static ArrayList splitTokens(Filename filename, String raw) { var tokens = new ArrayList(); var currToken = new StringBuilder(64); @@ -402,7 +400,7 @@ public class Parsing { return -1; } - private static boolean inBounds(List tokens, int i) { + public static boolean inBounds(List tokens, int i) { return i >= 0 && i < tokens.size(); } @@ -658,80 +656,6 @@ public class Parsing { return !reserved.contains(name); } - public static ParseRes parseString(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - if (inBounds(tokens, i)) { - if (tokens.get(i).isString()) { - return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1); - } - else return ParseRes.failed(); - } - else return ParseRes.failed(); - } - public static ParseRes parseNumber(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - if (inBounds(tokens, i)) { - if (tokens.get(i).isNumber()) { - return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1); - } - else return ParseRes.failed(); - } - else return ParseRes.failed(); - } - public static ParseRes parseRegex(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - if (inBounds(tokens, i)) { - if (tokens.get(i).isRegex()) { - var val = tokens.get(i).regex(); - var index = val.lastIndexOf('/'); - var first = val.substring(1, index); - var second = val.substring(index + 1); - return ParseRes.res(new RegexStatement(loc, first, second), 1); - } - else return ParseRes.failed(); - } - return ParseRes.failed(); - } - - public static ParseRes parseArray(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); - - var values = new ArrayList(); - - // Java allows labels, so labels were used - loop: while (true) { - if (isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { - n++; - break; - } - - while (isOperator(tokens, i + n, Operator.COMMA)) { - n++; - values.add(null); - if (isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { - n++; - break loop; - } - } - - var res = parseValue(filename, tokens, i + n, 2); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected an array element.", res); - else n += res.n; - - values.add(res.result); - - if (isOperator(tokens, i + n, Operator.COMMA)) n++; - else if (isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { - n++; - break; - } - } - - return ParseRes.res(new ArrayStatement(loc, values.toArray(Statement[]::new)), n); - } - public static ParseRes> parseParamList(Filename filename, List tokens, int i) { var loc = getLoc(filename, tokens, i); int n = 0; @@ -764,244 +688,6 @@ public class Parsing { return ParseRes.res(args, n); } - public static ParseRes parsePropName(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - - if (inBounds(tokens, i)) { - var token = tokens.get(i); - - if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1); - else return ParseRes.error(loc, "Expected identifier, string or number literal."); - } - else return ParseRes.failed(); - } - public static ParseRes parseObjectProp(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var accessRes = parseIdentifier(tokens, i + n++); - if (!accessRes.isSuccess()) return ParseRes.failed(); - var access = accessRes.result; - if (!access.equals("get") && !access.equals("set")) return ParseRes.failed(); - - var nameRes = parsePropName(filename, tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a property name after '" + access + "'."); - var name = nameRes.result; - n += nameRes.n; - - var argsRes = parseParamList(filename, tokens, i + n); - if (!argsRes.isSuccess()) return ParseRes.error(loc, "Expected an argument list.", argsRes); - n += argsRes.n; - - var res = parseCompound(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res); - n += res.n; - - var end = getLoc(filename, tokens, i + n - 1); - - return ParseRes.res(new ObjProp( - name, access, - new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result) - ), n); - } - public static ParseRes parseObject(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); - - var values = new LinkedHashMap(); - var getters = new LinkedHashMap(); - var setters = new LinkedHashMap(); - - if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { - n++; - return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); - } - - while (true) { - var propRes = parseObjectProp(filename, tokens, i + n); - - if (propRes.isSuccess()) { - n += propRes.n; - if (propRes.result.access.equals("set")) { - setters.put(propRes.result.name, propRes.result.func); - } - else { - getters.put(propRes.result.name, propRes.result.func); - } - } - else { - var nameRes = parsePropName(filename, tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a field name.", propRes); - n += nameRes.n; - - if (!isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected a colon."); - - var valRes = parseValue(filename, tokens, i + n, 2); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in array list.", valRes); - n += valRes.n; - - values.put(nameRes.result, valRes.result); - } - - if (isOperator(tokens, i + n, Operator.COMMA)) { - n++; - if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { - n++; - break; - } - continue; - } - else if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { - n++; - break; - } - else ParseRes.error(loc, "Expected a comma or a closing brace."); - } - - return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); - } - public static ParseRes parseNew(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - var n = 0; - if (!isIdentifier(tokens, i + n++, "new")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 18); - n += valRes.n; - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'new' keyword.", valRes); - var callRes = parseCall(filename, tokens, i + n, valRes.result, 0); - n += callRes.n; - if (callRes.isError()) return callRes.transform(); - else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); - var call = (CallStatement)callRes.result; - - return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n); - } - public static ParseRes parseTypeof(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - var n = 0; - if (!isIdentifier(tokens, i + n++, "typeof")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 15); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'typeof' keyword.", valRes); - n += valRes.n; - - return ParseRes.res(new TypeofStatement(loc, valRes.result), n); - } - public static ParseRes parseVoid(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - var n = 0; - if (!isIdentifier(tokens, i + n++, "void")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 14); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes); - n += valRes.n; - - return ParseRes.res(new DiscardStatement(loc, valRes.result), n); - } - public static ParseRes parseDelete(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isIdentifier(tokens, i + n++, "delete")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 15); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'delete'.", valRes); - n += valRes.n; - - if (valRes.result instanceof IndexStatement) { - var index = (IndexStatement)valRes.result; - return ParseRes.res(new DeleteStatement(loc, index.index, index.object), n); - } - else if (valRes.result instanceof VariableStatement) { - return ParseRes.error(loc, "A variable may not be deleted."); - } - else { - return ParseRes.res(new ConstantStatement(loc, true), n); - } - } - - public static ParseRes parseFunction(Filename filename, List tokens, int i, boolean statement) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (!isIdentifier(tokens, i + n++, "function")) return ParseRes.failed(); - - var nameRes = parseIdentifier(tokens, i + n); - if (!nameRes.isSuccess() && statement) return ParseRes.error(loc, "A statement function requires a name, one is not present."); - var name = nameRes.result; - n += nameRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a parameter list."); - - var args = new ArrayList(); - - if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - } - else { - while (true) { - var argRes = parseIdentifier(tokens, i + n); - if (argRes.isSuccess()) { - args.add(argRes.result); - n++; - if (isOperator(tokens, i + n, Operator.COMMA)) { - n++; - } - if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - break; - } - } - else return ParseRes.error(loc, "Expected an argument, comma or a closing brace."); - } - } - - var res = parseCompound(filename, tokens, i + n); - n += res.n; - var end = getLoc(filename, tokens, i + n - 1); - - if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n); - else return ParseRes.error(loc, "Expected a compound statement for function.", res); - } - - public static ParseRes parseUnary(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var opState = parseOperator(tokens, i + n++); - if (!opState.isSuccess()) return ParseRes.failed(); - var op = opState.result; - - Operation operation = null; - - if (op == Operator.ADD) operation = Operation.POS; - else if (op == Operator.SUBTRACT) operation = Operation.NEG; - else if (op == Operator.INVERSE) operation = Operation.INVERSE; - else if (op == Operator.NOT) operation = Operation.NOT; - else return ParseRes.failed(); - - var res = parseValue(filename, tokens, n + i, 14); - - if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); - else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res); - } - public static ParseRes parsePrefixChange(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var opState = parseOperator(tokens, i + n++); - if (!opState.isSuccess()) return ParseRes.failed(); - - int change = 0; - - if (opState.result == Operator.INCREASE) change = 1; - else if (opState.result == Operator.DECREASE) change = -1; - else return ParseRes.failed(); - - var res = parseValue(filename, tokens, i + n, 15); - if (!(res.result instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value after prefix operator."); - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, change, false), n + res.n); - } public static ParseRes parseParens(Filename filename, List tokens, int i) { int n = 0; if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed(); @@ -1018,269 +704,44 @@ public class Parsing { var res = new ArrayList>(); if (!statement) { - res.add(parseObject(filename, tokens, i)); - res.add(parseFunction(filename, tokens, i, false)); + res.add(ObjectStatement.parse(filename, tokens, i)); + res.add(FunctionStatement.parseFunction(filename, tokens, i, false)); } res.addAll(List.of( - parseVariable(filename, tokens, i), + VariableStatement.parseVariable(filename, tokens, i), parseLiteral(filename, tokens, i), - parseString(filename, tokens, i), - parseRegex(filename, tokens, i), - parseNumber(filename, tokens, i), - parseUnary(filename, tokens, i), - parseArray(filename, tokens, i), - parsePrefixChange(filename, tokens, i), + ConstantStatement.parseString(filename, tokens, i), + RegexStatement.parse(filename, tokens, i), + ConstantStatement.parseNumber(filename, tokens, i), + OperationStatement.parseUnary(filename, tokens, i), + ArrayStatement.parse(filename, tokens, i), + ChangeStatement.parsePrefix(filename, tokens, i), parseParens(filename, tokens, i), - parseNew(filename, tokens, i), - parseTypeof(filename, tokens, i), - parseVoid(filename, tokens, i), - parseDelete(filename, tokens, i) + CallStatement.parseNew(filename, tokens, i), + TypeofStatement.parse(filename, tokens, i), + DiscardStatement.parse(filename, tokens, i), + DeleteStatement.parseDelete(filename, tokens, i) )); return ParseRes.any(res); } - public static ParseRes parseVariable(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - var literal = parseIdentifier(tokens, i); - - if (!literal.isSuccess()) return ParseRes.failed(); - - if (!checkVarName(literal.result)) { - if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported."); - if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); - if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); - return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result)); - } - - return ParseRes.res(new VariableStatement(loc, literal.result), 1); - } public static ParseRes parseLiteral(Filename filename, List tokens, int i) { var loc = getLoc(filename, tokens, i); var id = parseIdentifier(tokens, i); if (!id.isSuccess()) return id.transform(); - if (id.result.equals("true")) { - return ParseRes.res(new ConstantStatement(loc, true), 1); - } - if (id.result.equals("false")) { - return ParseRes.res(new ConstantStatement(loc, false), 1); - } - if (id.result.equals("undefined")) { - return ParseRes.res(ConstantStatement.ofUndefined(loc), 1); - } - if (id.result.equals("null")) { - return ParseRes.res(ConstantStatement.ofNull(loc), 1); - } - if (id.result.equals("this")) { - return ParseRes.res(new VariableIndexStatement(loc, 0), 1); - } - if (id.result.equals("arguments")) { - return ParseRes.res(new VariableIndexStatement(loc, 1), 1); - } - if (id.result.equals("globalThis") || id.result.equals("window") || id.result.equals("self")) { - return ParseRes.res(new GlobalThisStatement(loc), 1); - } + if (id.result.equals("true")) return ParseRes.res(new ConstantStatement(loc, true), 1); + if (id.result.equals("false")) return ParseRes.res(new ConstantStatement(loc, false), 1); + if (id.result.equals("undefined")) return ParseRes.res(ConstantStatement.ofUndefined(loc), 1); + if (id.result.equals("null")) return ParseRes.res(ConstantStatement.ofNull(loc), 1); + if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), 1); + if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), 1); + if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), 1); + return ParseRes.failed(); } - public static ParseRes parseMember(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - var n = 0; - - if (precedence > 18) return ParseRes.failed(); - - if (!isOperator(tokens, i + n++, Operator.DOT)) return ParseRes.failed(); - - var literal = parseIdentifier(tokens, i + n++); - if (!literal.isSuccess()) return ParseRes.error(loc, "Expected an identifier after member access."); - - return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n); - } - public static ParseRes parseIndex(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - var n = 0; - - if (precedence > 18) return ParseRes.failed(); - - if (!isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 0); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in index expression.", valRes); - n += valRes.n; - - if (!isOperator(tokens, i + n++, Operator.BRACKET_CLOSE)) return ParseRes.error(loc, "Expected a closing bracket."); - - return ParseRes.res(new IndexStatement(loc, prev, valRes.result), n); - } - public static ParseRes parseAssign(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - int n = 0 ; - - if (precedence > 2) return ParseRes.failed(); - - var opRes = parseOperator(tokens, i + n++); - if (opRes.state != State.SUCCESS) return ParseRes.failed(); - - var op = opRes.result; - if (!op.isAssign()) return ParseRes.failed(); - - if (!(prev instanceof AssignableStatement)) { - return ParseRes.error(loc, "Invalid expression on left hand side of assign operator."); - } - - var res = parseValue(filename, tokens, i + n, 2); - if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res); - n += res.n; - - Operation operation = null; - - if (op == Operator.ASSIGN_ADD) operation = Operation.ADD; - if (op == Operator.ASSIGN_SUBTRACT) operation = Operation.SUBTRACT; - if (op == Operator.ASSIGN_MULTIPLY) operation = Operation.MULTIPLY; - if (op == Operator.ASSIGN_DIVIDE) operation = Operation.DIVIDE; - if (op == Operator.ASSIGN_MODULO) operation = Operation.MODULO; - if (op == Operator.ASSIGN_OR) operation = Operation.OR; - if (op == Operator.ASSIGN_XOR) operation = Operation.XOR; - if (op == Operator.ASSIGN_AND) operation = Operation.AND; - if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Operation.SHIFT_LEFT; - if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Operation.SHIFT_RIGHT; - if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Operation.USHIFT_RIGHT; - - return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n); - } - public static ParseRes parseCall(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - var n = 0; - - if (precedence > 17) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed(); - - var args = new ArrayList(); - boolean prevArg = false; - - while (true) { - var argRes = parseValue(filename, tokens, i + n, 2); - if (argRes.isSuccess()) { - args.add(argRes.result); - n += argRes.n; - prevArg = true; - } - else if (argRes.isError()) return argRes.transform(); - else if (prevArg && isOperator(tokens, i + n, Operator.COMMA)) { - prevArg = false; - n++; - } - else if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - break; - } - else return ParseRes.error(getLoc(filename, tokens, i + n), prevArg ? "Expected a comma or a closing paren." : "Expected an expression or a closing paren."); - } - - return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); - } - public static ParseRes parsePostfixChange(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (precedence > 15) return ParseRes.failed(); - - var opState = parseOperator(tokens, i + n++); - if (!opState.isSuccess()) return ParseRes.failed(); - - int change = 0; - - if (opState.result == Operator.INCREASE) change = 1; - else if (opState.result == Operator.DECREASE) change = -1; - else return ParseRes.failed(); - - if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value before suffix operator."); - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, change, true), n); - } - public static ParseRes parseInstanceof(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (precedence > 9) return ParseRes.failed(); - if (!isIdentifier(tokens, i + n++, "instanceof")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 10); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'instanceof'.", valRes); - n += valRes.n; - - return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n); - } - public static ParseRes parseIn(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (precedence > 9) return ParseRes.failed(); - if (!isIdentifier(tokens, i + n++, "in")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 10); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'in'.", valRes); - n += valRes.n; - - return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n); - } - public static ParseRes parseComma(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - var n = 0; - - if (precedence > 1) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.COMMA)) return ParseRes.failed(); - - var res = parseValue(filename, tokens, i + n, 2); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res); - n += res.n; - - return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n); - } - public static ParseRes parseTernary(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - var n = 0; - - if (precedence > 2) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.QUESTION)) return ParseRes.failed(); - - var a = parseValue(filename, tokens, i + n, 2); - if (!a.isSuccess()) return ParseRes.error(loc, "Expected a value after the ternary operator.", a); - n += a.n; - - if (!isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed(); - - var b = parseValue(filename, tokens, i + n, 2); - if (!b.isSuccess()) return ParseRes.error(loc, "Expected a second value after the ternary operator.", b); - n += b.n; - - return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n); - } - public static ParseRes parseOperator(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = getLoc(filename, tokens, i); - var n = 0; - - var opRes = parseOperator(tokens, i + n++); - if (!opRes.isSuccess()) return ParseRes.failed(); - var op = opRes.result; - - if (op.precedence < precedence) return ParseRes.failed(); - if (op.isAssign()) return parseAssign(filename, tokens, i + n - 1, prev, precedence); - - var res = parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1)); - if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res); - n += res.n; - - if (op == Operator.LAZY_AND) { - return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n); - } - if (op == Operator.LAZY_OR) { - return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n); - } - - return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n); - } - public static ParseRes parseValue(Filename filename, List tokens, int i, int precedence, boolean statement) { Statement prev = null; int n = 0; @@ -1297,15 +758,15 @@ public class Parsing { } else { var res = ParseRes.any( - parseOperator(filename, tokens, i + n, prev, precedence), - parseMember(filename, tokens, i + n, prev, precedence), - parseIndex(filename, tokens, i + n, prev, precedence), - parseCall(filename, tokens, i + n, prev, precedence), - parsePostfixChange(filename, tokens, i + n, prev, precedence), - parseInstanceof(filename, tokens, i + n, prev, precedence), - parseIn(filename, tokens, i + n, prev, precedence), - parseComma(filename, tokens, i + n, prev, precedence), - parseTernary(filename, tokens, i + n, prev, precedence) + OperationStatement.parseOperator(filename, tokens, i + n, prev, precedence), + IndexStatement.parseMember(filename, tokens, i + n, prev, precedence), + IndexStatement.parseIndex(filename, tokens, i + n, prev, precedence), + CallStatement.parseCall(filename, tokens, i + n, prev, precedence), + ChangeStatement.parsePostfix(filename, tokens, i + n, prev, precedence), + OperationStatement.parseInstanceof(filename, tokens, i + n, prev, precedence), + OperationStatement.parseIn(filename, tokens, i + n, prev, precedence), + CompoundStatement.parseComma(filename, tokens, i + n, prev, precedence), + IfStatement.parseTernary(filename, tokens, i + n, prev, precedence) ); if (res.isSuccess()) { @@ -1330,7 +791,6 @@ public class Parsing { var valRes = parseValue(filename, tokens, i, 0, true); if (!valRes.isSuccess()) return valRes.transform(); - // valRes.result.setLoc(loc); var res = ParseRes.res(valRes.result, valRes.n); if (isStatementEnd(tokens, i + res.n)) { @@ -1342,543 +802,26 @@ public class Parsing { } else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", res); } - public static ParseRes parseVariableDeclare(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isIdentifier(tokens, i + n++, "var")) return ParseRes.failed(); - - var res = new ArrayList(); - - if (isStatementEnd(tokens, i + n)) { - if (isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), 2); - else return ParseRes.res(new VariableDeclareStatement(loc, res), 1); - } - - while (true) { - var nameLoc = getLoc(filename, tokens, i + n); - var nameRes = parseIdentifier(tokens, i + n++); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name."); - - if (!checkVarName(nameRes.result)) { - return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", nameRes.result)); - } - - Statement val = null; - - if (isOperator(tokens, i + n, Operator.ASSIGN)) { - n++; - var valRes = parseValue(filename, tokens, i + n, 2); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes); - n += valRes.n; - val = valRes.result; - } - - res.add(new Pair(nameRes.result, val, nameLoc)); - - if (isOperator(tokens, i + n, Operator.COMMA)) { - n++; - continue; - } - else if (isStatementEnd(tokens, i + n)) { - if (isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), n + 1); - else return ParseRes.res(new VariableDeclareStatement(loc, res), n); - } - else return ParseRes.error(loc, "Expected a comma or end of statement."); - } - } - - public static ParseRes parseReturn(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isIdentifier(tokens, i + n++, "return")) return ParseRes.failed(); - - if (isStatementEnd(tokens, i + n)) { - if (isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new ReturnStatement(loc, null), 2); - else return ParseRes.res(new ReturnStatement(loc, null), 1); - } - - var valRes = parseValue(filename, tokens, i + n, 0); - n += valRes.n; - if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes); - - var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n); - - if (isStatementEnd(tokens, i + n)) { - if (isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); - else return res; - } - else - return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", valRes); - } - public static ParseRes parseThrow(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isIdentifier(tokens, i + n++, "throw")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 0); - n += valRes.n; - if (valRes.isError()) return ParseRes.error(loc, "Expected a throw value.", valRes); - - var res = ParseRes.res(new ThrowStatement(loc, valRes.result), n); - - if (isStatementEnd(tokens, i + n)) { - if (isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); - else return res; - } - else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", valRes); - } - - public static ParseRes parseBreak(Filename filename, List tokens, int i) { - if (!isIdentifier(tokens, i, "break")) return ParseRes.failed(); - - if (isStatementEnd(tokens, i + 1)) { - if (isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(getLoc(filename, tokens, i), null), 2); - else return ParseRes.res(new BreakStatement(getLoc(filename, tokens, i), null), 1); - } - - var labelRes = parseIdentifier(tokens, i + 1); - if (labelRes.isFailed()) return ParseRes.error(getLoc(filename, tokens, i), "Expected a label name or an end of statement."); - var label = labelRes.result; - - if (isStatementEnd(tokens, i + 2)) { - if (isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(getLoc(filename, tokens, i), label), 3); - else return ParseRes.res(new BreakStatement(getLoc(filename, tokens, i), label), 2); - } - else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement."); - } - public static ParseRes parseContinue(Filename filename, List tokens, int i) { - if (!isIdentifier(tokens, i, "continue")) return ParseRes.failed(); - - if (isStatementEnd(tokens, i + 1)) { - if (isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(getLoc(filename, tokens, i), null), 2); - else return ParseRes.res(new ContinueStatement(getLoc(filename, tokens, i), null), 1); - } - - var labelRes = parseIdentifier(tokens, i + 1); - if (labelRes.isFailed()) return ParseRes.error(getLoc(filename, tokens, i), "Expected a label name or an end of statement."); - var label = labelRes.result; - - if (isStatementEnd(tokens, i + 2)) { - if (isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(getLoc(filename, tokens, i), label), 3); - else return ParseRes.res(new ContinueStatement(getLoc(filename, tokens, i), label), 2); - } - else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement."); - } - public static ParseRes parseDebug(Filename filename, List tokens, int i) { - if (!isIdentifier(tokens, i, "debugger")) return ParseRes.failed(); - - if (isStatementEnd(tokens, i + 1)) { - if (isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new DebugStatement(getLoc(filename, tokens, i)), 2); - else return ParseRes.res(new DebugStatement(getLoc(filename, tokens, i)), 1); - } - else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement."); - } - - public static ParseRes parseCompound(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); - - var statements = new ArrayList(); - - while (true) { - if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { - n++; - break; - } - if (isOperator(tokens, i + n, Operator.SEMICOLON)) { - n++; - continue; - } - - var res = parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) { - return ParseRes.error(getLoc(filename, tokens, i), "Expected a statement.", res); - } - n += res.n; - - statements.add(res.result); - } - - return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(getLoc(filename, tokens, i + n - 1)), n); - } - public static ParseRes parseLabel(List tokens, int i) { - int n = 0; - - var nameRes = parseIdentifier(tokens, i + n++); - if (!isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed(); - - return ParseRes.res(nameRes.result, n); - } - public static ParseRes parseIf(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (!isIdentifier(tokens, i + n++, "if")) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'if'."); - - var condRes = parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes); - n += condRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition."); - - var res = parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res); - n += res.n; - - if (!isIdentifier(tokens, i + n, "else")) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); - n++; - - var elseRes = parseStatement(filename, tokens, i + n); - if (!elseRes.isSuccess()) return ParseRes.error(loc, "Expected an else body.", elseRes); - n += elseRes.n; - - return ParseRes.res(new IfStatement(loc, condRes.result, res.result, elseRes.result), n); - } - public static ParseRes parseWhile(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var labelRes = parseLabel(tokens, i + n); - n += labelRes.n; - - if (!isIdentifier(tokens, i + n++, "while")) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'."); - - var condRes = parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes); - n += condRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition."); - - var res = parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a while body.", res); - n += res.n; - - return ParseRes.res(new WhileStatement(loc, labelRes.result, condRes.result, res.result), n); - } - public static ParseRes parseSwitchCase(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (!isIdentifier(tokens, i + n++, "case")) return ParseRes.failed(); - - var valRes = parseValue(filename, tokens, i + n, 0); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'case'.", valRes); - n += valRes.n; - - if (!isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected colons after 'case' value."); - - return ParseRes.res(valRes.result, n); - } - public static ParseRes parseDefaultCase(List tokens, int i) { - if (!isIdentifier(tokens, i, "default")) return ParseRes.failed(); - if (!isOperator(tokens, i + 1, Operator.COLON)) return ParseRes.error(getLoc(null, tokens, i), "Expected colons after 'default'."); - - return ParseRes.res(null, 2); - } - public static ParseRes parseSwitch(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (!isIdentifier(tokens, i + n++, "switch")) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'switch'."); - - var valRes = parseValue(filename, tokens, i + n, 0); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a switch value.", valRes); - n += valRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after switch value."); - if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.error(loc, "Expected an opening brace after switch value."); - - var statements = new ArrayList(); - var cases = new ArrayList(); - var defaultI = -1; - - while (true) { - if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { - n++; - break; - } - if (isOperator(tokens, i + n, Operator.SEMICOLON)) { - n++; - continue; - } - - var defaultRes = parseDefaultCase(tokens, i + n); - var caseRes = parseSwitchCase(filename, tokens, i + n); - - if (defaultRes.isSuccess()) { - defaultI = statements.size(); - n += defaultRes.n; - } - else if (caseRes.isSuccess()) { - cases.add(new SwitchCase(caseRes.result, statements.size())); - n += caseRes.n; - } - else if (defaultRes.isError()) return defaultRes.transform(); - else if (caseRes.isError()) return defaultRes.transform(); - else { - var res = ParseRes.any( - parseStatement(filename, tokens, i + n), - parseCompound(filename, tokens, i + n) - ); - if (!res.isSuccess()) { - return ParseRes.error(getLoc(filename, tokens, i), "Expected a statement.", res); - } - n += res.n; - statements.add(res.result); - } - } - - return ParseRes.res(new SwitchStatement( - loc, valRes.result, defaultI, - cases.toArray(SwitchCase[]::new), - statements.toArray(Statement[]::new) - ), n); - } - public static ParseRes parseDoWhile(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var labelRes = parseLabel(tokens, i + n); - n += labelRes.n; - - if (!isIdentifier(tokens, i + n++, "do")) return ParseRes.failed(); - var bodyRes = parseStatement(filename, tokens, i + n); - if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a do-while body.", bodyRes); - n += bodyRes.n; - - if (!isIdentifier(tokens, i + n++, "while")) return ParseRes.error(loc, "Expected 'while' keyword."); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'."); - - var condRes = parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes); - n += condRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition."); - - var res = ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); - - if (isStatementEnd(tokens, i + n)) { - if (isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); - else return res; - } - else return ParseRes.error(getLoc(filename, tokens, i), "Expected a semicolon."); - } - public static ParseRes parseFor(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var labelRes = parseLabel(tokens, i + n); - n += labelRes.n; - - if (!isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); - - Statement decl, cond, inc; - - if (isOperator(tokens, i + n, Operator.SEMICOLON)) { - n++; - decl = new CompoundStatement(loc, false); - } - else { - var declRes = ParseRes.any( - parseVariableDeclare(filename, tokens, i + n), - parseValueStatement(filename, tokens, i + n) - ); - if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes); - n += declRes.n; - decl = declRes.result; - } - - if (isOperator(tokens, i + n, Operator.SEMICOLON)) { - n++; - cond = new ConstantStatement(loc, 1); - } - else { - var condRes = parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", condRes); - n += condRes.n; - if (!isOperator(tokens, i + n++, Operator.SEMICOLON)) return ParseRes.error(loc, "Expected a semicolon.", condRes); - cond = condRes.result; - } - - if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - inc = new CompoundStatement(loc, false); - } - else { - var incRes = parseValue(filename, tokens, i + n, 0); - if (!incRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", incRes); - n += incRes.n; - inc = incRes.result; - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); - } - - - var res = parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a for body.", res); - n += res.n; - - return ParseRes.res(new ForStatement(loc, labelRes.result, decl, cond, inc, res.result), n); - } - public static ParseRes parseForIn(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var labelRes = parseLabel(tokens, i + n); - var isDecl = false; - n += labelRes.n; - - if (!isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); - - if (isIdentifier(tokens, i + n, "var")) { - isDecl = true; - n++; - } - - var nameRes = parseIdentifier(tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop."); - var nameLoc = getLoc(filename, tokens, i + n); - n += nameRes.n; - - Statement varVal = null; - - if (isOperator(tokens, i + n, Operator.ASSIGN)) { - n++; - - var valRes = parseValue(filename, tokens, i + n, 2); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes); - n += nameRes.n; - - varVal = valRes.result; - } - - if (!isIdentifier(tokens, i + n++, "in")) { - if (varVal == null) { - if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); - else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); - } - return ParseRes.error(loc, "Expected 'in' keyword after variable declaration."); - } - - var objRes = parseValue(filename, tokens, i + n, 0); - if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes); - n += objRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); - - - var bodyRes = parseStatement(filename, tokens, i + n); - if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes); - n += bodyRes.n; - - return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n); - } - public static ParseRes parseForOf(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - var labelRes = parseLabel(tokens, i + n); - var isDecl = false; - n += labelRes.n; - - if (!isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); - - if (isIdentifier(tokens, i + n, "var")) { - isDecl = true; - n++; - } - - var nameRes = parseIdentifier(tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop."); - var nameLoc = getLoc(filename, tokens, i + n); - n += nameRes.n; - - if (!isIdentifier(tokens, i + n++, "of")) { - if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); - else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); - else return ParseRes.error(loc, "Expected 'of' keyword after variable declaration."); - } - - var objRes = parseValue(filename, tokens, i + n, 0); - if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes); - n += objRes.n; - - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); - - - var bodyRes = parseStatement(filename, tokens, i + n); - if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes); - n += bodyRes.n; - - return ParseRes.res(new ForOfStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, objRes.result, bodyRes.result), n); - } - public static ParseRes parseCatch(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; - - if (!isIdentifier(tokens, i + n++, "try")) return ParseRes.failed(); - - var res = parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res); - n += res.n; - - String name = null; - Statement catchBody = null, finallyBody = null; - - - if (isIdentifier(tokens, i + n, "catch")) { - n++; - if (isOperator(tokens, i + n, Operator.PAREN_OPEN)) { - n++; - var nameRes = parseIdentifier(tokens, i + n++); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a catch variable name."); - name = nameRes.result; - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after catch variable name."); - } - - var catchRes = parseStatement(filename, tokens, i + n); - if (!catchRes.isSuccess()) return ParseRes.error(loc, "Expected a catch body.", catchRes); - n += catchRes.n; - catchBody = catchRes.result; - } - - if (isIdentifier(tokens, i + n, "finally")) { - n++; - var finallyRes = parseStatement(filename, tokens, i + n); - if (!finallyRes.isSuccess()) return ParseRes.error(loc, "Expected a finally body.", finallyRes); - n += finallyRes.n; - finallyBody = finallyRes.result; - } - - return ParseRes.res(new TryStatement(loc, res.result, catchBody, finallyBody, name), n); - } - public static ParseRes parseStatement(Filename filename, List tokens, int i) { if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i), false), 1); if (isIdentifier(tokens, i, "with")) return ParseRes.error(getLoc(filename, tokens, i), "'with' statements are not allowed."); return ParseRes.any( - parseVariableDeclare(filename, tokens, i), - parseReturn(filename, tokens, i), - parseThrow(filename, tokens, i), - parseContinue(filename, tokens, i), - parseBreak(filename, tokens, i), - parseDebug(filename, tokens, i), - parseIf(filename, tokens, i), - parseWhile(filename, tokens, i), - parseSwitch(filename, tokens, i), - parseFor(filename, tokens, i), - parseForIn(filename, tokens, i), - parseForOf(filename, tokens, i), - parseDoWhile(filename, tokens, i), - parseCatch(filename, tokens, i), - parseCompound(filename, tokens, i), - parseFunction(filename, tokens, i, true), + VariableDeclareStatement.parse(filename, tokens, i), + ReturnStatement.parseReturn(filename, tokens, i), + ThrowStatement.parseThrow(filename, tokens, i), + ContinueStatement.parseContinue(filename, tokens, i), + BreakStatement.parseBreak(filename, tokens, i), + DebugStatement.parse(filename, tokens, i), + IfStatement.parse(filename, tokens, i), + WhileStatement.parseWhile(filename, tokens, i), + SwitchStatement.parse(filename, tokens, i), + ForStatement.parse(filename, tokens, i), + ForInStatement.parse(filename, tokens, i), + ForOfStatement.parse(filename, tokens, i), + DoWhileStatement.parseDoWhile(filename, tokens, i), + TryStatement.parse(filename, tokens, i), + CompoundStatement.parse(filename, tokens, i), + FunctionStatement.parseFunction(filename, tokens, i, true), parseValueStatement(filename, tokens, i) ); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java index e24e50b..8ffbf4d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java @@ -1,9 +1,17 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.ArrayList; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ArrayStatement extends Statement { public final Statement[] statements; @@ -37,4 +45,42 @@ public class ArrayStatement extends Statement { super(loc); this.statements = statements; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); + + var values = new ArrayList(); + + loop: while (true) { + if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + n++; + break; + } + + while (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + n++; + values.add(null); + if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + n++; + break loop; + } + } + + var res = Parsing.parseValue(filename, tokens, i + n, 2); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected an array element.", res); + else n += res.n; + + values.add(res.result); + + if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++; + else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + n++; + break; + } + } + + return ParseRes.res(new ArrayStatement(loc, values.toArray(Statement[]::new)), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java index 412eacc..e8a82d6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java @@ -1,18 +1,25 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.ArrayList; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class CallStatement extends Statement { public final Statement func; public final Statement[] args; public final boolean isNew; - @Override - public void compile(CompileResult target, boolean pollute, BreakpointType type) { + @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { if (isNew) func.compile(target, true); else if (func instanceof IndexStatement) { ((IndexStatement)func).compile(target, true, true); @@ -29,8 +36,7 @@ public class CallStatement extends Statement { if (!pollute) target.add(Instruction.discard()); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { compile(target, pollute, BreakpointType.STEP_IN); } @@ -40,4 +46,52 @@ public class CallStatement extends Statement { this.func = func; this.args = args; } + + public static ParseRes parseCall(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + + if (precedence > 17) return ParseRes.failed(); + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed(); + + var args = new ArrayList(); + boolean prevArg = false; + + while (true) { + var argRes = Parsing.parseValue(filename, tokens, i + n, 2); + if (argRes.isSuccess()) { + args.add(argRes.result); + n += argRes.n; + prevArg = true; + } + else if (argRes.isError()) return argRes.transform(); + else if (prevArg && Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + prevArg = false; + n++; + } + else if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { + n++; + break; + } + else return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), prevArg ? "Expected a comma or a closing paren." : "Expected an expression or a closing paren."); + } + + return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); + } + public static ParseRes parseNew(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "new")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 18); + n += valRes.n; + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'new' keyword.", valRes); + var callRes = CallStatement.parseCall(filename, tokens, i + n, valRes.result, 0); + n += callRes.n; + if (callRes.isError()) return callRes.transform(); + else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); + var call = (CallStatement)callRes.result; + + return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java index 26ae605..0531b4d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java @@ -1,19 +1,25 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ChangeStatement extends Statement { public final AssignableStatement value; public final double addAmount; public final boolean postfix; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true); if (!pollute) target.add(Instruction.discard()); else if (postfix) { @@ -28,4 +34,40 @@ public class ChangeStatement extends Statement { this.addAmount = addAmount; this.postfix = postfix; } + + public static ParseRes parsePrefix(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var opState = Parsing.parseOperator(tokens, i + n++); + if (!opState.isSuccess()) return ParseRes.failed(); + + int change = 0; + + if (opState.result == Operator.INCREASE) change = 1; + else if (opState.result == Operator.DECREASE) change = -1; + else return ParseRes.failed(); + + var res = Parsing.parseValue(filename, tokens, i + n, 15); + if (!(res.result instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value after prefix operator."); + return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, change, false), n + res.n); + } + public static ParseRes parsePostfix(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (precedence > 15) return ParseRes.failed(); + + var opState = Parsing.parseOperator(tokens, i + n++); + if (!opState.isSuccess()) return ParseRes.failed(); + + int change = 0; + + if (opState.result == Operator.INCREASE) change = 1; + else if (opState.result == Operator.DECREASE) change = -1; + else return ParseRes.failed(); + + if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value before suffix operator."); + return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, change, true), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java index 2def204..60a8503 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java @@ -1,9 +1,15 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ConstantStatement extends Statement { public final Object value; @@ -44,4 +50,25 @@ public class ConstantStatement extends Statement { public static ConstantStatement ofNull(Location loc) { return new ConstantStatement(loc, null, true); } + + public static ParseRes parseNumber(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + if (Parsing.inBounds(tokens, i)) { + if (tokens.get(i).isNumber()) { + return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1); + } + else return ParseRes.failed(); + } + else return ParseRes.failed(); + } + public static ParseRes parseString(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + if (Parsing.inBounds(tokens, i)) { + if (tokens.get(i).isString()) { + return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1); + } + else return ParseRes.failed(); + } + else return ParseRes.failed(); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java index 40ad0cd..cd13dd8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java @@ -1,9 +1,15 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class DiscardStatement extends Statement { public final Statement value; @@ -20,4 +26,16 @@ public class DiscardStatement extends Statement { super(loc); this.value = val; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "void")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 14); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes); + n += valRes.n; + + return ParseRes.res(new DiscardStatement(loc, valRes.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index db7e331..a54d082 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -1,12 +1,20 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.ArrayList; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class FunctionStatement extends Statement { @@ -124,4 +132,48 @@ public class FunctionStatement extends Statement { if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name, bp); else stm.compile(target, pollute, bp); } + + public static ParseRes parseFunction(Filename filename, List tokens, int i, boolean statement) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (!Parsing.isIdentifier(tokens, i + n++, "function")) return ParseRes.failed(); + + var nameRes = Parsing.parseIdentifier(tokens, i + n); + if (!nameRes.isSuccess() && statement) return ParseRes.error(loc, "A statement function requires a name, one is not present."); + var name = nameRes.result; + n += nameRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a parameter list."); + + var args = new ArrayList(); + + if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { + n++; + } + else { + while (true) { + var argRes = Parsing.parseIdentifier(tokens, i + n); + if (argRes.isSuccess()) { + args.add(argRes.result); + n++; + if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + n++; + } + if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { + n++; + break; + } + } + else return ParseRes.error(loc, "Expected an argument, comma or a closing brace."); + } + } + + var res = CompoundStatement.parse(filename, tokens, i + n); + n += res.n; + var end = Parsing.getLoc(filename, tokens, i + n - 1); + + if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n); + else return ParseRes.error(loc, "Expected a compound statement for function.", res); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java index 58315dc..29dcbd3 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java @@ -1,12 +1,19 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class IndexStatement extends AssignableStatement { public final Statement object; @@ -34,4 +41,34 @@ public class IndexStatement extends AssignableStatement { this.object = object; this.index = index; } + + public static ParseRes parseIndex(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + + if (precedence > 18) return ParseRes.failed(); + + if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 0); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in index expression.", valRes); + n += valRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_CLOSE)) return ParseRes.error(loc, "Expected a closing bracket."); + + return ParseRes.res(new IndexStatement(loc, prev, valRes.result), n); + } + public static ParseRes parseMember(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + + if (precedence > 18) return ParseRes.failed(); + + if (!Parsing.isOperator(tokens, i + n++, Operator.DOT)) return ParseRes.failed(); + + var literal = Parsing.parseIdentifier(tokens, i + n++); + if (!literal.isSuccess()) return ParseRes.error(loc, "Expected an identifier after member access."); + + return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java index f5af390..b98e84e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java @@ -1,12 +1,21 @@ package me.topchetoeu.jscript.compilation.values; import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Parsing.ObjProp; +import me.topchetoeu.jscript.compilation.parsing.Token; public class ObjectStatement extends Statement { public final Map map; @@ -58,4 +67,104 @@ public class ObjectStatement extends Statement { this.getters = getters; this.setters = setters; } + + private static ParseRes parsePropName(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + + if (Parsing.inBounds(tokens, i)) { + var token = tokens.get(i); + + if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1); + else return ParseRes.error(loc, "Expected identifier, string or number literal."); + } + else return ParseRes.failed(); + } + private static ParseRes parseObjectProp(Filename filename, List tokens, int i) { + var loc =Parsing. getLoc(filename, tokens, i); + int n = 0; + + var accessRes = Parsing.parseIdentifier(tokens, i + n++); + if (!accessRes.isSuccess()) return ParseRes.failed(); + var access = accessRes.result; + if (!access.equals("get") && !access.equals("set")) return ParseRes.failed(); + + var nameRes = parsePropName(filename, tokens, i + n); + if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a property name after '" + access + "'."); + var name = nameRes.result; + n += nameRes.n; + + var argsRes = Parsing.parseParamList(filename, tokens, i + n); + if (!argsRes.isSuccess()) return ParseRes.error(loc, "Expected an argument list.", argsRes); + n += argsRes.n; + + var res = CompoundStatement.parse(filename, tokens, i + n); + if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res); + n += res.n; + + var end = Parsing.getLoc(filename, tokens, i + n - 1); + + return ParseRes.res(new ObjProp( + name, access, + new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result) + ), n); + } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); + + var values = new LinkedHashMap(); + var getters = new LinkedHashMap(); + var setters = new LinkedHashMap(); + + if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n++; + return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); + } + + while (true) { + var propRes = parseObjectProp(filename, tokens, i + n); + + if (propRes.isSuccess()) { + n += propRes.n; + if (propRes.result.access.equals("set")) { + setters.put(propRes.result.name, propRes.result.func); + } + else { + getters.put(propRes.result.name, propRes.result.func); + } + } + else { + var nameRes = parsePropName(filename, tokens, i + n); + if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a field name.", propRes); + n += nameRes.n; + + if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected a colon."); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 2); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in array list.", valRes); + n += valRes.n; + + values.put(nameRes.result, valRes.result); + } + + if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + n++; + if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n++; + break; + } + continue; + } + else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n++; + break; + } + else ParseRes.error(loc, "Expected a comma or a closing brace."); + } + + return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); + } + } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java index cf66133..55d373d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java @@ -1,10 +1,18 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; +import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class OperationStatement extends Statement { public final Statement[] args; @@ -18,8 +26,7 @@ public class OperationStatement extends Statement { return true; } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { for (var arg : args) { arg.compile(target, true); } @@ -33,4 +40,76 @@ public class OperationStatement extends Statement { this.operation = operation; this.args = args; } + + public static ParseRes parseUnary(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + var opState = Parsing.parseOperator(tokens, i + n++); + if (!opState.isSuccess()) return ParseRes.failed(); + var op = opState.result; + + Operation operation = null; + + if (op == Operator.ADD) operation = Operation.POS; + else if (op == Operator.SUBTRACT) operation = Operation.NEG; + else if (op == Operator.INVERSE) operation = Operation.INVERSE; + else if (op == Operator.NOT) operation = Operation.NOT; + else return ParseRes.failed(); + + var res = Parsing.parseValue(filename, tokens, n + i, 14); + + if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); + else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res); + } + public static ParseRes parseInstanceof(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (precedence > 9) return ParseRes.failed(); + if (!Parsing.isIdentifier(tokens, i + n++, "instanceof")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 10); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'instanceof'.", valRes); + n += valRes.n; + + return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n); + } + public static ParseRes parseIn(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + int n = 0; + + if (precedence > 9) return ParseRes.failed(); + if (!Parsing.isIdentifier(tokens, i + n++, "in")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 10); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'in'.", valRes); + n += valRes.n; + + return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n); + } + public static ParseRes parseOperator(Filename filename, List tokens, int i, Statement prev, int precedence) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + + var opRes = Parsing.parseOperator(tokens, i + n++); + if (!opRes.isSuccess()) return ParseRes.failed(); + var op = opRes.result; + + if (op.precedence < precedence) return ParseRes.failed(); + if (op.isAssign()) return AssignableStatement.parse(filename, tokens, i + n - 1, prev, precedence); + + var res = Parsing.parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1)); + if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res); + n += res.n; + + if (op == Operator.LAZY_AND) { + return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n); + } + if (op == Operator.LAZY_OR) { + return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n); + } + + return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java index fe2bdb8..b7a753a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java @@ -1,9 +1,15 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class RegexStatement extends Statement { public final String pattern, flags; @@ -17,6 +23,21 @@ public class RegexStatement extends Statement { if (!pollute) target.add(Instruction.discard()); } + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + if (Parsing.inBounds(tokens, i)) { + if (tokens.get(i).isRegex()) { + var val = tokens.get(i).regex(); + var index = val.lastIndexOf('/'); + var first = val.substring(1, index); + var second = val.substring(index + 1); + return ParseRes.res(new RegexStatement(loc, first, second), 1); + } + else return ParseRes.failed(); + } + return ParseRes.failed(); + } + public RegexStatement(Location loc, String pattern, String flags) { super(loc); this.pattern = pattern; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java index 11ab78d..267c748 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java @@ -1,9 +1,15 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class TypeofStatement extends Statement { public final Statement value; @@ -29,4 +35,16 @@ public class TypeofStatement extends Statement { super(loc); this.value = value; } + + public static ParseRes parse(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + var n = 0; + if (!Parsing.isIdentifier(tokens, i + n++, "typeof")) return ParseRes.failed(); + + var valRes = Parsing.parseValue(filename, tokens, i + n, 15); + if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'typeof' keyword.", valRes); + n += valRes.n; + + return ParseRes.res(new TypeofStatement(loc, valRes.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java index 1ae3d2d..6349c40 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java @@ -1,11 +1,17 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.List; + +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Token; public class VariableStatement extends AssignableStatement { public final String name; @@ -28,4 +34,20 @@ public class VariableStatement extends AssignableStatement { super(loc); this.name = name; } + + public static ParseRes parseVariable(Filename filename, List tokens, int i) { + var loc = Parsing.getLoc(filename, tokens, i); + var literal = Parsing.parseIdentifier(tokens, i); + + if (!literal.isSuccess()) return ParseRes.failed(); + + if (!Parsing.checkVarName(literal.result)) { + if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported."); + if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); + if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); + return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result)); + } + + return ParseRes.res(new VariableStatement(loc, literal.result), 1); + } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 02f1360..c2a40c7 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.runtime; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import java.util.Stack; @@ -19,6 +20,7 @@ import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class Frame { public static final Key KEY = new Key<>(); @@ -124,7 +126,7 @@ public class Frame { else return stack[stackPtr - 1 - offset]; } public Value pop() { - if (stackPtr == 0) return null; + if (stackPtr == 0) return VoidValue.UNDEFINED; return stack[--stackPtr]; } public Value[] take(int n) { @@ -135,6 +137,7 @@ public class Frame { int copyN = stackPtr - srcI; Value[] res = new Value[n]; + Arrays.fill(res, VoidValue.UNDEFINED); System.arraycopy(stack, srcI, res, dstI, copyN); stackPtr -= copyN; diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 6cf802b..5253aa3 100644 --- a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -16,6 +16,7 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class SimpleRepl { static Thread engineTask, debugTask; @@ -48,7 +49,7 @@ public class SimpleRepl { if (raw == null) break; var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); - var res = engine.pushMsg(false, environment, func, null).await(); + var res = engine.pushMsg(false, environment, func, VoidValue.UNDEFINED).await(); System.err.println(res.toReadable(environment)); } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } -- 2.45.2 From f09feae08fd4d26429d0e7da6a60c70421bc8d99 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:53:52 +0300 Subject: [PATCH 04/48] refactor: clean up parsing --- .../topchetoeu/jscript/common/Location.java | 143 +-- .../topchetoeu/jscript/common/json/JSON.java | 138 ++- .../jscript/common/mapping/FunctionMap.java | 2 +- .../compilation/AssignableStatement.java | 60 +- .../compilation/CompoundStatement.java | 46 +- .../compilation/VariableDeclareStatement.java | 59 +- .../compilation/control/BreakStatement.java | 38 +- .../control/ContinueStatement.java | 38 +- .../compilation/control/DebugStatement.java | 25 +- .../compilation/control/DeleteStatement.java | 27 +- .../compilation/control/DoWhileStatement.java | 51 +- .../compilation/control/ForInStatement.java | 89 +- .../compilation/control/ForOfStatement.java | 64 +- .../compilation/control/ForStatement.java | 123 +- .../compilation/control/IfStatement.java | 75 +- .../compilation/control/ReturnStatement.java | 42 +- .../compilation/control/SwitchStatement.java | 110 +- .../compilation/control/ThrowStatement.java | 41 +- .../compilation/control/TryStatement.java | 71 +- .../compilation/control/WhileStatement.java | 55 +- .../jscript/compilation/parsing/Operator.java | 123 +- .../parsing}/ParseRes.java | 56 +- .../jscript/compilation/parsing/Parsing.java | 1082 ++++++----------- .../jscript/compilation/parsing/Source.java | 69 ++ .../compilation/parsing/SourceLocation.java | 55 + .../jscript/compilation/parsing/TestRes.java | 3 +- .../compilation/values/ArrayStatement.java | 37 +- .../compilation/values/CallStatement.java | 60 +- .../compilation/values/ChangeStatement.java | 91 +- .../compilation/values/ConstantStatement.java | 74 -- .../compilation/values/DiscardStatement.java | 26 +- .../compilation/values/FunctionStatement.java | 62 +- .../compilation/values/IndexStatement.java | 60 +- .../compilation/values/LazyAndStatement.java | 19 + .../compilation/values/LazyOrStatement.java | 19 + .../compilation/values/ObjectStatement.java | 163 +-- .../values/OperationStatement.java | 99 +- .../compilation/values/RegexStatement.java | 66 +- .../compilation/values/TypeofStatement.java | 23 +- .../compilation/values/VariableStatement.java | 33 +- .../values/constants/BoolStatement.java | 21 + .../values/constants/ConstantStatements.java | 4 + .../values/constants/NullStatement.java | 16 + .../values/constants/NumberStatement.java | 42 + .../values/constants/StringStatement.java | 33 + .../jscript/runtime/SimpleRepl.java | 28 +- .../values/functions/FunctionValue.java | 2 +- 47 files changed, 1760 insertions(+), 1903 deletions(-) rename src/java/me/topchetoeu/jscript/{common => compilation/parsing}/ParseRes.java (53%) create mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/Source.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java diff --git a/src/java/me/topchetoeu/jscript/common/Location.java b/src/java/me/topchetoeu/jscript/common/Location.java index e3030b8..2118ee3 100644 --- a/src/java/me/topchetoeu/jscript/common/Location.java +++ b/src/java/me/topchetoeu/jscript/common/Location.java @@ -1,101 +1,108 @@ package me.topchetoeu.jscript.common; import java.util.ArrayList; +import java.util.Objects; -public class Location implements Comparable { - public static final Location INTERNAL = new Location(-1, -1, new Filename("jscript", "native")); - private int line; - private int start; - private Filename filename; +public abstract class Location implements Comparable { + public static final Location INTERNAL = Location.of("jscript://native"); - public int line() { return line; } - public int start() { return start; } - public Filename filename() { return filename; } + public abstract int line(); + public abstract int start(); + public abstract Filename filename(); - @Override - public String toString() { + public final String toString() { var res = new ArrayList(); - if (filename != null) res.add(filename.toString()); - if (line >= 0) res.add(line + ""); - if (start >= 0) res.add(start + ""); + if (filename() != null) res.add(filename().toString()); + if (line() >= 0) res.add(line() + 1 + ""); + if (start() >= 0) res.add(start() + 1 + ""); return String.join(":", res); } - public Location add(int n, boolean clone) { - if (clone) return new Location(line, start + n, filename); - this.start += n; - return this; + public final Location add(int n) { + var self = this; + + return new Location() { + @Override public Filename filename() { return self.filename(); } + @Override public int start() { return self.start() + n; } + @Override public int line() { return self.line(); } + }; } - public Location add(int n) { - return add(n, false); - } - public Location nextLine() { - line++; - start = 0; - return this; - } - public Location clone() { - return new Location(line, start, filename); + public final Location nextLine() { + var self = this; + + return new Location() { + @Override public Filename filename() { return self.filename(); } + @Override public int start() { return 0; } + @Override public int line() { return self.line() + 1; } + }; } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + line; - result = prime * result + start; - result = prime * result + ((filename == null) ? 0 : filename.hashCode()); - return result; + @Override public final int hashCode() { + return Objects.hash(line(), start(), filename()); } - - @Override - public boolean equals(Object obj) { + @Override public final boolean equals(Object obj) { if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Location other = (Location) obj; - if (line != other.line) return false; - if (start != other.start) return false; - if (filename == null && other.filename != null) return false; - else if (!filename.equals(other.filename)) return false; + if (!(obj instanceof Location)) return false; + var other = (Location)obj; + + if (!Objects.equals(this.start(), other.start())) return false; + if (!Objects.equals(this.line(), other.line())) return false; + if (!Objects.equals(this.filename(), other.filename())) return false; + return true; } - @Override - public int compareTo(Location other) { - int a = filename.toString().compareTo(other.filename.toString()); - int b = Integer.compare(line, other.line); - int c = Integer.compare(start, other.start); + @Override public int compareTo(Location other) { + int a = filename().toString().compareTo(other.filename().toString()); + int b = Integer.compare(line(), other.line()); + int c = Integer.compare(start(), other.start()); if (a != 0) return a; if (b != 0) return b; + return c; } - public Location(int line, int start, Filename filename) { - this.line = line; - this.start = start; - this.filename = filename; + public static Location of(Filename filename, int line, int start) { + return new Location() { + @Override public Filename filename() { return filename; } + @Override public int start() { return start; } + @Override public int line() { return line; } + }; } - public static Location parse(String raw) { - int i0 = -1, i1 = -1; - for (var i = raw.length() - 1; i >= 0; i--) { - if (raw.charAt(i) == ':') { - if (i1 == -1) i1 = i; - else if (i0 == -1) { - i0 = i; - break; - } + public static Location of(String raw) { + var i0 = raw.lastIndexOf(':'); + if (i0 < 0) return Location.of(Filename.parse(raw), -1, -1); + + var i1 = raw.lastIndexOf(':', i0); + if (i0 < 0) { + try { + return Location.of(Filename.parse(raw.substring(0, i0)), Integer.parseInt(raw.substring(i0 + 1)), -1); + } + catch (NumberFormatException e) { + return Location.of(Filename.parse(raw), -1, -1); } } - return new Location( - Integer.parseInt(raw.substring(i0 + 1, i1)), - Integer.parseInt(raw.substring(i1 + 1)), - Filename.parse(raw.substring(0, i0)) - ); + int start, line; + + try { + start = Integer.parseInt(raw.substring(i1 + 1)); + } + catch (NumberFormatException e) { + return Location.of(Filename.parse(raw), -1, -1); + } + + try { + line = Integer.parseInt(raw.substring(i0 + 1, i1)); + } + catch (NumberFormatException e) { + return Location.of(Filename.parse(raw.substring(i1 + 1)), start, -1); + } + + return Location.of(Filename.parse(raw.substring(0, i0)), start, line); } } diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index 34704d6..66d6ed8 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -1,85 +1,85 @@ package me.topchetoeu.jscript.common.json; -import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.compilation.values.ConstantStatement; +import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class JSON { - public static ParseRes parseIdentifier(List tokens, int i) { - return Parsing.parseIdentifier(tokens, i); + public static ParseRes parseString(Source src, int i) { + var res = Parsing.parseString(src, i); + if (!res.isSuccess()) return res.chainError(); + return ParseRes.res(JSONElement.string(res.result), res.n); } - public static ParseRes parseString(Filename filename, List tokens, int i) { - var res = ConstantStatement.parseString(filename, tokens, i); - if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n); - else return res.transform(); - } - public static ParseRes parseNumber(Filename filename, List tokens, int i) { - var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT); - if (minus) i++; + public static ParseRes parseNumber(Source src, int i) { + var n = Parsing.skipEmpty(src, i); - var res = ConstantStatement.parseNumber(filename, tokens, i); - if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0)); - else return res.transform(); + if (src.is(i + n, "-")) { + n++; + + var res = Parsing.parseNumber(src, i); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a number after minus"); + n += res.n; + + return ParseRes.res(JSONElement.number(-res.result), n); + } + else { + var res = Parsing.parseNumber(src, i + n).addN(n); + if (!res.isSuccess()) return res.chainError(); + n += res.n; + return ParseRes.res(JSONElement.number(res.result), n); + } } - public static ParseRes parseBool(Filename filename, List tokens, int i) { - var id = parseIdentifier(tokens, i); + public static ParseRes parseLiteral(Source src, int i) { + var id = Parsing.parseIdentifier(src, i); if (!id.isSuccess()) return ParseRes.failed(); - else if (id.result.equals("true")) return ParseRes.res(true, 1); - else if (id.result.equals("false")) return ParseRes.res(false, 1); + else if (id.result.equals("true")) return ParseRes.res(JSONElement.bool(true), id.n); + else if (id.result.equals("false")) return ParseRes.res(JSONElement.bool(false), id.n); + else if (id.result.equals("null")) return ParseRes.res(JSONElement.NULL, id.n); else return ParseRes.failed(); } - public static ParseRes parseValue(Filename filename, List tokens, int i) { - return ParseRes.any( - parseString(filename, tokens, i), - parseNumber(filename, tokens, i), - parseBool(filename, tokens, i), - parseMap(filename, tokens, i), - parseList(filename, tokens, i) + public static ParseRes parseValue(Source src, int i) { + return ParseRes.first(src, i, + JSON::parseString, + JSON::parseNumber, + JSON::parseLiteral, + JSON::parseMap, + JSON::parseList ); } - public static ParseRes parseMap(Filename filename, List tokens, int i) { - int n = 0; - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); + public static ParseRes parseMap(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + + if (!src.is(i + n, "{")) return ParseRes.failed(); + n++; var values = new JSONMap(); + if (src.is(i + n, "}")) return ParseRes.res(new JSONMap(Map.of()), n + 1); while (true) { - if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { - n++; - break; - } + var name = parseString(src, i + n); + if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index"); + n += name.n; + n += Parsing.skipEmpty(src, i + n); - var name = ParseRes.any( - parseIdentifier(tokens, i + n), - parseString(filename, tokens, i + n), - parseNumber(filename, tokens, i + n) - ); - if (!name.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected an index.", name); - else n += name.n; - - if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) { - return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name); - } + if (!src.is(i + n, ":")) return name.chainError(src.loc(i + n), "Expected a colon"); n++; - var res = parseValue(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res); - else n += res.n; + var res = parseValue(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element"); + values.put(name.result.toString(), res.result); + n += res.n; + n += Parsing.skipEmpty(src, i + n); - values.put(name.result.toString(), JSONElement.of(res.result)); - - if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++; - else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + if (src.is(i + n, ",")) n++; + else if (src.is(i + n, "}")) { n++; break; } @@ -87,26 +87,23 @@ public class JSON { return ParseRes.res(values, n); } - public static ParseRes parseList(Filename filename, List tokens, int i) { - int n = 0; - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); + public static ParseRes parseList(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + + if (!src.is(i + n++, "[]")) return ParseRes.failed(); var values = new JSONList(); + if (src.is(i + n, "]")) return ParseRes.res(new JSONList(), n + 1); while (true) { - if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { - n++; - break; - } + var res = parseValue(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element"); + values.add(res.result); + n += res.n; + n += Parsing.skipEmpty(src, i + n); - var res = parseValue(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res); - else n += res.n; - - values.add(JSONElement.of(res.result)); - - if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++; - else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + if (src.is(i + n, ",")) n++; + else if (src.is(i + n, "]")) { n++; break; } @@ -116,7 +113,8 @@ public class JSON { } public static JSONElement parse(Filename filename, String raw) { if (filename == null) filename = new Filename("jscript", "json"); - var res = parseValue(filename, Parsing.tokenize(filename, raw), 0); + + var res = parseValue(new Source(filename, raw), 0); if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given."); else if (res.isError()) throw new SyntaxException(null, res.error); else return JSONElement.of(res.result); diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index 153c3a2..d0d5d47 100644 --- a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -103,7 +103,7 @@ public class FunctionMap { var res = new ArrayList(candidates.size()); for (var candidate : candidates.entrySet()) { - var val = correctBreakpoint(new Location(line, column, candidate.getKey())); + var val = correctBreakpoint(Location.of(candidate.getKey(), line, column)); if (val == null) continue; res.add(val); } diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java index c4c17d2..53a557a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java @@ -1,15 +1,11 @@ package me.topchetoeu.jscript.compilation; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; -import me.topchetoeu.jscript.common.ParseRes.State; import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public abstract class AssignableStatement extends Statement { public abstract Statement toAssign(Statement val, Operation operation); @@ -18,41 +14,37 @@ public abstract class AssignableStatement extends Statement { super(loc); } - public static ParseRes parse(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - + public static ParseRes parse(Source src, int i, Statement prev, int precedence) { if (precedence > 2) return ParseRes.failed(); - var opRes = Parsing.parseOperator(tokens, i + n++); - if (opRes.state != State.SUCCESS) return ParseRes.failed(); + var n = Parsing.skipEmpty(src, i); - var op = opRes.result; - if (!op.isAssign()) return ParseRes.failed(); + for (var op : Operator.opsByLength) { + if (!op.assignable || !src.is(i + n, op.readable + "=")) continue; + n += op.readable.length() + 1; - if (!(prev instanceof AssignableStatement)) { - return ParseRes.error(loc, "Invalid expression on left hand side of assign operator."); + if (!(prev instanceof AssignableStatement)) { + return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); + } + + var res = Parsing.parseValue(src, i + n, 2); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s=' operator", op.readable)); + n += res.n; + + return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, op.operation), n); } - var res = Parsing.parseValue(filename, tokens, i + n, 2); - if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res); + if (!src.is(i + n, "=")) return ParseRes.failed(); + n++; + + if (!(prev instanceof AssignableStatement)) { + return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); + } + + var res = Parsing.parseValue(src, i + n, 2); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '=' operator"); n += res.n; - Operation operation = null; - - if (op == Operator.ASSIGN_ADD) operation = Operation.ADD; - if (op == Operator.ASSIGN_SUBTRACT) operation = Operation.SUBTRACT; - if (op == Operator.ASSIGN_MULTIPLY) operation = Operation.MULTIPLY; - if (op == Operator.ASSIGN_DIVIDE) operation = Operation.DIVIDE; - if (op == Operator.ASSIGN_MODULO) operation = Operation.MODULO; - if (op == Operator.ASSIGN_OR) operation = Operation.OR; - if (op == Operator.ASSIGN_XOR) operation = Operation.XOR; - if (op == Operator.ASSIGN_AND) operation = Operation.AND; - if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Operation.SHIFT_LEFT; - if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Operation.SHIFT_RIGHT; - if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Operation.USHIFT_RIGHT; - - return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n); + return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n); } - } diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java index 62fa7a9..5adf30c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java @@ -4,14 +4,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Vector; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class CompoundStatement extends Statement { @@ -68,45 +66,49 @@ public class CompoundStatement extends Statement { this.statements = statements; } - public static ParseRes parseComma(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - + public static ParseRes parseComma(Source src, int i, Statement prev, int precedence) { if (precedence > 1) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.COMMA)) return ParseRes.failed(); - var res = Parsing.parseValue(filename, tokens, i + n, 2); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res); + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, ",")) return ParseRes.failed(); + n++; + + var res = Parsing.parseValue(src, i + n, 2); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma"); n += res.n; return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n); } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "{")) return ParseRes.failed(); + n++; var statements = new ArrayList(); while (true) { - if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n += Parsing.skipEmpty(src, i + n); + + if (src.is(i + n, "}")) { n++; break; } - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { + if (src.is(i + n, ";")) { n++; continue; } - var res = Parsing.parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) { - return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a statement.", res); - } + var res = Parsing.parseStatement(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement"); n += res.n; statements.add(res.result); } - return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(Parsing.getLoc(filename, tokens, i + n - 1)), n); + return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(src.loc(i + n - 1)), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java index 25d16f1..dd10c58 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java @@ -3,14 +3,12 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; import java.util.List; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class VariableDeclareStatement extends Statement { @@ -56,48 +54,59 @@ public class VariableDeclareStatement extends Statement { this.values = values; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "var")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!Parsing.isIdentifier(src, i + n, "var")) return ParseRes.failed(); + n += 3; var res = new ArrayList(); - if (Parsing.isStatementEnd(tokens, i + n)) { - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), 2); - else return ParseRes.res(new VariableDeclareStatement(loc, res), 1); + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new VariableDeclareStatement(loc, res), n); } while (true) { - var nameLoc = Parsing.getLoc(filename, tokens, i + n); - var nameRes = Parsing.parseIdentifier(tokens, i + n++); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name."); + var nameLoc = src.loc(i + n); + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name"); + n += name.n; - if (!Parsing.checkVarName(nameRes.result)) { - return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", nameRes.result)); + if (!Parsing.checkVarName(name.result)) { + return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result)); } Statement val = null; + n += Parsing.skipEmpty(src, i + n); - if (Parsing.isOperator(tokens, i + n, Operator.ASSIGN)) { + if (src.is(i + n, "=")) { n++; - var valRes = Parsing.parseValue(filename, tokens, i + n, 2); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes); + + var valRes = Parsing.parseValue(src, i + n, 2); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='"); + n += valRes.n; + n += Parsing.skipEmpty(src, i + n); val = valRes.result; } - res.add(new Pair(nameRes.result, val, nameLoc)); + res.add(new Pair(name.result, val, nameLoc)); - if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + if (src.is(i + n, ",")) { n++; continue; } - else if (Parsing.isStatementEnd(tokens, i + n)) { - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new VariableDeclareStatement(loc, res), n + 1); - else return ParseRes.res(new VariableDeclareStatement(loc, res), n); + + end = Parsing.parseStatementEnd(src, i + n); + + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new VariableDeclareStatement(loc, res), n); } - else return ParseRes.error(loc, "Expected a comma or end of statement."); + else return end.chainError(src.loc(i + n), "Expected a comma or end of statement"); } } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java index 4c39bc7..5a62233 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java @@ -1,16 +1,12 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class BreakStatement extends Statement { public final String label; @@ -25,22 +21,28 @@ public class BreakStatement extends Statement { this.label = label; } - public static ParseRes parseBreak(Filename filename, List tokens, int i) { - if (!Parsing.isIdentifier(tokens, i, "break")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (Parsing.isStatementEnd(tokens, i + 1)) { - if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), null), 2); - else return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), null), 1); + if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed(); + n += 5; + + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new BreakStatement(loc, null), n); } - var labelRes = Parsing.parseIdentifier(tokens, i + 1); - if (labelRes.isFailed()) return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a label name or an end of statement."); - var label = labelRes.result; + var label = Parsing.parseIdentifier(src, i + n); + if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); + n += label.n; - if (Parsing.isStatementEnd(tokens, i + 2)) { - if (Parsing.isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), label), 3); - else return ParseRes.res(new BreakStatement(Parsing.getLoc(filename, tokens, i), label), 2); + end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new BreakStatement(loc, label.result), n); } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement."); + else return end.chainError(src.loc(i + n), "Expected end of statement"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java index 2513892..281e11c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java @@ -1,16 +1,12 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ContinueStatement extends Statement { public final String label; @@ -25,22 +21,28 @@ public class ContinueStatement extends Statement { this.label = label; } - public static ParseRes parseContinue(Filename filename, List tokens, int i) { - if (!Parsing.isIdentifier(tokens, i, "continue")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (Parsing.isStatementEnd(tokens, i + 1)) { - if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), null), 2); - else return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), null), 1); + if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed(); + n += 8; + + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new ContinueStatement(loc, null), n); } - var labelRes = Parsing.parseIdentifier(tokens, i + 1); - if (labelRes.isFailed()) return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a label name or an end of statement."); - var label = labelRes.result; + var label = Parsing.parseIdentifier(src, i + n); + if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); + n += label.n; - if (Parsing.isStatementEnd(tokens, i + 2)) { - if (Parsing.isOperator(tokens, i + 2, Operator.SEMICOLON)) return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), label), 3); - else return ParseRes.res(new ContinueStatement(Parsing.getLoc(filename, tokens, i), label), 2); + end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new ContinueStatement(loc, label.result), n); } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement."); + else return end.chainError(src.loc(i + n), "Expected end of statement"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java index bf1c352..4150118 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java @@ -1,16 +1,12 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class DebugStatement extends Statement { @Override public void compile(CompileResult target, boolean pollute) { @@ -22,14 +18,19 @@ public class DebugStatement extends Statement { super(loc); } - public static ParseRes parse(Filename filename, List tokens, int i) { - if (!Parsing.isIdentifier(tokens, i, "debugger")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (Parsing.isStatementEnd(tokens, i + 1)) { - if (Parsing.isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new DebugStatement(Parsing.getLoc(filename, tokens, i)), 2); - else return ParseRes.res(new DebugStatement(Parsing.getLoc(filename, tokens, i)), 1); + if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed(); + n += 8; + + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new DebugStatement(loc), n); } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement."); + else return end.chainError(src.loc(i + n), "Expected end of statement"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java index cdc35db..52a8c7d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java @@ -1,18 +1,15 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.compilation.values.ConstantStatement; +import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.compilation.values.IndexStatement; import me.topchetoeu.jscript.compilation.values.VariableStatement; +import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; public class DeleteStatement extends Statement { public final Statement key; @@ -27,13 +24,15 @@ public class DeleteStatement extends Statement { if (pollute) target.add(Instruction.pushValue(true)); } - public static ParseRes parseDelete(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "delete")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var valRes = Parsing.parseValue(filename, tokens, i + n, 15); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'delete'.", valRes); + if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed(); + n += 6; + + var valRes = Parsing.parseValue(src, i + n, 15); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'"); n += valRes.n; if (valRes.result instanceof IndexStatement) { @@ -41,9 +40,9 @@ public class DeleteStatement extends Statement { return ParseRes.res(new DeleteStatement(loc, index.index, index.object), n); } else if (valRes.result instanceof VariableStatement) { - return ParseRes.error(loc, "A variable may not be deleted."); + return ParseRes.error(src.loc(i + n), "A variable may not be deleted"); } - else return ParseRes.res(new ConstantStatement(loc, true), n); + else return ParseRes.res(new BoolStatement(loc, true), n); } public DeleteStatement(Location loc, Statement key, Statement value) { diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java index 456b487..1d64dd8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java @@ -1,17 +1,13 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class DoWhileStatement extends Statement { public final Statement condition, body; @@ -41,34 +37,41 @@ public class DoWhileStatement extends Statement { this.body = body; } - public static ParseRes parseDoWhile(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var labelRes = WhileStatement.parseLabel(tokens, i + n); + var labelRes = WhileStatement.parseLabel(src, i + n); n += labelRes.n; - if (!Parsing.isIdentifier(tokens, i + n++, "do")) return ParseRes.failed(); - var bodyRes = Parsing.parseStatement(filename, tokens, i + n); - if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a do-while body.", bodyRes); + if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed(); + n += 2; + + var bodyRes = Parsing.parseStatement(src, i + n); + if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body."); n += bodyRes.n; - if (!Parsing.isIdentifier(tokens, i + n++, "while")) return ParseRes.error(loc, "Expected 'while' keyword."); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'."); + if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed(); + n += 5; + n += Parsing.skipEmpty(src, i + n); - var condRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes); + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); + n++; + + var condRes = Parsing.parseValue(src, i + n, 0); + if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition."); n += condRes.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition."); + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition."); + n++; - var res = ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); - - if (Parsing.isStatementEnd(tokens, i + n)) { - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); - else return res; + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a semicolon."); + else return end.chainError(src.loc(i + n), "Expected end of statement"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java index d4d162f..bbf4e03 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java @@ -1,23 +1,19 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ForInStatement extends Statement { public final String varName; public final boolean isDeclaration; - public final Statement varValue, object, body; + public final Statement object, body; public final String label; public final Location varLocation; @@ -31,11 +27,6 @@ public class ForInStatement extends Statement { if (key instanceof String) target.add(Instruction.makeVar((String)key)); - if (varValue != null) { - varValue.compile(target, true); - target.add(Instruction.storeVar(target.scope.getKey(varName))); - } - object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(true)); @@ -61,70 +52,56 @@ public class ForInStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) { + public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement object, Statement body) { super(loc); this.varLocation = varLocation; this.label = label; this.isDeclaration = isDecl; this.varName = varName; - this.varValue = varValue; this.object = object; this.body = body; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var label = WhileStatement.parseLabel(src, i + n); + n += label.n; + n += Parsing.skipEmpty(src, i + n); + + if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed(); + n += 3; + n += Parsing.skipEmpty(src, i + n); - var labelRes = WhileStatement.parseLabel(tokens, i + n); var isDecl = false; - n += labelRes.n; - if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); - - if (Parsing.isIdentifier(tokens, i + n, "var")) { + if (Parsing.isIdentifier(src, i + n, "var")) { isDecl = true; - n++; + n += 3; } - var nameRes = Parsing.parseIdentifier(tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop."); - var nameLoc = Parsing.getLoc(filename, tokens, i + n); - n += nameRes.n; + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop"); + var nameLoc = src.loc(i + n); + n += name.n; + n += Parsing.skipEmpty(src, i + n); - Statement varVal = null; + if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration"); + n += 2; - if (Parsing.isOperator(tokens, i + n, Operator.ASSIGN)) { - n++; + var obj = Parsing.parseValue(src, i + n, 0); + if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); + n += obj.n; + n += Parsing.skipEmpty(src, i + n); - var valRes = Parsing.parseValue(filename, tokens, i + n, 2); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after '='.", valRes); - n += nameRes.n; + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); + n++; - varVal = valRes.result; - } - - if (!Parsing.isIdentifier(tokens, i + n++, "in")) { - if (varVal == null) { - if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); - else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); - } - return ParseRes.error(loc, "Expected 'in' keyword after variable declaration."); - } - - var objRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes); - n += objRes.n; - - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); - - - var bodyRes = Parsing.parseStatement(filename, tokens, i + n); - if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes); + var bodyRes = Parsing.parseStatement(src, i + n); + if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; - return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n); + return ParseRes.res(new ForInStatement(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); } - } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java index a304252..b56c3d9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java @@ -1,17 +1,13 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ForOfStatement extends Statement { public final String varName; @@ -78,44 +74,46 @@ public class ForOfStatement extends Statement { this.body = body; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var label = WhileStatement.parseLabel(src, i + n); + n += label.n; + n += Parsing.skipEmpty(src, i + n); + + if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed(); + n += 3; + n += Parsing.skipEmpty(src, i + n); - var labelRes = WhileStatement.parseLabel(tokens, i + n); var isDecl = false; - n += labelRes.n; - if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); - - if (Parsing.isIdentifier(tokens, i + n, "var")) { + if (Parsing.isIdentifier(src, i + n, "var")) { isDecl = true; - n++; + n += 3; } - var nameRes = Parsing.parseIdentifier(tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop."); - var nameLoc = Parsing.getLoc(filename, tokens, i + n); - n += nameRes.n; + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop"); + var nameLoc = src.loc(i + n); + n += name.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isIdentifier(tokens, i + n++, "of")) { - if (nameRes.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); - else if (nameRes.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); - else return ParseRes.error(loc, "Expected 'of' keyword after variable declaration."); - } + if (!Parsing.isIdentifier(src, i + n, "fo")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); + n += 2; - var objRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!objRes.isSuccess()) return ParseRes.error(loc, "Expected a value.", objRes); - n += objRes.n; + var obj = Parsing.parseValue(src, i + n, 0); + if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); + n += obj.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); - + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); + n++; - var bodyRes = Parsing.parseStatement(filename, tokens, i + n); - if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes); + var bodyRes = Parsing.parseStatement(src, i + n); + if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); n += bodyRes.n; - return ParseRes.res(new ForOfStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, objRes.result, bodyRes.result), n); + return ParseRes.res(new ForOfStatement(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java index e97a073..8dbfec7 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java @@ -1,20 +1,15 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.VariableDeclareStatement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.compilation.values.ConstantStatement; +import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.compilation.values.DiscardStatement; public class ForStatement extends Statement { public final Statement declaration, assignment, condition, body; @@ -53,61 +48,69 @@ public class ForStatement extends Statement { this.body = body; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + private static ParseRes parseSemicolon(Source src, int i) { + var n = Parsing.skipEmpty(src, i); - var labelRes = WhileStatement.parseLabel(tokens, i + n); - n += labelRes.n; + if (!src.is(i + n, ";")) return ParseRes.failed(); + else return ParseRes.res(new DiscardStatement(src.loc(i), null), n + 1); + } + private static ParseRes parseCondition(Source src, int i) { + var n = Parsing.skipEmpty(src, i); - if (!Parsing.isIdentifier(tokens, i + n++, "for")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'for'."); - - Statement decl, cond, inc; - - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { - n++; - decl = new CompoundStatement(loc, false); - } - else { - var declRes = ParseRes.any( - VariableDeclareStatement.parse(filename, tokens, i + n), - Parsing.parseValueStatement(filename, tokens, i + n) - ); - if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes); - n += declRes.n; - decl = declRes.result; - } - - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { - n++; - cond = new ConstantStatement(loc, 1); - } - else { - var condRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", condRes); - n += condRes.n; - if (!Parsing.isOperator(tokens, i + n++, Operator.SEMICOLON)) return ParseRes.error(loc, "Expected a semicolon.", condRes); - cond = condRes.result; - } - - if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - inc = new CompoundStatement(loc, false); - } - else { - var incRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!incRes.isSuccess()) return ParseRes.error(loc, "Expected a condition.", incRes); - n += incRes.n; - inc = incRes.result; - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after for."); - } - - - var res = Parsing.parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a for body.", res); + var res = Parsing.parseValue(src, i + n, 0); + if (!res.isSuccess()) return res.chainError(); n += res.n; + n += Parsing.skipEmpty(src, i + n); - return ParseRes.res(new ForStatement(loc, labelRes.result, decl, cond, inc, res.result), n); + if (!src.is(i + n, ";")) return ParseRes.error(src.loc(i + n), "Expected a semicolon"); + else return ParseRes.res(res.result, n + 1); + } + private static ParseRes parseUpdater(Source src, int i) { + return Parsing.parseValue(src, i, 0); + } + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var labelRes = WhileStatement.parseLabel(src, i + n); + n += labelRes.n; + n += Parsing.skipEmpty(src, i + n); + + if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed(); + n += 3; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'for'"); + n++; + + ParseRes decl = ParseRes.first(src, i + n, + ForStatement::parseSemicolon, + VariableDeclareStatement::parse, + ForStatement::parseCondition + ); + if (!decl.isSuccess()) return decl.chainError(src.loc(i + n), "Expected a declaration or an expression"); + n += decl.n; + + ParseRes cond = ParseRes.first(src, i + n, + ForStatement::parseSemicolon, + ForStatement::parseCondition + ); + if (!cond.isSuccess()) return cond.chainError(src.loc(i + n), "Expected a condition"); + n += cond.n; + + var update = parseUpdater(src, i + n); + if (update.isError()) return update.chainError(); + n += update.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater"); + n++; + + var body = Parsing.parseStatement(src, i + n); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body."); + n += body.n; + + return ParseRes.res(new ForStatement(loc, labelRes.result, decl.result, cond.result, update.result, body.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java index 8dd2034..5c0fcdb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java @@ -1,17 +1,13 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class IfStatement extends Statement { public final Statement condition, body, elseBody; @@ -53,47 +49,58 @@ public class IfStatement extends Statement { this.elseBody = elseBody; } - public static ParseRes parseTernary(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - + public static ParseRes parseTernary(Source src, int i, Statement prev, int precedence) { if (precedence > 2) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.QUESTION)) return ParseRes.failed(); - var a = Parsing.parseValue(filename, tokens, i + n, 2); - if (!a.isSuccess()) return ParseRes.error(loc, "Expected a value after the ternary operator.", a); + var n = Parsing.skipEmpty(src, i); + + if (!src.is(i + n, "?")) return ParseRes.failed(); + var loc = src.loc(i + n); + n++; + + var a = Parsing.parseValue(src, i + n, 2); + if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator."); n += a.n; + n += Parsing.skipEmpty(src, i); - if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed(); + if (!src.is(i + n, ":")) return ParseRes.failed(); + n++; - var b = Parsing.parseValue(filename, tokens, i + n, 2); - if (!b.isSuccess()) return ParseRes.error(loc, "Expected a second value after the ternary operator.", b); + var b = Parsing.parseValue(src, i + n, 2); + if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator."); n += b.n; return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n); } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (!Parsing.isIdentifier(tokens, i + n++, "if")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'if'."); + if (!Parsing.isIdentifier(src, i + n, "if")) return ParseRes.failed(); + n += 2; + n += Parsing.skipEmpty(src, i + n); - var condRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes); - n += condRes.n; - - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition."); - - var res = Parsing.parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res); - n += res.n; - - if (!Parsing.isIdentifier(tokens, i + n, "else")) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'."); n++; - var elseRes = Parsing.parseStatement(filename, tokens, i + n); - if (!elseRes.isSuccess()) return ParseRes.error(loc, "Expected an else body.", elseRes); + var condRes = Parsing.parseValue(src, i + n, 0); + if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition."); + n += condRes.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition."); + n++; + + var res = Parsing.parseStatement(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body."); + n += res.n; + + var elseKw = Parsing.parseIdentifier(src, i + n, "else"); + if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); + n += elseKw.n; + + var elseRes = Parsing.parseStatement(src, i + n); + if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body."); n += elseRes.n; return ParseRes.res(new IfStatement(loc, condRes.result, res.result, elseRes.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java index a9e0f5c..84dd2fa 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java @@ -1,16 +1,12 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ReturnStatement extends Statement { public final Statement value; @@ -27,26 +23,28 @@ public class ReturnStatement extends Statement { this.value = value; } - public static ParseRes parseReturn(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "return")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (Parsing.isStatementEnd(tokens, i + n)) { - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return ParseRes.res(new ReturnStatement(loc, null), 2); - else return ParseRes.res(new ReturnStatement(loc, null), 1); + if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed(); + n += 6; + + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new ReturnStatement(loc, null), n); } - var valRes = Parsing.parseValue(filename, tokens, i + n, 0); - n += valRes.n; - if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes); + var val = Parsing.parseValue(src, i + n, 0); + if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); + n += val.n; - var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n); - - if (Parsing.isStatementEnd(tokens, i + n)) { - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); - else return res; + end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new ReturnStatement(loc, val.result), n); } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.", valRes); + else return end.chainError(src.loc(i + n), "Expected end of statement"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java index 1883bd8..6d44178 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java @@ -2,21 +2,17 @@ package me.topchetoeu.jscript.compilation.control; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class SwitchStatement extends Statement { public static class SwitchCase { @@ -87,78 +83,96 @@ public class SwitchStatement extends Statement { this.body = body; } - private static ParseRes parseSwitchCase(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + private static ParseRes parseSwitchCase(Source src, int i) { + var n = Parsing.skipEmpty(src, i); - if (!Parsing.isIdentifier(tokens, i + n++, "case")) return ParseRes.failed(); + if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed(); + n += 4; - var valRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'case'.", valRes); + var valRes = Parsing.parseValue(src, i + n, 0); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'"); n += valRes.n; - if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected colons after 'case' value."); + if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'case' value"); + n++; return ParseRes.res(valRes.result, n); } - private static ParseRes parseDefaultCase(List tokens, int i) { - if (!Parsing.isIdentifier(tokens, i, "default")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + 1, Operator.COLON)) return ParseRes.error(Parsing.getLoc(null, tokens, i), "Expected colons after 'default'."); + private static ParseRes parseDefaultCase(Source src, int i) { + var n = Parsing.skipEmpty(src, i); - return ParseRes.res(null, 2); + if (!Parsing.isIdentifier(src, i + n, "default")) return ParseRes.failed(); + n += 7; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'default'"); + n++; + + return ParseRes.res(null, n); } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + @SuppressWarnings("unused") + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (!Parsing.isIdentifier(tokens, i + n++, "switch")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'switch'."); + if (!Parsing.isIdentifier(src, i + n, "switch")) return ParseRes.failed(); + n += 6; + n += Parsing.skipEmpty(src, i + n); + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'"); + n++; - var valRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a switch value.", valRes); + var valRes = Parsing.parseValue(src, i + n, 0); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value"); n += valRes.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after switch value."); - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.error(loc, "Expected an opening brace after switch value."); + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after switch value"); + n++; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected an opening brace after switch value"); + n++; + n += Parsing.skipEmpty(src, i + n); var statements = new ArrayList(); var cases = new ArrayList(); var defaultI = -1; while (true) { - if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + n += Parsing.skipEmpty(src, i + n); + + if (src.is(i + n, "}")) { n++; break; } - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) { + if (src.is(i + n, ";")) { n++; continue; } - var defaultRes = SwitchStatement.parseDefaultCase(tokens, i + n); - var caseRes = SwitchStatement.parseSwitchCase(filename, tokens, i + n); + ParseRes caseRes = ParseRes.first(src, i + n, + SwitchStatement::parseDefaultCase, + SwitchStatement::parseSwitchCase + ); - if (defaultRes.isSuccess()) { - defaultI = statements.size(); - n += defaultRes.n; - } - else if (caseRes.isSuccess()) { - cases.add(new SwitchCase(caseRes.result, statements.size())); + // Parsing::parseStatement + + if (caseRes.isSuccess()) { n += caseRes.n; + + if (caseRes.result == null) defaultI = statements.size(); + else cases.add(new SwitchCase(caseRes.result, statements.size())); + continue; } - else if (defaultRes.isError()) return defaultRes.transform(); - else if (caseRes.isError()) return defaultRes.transform(); - else { - var res = ParseRes.any( - Parsing.parseStatement(filename, tokens, i + n), - CompoundStatement.parse(filename, tokens, i + n) - ); - if (!res.isSuccess()) { - return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected a statement.", res); - } - n += res.n; - statements.add(res.result); + if (caseRes.isError()) return caseRes.chainError(); + + var stm = Parsing.parseStatement(src, i + n); + if (stm.isSuccess()) { + n += stm.n; + statements.add(stm.result); + continue; } + else stm.chainError(src.loc(i + n), "Expected a statement, 'case' or 'default'"); } return ParseRes.res(new SwitchStatement( diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java index 3e19c1f..0993321 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java @@ -1,16 +1,12 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ThrowStatement extends Statement { public final Statement value; @@ -26,21 +22,28 @@ public class ThrowStatement extends Statement { this.value = value; } - public static ParseRes parseThrow(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "throw")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var valRes = Parsing.parseValue(filename, tokens, i + n, 0); - n += valRes.n; - if (valRes.isError()) return ParseRes.error(loc, "Expected a throw value.", valRes); + if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed(); + n += 5; - var res = ParseRes.res(new ThrowStatement(loc, valRes.result), n); - - if (Parsing.isStatementEnd(tokens, i + n)) { - if (Parsing.isOperator(tokens, i + n, Operator.SEMICOLON)) return res.addN(1); - else return res; + var end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new ThrowStatement(loc, null), n); } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i), "Expected an end of statement.", valRes); + + var val = Parsing.parseValue(src, i + n, 0); + if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); + n += val.n; + + end = Parsing.parseStatementEnd(src, i + n); + if (end.isSuccess()) { + n += end.n; + return ParseRes.res(new ThrowStatement(loc, val.result), n); + } + else return end.chainError(src.loc(i + n), "Expected end of statement"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java index 5854df2..78120b0 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java @@ -1,17 +1,14 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class TryStatement extends Statement { public final Statement tryBody; @@ -61,44 +58,56 @@ public class TryStatement extends Statement { this.name = name; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (!Parsing.isIdentifier(tokens, i + n++, "try")) return ParseRes.failed(); + if (!Parsing.isIdentifier(src, i + n, "try")) return ParseRes.failed(); + n += 3; - var res = Parsing.parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res); - n += res.n; + var tryBody = CompoundStatement.parse(src, i + n); + if (!tryBody.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a try body"); + n += tryBody.n; + n += Parsing.skipEmpty(src, i + n); String name = null; Statement catchBody = null, finallyBody = null; - - if (Parsing.isIdentifier(tokens, i + n, "catch")) { - n++; - if (Parsing.isOperator(tokens, i + n, Operator.PAREN_OPEN)) { + if (Parsing.isIdentifier(src, i + n, "catch")) { + n += 5; + n += Parsing.skipEmpty(src, i + n); + if (src.is(i + n, "(")) { n++; - var nameRes = Parsing.parseIdentifier(tokens, i + n++); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a catch variable name."); + var nameRes = Parsing.parseIdentifier(src, i + n); + if (!nameRes.isSuccess()) return nameRes.chainError(src.loc(i + n), "xpected a catch variable name"); name = nameRes.result; - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after catch variable name."); + n += nameRes.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after catch variable name"); + n++; } - var catchRes = Parsing.parseStatement(filename, tokens, i + n); - if (!catchRes.isSuccess()) return ParseRes.error(loc, "Expected a catch body.", catchRes); - n += catchRes.n; - catchBody = catchRes.result; + var bodyRes = CompoundStatement.parse(src, i + n); + if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a catch body"); + n += bodyRes.n; + n += Parsing.skipEmpty(src, i + n); + + catchBody = bodyRes.result; } - if (Parsing.isIdentifier(tokens, i + n, "finally")) { - n++; - var finallyRes = Parsing.parseStatement(filename, tokens, i + n); - if (!finallyRes.isSuccess()) return ParseRes.error(loc, "Expected a finally body.", finallyRes); - n += finallyRes.n; - finallyBody = finallyRes.result; + if (Parsing.isIdentifier(src, i + n, "finally")) { + n += 7; + + var bodyRes = CompoundStatement.parse(src, i + n); + if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a finally body"); + n += bodyRes.n; + n += Parsing.skipEmpty(src, i + n); + finallyBody = bodyRes.result; } - return ParseRes.res(new TryStatement(loc, res.result, catchBody, finallyBody, name), n); + if (finallyBody == null && catchBody == null) ParseRes.error(src.loc(i + n), "Expected catch or finally"); + + return ParseRes.res(new TryStatement(loc, tryBody.result, catchBody, finallyBody, name), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java index ed9cc06..2aa1cdb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java @@ -1,18 +1,14 @@ package me.topchetoeu.jscript.compilation.control; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class WhileStatement extends Statement { public final Statement condition, body; @@ -38,14 +34,20 @@ public class WhileStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public static ParseRes parseLabel(List tokens, int i) { - int n = 0; - - var nameRes = Parsing.parseIdentifier(tokens, i + n++); - if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.failed(); - + public static ParseRes parseLabel(Source src, int i) { + int n = Parsing.skipEmpty(src, i); + + var nameRes = Parsing.parseIdentifier(src, i + n); + if (!nameRes.isSuccess()) return nameRes.chainError(); + n += nameRes.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ":")) return ParseRes.failed(); + n++; + return ParseRes.res(nameRes.result, n); } + public WhileStatement(Location loc, String label, Statement condition, Statement body) { super(loc); this.label = label; @@ -65,24 +67,31 @@ public class WhileStatement extends Statement { } } - public static ParseRes parseWhile(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var labelRes = WhileStatement.parseLabel(tokens, i + n); + var labelRes = WhileStatement.parseLabel(src, i + n); n += labelRes.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isIdentifier(tokens, i + n++, "while")) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a open paren after 'while'."); + if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed(); + n += 5; + n += Parsing.skipEmpty(src, i + n); - var condRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected a while condition.", condRes); + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); + n++; + + var condRes = Parsing.parseValue(src, i + n, 0); + if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition."); n += condRes.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after while condition."); + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after while condition."); + n++; - var res = Parsing.parseStatement(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a while body.", res); + var res = Parsing.parseStatement(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body."); n += res.n; return ParseRes.res(new WhileStatement(loc, labelRes.result, condRes.result, res.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java index 3e06258..acfb9c9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java @@ -1,113 +1,48 @@ package me.topchetoeu.jscript.compilation.parsing; -import java.util.HashMap; -import java.util.Map; +import java.util.Arrays; +import java.util.LinkedHashSet; import me.topchetoeu.jscript.common.Operation; public enum Operator { - MULTIPLY("*", Operation.MULTIPLY, 13), - DIVIDE("/", Operation.DIVIDE, 12), - MODULO("%", Operation.MODULO, 12), - SUBTRACT("-", Operation.SUBTRACT, 11), - ADD("+", Operation.ADD, 11), - SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10), - SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10), - USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10), - GREATER(">", Operation.GREATER, 9), - LESS("<", Operation.LESS, 9), - GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9), - LESS_EQUALS("<=", Operation.LESS_EQUALS, 9), - NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8), - LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8), - EQUALS("==", Operation.LOOSE_EQUALS, 8), - LOOSE_EQUALS("===", Operation.EQUALS, 8), - AND("&", Operation.AND, 7), - XOR("^", Operation.XOR, 6), - OR("|", Operation.OR, 5), - LAZY_AND("&&", 4), - LAZY_OR("||", 3), - ASSIGN_SHIFT_LEFT("<<=", 2, true), - ASSIGN_SHIFT_RIGHT(">>=", 2, true), - ASSIGN_USHIFT_RIGHT(">>>=", 2, true), - ASSIGN_AND("&=", 2, true), - ASSIGN_OR("|=", 2, true), - ASSIGN_XOR("^=", 2, true), - ASSIGN_MODULO("%=", 2, true), - ASSIGN_DIVIDE("/=", 2, true), - ASSIGN_MULTIPLY("*=", 2, true), - ASSIGN_SUBTRACT("-=", 2, true), - ASSIGN_ADD("+=", 2, true), - ASSIGN("=", 2, true), - SEMICOLON(";"), - COLON(":"), - PAREN_OPEN("("), - PAREN_CLOSE(")"), - BRACKET_OPEN("["), - BRACKET_CLOSE("]"), - BRACE_OPEN("{"), - BRACE_CLOSE("}"), - DOT("."), - COMMA(","), - NOT("!"), - QUESTION("?"), - INVERSE("~"), - INCREASE("++"), - DECREASE("--"); + MULTIPLY("*", Operation.MULTIPLY, 13, true), + DIVIDE("/", Operation.DIVIDE, 12, true), + MODULO("%", Operation.MODULO, 12, true), + SUBTRACT("-", Operation.SUBTRACT, 11, true), + ADD("+", Operation.ADD, 11, true), + SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10, true), + SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10, true), + USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10, true), + GREATER(">", Operation.GREATER, 9, false), + LESS("<", Operation.LESS, 9, false), + GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9, false), + LESS_EQUALS("<=", Operation.LESS_EQUALS, 9, false), + NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8, false), + LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8, false), + EQUALS("==", Operation.LOOSE_EQUALS, 8, false), + LOOSE_EQUALS("===", Operation.EQUALS, 8, false), + AND("&", Operation.AND, 7, true), + XOR("^", Operation.XOR, 6, true), + OR("|", Operation.OR, 5, true); public final String readable; public final Operation operation; public final int precedence; - public final boolean reverse; - private static final Map ops = new HashMap<>(); + public final boolean assignable; + + public static final LinkedHashSet opsByLength = new LinkedHashSet(); static { - for (var el : Operator.values()) { - ops.put(el.readable, el); - } + var vals = Operator.values(); + Arrays.sort(vals, (a, b) -> b.readable.length() - a.readable.length()); + for (var el : vals) opsByLength.add(el); } - public boolean isAssign() { return precedence == 2; } - - public static Operator parse(String val) { - return ops.get(val); - } - - private Operator() { - this.readable = null; - this.operation = null; - this.precedence = -1; - this.reverse = false; - } - private Operator(String value) { - this.readable = value; - this.operation = null; - this.precedence = -1; - this.reverse = false; - } - private Operator(String value, int precedence) { - this.readable = value; - this.operation = null; - this.precedence = precedence; - this.reverse = false; - } - private Operator(String value, int precedence, boolean reverse) { - this.readable = value; - this.operation = null; - this.precedence = precedence; - this.reverse = reverse; - } - - private Operator(String value, Operation funcName, int precedence) { + private Operator(String value, Operation funcName, int precedence, boolean assignable) { this.readable = value; this.operation = funcName; this.precedence = precedence; - this.reverse = false; - } - private Operator(String value, Operation funcName, int precedence, boolean reverse) { - this.readable = value; - this.operation = funcName; - this.precedence = precedence; - this.reverse = reverse; + this.assignable = assignable; } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/common/ParseRes.java b/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java similarity index 53% rename from src/java/me/topchetoeu/jscript/common/ParseRes.java rename to src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java index a373a5d..ae539aa 100644 --- a/src/java/me/topchetoeu/jscript/common/ParseRes.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java @@ -1,13 +1,12 @@ -package me.topchetoeu.jscript.common; +package me.topchetoeu.jscript.compilation.parsing; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.compilation.parsing.TestRes; -import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser; +import me.topchetoeu.jscript.common.Location; public class ParseRes { + public static interface Parser { + public ParseRes parse(Source src, int i); + } + public static enum State { SUCCESS, FAILED, @@ -34,11 +33,11 @@ public class ParseRes { if (!state.isSuccess()) return this; return new ParseRes<>(state, null, result, i); } - public ParseRes addN(int i) { + public ParseRes addN(int n) { if (!state.isSuccess()) return this; - return new ParseRes<>(state, null, result, this.n + i); + return new ParseRes<>(state, null, result, this.n + n); } - public ParseRes transform() { + public ParseRes chainError() { if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed."); return new ParseRes<>(state, error, null, 0); } @@ -59,41 +58,26 @@ public class ParseRes { if (loc != null) error = loc + ": " + error; return new ParseRes<>(State.ERROR, error, null, 0); } - public static ParseRes error(Location loc, String error, ParseRes other) { + public ParseRes chainError(Location loc, String error) { if (loc != null) error = loc + ": " + error; - if (!other.isError()) return new ParseRes<>(State.ERROR, error, null, 0); - return new ParseRes<>(State.ERROR, other.error, null, 0); + if (!this.isError()) return new ParseRes<>(State.ERROR, error, null, 0); + return new ParseRes<>(State.ERROR, this.error, null, 0); } public static ParseRes res(T val, int i) { return new ParseRes<>(State.SUCCESS, null, val, i); } @SafeVarargs - public static ParseRes any(ParseRes ...parsers) { - return any(List.of(parsers)); - } - public static ParseRes any(List> parsers) { - ParseRes best = null; - ParseRes error = ParseRes.failed(); + @SuppressWarnings("all") + // to hell with all of java's bullshit generics that do jack shit nothing + public static ParseRes first(Source src, int i, Parser ...parsers) { + int n = Parsing.skipEmpty(src, i); + ParseRes error = ParseRes.failed(); for (var parser : parsers) { - if (parser.isSuccess()) { - if (best == null || best.n < parser.n) best = parser; - } - else if (parser.isError() && error.isFailed()) error = parser.transform(); - } - - if (best != null) return best; - else return error; - } - @SafeVarargs - public static ParseRes first(String filename, List tokens, Map> named, Parser ...parsers) { - ParseRes error = ParseRes.failed(); - - for (var parser : parsers) { - var res = parser.parse(null, tokens, 0); - if (res.isSuccess()) return res; - else if (res.isError() && error.isFailed()) error = res.transform(); + var res = parser.parse(src, i + n); + if (res.isSuccess()) return res.addN(n); + if (res.isError() && error.isFailed()) error = res.chainError(); } return error; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java index 321ec1a..c5b377a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java @@ -1,398 +1,49 @@ package me.topchetoeu.jscript.compilation.parsing; -import java.lang.invoke.CallSite; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; +import java.util.Set; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.*; import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; import me.topchetoeu.jscript.compilation.values.*; +import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; +import me.topchetoeu.jscript.compilation.values.constants.NullStatement; +import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; +import me.topchetoeu.jscript.compilation.values.constants.StringStatement; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; // TODO: this has to be rewritten // @SourceFile public class Parsing { - public static interface Parser { - ParseRes parse(Filename filename, List tokens, int i); - } - - public static class ObjProp { - public final String name; - public final String access; - public final FunctionStatement func; - - public ObjProp(String name, String access, FunctionStatement func) { - this.name = name; - this.access = access; - this.func = func; - } - } - public static final HashMap> functions = new HashMap<>(); - private static final HashSet reserved = new HashSet(); - static { - reserved.add("true"); - reserved.add("false"); - reserved.add("void"); - reserved.add("null"); - reserved.add("this"); - reserved.add("if"); - reserved.add("else"); - reserved.add("try"); - reserved.add("catch"); - reserved.add("finally"); - reserved.add("for"); - reserved.add("do"); - reserved.add("while"); - reserved.add("switch"); - reserved.add("case"); - reserved.add("default"); - reserved.add("new"); - reserved.add("function"); - reserved.add("var"); - reserved.add("return"); - reserved.add("throw"); - reserved.add("typeof"); - reserved.add("delete"); - reserved.add("break"); - reserved.add("continue"); - reserved.add("debugger"); - reserved.add("implements"); - reserved.add("interface"); - reserved.add("package"); - reserved.add("private"); - reserved.add("protected"); - reserved.add("public"); - reserved.add("static"); - // Although ES5 allow these, we will comply to ES6 here - reserved.add("const"); - reserved.add("let"); - // 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 - } + private static final Set reserved = Set.of( + "true", "false", "void", "null", "this", "if", "else", "try", "catch", + "finally", "for", "do", "while", "switch", "case", "default", "new", + "function", "var", "return", "throw", "typeof", "delete", "break", + "continue", "debugger", "implements", "interface", "package", "private", + "protected", "public", "static", - public static boolean isDigit(char c) { - return c >= '0' && c <= '9'; - } - public static boolean isWhitespace(char c) { - return isAny(c, " \t\r\n"); - } - public static boolean isLetter(char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); - } - public static boolean isAlphanumeric(char c) { - return isLetter(c) || isDigit(c); + // Although ES5 allow these, we will comply to ES6 here + "const", "let", + + // These are allowed too, however our parser considers them keywords + // We allow yield and await, because they're part of the custom async and generator functions + "undefined", "arguments", "globalThis", "window", "self" + ); + + public static boolean isDigit(Character c) { + return c != null && c >= '0' && c <= '9'; } public static boolean isAny(char c, String alphabet) { return alphabet.contains(Character.toString(c)); } - private static final int CURR_NONE = 0; - private static final int CURR_NUMBER = 1; - private static final int CURR_FLOAT = 11; - private static final int CURR_SCIENTIFIC_NOT = 12; - private static final int CURR_NEG_SCIENTIFIC_NOT = 13; - private static final int CURR_HEX = 14; - private static final int CURR_STRING = 2; - private static final int CURR_LITERAL = 3; - private static final int CURR_OPERATOR = 4; - private static final int CURR_REGEX = 6; - private static final int CURR_REGEX_FLAGS = 7; - private static final int CURR_MULTI_COMMENT = 8; - private static final int CURR_SINGLE_COMMENT = 9; - - private static void addToken(StringBuilder currToken, int currStage, int line, int lastStart, Filename filename, List tokens) { - var res = currToken.toString(); - - switch (currStage) { - case CURR_STRING: tokens.add(new RawToken(res, TokenType.STRING, line, lastStart)); break; - case CURR_REGEX_FLAGS: tokens.add(new RawToken(res, TokenType.REGEX, line, lastStart)); break; - case CURR_NUMBER: - case CURR_HEX: - case CURR_NEG_SCIENTIFIC_NOT: - case CURR_SCIENTIFIC_NOT: - case CURR_FLOAT: - tokens.add(new RawToken(res, TokenType.NUMBER, line, lastStart)); break; - case CURR_LITERAL: tokens.add(new RawToken(res, TokenType.LITERAL, line, lastStart)); break; - case CURR_OPERATOR: tokens.add(new RawToken(res, TokenType.OPERATOR, line, lastStart)); break; - } - - currToken.delete(0, currToken.length()); - } - - // This method is so long because we're tokenizing the string using an iterative approach - // instead of a recursive descent parser. This is mainly done for performance reasons. - // preformance reasons my ass - private static ArrayList splitTokens(Filename filename, String raw) { - var tokens = new ArrayList(); - var currToken = new StringBuilder(64); - - // Those are state variables, and will be reset every time a token has ended parsing - boolean lastEscape = false, inBrackets = false; - - int line = 1, start = 1, lastStart = 1, parenI = 0; - var loc = new Location(line, lastStart, filename); - int currStage = CURR_NONE; - - // when we want to continue parsing a token, we will execute continue;, which will skip - // the token end logic - loop: for (int i = 0; i < raw.length(); i++) { - char c = raw.charAt(i); - - start++; - - switch (currStage) { - case CURR_STRING: - currToken.append(c); - - if (!lastEscape) { - if (c == '\n') throw new SyntaxException(loc, "Can't have a multiline string."); - else if (c == '\\') { - lastEscape = true; - continue; - } - else if (c != currToken.charAt(0)) continue; - } - else { - lastEscape = false; - continue; - } - break; - case CURR_REGEX: - currToken.append(c); - if (!lastEscape) { - if (c == '\\') lastEscape = true; - if (c == '/' & parenI == 0 & !inBrackets) { - currStage = CURR_REGEX_FLAGS; - continue; - } - if (c == '[') inBrackets = true; - if (c == ']') inBrackets = false; - if (c == '(' && !inBrackets) parenI++; - if (c == ')' && !inBrackets) parenI--; - } - else lastEscape = false; - continue; - case CURR_REGEX_FLAGS: - if (isAny(c, "dgimsuy")) { - currToken.append(c); - continue; - } - i--; start--; - break; - case CURR_NUMBER: - if (c == '.') currStage = CURR_FLOAT; - else if (c == 'e' || c == 'E') currStage = CURR_SCIENTIFIC_NOT; - else if ((c == 'x' || c == 'X') && currToken.toString().equals("0")) currStage = CURR_HEX; - else if (!isDigit(c)) { - i--; start--; - break; - } - currToken.append(c); - continue; - case CURR_FLOAT: - if (c == 'e' || c == 'E') currStage = CURR_SCIENTIFIC_NOT; - else if (!isDigit(c)) { - i--; start--; - break; - } - currToken.append(c); - continue; - case CURR_SCIENTIFIC_NOT: - if (c == '-') { - if (currToken.toString().endsWith("e")) currStage = CURR_NEG_SCIENTIFIC_NOT; - } - if (currStage == CURR_SCIENTIFIC_NOT && !isDigit(c)) { - i--; start--; - break; - } - currToken.append(c); - continue; - case CURR_NEG_SCIENTIFIC_NOT: - if (isDigit(c)) currToken.append(c); - else { - i--; start--; - break; - } - continue; - case CURR_HEX: - if (isDigit(c) || isAny(c, "ABCDEFabcdef")) currToken.append(c); - else { - i--; start--; - break; - } - continue; - case CURR_SINGLE_COMMENT: - currToken.delete(0, currToken.length()); - if (c != '\n') continue; - else { - line++; - start = 1; - } - break; - case CURR_MULTI_COMMENT: - if (c == '\n') line++; - if (!(currToken.charAt(0) == '*' && c == '/')) { - currToken.delete(0, currToken.length()); - currToken.append(c); - continue; - } - break; - case CURR_LITERAL: - if (isAlphanumeric(c) || c == '_' || c == '$') { - currToken.append(c); - continue; - } - else { i--; start--; } - break; - case CURR_OPERATOR: { - // here we do several things: - // - detect a comment - // - detect a regular expression - // - detect a float number (.xxxx) - // - read an operator greedily - - // this variable keeps track of whether we're still reading an operator - boolean ok = false; - if (currToken.length() == 1) { - // double operators - if (currToken.charAt(0) == c && isAny(c, "&|=+-<>")) ok = true; - // assignments - else if (c == '=' && isAny(currToken.charAt(0), "&|^+-/*%!<>")) ok = true; - // detect float numbers - else if (isDigit(c) && currToken.charAt(0) == '.') { - currStage = CURR_FLOAT; - currToken.append(c); - continue; - } - else if (currToken.charAt(0) == '/') { - // single line comments - if (c == '/') { - currStage = CURR_SINGLE_COMMENT; - continue; - } - // multiline comments - else if (c == '*') { - currStage = CURR_MULTI_COMMENT; - continue; - } - // regular expressions - else { - // regular expressions must be in the start of a file, or be followed by a - // newline, or an operator - // this is because of expressions like 1 / 2 / 3 (/ 2 /) will get recognized as regex - // still, the closing paren must be ignored, because in an expression, we can't have a value, following a paren - var prevToken = tokens.size() == 0 ? null : tokens.get(tokens.size() - 1); - if (tokens.size() == 0 || ( - prevToken.line < line || - prevToken.type == TokenType.OPERATOR && !prevToken.value.equals(")") || - prevToken.value.equals("return") || - prevToken.value.equals("throe") - )) { - // we look for a second / on the same line - // if we don't find one, we determine the current operator - // to be a division - for (int j = i; j < raw.length(); j++) { - if (raw.charAt(j) == '/') { - i--; start--; - currStage = CURR_REGEX; - continue loop; - } - if (raw.charAt(j) == '\n') break; - } - } - } - } - } - if (currToken.length() == 2) { - var a = currToken.charAt(0); - var b = currToken.charAt(1); - if (( - a == '=' && b == '=' || - a == '!' && b == '=' || - a == '<' && b == '<' || - a == '>' && b == '>' || - a == '>' && b == '>' - ) && c == '=') ok = true; - if (a == '>' && b == '>' && c == '>') ok = true; - } - if ( - currToken.length() == 3 && - currToken.charAt(0) == '>' && - currToken.charAt(1) == '>' && - currToken.charAt(2) == '>' && - c == '=' - ) ok = true; - - if (ok) { - currToken.append(c); - continue; - } - else { i--; start--; } - break; - } - default: - // here we detect what type of token we're reading - if (isAny(c, " \t\n\r")) { - if (c == '\n') { - line++; - start = 1; - } - } - else if (isDigit(c)) { - currToken.append(c); - currStage = CURR_NUMBER; - continue; - } - else if (isAlphanumeric(c) || c == '_' || c == '$') { - currToken.append(c); - currStage = CURR_LITERAL; - continue; - } - else if (isAny(c, "+-/*%=!&|^(){}[];.,<>!:~?")) { - currToken.append(c); - currStage = CURR_OPERATOR; - continue; - } - else if (c == '"' || c == '\'') { - currToken.append(c); - currStage = CURR_STRING; - continue; - } - else throw new SyntaxException(new Location(line, start, filename), String.format("Unrecognized character %s.", c)); - } - - // if we got here, we know that we have encountered the end of a token - addToken(currToken, currStage, line, lastStart, filename, tokens); - lastEscape = inBrackets = false; - currStage = CURR_NONE; - lastStart = start; - } - - // here, we save a leftover token (if any) - switch (currStage) { - case CURR_STRING: throw new SyntaxException(new Location(line, start, filename), "Unterminated string literal."); - case CURR_REGEX: throw new SyntaxException(new Location(line, start, filename), "Incomplete regex."); - } - addToken(currToken, currStage, line, lastStart, filename, tokens); - - return tokens; - } - public static int fromHex(char c) { if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; @@ -400,373 +51,256 @@ public class Parsing { return -1; } - public static boolean inBounds(List tokens, int i) { - return i >= 0 && i < tokens.size(); + public static int skipEmpty(Source src, int i) { + int n = 0; + + while (n < src.size() && src.is(i + n, Character::isWhitespace)) n++; + + return n; } - private static String parseString(Location loc, String literal) { + public static ParseRes parseChar(Source src, int i) { + int n = 0; + + if (src.is(i + n, '\\')) { + n++; + char c = src.at(i + n++); + + if (c == 'b') return ParseRes.res('\b', n); + else if (c == 't') return ParseRes.res('\t', n); + else if (c == 'n') return ParseRes.res('\n', n); + else if (c == 'f') return ParseRes.res('\f', n); + else if (c == 'r') return ParseRes.res('\r', n); + else if (c == '0') { + if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed"); + else return ParseRes.res('\0', n); + } + else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed"); + else if (c == 'x') { + var newC = 0; + + for (var j = 0; j < 2; j++) { + if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence."); + + int val = fromHex(src.at(i + n)); + if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence."); + n++; + + newC = (newC << 4) | val; + } + + return ParseRes.res((char)newC, n); + } + else if (c == 'u') { + var newC = 0; + + for (var j = 0; j < 4; j++) { + if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence"); + + int val = fromHex(src.at(i + n)); + if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence"); + n++; + + newC = (newC << 4) | val; + } + + return ParseRes.res((char)newC, n); + } + else if (c == '\n') return ParseRes.res(null, n); + } + + return ParseRes.res(src.at(i + n), n + 1); + } + + public static ParseRes parseIdentifier(Source src, int i) { + var n = skipEmpty(src, i); var res = new StringBuilder(); + var first = false; - for (var i = 1; i < literal.length() - 1; i++) { - if (literal.charAt(i) == '\\') { - char c = literal.charAt(++i); - if (c == 'b') res.append('\b'); - else if (c == 't') res.append('\t'); - else if (c == 'n') res.append('\n'); - else if (c == 'f') res.append('\f'); - else if (c == 'r') res.append('\r'); - else if (c == '0') { - if (i + 1 >= literal.length()) res.append((char)0); - c = literal.charAt(i + 1); - if (c >= '0' && c <= '9') throw new SyntaxException(loc.add(i), "Octal escape sequences not allowed."); - res.append((char)0); - } - else if (c >= '1' && c <= '9') { - throw new SyntaxException(loc.add(i), "Octal escape sequences not allowed."); - } - else if (c == 'x') { - var newC = 0; - i++; - for (var j = 0; j < 2; j++) { - if (i >= literal.length()) throw new SyntaxException(loc.add(i), "Incomplete unicode escape sequence."); - int val = fromHex(literal.charAt(i++)); - if (val == -1) throw new SyntaxException(loc.add(i + 1), "Invalid character in unicode escape sequence."); - newC = (newC << 4) | val; - } - i--; + while (true) { + if (i + n > src.size()) break; + char c = src.at(i + n, '\0'); - res.append((char)newC); - } - else if (c == 'u') { - var newC = 0; - i++; - for (var j = 0; j < 4; j++) { - if (i >= literal.length()) throw new SyntaxException(loc.add(i), "Incomplete unicode escape sequence."); - int val = fromHex(literal.charAt(i++)); - if (val == -1) throw new SyntaxException(loc.add(i + 1), "Invalid character in unicode escape sequence."); - newC = (newC << 4) | val; - } - i--; - - res.append((char)newC); - } - else res.append(c); - } - else res.append(literal.charAt(i)); + if (first && !Character.isLetterOrDigit(c) && c != '_' && c != '$') break; + if (!first && !Character.isLetter(c) && c != '_' && c != '$') break; + res.append(c); + n++; } - return res.toString(); + if (res.length() <= 0) return ParseRes.failed(); + else return ParseRes.res(res.toString(), n); } - private static String parseRegex(Location loc, String literal) { + public static ParseRes parseIdentifier(Source src, int i, String test) { + var n = skipEmpty(src, i); var res = new StringBuilder(); + var first = true; - int end = literal.lastIndexOf('/'); + while (true) { + if (i + n > src.size()) break; + char c = src.at(i + n, '\0'); - for (var i = 1; i < end; i++) { - if (literal.charAt(i) == '\\') { - char c = literal.charAt(++i); - if (c == 'b') res.append('\b'); - else if (c == 't') res.append('\t'); - else if (c == 'n') res.append('\n'); - else if (c == 'f') res.append('\f'); - else if (c == 'r') res.append('\r'); - else if (c == '0') { - if (i + 1 >= literal.length()) res.append((char)0); - c = literal.charAt(i + 1); - if (c >= '0' && c <= '9') throw new SyntaxException(loc.add(i), "Octal escape sequences not allowed."); - res.append((char)0); - } - else if (c >= '1' && c <= '9') { - res.append((char)(c - '0')); - i++; - } - else if (c == 'x') { - var newC = 0; - i++; - for (var j = 0; j < 2; j++) { - if (i >= literal.length()) throw new SyntaxException(loc.add(i), "Incomplete unicode escape sequence."); - int val = fromHex(literal.charAt(i++)); - if (val == -1) throw new SyntaxException(loc.add(i + 1), "Invalid character in unicode escape sequence."); - newC = (newC << 4) | val; - } - i--; - - res.append((char)newC); - } - else if (c == 'u') { - var newC = 0; - i++; - for (var j = 0; j < 4; j++) { - if (i >= literal.length()) throw new SyntaxException(loc.add(i), "Incomplete unicode escape sequence."); - int val = fromHex(literal.charAt(i++)); - if (val == -1) throw new SyntaxException(loc.add(i + 1), "Invalid character in unicode escape sequence."); - newC = (newC << 4) | val; - } - i--; - - res.append((char)newC); - } - else res.append("\\" + c); - } - else res.append(literal.charAt(i)); + if (first && !Character.isLetterOrDigit(c) && c != '_' && c != '$') break; + if (!first && !Character.isLetter(c) && c != '_' && c != '$') break; + first = false; + res.append(c); + n++; } - return '/' + res.toString() + literal.substring(end); - } - - private static double parseHex(String literal) { - double res = 0; - - for (int i = 2; i < literal.length(); i++) { - res *= 16; - int dig = fromHex(literal.charAt(i)); - res += dig; - } - - return res; - } - - public static Double parseNumber(boolean octals, String value) { - if (value.startsWith("0x") || value.startsWith("0X")) { - if (value.length() == 2) return null; - return parseHex(value); - } - if (value.endsWith("e") || value.endsWith("E") || value.endsWith("-")) return null; - - int i = 0; - double res = 0, dotDivisor = 1; - boolean e = false, dot = false; - int exponent = 0; - - for (; i < value.length(); i++) { - char c = value.charAt(i); - if (c == '.') { dot = true; break; } - if (c == 'e') { e = true; break; } - if (!isDigit(c)) break; - - res = res * 10 + c - '0'; - } - - if (dot) for (i++; i < value.length(); i++) { - char c = value.charAt(i); - if (c == 'e') { e = true; break; } - if (!isDigit(c)) break; - - res += (c - '0') / (dotDivisor *= 10); - } - - if (e) for (i++; i < value.length(); i++) { - char c = value.charAt(i); - if (!isDigit(c)) break; - exponent = 10 * exponent + c - '0'; - } - - if (exponent < 0) for (int j = 0; j < -exponent; j++) res /= 10; - else for (int j = 0; j < exponent; j++) res *= 10; - - return res; - } - private static double parseNumber(Location loc, String value) { - var res = parseNumber(false, value); - if (res == null) - throw new SyntaxException(loc, "Invalid number format."); - else return res; - } - - private static List parseTokens(Filename filename, Collection tokens) { - var res = new ArrayList(); - - for (var el : tokens) { - var loc = new Location(el.line, el.start, filename); - switch (el.type) { - case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break; - case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value), el.value)); break; - case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value), el.value)); break; - case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value), el.value)); break; - case OPERATOR: - Operator op = Operator.parse(el.value); - if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value)); - res.add(Token.operator(el.line, el.start, op)); - break; - } - } - - return res; - } - - public static List tokenize(Filename filename, String raw) { - return parseTokens(filename, splitTokens(filename, raw)); - } - - public static Location getLoc(Filename filename, List tokens, int i) { - if (tokens.size() == 0 || tokens.size() == 0) return new Location(1, 1, filename); - if (i >= tokens.size()) i = tokens.size() - 1; - return new Location(tokens.get(i).line, tokens.get(i).start, filename); - } - public static int getLines(List tokens) { - if (tokens.size() == 0) return 1; - return tokens.get(tokens.size() - 1).line; - } - - public static ParseRes parseIdentifier(List tokens, int i) { - if (inBounds(tokens, i)) { - if (tokens.get(i).isIdentifier()) { - return ParseRes.res(tokens.get(i).identifier(), 1); - } - else return ParseRes.failed(); - } + if (res.length() <= 0) return ParseRes.failed(); + else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n); else return ParseRes.failed(); } - public static ParseRes parseOperator(List tokens, int i) { - if (inBounds(tokens, i)) { - if (tokens.get(i).isOperator()) { - return ParseRes.res(tokens.get(i).operator(), 1); - } - else return ParseRes.failed(); - } + public static boolean isIdentifier(Source src, int i, String test) { + return parseIdentifier(src, i, test).isSuccess(); + } + + public static ParseRes parseOperator(Source src, int i, String op) { + var n = skipEmpty(src, i); + + if (src.is(i + n, op)) return ParseRes.res(op, n + op.length()); else return ParseRes.failed(); } - public static boolean isIdentifier(List tokens, int i, String lit) { - if (inBounds(tokens, i)) { - if (tokens.get(i).isIdentifier(lit)) { - return true; - } - else return false; + public static ParseRes parseStatementEnd(Source src, int i) { + var n = skipEmpty(src, i); + if (i >= src.size()) return ParseRes.res(true, n + 1); + + for (var j = i; j < i + n; j++) { + if (src.is(j, '\n')) return ParseRes.res(true, n); } - else return false; - } - public static boolean isOperator(List tokens, int i, Operator op) { - if (inBounds(tokens, i)) { - if (tokens.get(i).isOperator(op)) { - return true; - } - else return false; - } - else return false; - } - public static boolean isStatementEnd(List tokens, int i) { - if (isOperator(tokens, i, Operator.SEMICOLON)) return true; - if (isOperator(tokens, i, Operator.BRACE_CLOSE)) return true; - if (i < 0) return false; - if (i >= tokens.size()) return true; - return getLoc(null, tokens, i).line() > getLoc(null, tokens, i - 1).line(); + + if (src.is(i + n, ';')) return ParseRes.res(true, n + 1); + if (src.is(i + n, '}')) return ParseRes.res(true, n); + + return ParseRes.failed(); } public static boolean checkVarName(String name) { return !reserved.contains(name); } - public static ParseRes> parseParamList(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - int n = 0; + public static ParseRes> parseParamList(Source src, int i) { + var n = skipEmpty(src, i); - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a parameter list."); + var openParen = parseOperator(src, i + n, "("); + if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list."); + n += openParen.n; var args = new ArrayList(); - if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - } - else { + var closeParen = parseOperator(src, i + n, ")"); + n += closeParen.n; + + if (!closeParen.isSuccess()) { while (true) { - var argRes = parseIdentifier(tokens, i + n); + var argRes = parseIdentifier(src, i + n); if (argRes.isSuccess()) { args.add(argRes.result); n++; - if (isOperator(tokens, i + n, Operator.COMMA)) { + + n += skipEmpty(src, i); + + if (src.is(i + n, ",")) { n++; + n += skipEmpty(src, i + n); } - if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { + if (src.is(i + n, ")")) { n++; break; } } - else return ParseRes.error(loc, "Expected an argument, comma or a closing brace."); + else return ParseRes.error(src.loc(i + n), "Expected an argument, or a closing brace."); } } return ParseRes.res(args, n); } - public static ParseRes parseParens(Filename filename, List tokens, int i) { + public static ParseRes parseParens(Source src, int i) { int n = 0; - if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed(); - var res = parseValue(filename, tokens, i + n, 0); - if (!res.isSuccess()) return res; + var openParen = parseOperator(src, i + n, "("); + if (!openParen.isSuccess()) return openParen.chainError(); + n += openParen.n; + + var res = parseValue(src, i + n, 0); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens"); n += res.n; - if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.failed(); + var closeParen = parseOperator(src, i + n, ")"); + if (!closeParen.isSuccess()) return closeParen.chainError(src.loc(i + n), "Expected a closing paren"); + n += closeParen.n; return ParseRes.res(res.result, n); } - public static ParseRes parseSimple(Filename filename, List tokens, int i, boolean statement) { - var res = new ArrayList>(); - - if (!statement) { - res.add(ObjectStatement.parse(filename, tokens, i)); - res.add(FunctionStatement.parseFunction(filename, tokens, i, false)); - } - - res.addAll(List.of( - VariableStatement.parseVariable(filename, tokens, i), - parseLiteral(filename, tokens, i), - ConstantStatement.parseString(filename, tokens, i), - RegexStatement.parse(filename, tokens, i), - ConstantStatement.parseNumber(filename, tokens, i), - OperationStatement.parseUnary(filename, tokens, i), - ArrayStatement.parse(filename, tokens, i), - ChangeStatement.parsePrefix(filename, tokens, i), - parseParens(filename, tokens, i), - CallStatement.parseNew(filename, tokens, i), - TypeofStatement.parse(filename, tokens, i), - DiscardStatement.parse(filename, tokens, i), - DeleteStatement.parseDelete(filename, tokens, i) - )); - - return ParseRes.any(res); + public static ParseRes parseSimple(Source src, int i, boolean statement) { + return ParseRes.first(src, i, + (a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b), + (a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false), + VariableStatement::parse, + Parsing::parseLiteral, + StringStatement::parse, + RegexStatement::parse, + NumberStatement::parse, + ChangeStatement::parsePrefixDecrease, + ChangeStatement::parsePrefixIncrease, + OperationStatement::parsePrefix, + ArrayStatement::parse, + Parsing::parseParens, + CallStatement::parseNew, + TypeofStatement::parse, + DiscardStatement::parse, + DeleteStatement::parse + ); } - public static ParseRes parseLiteral(Filename filename, List tokens, int i) { - var loc = getLoc(filename, tokens, i); - var id = parseIdentifier(tokens, i); - if (!id.isSuccess()) return id.transform(); + public static ParseRes parseLiteral(Source src, int i) { + var n = skipEmpty(src, i); + var loc = src.loc(i + n); - if (id.result.equals("true")) return ParseRes.res(new ConstantStatement(loc, true), 1); - if (id.result.equals("false")) return ParseRes.res(new ConstantStatement(loc, false), 1); - if (id.result.equals("undefined")) return ParseRes.res(ConstantStatement.ofUndefined(loc), 1); - if (id.result.equals("null")) return ParseRes.res(ConstantStatement.ofNull(loc), 1); - if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), 1); - if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), 1); - if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), 1); + var id = parseIdentifier(src, i); + if (!id.isSuccess()) return id.chainError(); + n += id.n; + + if (id.result.equals("true")) return ParseRes.res(new BoolStatement(loc, true), n); + if (id.result.equals("false")) return ParseRes.res(new BoolStatement(loc, false), n); + if (id.result.equals("undefined")) return ParseRes.res(new DiscardStatement(loc, null), n); + if (id.result.equals("null")) return ParseRes.res(new NullStatement(loc), n); + if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), n); + if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), n); + if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), n); return ParseRes.failed(); } - public static ParseRes parseValue(Filename filename, List tokens, int i, int precedence, boolean statement) { + public static ParseRes parseValue(Source src, int i, int precedence, boolean statement) { + var n = skipEmpty(src, i); Statement prev = null; - int n = 0; while (true) { if (prev == null) { - var res = parseSimple(filename, tokens, i + n, statement); + var res = parseSimple(src, i + n, statement); if (res.isSuccess()) { n += res.n; prev = res.result; } - else if (res.isError()) return res.transform(); + else if (res.isError()) return res.chainError(); else break; } else { - var res = ParseRes.any( - OperationStatement.parseOperator(filename, tokens, i + n, prev, precedence), - IndexStatement.parseMember(filename, tokens, i + n, prev, precedence), - IndexStatement.parseIndex(filename, tokens, i + n, prev, precedence), - CallStatement.parseCall(filename, tokens, i + n, prev, precedence), - ChangeStatement.parsePostfix(filename, tokens, i + n, prev, precedence), - OperationStatement.parseInstanceof(filename, tokens, i + n, prev, precedence), - OperationStatement.parseIn(filename, tokens, i + n, prev, precedence), - CompoundStatement.parseComma(filename, tokens, i + n, prev, precedence), - IfStatement.parseTernary(filename, tokens, i + n, prev, precedence) + var _prev = prev; + ParseRes res = ParseRes.first(src, i + n, + (s, j) -> OperationStatement.parseInstanceof(s, j, _prev, precedence), + (s, j) -> OperationStatement.parseIn(s, j, _prev, precedence), + (s, j) -> LazyOrStatement.parse(s, j, _prev, precedence), + (s, j) -> LazyAndStatement.parse(s, j, _prev, precedence), + (s, j) -> ChangeStatement.parsePostfixIncrease(s, j, _prev, precedence), + (s, j) -> ChangeStatement.parsePostfixDecrease(s, j, _prev, precedence), + (s, j) -> AssignableStatement.parse(s, j, _prev, precedence), + (s, j) -> OperationStatement.parseOperator(s, j, _prev, precedence), + (s, j) -> IfStatement.parseTernary(s, j, _prev, precedence), + (s, j) -> IndexStatement.parseMember(s, j, _prev, precedence), + (s, j) -> IndexStatement.parseIndex(s, j, _prev, precedence), + (s, j) -> CallStatement.parseCall(s, j, _prev, precedence), + (s, j) -> CompoundStatement.parseComma(s, j, _prev, precedence) ); if (res.isSuccess()) { @@ -774,7 +308,7 @@ public class Parsing { prev = res.result; continue; } - else if (res.isError()) return res.transform(); + else if (res.isError()) return res.chainError(); break; } @@ -783,61 +317,59 @@ public class Parsing { if (prev == null) return ParseRes.failed(); else return ParseRes.res(prev, n); } - public static ParseRes parseValue(Filename filename, List tokens, int i, int precedence) { - return parseValue(filename, tokens, i, precedence, false); + public static ParseRes parseValue(Source src, int i, int precedence) { + return parseValue(src, i, precedence, false); } - public static ParseRes parseValueStatement(Filename filename, List tokens, int i) { - var valRes = parseValue(filename, tokens, i, 0, true); - if (!valRes.isSuccess()) return valRes.transform(); + public static ParseRes parseValueStatement(Source src, int i) { + var res = parseValue(src, i, 0, true); + if (!res.isSuccess()) return res.chainError(); - var res = ParseRes.res(valRes.result, valRes.n); + var end = parseStatementEnd(src, i + res.n); + if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement"); - if (isStatementEnd(tokens, i + res.n)) { - if (isOperator(tokens, i + res.n, Operator.SEMICOLON)) return res.addN(1); - else return res; - } - else if (isIdentifier(tokens, i, "const") || isIdentifier(tokens, i, "let")) { - return ParseRes.error(getLoc(filename, tokens, i), "Detected the usage of 'const'/'let'. Please, use 'var' instead."); - } - else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", res); + return res.addN(end.n); } - public static ParseRes parseStatement(Filename filename, List tokens, int i) { - if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i), false), 1); - if (isIdentifier(tokens, i, "with")) return ParseRes.error(getLoc(filename, tokens, i), "'with' statements are not allowed."); - return ParseRes.any( - VariableDeclareStatement.parse(filename, tokens, i), - ReturnStatement.parseReturn(filename, tokens, i), - ThrowStatement.parseThrow(filename, tokens, i), - ContinueStatement.parseContinue(filename, tokens, i), - BreakStatement.parseBreak(filename, tokens, i), - DebugStatement.parse(filename, tokens, i), - IfStatement.parse(filename, tokens, i), - WhileStatement.parseWhile(filename, tokens, i), - SwitchStatement.parse(filename, tokens, i), - ForStatement.parse(filename, tokens, i), - ForInStatement.parse(filename, tokens, i), - ForOfStatement.parse(filename, tokens, i), - DoWhileStatement.parseDoWhile(filename, tokens, i), - TryStatement.parse(filename, tokens, i), - CompoundStatement.parse(filename, tokens, i), - FunctionStatement.parseFunction(filename, tokens, i, true), - parseValueStatement(filename, tokens, i) + public static ParseRes parseStatement(Source src, int i) { + var n = skipEmpty(src, i); + + if (src.is(i + n, ";")) return ParseRes.res(new DiscardStatement(src.loc(i+ n), null), n + 1); + if (isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed."); + + ParseRes res = ParseRes.first(src, i + n, + VariableDeclareStatement::parse, + ReturnStatement::parse, + ThrowStatement::parse, + ContinueStatement::parse, + BreakStatement::parse, + DebugStatement::parse, + IfStatement::parse, + WhileStatement::parse, + SwitchStatement::parse, + ForStatement::parse, + ForInStatement::parse, + ForOfStatement::parse, + DoWhileStatement::parse, + TryStatement::parse, + CompoundStatement::parse, + (s, j) -> FunctionStatement.parseFunction(s, j, true), + Parsing::parseValueStatement ); + return res.addN(n); } public static Statement[] parse(Filename filename, String raw) { - var tokens = tokenize(filename, raw); + var src = new Source(filename, raw); var list = new ArrayList(); int i = 0; while (true) { - if (i >= tokens.size()) break; + if (i >= src.size()) break; - var res = Parsing.parseStatement(filename, tokens, i); + var res = Parsing.parseStatement(src, i); - if (res.isError()) throw new SyntaxException(getLoc(filename, tokens, i), res.error); - else if (res.isFailed()) throw new SyntaxException(getLoc(filename, tokens, i), "Unexpected syntax."); + if (res.isError()) throw new SyntaxException(src.loc(i), res.error); + else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax"); i += res.n; @@ -874,4 +406,134 @@ public class Parsing { public static CompileResult compile(Filename filename, String raw) { return compile(parse(filename, raw)); } + + private static ParseRes parseHex(Source src, int i) { + int n = 0; + double res = 0; + + while (true) { + int digit = Parsing.fromHex(src.at(i + n, '\0')); + if (digit < 0) { + if (n <= 0) return ParseRes.failed(); + else return ParseRes.res(res, n); + } + n++; + + res *= 16; + res += digit; + } + } + private static ParseRes parseOct(Source src, int i) { + int n = 0; + double res = 0; + + while (true) { + int digit = src.at(i + n, '\0') - '0'; + if (digit < 0 || digit > 9) break; + if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit); + + if (digit < 0) { + if (n <= 0) return ParseRes.failed(); + else return ParseRes.res(res, n); + } + n++; + + res *= 8; + res += digit; + } + + return ParseRes.res(res, n); + } + + public static ParseRes parseString(Source src, int i) { + var n = skipEmpty(src, i); + + char quote; + + if (src.is(i + n, '\'')) quote = '\''; + else if (src.is(i + n, '"')) quote = '"'; + else return ParseRes.failed(); + n++; + + var res = new StringBuilder(); + + while (true) { + if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal"); + if (src.is(i + n, quote)) { + n++; + return ParseRes.res(res.toString(), n); + } + + var charRes = parseChar(src, i + n); + if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character"); + n += charRes.n; + + if (charRes.result != null) res.append(charRes.result); + } + } + public static ParseRes parseNumber(Source src, int i) { + var n = skipEmpty(src, i); + + double whole = 0; + double fract = 0; + long exponent = 0; + boolean parsedAny = false; + + if (src.is(i + n, "0x") || src.is(i + n, "0X")) { + n += 2; + + var res = parseHex(src, i); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal"); + else return res.addN(2); + } + else if (src.is(i + n, "0o") || src.is(i + n, "0O")) { + n += 2; + + var res = parseOct(src, i); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal"); + else return res.addN(2); + } + else if (src.is(i + n, '0')) { + n++; + parsedAny = true; + if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed"); + } + + while (src.is(i + n, Parsing::isDigit)) { + parsedAny = true; + whole *= 10; + whole += src.at(i + n++) - '0'; + } + + if (src.is(i + n, '.')) { + parsedAny = true; + n++; + + while (src.is(i + n, Parsing::isDigit)) { + fract += src.at(i + n++) - '0'; + fract /= 10; + } + } + + if (src.is(i + n, 'e') || src.is(i + n, 'E')) { + n++; + parsedAny = true; + boolean negative = src.is(i + n, '-'); + boolean parsedE = false; + if (negative) n++; + + while (src.is(i + n, Parsing::isDigit)) { + parsedE = true; + exponent *= 10; + + if (negative) exponent -= src.at(i + n++) - '0'; + else exponent += src.at(i + n++) - '0'; + } + + if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent"); + } + + if (!parsedAny) return ParseRes.failed(); + else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Source.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Source.java new file mode 100644 index 0000000..bcfc56e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Source.java @@ -0,0 +1,69 @@ +package me.topchetoeu.jscript.compilation.parsing; + +import java.util.function.Predicate; + +import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.Location; + +public class Source { + public final Filename filename; + public final String src; + + private int[] lineStarts; + + public Location loc(int offset) { + return new SourceLocation(filename, lineStarts, offset); + } + public boolean is(int i, char c) { + return i >= 0 && i < src.length() && src.charAt(i) == c; + } + public boolean is(int i, String src) { + if (i < 0 || i + src.length() > size()) return false; + + for (int j = 0; j < src.length(); j++) { + if (at(i + j) != src.charAt(j)) return false; + } + + return true; + } + public boolean is(int i, Predicate predicate) { + if (i < 0 || i >= src.length()) return false; + return predicate.test(at(i)); + } + public char at(int i) { + return src.charAt(i); + } + public char at(int i, char defaultVal) { + if (i < 0 || i >= src.length()) return defaultVal; + else return src.charAt(i); + } + public int size() { + return src.length(); + } + public String slice(int start, int end) { + return src.substring(start, end); + } + + public Source(Filename filename, String src) { + this.filename = filename; + this.src = src; + + int n = 1; + lineStarts = new int[16]; + lineStarts[0] = 0; + + for (int i = src.indexOf("\n"); i > 0; i = src.indexOf("\n", i + 1)) { + if (n >= lineStarts.length) { + var newArr = new int[lineStarts.length * 2]; + System.arraycopy(lineStarts, 0, newArr, 0, n); + lineStarts = newArr; + } + + lineStarts[n++] = i + 1; + } + + var newArr = new int[n]; + System.arraycopy(lineStarts, 0, newArr, 0, n); + lineStarts = newArr; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java b/src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java new file mode 100644 index 0000000..8eafffa --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java @@ -0,0 +1,55 @@ +package me.topchetoeu.jscript.compilation.parsing; + +import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.Location; + +public class SourceLocation extends Location { + private int[] lineStarts; + private int line; + private int start; + private final Filename filename; + private final int offset; + + private void update() { + if (lineStarts == null) return; + + int start = 0; + int end = lineStarts.length - 1; + + while (true) { + if (start + 1 >= end) break; + var mid = -((-start - end) >> 1); + var el = lineStarts[mid]; + + if (el < offset) start = mid; + else if (el > offset) end = mid; + else { + this.line = mid; + this.start = 0; + this.lineStarts = null; + return; + } + } + + this.line = start; + this.start = offset - lineStarts[start]; + this.lineStarts = null; + return; + } + + @Override public Filename filename() { return filename; } + @Override public int line() { + update(); + return line; + } + @Override public int start() { + update(); + return start; + } + + public SourceLocation(Filename filename, int[] lineStarts, int offset) { + this.filename = filename; + this.lineStarts = lineStarts; + this.offset = offset; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java index ec234df..c399a9e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java @@ -1,8 +1,7 @@ package me.topchetoeu.jscript.compilation.parsing; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; -import me.topchetoeu.jscript.common.ParseRes.State; +import me.topchetoeu.jscript.compilation.parsing.ParseRes.State; public class TestRes { public final State state; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java index 8ffbf4d..9d60587 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java @@ -1,17 +1,14 @@ package me.topchetoeu.jscript.compilation.values; import java.util.ArrayList; -import java.util.List; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ArrayStatement extends Statement { public final Statement[] statements; @@ -46,36 +43,42 @@ public class ArrayStatement extends Statement { this.statements = statements; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "[")) return ParseRes.failed(); + n++; var values = new ArrayList(); loop: while (true) { - if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + n += Parsing.skipEmpty(src, i + n); + if (src.is(i + n, "]")) { n++; break; } - while (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + while (src.is(i + n, ",")) { n++; + n += Parsing.skipEmpty(src, i + n); values.add(null); - if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + + if (src.is(i + n, "]")) { n++; break loop; } } - var res = Parsing.parseValue(filename, tokens, i + n, 2); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected an array element.", res); - else n += res.n; + var res = Parsing.parseValue(src, i + n, 2); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an array element."); + n += res.n; + n += Parsing.skipEmpty(src, i + n); values.add(res.result); - if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++; - else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) { + if (src.is(i + n, ",")) n++; + else if (src.is(i + n, "]")) { n++; break; } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java index e8a82d6..4cb52ed 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java @@ -1,18 +1,15 @@ package me.topchetoeu.jscript.compilation.values; import java.util.ArrayList; -import java.util.List; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class CallStatement extends Statement { public final Statement func; @@ -47,51 +44,58 @@ public class CallStatement extends Statement { this.args = args; } - public static ParseRes parseCall(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - + public static ParseRes parseCall(Source src, int i, Statement prev, int precedence) { if (precedence > 17) return ParseRes.failed(); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed(); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "(")) return ParseRes.failed(); + n++; var args = new ArrayList(); boolean prevArg = false; while (true) { - var argRes = Parsing.parseValue(filename, tokens, i + n, 2); + var argRes = Parsing.parseValue(src, i + n, 2); + n += argRes.n; + n += Parsing.skipEmpty(src, i + n); + if (argRes.isSuccess()) { args.add(argRes.result); - n += argRes.n; prevArg = true; } - else if (argRes.isError()) return argRes.transform(); - else if (prevArg && Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + else if (argRes.isError()) return argRes.chainError(); + else if (prevArg && src.is(i + n, ",")) { prevArg = false; n++; } - else if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { + else if (src.is(i + n, ")")) { n++; break; } - else return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), prevArg ? "Expected a comma or a closing paren." : "Expected an expression or a closing paren."); + else if (prevArg) return ParseRes.error(src.loc(i + n), "Expected a comma or a closing paren"); + else return ParseRes.error(src.loc(i + n), "Expected an expression or a closing paren"); } return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); } - public static ParseRes parseNew(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "new")) return ParseRes.failed(); + public static ParseRes parseNew(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var valRes = Parsing.parseValue(filename, tokens, i + n, 18); + if (!Parsing.isIdentifier(src, i + n, "new")) return ParseRes.failed(); + n += 3; + + var valRes = Parsing.parseValue(src, i + n, 18); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword."); n += valRes.n; - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'new' keyword.", valRes); - var callRes = CallStatement.parseCall(filename, tokens, i + n, valRes.result, 0); - n += callRes.n; - if (callRes.isError()) return callRes.transform(); - else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); - var call = (CallStatement)callRes.result; - return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n); + var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0); + if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); + if (callRes.isError()) return callRes.chainError(); + n += callRes.n; + + return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java index 0531b4d..e8cd584 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java @@ -1,18 +1,15 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; public class ChangeStatement extends Statement { public final AssignableStatement value; @@ -20,7 +17,7 @@ public class ChangeStatement extends Statement { public final boolean postfix; @Override public void compile(CompileResult target, boolean pollute) { - value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true); + value.toAssign(new NumberStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true); if (!pollute) target.add(Instruction.discard()); else if (postfix) { target.add(Instruction.pushValue(addAmount)); @@ -35,39 +32,55 @@ public class ChangeStatement extends Statement { this.postfix = postfix; } - public static ParseRes parsePrefix(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - - var opState = Parsing.parseOperator(tokens, i + n++); - if (!opState.isSuccess()) return ParseRes.failed(); - - int change = 0; - - if (opState.result == Operator.INCREASE) change = 1; - else if (opState.result == Operator.DECREASE) change = -1; - else return ParseRes.failed(); - - var res = Parsing.parseValue(filename, tokens, i + n, 15); - if (!(res.result instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value after prefix operator."); - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, change, false), n + res.n); + public static ParseRes parsePrefixIncrease(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "++")) return ParseRes.failed(); + n += 2; + + var res = Parsing.parseValue(src, i + n, 15); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); + else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); + + return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, 1, false), n + res.n); } - public static ParseRes parsePostfix(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - + public static ParseRes parsePrefixDecrease(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "--")) return ParseRes.failed(); + n += 2; + + var res = Parsing.parseValue(src, i + n, 15); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); + else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); + + return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, -1, false), n + res.n); + } + + public static ParseRes parsePostfixIncrease(Source src, int i, Statement prev, int precedence) { if (precedence > 15) return ParseRes.failed(); - - var opState = Parsing.parseOperator(tokens, i + n++); - if (!opState.isSuccess()) return ParseRes.failed(); - - int change = 0; - - if (opState.result == Operator.INCREASE) change = 1; - else if (opState.result == Operator.DECREASE) change = -1; - else return ParseRes.failed(); - - if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value before suffix operator."); - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, change, true), n); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "++")) return ParseRes.failed(); + if (!(prev instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator."); + n += 2; + + return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, 1, true), n); + } + public static ParseRes parsePostfixDecrease(Source src, int i, Statement prev, int precedence) { + if (precedence > 15) return ParseRes.failed(); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "--")) return ParseRes.failed(); + if (!(prev instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator."); + n += 2; + + return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, -1, true), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java deleted file mode 100644 index 60a8503..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/ConstantStatement.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; - -public class ConstantStatement extends Statement { - public final Object value; - public final boolean isNull; - - @Override public boolean pure() { return true; } - - @Override - public void compile(CompileResult target, boolean pollute) { - if (pollute) { - if (isNull) target.add(Instruction.pushNull()); - else if (value instanceof Double) target.add(Instruction.pushValue((Double)value)); - else if (value instanceof String) target.add(Instruction.pushValue((String)value)); - else if (value instanceof Boolean) target.add(Instruction.pushValue((Boolean)value)); - else target.add(Instruction.pushUndefined()); - } - } - - private ConstantStatement(Location loc, Object val, boolean isNull) { - super(loc); - this.value = val; - this.isNull = isNull; - } - - public ConstantStatement(Location loc, boolean val) { - this(loc, val, false); - } - public ConstantStatement(Location loc, String val) { - this(loc, val, false); - } - public ConstantStatement(Location loc, double val) { - this(loc, val, false); - } - - public static ConstantStatement ofUndefined(Location loc) { - return new ConstantStatement(loc, null, false); - } - public static ConstantStatement ofNull(Location loc) { - return new ConstantStatement(loc, null, true); - } - - public static ParseRes parseNumber(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - if (Parsing.inBounds(tokens, i)) { - if (tokens.get(i).isNumber()) { - return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1); - } - else return ParseRes.failed(); - } - else return ParseRes.failed(); - } - public static ParseRes parseString(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - if (Parsing.inBounds(tokens, i)) { - if (tokens.get(i).isString()) { - return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1); - } - else return ParseRes.failed(); - } - else return ParseRes.failed(); - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java index cd13dd8..14041f6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java @@ -1,24 +1,20 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class DiscardStatement extends Statement { public final Statement value; @Override public boolean pure() { return value.pure(); } - @Override - public void compile(CompileResult target, boolean pollute) { - value.compile(target, false); + @Override public void compile(CompileResult target, boolean pollute) { + if (value != null) value.compile(target, false); if (pollute) target.add(Instruction.pushUndefined()); } @@ -27,13 +23,15 @@ public class DiscardStatement extends Statement { this.value = val; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "void")) return ParseRes.failed(); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var valRes = Parsing.parseValue(filename, tokens, i + n, 14); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes); + if (!Parsing.isIdentifier(src, i + n, "void")) return ParseRes.failed(); + n += 4; + + var valRes = Parsing.parseValue(src, i + n, 14); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword."); n += valRes.n; return ParseRes.res(new DiscardStatement(loc, valRes.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index a54d082..f037bd8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -1,20 +1,15 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.ArrayList; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class FunctionStatement extends Statement { @@ -133,47 +128,30 @@ public class FunctionStatement extends Statement { else stm.compile(target, pollute, bp); } - public static ParseRes parseFunction(Filename filename, List tokens, int i, boolean statement) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; + public static ParseRes parseFunction(Source src, int i, boolean statement) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - if (!Parsing.isIdentifier(tokens, i + n++, "function")) return ParseRes.failed(); + if (!Parsing.isIdentifier(src, i + n, "function")) return ParseRes.failed(); + n += 8; - var nameRes = Parsing.parseIdentifier(tokens, i + n); - if (!nameRes.isSuccess() && statement) return ParseRes.error(loc, "A statement function requires a name, one is not present."); - var name = nameRes.result; + var nameRes = Parsing.parseIdentifier(src, i + n); + if (!nameRes.isSuccess() && statement) return ParseRes.error(src.loc(i + n), "A statement function requires a name"); n += nameRes.n; + n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.error(loc, "Expected a parameter list."); + var args = Parsing.parseParamList(src, i + n); + if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); + n += args.n; - var args = new ArrayList(); - - if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - } - else { - while (true) { - var argRes = Parsing.parseIdentifier(tokens, i + n); - if (argRes.isSuccess()) { - args.add(argRes.result); - n++; - if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { - n++; - } - if (Parsing.isOperator(tokens, i + n, Operator.PAREN_CLOSE)) { - n++; - break; - } - } - else return ParseRes.error(loc, "Expected an argument, comma or a closing brace."); - } - } - - var res = CompoundStatement.parse(filename, tokens, i + n); + var res = CompoundStatement.parse(src, i + n); + if (!res.isSuccess()) res.chainError(src.loc(i + n), "Expected a compound statement for function."); n += res.n; - var end = Parsing.getLoc(filename, tokens, i + n - 1); - if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n); - else return ParseRes.error(loc, "Expected a compound statement for function.", res); + return ParseRes.res(new FunctionStatement( + loc, src.loc(i + n - 1), + nameRes.result, args.result.toArray(String[]::new), + statement, res.result + ), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java index 29dcbd3..0b4bf48 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java @@ -1,19 +1,16 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.compilation.values.constants.StringStatement; public class IndexStatement extends AssignableStatement { public final Statement object; @@ -42,33 +39,38 @@ public class IndexStatement extends AssignableStatement { this.index = index; } - public static ParseRes parseIndex(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - + public static ParseRes parseIndex(Source src, int i, Statement prev, int precedence) { if (precedence > 18) return ParseRes.failed(); - - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed(); - - var valRes = Parsing.parseValue(filename, tokens, i + n, 0); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in index expression.", valRes); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "[")) return ParseRes.failed(); + n++; + + var valRes = Parsing.parseValue(src, i + n, 0); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in index expression"); n += valRes.n; - - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_CLOSE)) return ParseRes.error(loc, "Expected a closing bracket."); - + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, "]")) return ParseRes.error(src.loc(i + n), "Expected a closing bracket"); + n++; + return ParseRes.res(new IndexStatement(loc, prev, valRes.result), n); } - public static ParseRes parseMember(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - + public static ParseRes parseMember(Source src, int i, Statement prev, int precedence) { if (precedence > 18) return ParseRes.failed(); - - if (!Parsing.isOperator(tokens, i + n++, Operator.DOT)) return ParseRes.failed(); - - var literal = Parsing.parseIdentifier(tokens, i + n++); - if (!literal.isSuccess()) return ParseRes.error(loc, "Expected an identifier after member access."); - - return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, ".")) return ParseRes.failed(); + n++; + + var literal = Parsing.parseIdentifier(src, i + n); + if (!literal.isSuccess()) return literal.chainError(src.loc(i + n), "Expected an identifier after member access."); + n += literal.n; + + return ParseRes.res(new IndexStatement(loc, prev, new StringStatement(loc, literal.result)), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java index 306ec25..b556e07 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java @@ -4,6 +4,9 @@ import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Source; public class LazyAndStatement extends Statement { public final Statement first, second; @@ -25,4 +28,20 @@ public class LazyAndStatement extends Statement { this.first = first; this.second = second; } + + + public static ParseRes parse(Source src, int i, Statement prev, int precedence) { + if (precedence < 4) return ParseRes.failed(); + var n = Parsing.skipEmpty(src, i); + + if (!src.is(i + n, "&&")) return ParseRes.failed(); + var loc = src.loc(i + n); + n += 2; + + var res = Parsing.parseValue(src, i + n, 4); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator."); + n += res.n; + + return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java index b1461ed..80ec4be 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java @@ -4,6 +4,9 @@ import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Source; public class LazyOrStatement extends Statement { public final Statement first, second; @@ -25,4 +28,20 @@ public class LazyOrStatement extends Statement { this.first = first; this.second = second; } + + + public static ParseRes parse(Source src, int i, Statement prev, int precedence) { + if (precedence < 3) return ParseRes.failed(); + var n = Parsing.skipEmpty(src, i); + + if (!src.is(i + n, "||")) return ParseRes.failed(); + var loc = src.loc(i + n); + n += 2; + + var res = Parsing.parseValue(src, i + n, 4); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator."); + n += res.n; + + return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java index b98e84e..91dbb6f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java @@ -2,22 +2,30 @@ package me.topchetoeu.jscript.compilation.values; import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Parsing.ObjProp; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class ObjectStatement extends Statement { + public static class ObjProp { + public final String name; + public final String access; + public final FunctionStatement func; + + public ObjProp(String name, String access, FunctionStatement func) { + this.name = name; + this.access = access; + this.func = func; + } + } + public final Map map; public final Map getters; public final Map setters; @@ -68,100 +76,107 @@ public class ObjectStatement extends Statement { this.setters = setters; } - private static ParseRes parsePropName(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); + private static ParseRes parsePropName(Source src, int i) { + var n = Parsing.skipEmpty(src, i); - if (Parsing.inBounds(tokens, i)) { - var token = tokens.get(i); - - if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1); - else return ParseRes.error(loc, "Expected identifier, string or number literal."); - } - else return ParseRes.failed(); - } - private static ParseRes parseObjectProp(Filename filename, List tokens, int i) { - var loc =Parsing. getLoc(filename, tokens, i); - int n = 0; - - var accessRes = Parsing.parseIdentifier(tokens, i + n++); - if (!accessRes.isSuccess()) return ParseRes.failed(); - var access = accessRes.result; - if (!access.equals("get") && !access.equals("set")) return ParseRes.failed(); - - var nameRes = parsePropName(filename, tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a property name after '" + access + "'."); - var name = nameRes.result; - n += nameRes.n; - - var argsRes = Parsing.parseParamList(filename, tokens, i + n); - if (!argsRes.isSuccess()) return ParseRes.error(loc, "Expected an argument list.", argsRes); - n += argsRes.n; - - var res = CompoundStatement.parse(filename, tokens, i + n); - if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res); + var res = ParseRes.first(src, i + n, + Parsing::parseIdentifier, + Parsing::parseString, + Parsing::parseNumber + ); n += res.n; - var end = Parsing.getLoc(filename, tokens, i + n - 1); + if (!res.isSuccess()) return res.chainError(); + return ParseRes.res(res.result.toString(), n); + } + private static ParseRes parseObjectProp(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var access = Parsing.parseIdentifier(src, i + n); + if (!access.isSuccess()) return ParseRes.failed(); + if (!access.result.equals("get") && !access.result.equals("set")) return ParseRes.failed(); + n += access.n; + + var name = parsePropName(src, i + n); + if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); + n += name.n; + + var params = Parsing.parseParamList(src, i + n); + if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); + n += params.n; + + var body = CompoundStatement.parse(src, i + n); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor."); + n += body.n; + + var end = src.loc(i + n - 1); return ParseRes.res(new ObjProp( - name, access, - new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result) + name.result, access.result, + new FunctionStatement(loc, end, access + " " + name.result.toString(), params.result.toArray(String[]::new), false, body.result) ), n); } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed(); - + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "{")) return ParseRes.failed(); + n++; + n += Parsing.skipEmpty(src, i + n); + var values = new LinkedHashMap(); var getters = new LinkedHashMap(); var setters = new LinkedHashMap(); - - if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + + if (src.is(i + n, "}")) { n++; return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); } - + while (true) { - var propRes = parseObjectProp(filename, tokens, i + n); - - if (propRes.isSuccess()) { - n += propRes.n; - if (propRes.result.access.equals("set")) { - setters.put(propRes.result.name, propRes.result.func); - } - else { - getters.put(propRes.result.name, propRes.result.func); - } + var prop = parseObjectProp(src, i + n); + + if (prop.isSuccess()) { + n += prop.n; + + if (prop.result.access.equals("set")) setters.put(prop.result.name, prop.result.func); + else getters.put(prop.result.name, prop.result.func); } else { - var nameRes = parsePropName(filename, tokens, i + n); - if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a field name.", propRes); - n += nameRes.n; - - if (!Parsing.isOperator(tokens, i + n++, Operator.COLON)) return ParseRes.error(loc, "Expected a colon."); - - var valRes = Parsing.parseValue(filename, tokens, i + n, 2); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value in array list.", valRes); - n += valRes.n; - - values.put(nameRes.result, valRes.result); - } - - if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) { + var name = parsePropName(src, i + n); + if (!name.isSuccess()) return prop.chainError(src.loc(i + n), "Expected a field name"); + n += name.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon"); n++; - if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + + var valRes = Parsing.parseValue(src, i + n, 2); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list"); + n += valRes.n; + + values.put(name.result, valRes.result); + } + + n += Parsing.skipEmpty(src, i + n); + if (src.is(i + n, ",")) { + n++; + n += Parsing.skipEmpty(src, i + n); + + if (src.is(i + n, "}")) { n++; break; } + continue; } - else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) { + else if (src.is(i + n, "}")) { n++; break; } - else ParseRes.error(loc, "Expected a comma or a closing brace."); + else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); } return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java index 55d373d..2c98e42 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java @@ -1,18 +1,14 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; -import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.parsing.Operator; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class OperationStatement extends Statement { public final Statement[] args; @@ -41,75 +37,74 @@ public class OperationStatement extends Statement { this.args = args; } - public static ParseRes parseUnary(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - - var opState = Parsing.parseOperator(tokens, i + n++); - if (!opState.isSuccess()) return ParseRes.failed(); - var op = opState.result; + public static ParseRes parsePrefix(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); Operation operation = null; + String op; - if (op == Operator.ADD) operation = Operation.POS; - else if (op == Operator.SUBTRACT) operation = Operation.NEG; - else if (op == Operator.INVERSE) operation = Operation.INVERSE; - else if (op == Operator.NOT) operation = Operation.NOT; + if (src.is(i + n, op = "+")) operation = Operation.POS; + else if (src.is(i + n, op = "-")) operation = Operation.NEG; + else if (src.is(i + n, op = "~")) operation = Operation.INVERSE; + else if (src.is(i + n, op = "!")) operation = Operation.NOT; else return ParseRes.failed(); - var res = Parsing.parseValue(filename, tokens, n + i, 14); + n++; + + var res = Parsing.parseValue(src, i + n, 14); if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); - else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res); + else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op)); } - public static ParseRes parseInstanceof(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - + public static ParseRes parseInstanceof(Source src, int i, Statement prev, int precedence) { if (precedence > 9) return ParseRes.failed(); - if (!Parsing.isIdentifier(tokens, i + n++, "instanceof")) return ParseRes.failed(); - var valRes = Parsing.parseValue(filename, tokens, i + n, 10); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'instanceof'.", valRes); + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var kw = Parsing.parseIdentifier(src, i + n, "instanceof"); + if (!kw.isSuccess()) return kw.chainError(); + n += kw.n; + + var valRes = Parsing.parseValue(src, i + n, 10); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'instanceof'."); n += valRes.n; return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n); } - public static ParseRes parseIn(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - int n = 0; - + public static ParseRes parseIn(Source src, int i, Statement prev, int precedence) { if (precedence > 9) return ParseRes.failed(); - if (!Parsing.isIdentifier(tokens, i + n++, "in")) return ParseRes.failed(); - var valRes = Parsing.parseValue(filename, tokens, i + n, 10); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'in'.", valRes); + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var kw = Parsing.parseIdentifier(src, i + n, "in"); + if (!kw.isSuccess()) return kw.chainError(); + n += kw.n; + + var valRes = Parsing.parseValue(src, i + n, 10); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'in'."); n += valRes.n; - return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n); + return ParseRes.res(new OperationStatement(loc, Operation.IN, valRes.result, prev), n); } - public static ParseRes parseOperator(Filename filename, List tokens, int i, Statement prev, int precedence) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; + public static ParseRes parseOperator(Source src, int i, Statement prev, int precedence) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); - var opRes = Parsing.parseOperator(tokens, i + n++); - if (!opRes.isSuccess()) return ParseRes.failed(); - var op = opRes.result; + for (var op : Operator.opsByLength) { + if (!src.is(i + n, op.readable)) continue; + if (op.precedence < precedence) return ParseRes.failed(); + n += op.readable.length(); - if (op.precedence < precedence) return ParseRes.failed(); - if (op.isAssign()) return AssignableStatement.parse(filename, tokens, i + n - 1, prev, precedence); + var res = Parsing.parseValue(src, i + n, op.precedence + 1); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s' operator.", op.readable)); + n += res.n; - var res = Parsing.parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1)); - if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res); - n += res.n; - - if (op == Operator.LAZY_AND) { - return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n); - } - if (op == Operator.LAZY_OR) { - return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n); + return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n); } - return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n); + return ParseRes.failed(); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java index b7a753a..8b240d0 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java @@ -1,15 +1,12 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class RegexStatement extends Statement { public final String pattern, flags; @@ -23,19 +20,56 @@ public class RegexStatement extends Statement { if (!pollute) target.add(Instruction.discard()); } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - if (Parsing.inBounds(tokens, i)) { - if (tokens.get(i).isRegex()) { - var val = tokens.get(i).regex(); - var index = val.lastIndexOf('/'); - var first = val.substring(1, index); - var second = val.substring(index + 1); - return ParseRes.res(new RegexStatement(loc, first, second), 1); + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + + if (!src.is(i + n, '/')) return ParseRes.failed(); + var loc = src.loc(i + n); + n++; + + var source = new StringBuilder(); + var flags = new StringBuilder(); + + var inBrackets = false; + + while (true) { + if (src.is(i + n, '[')) { + n++; + inBrackets = true; + source.append(src.at(i + n)); + continue; } - else return ParseRes.failed(); + else if (src.is(i + n, ']')) { + n++; + inBrackets = false; + source.append(src.at(i + n)); + continue; + } + else if (src.is(i + n, '/') && !inBrackets) { + n++; + break; + } + + var charRes = Parsing.parseChar(src, i + n); + if (charRes.result == null) return ParseRes.error(src.loc(i + n), "Multiline regular expressions are not allowed"); + source.append(charRes.result); + n++; } - return ParseRes.failed(); + + while (true) { + char c = src.at(i + n, '\0'); + + if (src.is(i + n, v -> Parsing.isAny(c, "dgimsuy"))) { + if (flags.indexOf(c + "") >= 0) return ParseRes.error(src.loc(i + n), "The flags of a regular expression may not be repeated"); + flags.append(c); + } + else break; + + n++; + } + + return ParseRes.res(new RegexStatement(loc, source.toString(), flags.toString()), n); } public RegexStatement(Location loc, String pattern, String flags) { diff --git a/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java index 267c748..89ddc98 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java @@ -1,15 +1,12 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class TypeofStatement extends Statement { public final Statement value; @@ -36,13 +33,15 @@ public class TypeofStatement extends Statement { this.value = value; } - public static ParseRes parse(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - var n = 0; - if (!Parsing.isIdentifier(tokens, i + n++, "typeof")) return ParseRes.failed(); - - var valRes = Parsing.parseValue(filename, tokens, i + n, 15); - if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'typeof' keyword.", valRes); + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!Parsing.isIdentifier(src, i + n, "typeof")) return ParseRes.failed(); + n += 6; + + var valRes = Parsing.parseValue(src, i + n, 15); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword."); n += valRes.n; return ParseRes.res(new TypeofStatement(loc, valRes.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java index 6349c40..53933bb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java @@ -1,17 +1,14 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.List; - -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Token; +import me.topchetoeu.jscript.compilation.parsing.Source; public class VariableStatement extends AssignableStatement { public final String name; @@ -35,19 +32,21 @@ public class VariableStatement extends AssignableStatement { this.name = name; } - public static ParseRes parseVariable(Filename filename, List tokens, int i) { - var loc = Parsing.getLoc(filename, tokens, i); - var literal = Parsing.parseIdentifier(tokens, i); - - if (!literal.isSuccess()) return ParseRes.failed(); - + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var literal = Parsing.parseIdentifier(src, i); + if (!literal.isSuccess()) return literal.chainError(); + n += literal.n; + if (!Parsing.checkVarName(literal.result)) { - if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported."); - if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported."); - if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported."); - return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result)); + if (literal.result.equals("await")) return ParseRes.error(src.loc(i + n), "'await' expressions are not supported."); + if (literal.result.equals("const")) return ParseRes.error(src.loc(i + n), "'const' declarations are not supported."); + if (literal.result.equals("let")) return ParseRes.error(src.loc(i + n), "'let' declarations are not supported."); + return ParseRes.error(src.loc(i + n), String.format("Unexpected keyword '%s'.", literal.result)); } - - return ParseRes.res(new VariableStatement(loc, literal.result), 1); + + return ParseRes.res(new VariableStatement(loc, literal.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java new file mode 100644 index 0000000..51c6db7 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java @@ -0,0 +1,21 @@ +package me.topchetoeu.jscript.compilation.values.constants; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Statement; + +public class BoolStatement extends Statement { + public final boolean value; + + @Override public boolean pure() { return true; } + + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.pushValue(value)); + } + + public BoolStatement(Location loc, boolean value) { + super(loc); + this.value = value; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java new file mode 100644 index 0000000..b21545f --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java @@ -0,0 +1,4 @@ +package me.topchetoeu.jscript.compilation.values.constants; + +public class ConstantStatements { +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java new file mode 100644 index 0000000..7238ce6 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java @@ -0,0 +1,16 @@ +package me.topchetoeu.jscript.compilation.values.constants; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Statement; + +public class NullStatement extends Statement { + @Override public boolean pure() { return true; } + + @Override public void compile(CompileResult target, boolean pollute) { + target.add(Instruction.pushNull()); + } + + public NullStatement(Location loc) { super(loc); } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java new file mode 100644 index 0000000..2640cd3 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java @@ -0,0 +1,42 @@ +package me.topchetoeu.jscript.compilation.values.constants; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Source; + +public class NumberStatement extends Statement { + public final double value; + + @Override public boolean pure() { return true; } + + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.pushValue(value)); + } + + public NumberStatement(Location loc, double value) { + super(loc); + this.value = value; + } + + public static double power(double a, long b) { + if (b == 0) return 1; + if (b == 1) return a; + if (b < 0) return 1 / power(a, -b); + + if ((b & 1) == 0) return power(a * a, b / 2); + else return a * power(a * a, b / 2); + } + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var res = Parsing.parseNumber(src, i + n); + if (res.isSuccess()) return ParseRes.res(new NumberStatement(loc, res.result), n + res.n); + else return res.chainError(); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java new file mode 100644 index 0000000..02fad21 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java @@ -0,0 +1,33 @@ +package me.topchetoeu.jscript.compilation.values.constants; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.parsing.ParseRes; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.compilation.parsing.Source; + +public class StringStatement extends Statement { + public final String value; + + @Override public boolean pure() { return true; } + + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.pushValue(value)); + } + + public StringStatement(Location loc, String value) { + super(loc); + this.value = value; + } + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var res = Parsing.parseString(src, i + n); + if (res.isSuccess()) return ParseRes.res(new StringStatement(loc, res.result), n + res.n); + else return res.chainError(); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 5253aa3..4381f4f 100644 --- a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -19,7 +19,7 @@ import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class SimpleRepl { - static Thread engineTask, debugTask; + static Thread engineTask; static Engine engine = new Engine(); static Environment environment = Environment.empty(); @@ -69,11 +69,6 @@ public class SimpleRepl { } private static void initEnv() { - var glob = GlobalScope.get(environment); - - glob.define(null, false, new NativeFunction("exit", args -> { - throw new InterruptException(); - })); // glob.define(null, false, new NativeFunction("go", args -> { // try { // var f = Path.of("do.js"); @@ -84,13 +79,6 @@ public class SimpleRepl { // throw new EngineException("Couldn't open do.js"); // } // })); - glob.define(null, false, new NativeFunction("log", args -> { - for (var el : args.args) { - System.out.print(el.toReadable(args.env)); - } - - return null; - })); // var fs = new RootFilesystem(PermissionsProvider.get(environment)); // fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); @@ -107,6 +95,18 @@ public class SimpleRepl { environment.add(Compiler.KEY, (filename, source) -> { return Parsing.compile(filename, source).body(); }); + + var glob = GlobalScope.get(environment); + glob.define(null, false, new NativeFunction("exit", args -> { + throw new InterruptException(); + })); + glob.define(null, false, new NativeFunction("log", args -> { + for (var el : args.args) { + System.out.print(el.toReadable(args.env)); + } + + return null; + })); } private static void initEngine() { // var ctx = new DebugContext(); @@ -131,7 +131,7 @@ public class SimpleRepl { reader.start(); engine.thread().join(); - debugTask.interrupt(); + // debugTask.interrupt(); engineTask.interrupt(); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index 63a778b..83f0770 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -53,7 +53,7 @@ public abstract class FunctionValue extends ObjectValue { return super.getOwnMember(env, key); } @Override public boolean deleteOwnMember(Environment env, Value key) { - if (!super.deleteMember(env, key)) return false; + if (!super.deleteOwnMember(env, key)) return false; var el = key.toString(env).value; -- 2.45.2 From d0ccf00f1428c985ad8f7d841ac70dc75c08bfbc Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:36:33 +0300 Subject: [PATCH 05/48] everything all at once --- .../topchetoeu/jscript/common/Compiler.java | 31 +- .../jscript/common/Instruction.java | 113 +-- .../topchetoeu/jscript/common/RefTracker.java | 23 - .../jscript/common/ResultRunnable.java | 5 - .../jscript/common/events/DataNotifier.java | 31 - .../jscript/common/events/Notifier.java | 19 - .../topchetoeu/jscript/common/json/JSON.java | 37 +- .../jscript/common/mapping/FunctionMap.java | 4 +- .../common/{ => parsing}/Filename.java | 2 +- .../common/{ => parsing}/Location.java | 2 +- .../parsing/ParseRes.java | 9 +- .../jscript/common/parsing/Parsing.java | 383 +++++++++ .../parsing/Source.java | 5 +- .../parsing/SourceLocation.java | 5 +- .../compilation/AssignableStatement.java | 69 +- .../jscript/compilation/CompileResult.java | 2 +- .../compilation/CompoundStatement.java | 12 +- .../topchetoeu/jscript/compilation/ES5.java | 302 +++++++ .../jscript/compilation/Statement.java | 2 +- .../compilation/VariableDeclareStatement.java | 16 +- .../compilation/control/BreakStatement.java | 13 +- .../control/ContinueStatement.java | 13 +- .../compilation/control/DebugStatement.java | 11 +- .../compilation/control/DeleteStatement.java | 13 +- .../compilation/control/DoWhileStatement.java | 15 +- .../compilation/control/ForInStatement.java | 13 +- .../compilation/control/ForOfStatement.java | 13 +- .../compilation/control/ForStatement.java | 17 +- .../compilation/control/IfStatement.java | 19 +- .../compilation/control/ReturnStatement.java | 19 +- .../compilation/control/SwitchStatement.java | 15 +- .../compilation/control/ThrowStatement.java | 15 +- .../compilation/control/TryStatement.java | 8 +- .../compilation/control/WhileStatement.java | 13 +- .../jscript/compilation/parsing/Operator.java | 48 -- .../jscript/compilation/parsing/Parsing.java | 539 ------------ .../jscript/compilation/parsing/RawToken.java | 15 - .../jscript/compilation/parsing/TestRes.java | 45 -- .../jscript/compilation/parsing/Token.java | 58 -- .../compilation/parsing/TokenType.java | 9 - .../compilation/values/ArrayStatement.java | 14 +- .../compilation/values/CallStatement.java | 101 --- .../compilation/values/FunctionStatement.java | 27 +- .../values/GlobalThisStatement.java | 2 +- .../compilation/values/ObjectStatement.java | 15 +- .../values/OperationStatement.java | 110 --- .../compilation/values/RegexStatement.java | 8 +- .../compilation/values/VariableStatement.java | 12 +- .../values/constants/BoolStatement.java | 2 +- .../values/constants/NullStatement.java | 2 +- .../values/constants/NumberStatement.java | 10 +- .../values/constants/StringStatement.java | 8 +- .../values/operations/CallStatement.java | 180 +++++ .../{ => operations}/ChangeStatement.java | 15 +- .../{ => operations}/DiscardStatement.java | 13 +- .../IndexAssignStatement.java | 4 +- .../{ => operations}/IndexStatement.java | 13 +- .../{ => operations}/LazyAndStatement.java | 13 +- .../{ => operations}/LazyOrStatement.java | 13 +- .../values/operations/OperationStatement.java | 234 ++++++ .../{ => operations}/TypeofStatement.java | 14 +- .../VariableAssignStatement.java | 5 +- .../VariableIndexStatement.java | 4 +- .../jscript/runtime/ArgumentsValue.java | 13 + .../me/topchetoeu/jscript/runtime/Engine.java | 20 +- .../topchetoeu/jscript/runtime/EventLoop.java | 19 +- .../me/topchetoeu/jscript/runtime/Frame.java | 18 +- .../jscript/runtime/InstructionRunner.java | 39 +- .../jscript/runtime/JSONConverter.java | 2 +- .../jscript/runtime/SimpleRepl.java | 81 +- .../jscript/runtime/WrapperProvider.java | 13 - .../jscript/runtime/debug/DebugContext.java | 4 +- .../jscript/runtime/debug/DebugHandler.java | 2 +- .../runtime/exceptions/ConvertException.java | 11 - .../runtime/exceptions/EngineException.java | 21 +- .../runtime/exceptions/SyntaxException.java | 2 +- .../jscript/runtime/scope/GlobalScope.java | 12 +- .../jscript/runtime/values/ConvertHint.java | 6 - .../jscript/runtime/values/KeyCache.java | 59 ++ .../jscript/runtime/values/Member.java | 21 +- .../jscript/runtime/values/Value.java | 175 +++- .../jscript/runtime/values/Values.java.old | 765 ------------------ .../runtime/values/functions/Arguments.java | 4 +- .../values/functions/CodeFunction.java | 4 +- .../values/functions/FunctionValue.java | 59 +- .../values/functions/NativeFunction.java | 4 +- .../runtime/values/objects/ArrayValue.java | 41 +- .../runtime/values/objects/ObjectValue.java | 28 +- .../runtime/values/objects/ScopeValue.java | 3 +- .../runtime/values/primitives/BoolValue.java | 2 +- .../values/primitives/NumberValue.java | 66 +- .../values/primitives/PrimitiveValue.java | 7 +- .../values/primitives/StringValue.java | 23 +- .../values/primitives/SymbolValue.java | 14 +- .../runtime/values/primitives/VoidValue.java | 13 +- 95 files changed, 1976 insertions(+), 2377 deletions(-) delete mode 100644 src/java/me/topchetoeu/jscript/common/RefTracker.java delete mode 100644 src/java/me/topchetoeu/jscript/common/ResultRunnable.java delete mode 100644 src/java/me/topchetoeu/jscript/common/events/DataNotifier.java delete mode 100644 src/java/me/topchetoeu/jscript/common/events/Notifier.java rename src/java/me/topchetoeu/jscript/common/{ => parsing}/Filename.java (97%) rename src/java/me/topchetoeu/jscript/common/{ => parsing}/Location.java (95%) rename src/java/me/topchetoeu/jscript/{compilation => common}/parsing/ParseRes.java (90%) create mode 100644 src/java/me/topchetoeu/jscript/common/parsing/Parsing.java rename src/java/me/topchetoeu/jscript/{compilation => common}/parsing/Source.java (92%) rename src/java/me/topchetoeu/jscript/{compilation => common}/parsing/SourceLocation.java (89%) create mode 100644 src/java/me/topchetoeu/jscript/compilation/ES5.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/RawToken.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/Token.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/parsing/TokenType.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/ChangeStatement.java (89%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/DiscardStatement.java (74%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/IndexAssignStatement.java (92%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/IndexStatement.java (87%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/LazyAndStatement.java (79%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/LazyOrStatement.java (78%) create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/TypeofStatement.java (76%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/VariableAssignStatement.java (86%) rename src/java/me/topchetoeu/jscript/compilation/values/{ => operations}/VariableIndexStatement.java (82%) create mode 100644 src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/exceptions/ConvertException.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/ConvertHint.java create mode 100644 src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/values/Values.java.old diff --git a/src/java/me/topchetoeu/jscript/common/Compiler.java b/src/java/me/topchetoeu/jscript/common/Compiler.java index 01d2bb0..812530a 100644 --- a/src/java/me/topchetoeu/jscript/common/Compiler.java +++ b/src/java/me/topchetoeu/jscript/common/Compiler.java @@ -1,5 +1,8 @@ package me.topchetoeu.jscript.common; +import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; @@ -8,18 +11,36 @@ import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { + public static final Compiler DEFAULT = (env, filename, raw) -> { + var res = ES5.compile(filename, raw); + var body = res.body(); + DebugContext.get(env).onSource(filename, raw); + registerFunc(env, body, res); + + return body; + }; + public Key KEY = new Key<>(); - public FunctionBody compile(Filename filename, String source); + public FunctionBody compile(Environment env, Filename filename, String source); public static Compiler get(Environment ext) { - return ext.get(KEY, (filename, src) -> { + return ext.get(KEY, (env, filename, src) -> { throw EngineException.ofError("No compiler attached to engine."); }); } - public static CodeFunction compile(Environment env, Filename filename, String raw) { - DebugContext.get(env).onSource(filename, raw); - return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]); + private static void registerFunc(Environment env, FunctionBody body, CompileResult res) { + var map = res.map(); + + DebugContext.get(env).onFunctionLoad(body, map); + + for (var i = 0; i < body.children.length; i++) { + registerFunc(env, body.children[i], res.children.get(i)); + } + } + + public static CodeFunction compileFunc(Environment env, Filename filename, String raw) { + return new CodeFunction(env, filename.toString(), get(env).compile(env, filename, raw), new ValueVariable[0]); } } diff --git a/src/java/me/topchetoeu/jscript/common/Instruction.java b/src/java/me/topchetoeu/jscript/common/Instruction.java index 9460c39..edc0652 100644 --- a/src/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/java/me/topchetoeu/jscript/common/Instruction.java @@ -9,48 +9,46 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class Instruction { public static enum Type { - NOP(0), - RETURN(1), - THROW(2), - THROW_SYNTAX(3), - DELETE(4), - TRY_START(5), - TRY_END(6), + NOP(0x00), + RETURN(0x01), + THROW(0x02), + THROW_SYNTAX(0x03), + DELETE(0x04), + TRY_START(0x05), + TRY_END(0x06), - CALL(7), - CALL_NEW(8), - JMP_IF(9), - JMP_IFN(10), - JMP(11), + CALL(0x10), + CALL_MEMBER(0x11), + CALL_NEW(0x12), + JMP_IF(0x13), + JMP_IFN(0x14), + JMP(0x15), - PUSH_UNDEFINED(12), - PUSH_NULL(13), - PUSH_BOOL(14), - PUSH_NUMBER(15), - PUSH_STRING(16), + PUSH_UNDEFINED(0x20), + PUSH_NULL(0x21), + PUSH_BOOL(0x22), + PUSH_NUMBER(0x23), + PUSH_STRING(0x24), + DUP(0x25), + DISCARD(0x26), - LOAD_VAR(17), - LOAD_MEMBER(18), - LOAD_GLOB(20), + LOAD_FUNC(0x30), + LOAD_ARR(0x31), + LOAD_OBJ(0x32), + STORE_SELF_FUNC(0x33), + LOAD_REGEX(0x34), - LOAD_FUNC(21), - LOAD_ARR(22), - LOAD_OBJ(23), - STORE_SELF_FUNC(24), - LOAD_REGEX(25), + LOAD_VAR(0x40), + LOAD_MEMBER(0x41), + LOAD_GLOB(0x42), + STORE_VAR(0x43), + STORE_MEMBER(0x44), - DUP(26), - - STORE_VAR(27), - STORE_MEMBER(28), - DISCARD(29), - - MAKE_VAR(30), - DEF_PROP(31), - KEYS(32), - - TYPEOF(33), - OPERATION(34); + MAKE_VAR(0x50), + DEF_PROP(0x51), + KEYS(0x52), + TYPEOF(0x53), + OPERATION(0x54); private static final HashMap types = new HashMap<>(); public final int numeric; @@ -125,8 +123,12 @@ public class Instruction { writer.writeByte(rawType); switch (type) { - case CALL: writer.writeInt(get(0)); break; - case CALL_NEW: writer.writeInt(get(0)); break; + case CALL: + case CALL_NEW: + case CALL_MEMBER: + writer.writeInt(get(0)); + writer.writeUTF(get(1)); + break; case DUP: writer.writeInt(get(0)); break; case JMP: writer.writeInt(get(0)); break; case JMP_IF: writer.writeInt(get(0)); break; @@ -140,6 +142,7 @@ public class Instruction { } writer.writeInt(get(0)); + writer.writeUTF(get(0)); break; } case LOAD_REGEX: writer.writeUTF(get(0)); break; @@ -174,8 +177,9 @@ public class Instruction { var flag = (rawType & 128) != 0; switch (type) { - case CALL: return call(stream.readInt()); - case CALL_NEW: return callNew(stream.readInt()); + case CALL: return call(stream.readInt(), stream.readUTF()); + case CALL_NEW: return callNew(stream.readInt(), stream.readUTF()); + case CALL_MEMBER: return callNew(stream.readInt(), stream.readUTF()); case DEF_PROP: return defProp(); case DELETE: return delete(); case DISCARD: return discard(); @@ -192,7 +196,7 @@ public class Instruction { captures[i] = stream.readInt(); } - return loadFunc(stream.readInt(), captures); + return loadFunc(stream.readInt(), stream.readUTF(), captures); } case LOAD_GLOB: return loadGlob(); case LOAD_MEMBER: return loadMember(); @@ -251,11 +255,23 @@ public class Instruction { return new Instruction(Type.NOP, params); } + public static Instruction call(int argn, String name) { + return new Instruction(Type.CALL, argn, name); + } public static Instruction call(int argn) { - return new Instruction(Type.CALL, argn); + return call(argn, ""); + } + public static Instruction callMember(int argn, String name) { + return new Instruction(Type.CALL_MEMBER, argn, name); + } + public static Instruction callMember(int argn) { + return new Instruction(Type.CALL_MEMBER, argn, ""); + } + public static Instruction callNew(int argn, String name) { + return new Instruction(Type.CALL_NEW, argn, name); } public static Instruction callNew(int argn) { - return new Instruction(Type.CALL_NEW, argn); + return new Instruction(Type.CALL_NEW, argn, ""); } public static Instruction jmp(int offset) { return new Instruction(Type.JMP, offset); @@ -299,10 +315,13 @@ public class Instruction { public static Instruction loadRegex(String pattern, String flags) { return new Instruction(Type.LOAD_REGEX, pattern, flags); } - public static Instruction loadFunc(int id, int[] captures) { - var args = new Object[1 + captures.length]; + public static Instruction loadFunc(int id, String name, int[] captures) { + if (name == null) name = ""; + + var args = new Object[2 + captures.length]; args[0] = id; - for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i]; + args[1] = name; + for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i]; return new Instruction(Type.LOAD_FUNC, args); } public static Instruction loadObj() { diff --git a/src/java/me/topchetoeu/jscript/common/RefTracker.java b/src/java/me/topchetoeu/jscript/common/RefTracker.java deleted file mode 100644 index 249605f..0000000 --- a/src/java/me/topchetoeu/jscript/common/RefTracker.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.topchetoeu.jscript.common; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; - -public class RefTracker { - public static void onDestroy(Object obj, Runnable runnable) { - var queue = new ReferenceQueue<>(); - var ref = new WeakReference<>(obj, queue); - obj = null; - - var th = new Thread(() -> { - try { - queue.remove(); - ref.get(); - runnable.run(); - } - catch (InterruptedException e) { return; } - }); - th.setDaemon(true); - th.start(); - } -} diff --git a/src/java/me/topchetoeu/jscript/common/ResultRunnable.java b/src/java/me/topchetoeu/jscript/common/ResultRunnable.java deleted file mode 100644 index 7bfc608..0000000 --- a/src/java/me/topchetoeu/jscript/common/ResultRunnable.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.common; - -public interface ResultRunnable { - T run(); -} diff --git a/src/java/me/topchetoeu/jscript/common/events/DataNotifier.java b/src/java/me/topchetoeu/jscript/common/events/DataNotifier.java deleted file mode 100644 index b498b85..0000000 --- a/src/java/me/topchetoeu/jscript/common/events/DataNotifier.java +++ /dev/null @@ -1,31 +0,0 @@ -package me.topchetoeu.jscript.common.events; - -public class DataNotifier { - private Notifier notifier = new Notifier(); - private boolean isErr; - private T val; - private RuntimeException err; - - public void error(RuntimeException t) { - err = t; - isErr = true; - notifier.next(); - } - public void next(T val) { - this.val = val; - isErr = false; - notifier.next(); - } - public T await() { - notifier.await(); - - try { - if (isErr) throw err; - else return val; - } - finally { - this.err = null; - this.val = null; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/common/events/Notifier.java b/src/java/me/topchetoeu/jscript/common/events/Notifier.java deleted file mode 100644 index eb6ad4d..0000000 --- a/src/java/me/topchetoeu/jscript/common/events/Notifier.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.common.events; - -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; - -public class Notifier { - private boolean ok = false; - - public synchronized void next() { - ok = true; - notifyAll(); - } - public synchronized void await() { - try { - while (!ok) wait(); - ok = false; - } - catch (InterruptedException e) { throw new InterruptException(e); } - } -} diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index 66d6ed8..74434ba 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -1,12 +1,13 @@ package me.topchetoeu.jscript.common.json; +import java.math.BigDecimal; import java.util.Map; import java.util.stream.Collectors; -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class JSON { @@ -16,23 +17,9 @@ public class JSON { return ParseRes.res(JSONElement.string(res.result), res.n); } public static ParseRes parseNumber(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - - if (src.is(i + n, "-")) { - n++; - - var res = Parsing.parseNumber(src, i); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a number after minus"); - n += res.n; - - return ParseRes.res(JSONElement.number(-res.result), n); - } - else { - var res = Parsing.parseNumber(src, i + n).addN(n); - if (!res.isSuccess()) return res.chainError(); - n += res.n; - return ParseRes.res(JSONElement.number(res.result), n); - } + var res = Parsing.parseNumber(src, i, true); + if (!res.isSuccess()) return res.chainError(); + else return ParseRes.res(JSONElement.number(res.result), res.n); } public static ParseRes parseLiteral(Source src, int i) { var id = Parsing.parseIdentifier(src, i); @@ -121,7 +108,13 @@ public class JSON { } public static String stringify(JSONElement el) { - if (el.isNumber()) return Double.toString(el.number()); + if (el.isNumber()) { + var d = el.number(); + if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; + if (d == Double.POSITIVE_INFINITY) return "Infinity"; + if (Double.isNaN(d)) return "NaN"; + return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); + } if (el.isBoolean()) return el.bool() ? "true" : "false"; if (el.isNull()) return "null"; if (el.isString()) { diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index d0d5d47..5d54895 100644 --- a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -11,9 +11,9 @@ import java.util.TreeSet; import java.util.regex.Pattern; import java.util.stream.Collectors; -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; public class FunctionMap { diff --git a/src/java/me/topchetoeu/jscript/common/Filename.java b/src/java/me/topchetoeu/jscript/common/parsing/Filename.java similarity index 97% rename from src/java/me/topchetoeu/jscript/common/Filename.java rename to src/java/me/topchetoeu/jscript/common/parsing/Filename.java index 7ced6cb..8e57751 100644 --- a/src/java/me/topchetoeu/jscript/common/Filename.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Filename.java @@ -1,4 +1,4 @@ -package me.topchetoeu.jscript.common; +package me.topchetoeu.jscript.common.parsing; import java.io.File; import java.nio.file.Path; diff --git a/src/java/me/topchetoeu/jscript/common/Location.java b/src/java/me/topchetoeu/jscript/common/parsing/Location.java similarity index 95% rename from src/java/me/topchetoeu/jscript/common/Location.java rename to src/java/me/topchetoeu/jscript/common/parsing/Location.java index 2118ee3..1ef86e8 100644 --- a/src/java/me/topchetoeu/jscript/common/Location.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Location.java @@ -1,4 +1,4 @@ -package me.topchetoeu.jscript.common; +package me.topchetoeu.jscript.common.parsing; import java.util.ArrayList; import java.util.Objects; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java b/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java similarity index 90% rename from src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java rename to src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java index ae539aa..d50d289 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java @@ -1,6 +1,4 @@ -package me.topchetoeu.jscript.compilation.parsing; - -import me.topchetoeu.jscript.common.Location; +package me.topchetoeu.jscript.common.parsing; public class ParseRes { public static interface Parser { @@ -41,11 +39,6 @@ public class ParseRes { if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed."); return new ParseRes<>(state, error, null, 0); } - public TestRes toTest() { - if (isSuccess()) return TestRes.res(n); - else if (isError()) return TestRes.error(null, error); - else return TestRes.failed(); - } public boolean isSuccess() { return state.isSuccess(); } public boolean isFailed() { return state.isFailed(); } diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java new file mode 100644 index 0000000..a0a5f27 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java @@ -0,0 +1,383 @@ +package me.topchetoeu.jscript.common.parsing; + +import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; + +// TODO: this has to be rewritten +// @SourceFile +public class Parsing { + public static boolean isDigit(Character c) { + return c != null && c >= '0' && c <= '9'; + } + public static boolean isAny(char c, String alphabet) { + return alphabet.contains(Character.toString(c)); + } + + public static int fromHex(char c) { + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= '0' && c <= '9') return c - '0'; + return -1; + } + + public static int skipEmpty(Source src, int i) { + int n = 0; + + while (n < src.size() && src.is(i + n, Character::isWhitespace)) n++; + + return n; + } + + public static ParseRes parseChar(Source src, int i) { + int n = 0; + + if (src.is(i + n, '\\')) { + n++; + char c = src.at(i + n++); + + if (c == 'b') return ParseRes.res('\b', n); + else if (c == 't') return ParseRes.res('\t', n); + else if (c == 'n') return ParseRes.res('\n', n); + else if (c == 'f') return ParseRes.res('\f', n); + else if (c == 'r') return ParseRes.res('\r', n); + else if (c == '0') { + if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed"); + else return ParseRes.res('\0', n); + } + else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed"); + else if (c == 'x') { + var newC = 0; + + for (var j = 0; j < 2; j++) { + if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence."); + + int val = fromHex(src.at(i + n)); + if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence."); + n++; + + newC = (newC << 4) | val; + } + + return ParseRes.res((char)newC, n); + } + else if (c == 'u') { + var newC = 0; + + for (var j = 0; j < 4; j++) { + if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence"); + + int val = fromHex(src.at(i + n)); + if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence"); + n++; + + newC = (newC << 4) | val; + } + + return ParseRes.res((char)newC, n); + } + else if (c == '\n') return ParseRes.res(null, n); + } + + return ParseRes.res(src.at(i + n), n + 1); + } + + public static ParseRes parseIdentifier(Source src, int i) { + var n = skipEmpty(src, i); + var res = new StringBuilder(); + var first = true; + + while (true) { + if (i + n > src.size()) break; + char c = src.at(i + n, '\0'); + + if (first && Parsing.isDigit(c)) break; + if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break; + res.append(c); + n++; + first = false; + } + + if (res.length() <= 0) return ParseRes.failed(); + else return ParseRes.res(res.toString(), n); + } + public static ParseRes parseIdentifier(Source src, int i, String test) { + var n = skipEmpty(src, i); + var res = new StringBuilder(); + var first = true; + + while (true) { + if (i + n > src.size()) break; + char c = src.at(i + n, '\0'); + + if (first && Parsing.isDigit(c)) break; + if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break; + res.append(c); + n++; + first = false; + } + + if (res.length() <= 0) return ParseRes.failed(); + else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n); + else return ParseRes.failed(); + } + public static boolean isIdentifier(Source src, int i, String test) { + return parseIdentifier(src, i, test).isSuccess(); + } + + public static ParseRes parseOperator(Source src, int i, String op) { + var n = skipEmpty(src, i); + + if (src.is(i + n, op)) return ParseRes.res(op, n + op.length()); + else return ParseRes.failed(); + } + + private static ParseRes parseHex(Source src, int i) { + int n = 0; + double res = 0; + + while (true) { + int digit = Parsing.fromHex(src.at(i + n, '\0')); + if (digit < 0) { + if (n <= 0) return ParseRes.failed(); + else return ParseRes.res(res, n); + } + n++; + + res *= 16; + res += digit; + } + } + private static ParseRes parseOct(Source src, int i) { + int n = 0; + double res = 0; + + while (true) { + int digit = src.at(i + n, '\0') - '0'; + if (digit < 0 || digit > 9) break; + if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit); + + if (digit < 0) { + if (n <= 0) return ParseRes.failed(); + else return ParseRes.res(res, n); + } + n++; + + res *= 8; + res += digit; + } + + return ParseRes.res(res, n); + } + + public static ParseRes parseString(Source src, int i) { + var n = skipEmpty(src, i); + + char quote; + + if (src.is(i + n, '\'')) quote = '\''; + else if (src.is(i + n, '"')) quote = '"'; + else return ParseRes.failed(); + n++; + + var res = new StringBuilder(); + + while (true) { + if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal"); + if (src.is(i + n, quote)) { + n++; + return ParseRes.res(res.toString(), n); + } + + var charRes = parseChar(src, i + n); + if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character"); + n += charRes.n; + + if (charRes.result != null) res.append(charRes.result); + } + } + public static ParseRes parseNumber(Source src, int i, boolean withMinus) { + var n = skipEmpty(src, i); + + double whole = 0; + double fract = 0; + long exponent = 0; + boolean parsedAny = false; + boolean negative = false; + + if (withMinus && src.is(i + n, "-")) { + negative = true; + n++; + } + + if (src.is(i + n, "0x") || src.is(i + n, "0X")) { + n += 2; + + var res = parseHex(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal"); + n += res.n; + + if (negative) return ParseRes.res(-res.result, n); + else return ParseRes.res(res.result, n); + } + else if (src.is(i + n, "0o") || src.is(i + n, "0O")) { + n += 2; + + var res = parseOct(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal"); + n += res.n; + + if (negative) return ParseRes.res(-res.result, n); + else return ParseRes.res(res.result, n); + } + else if (src.is(i + n, '0')) { + n++; + parsedAny = true; + if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed"); + } + + while (src.is(i + n, Parsing::isDigit)) { + parsedAny = true; + whole *= 10; + whole += src.at(i + n++) - '0'; + } + + if (src.is(i + n, '.')) { + parsedAny = true; + n++; + + while (src.is(i + n, Parsing::isDigit)) { + fract += src.at(i + n++) - '0'; + fract /= 10; + } + } + + if (src.is(i + n, 'e') || src.is(i + n, 'E')) { + n++; + parsedAny = true; + boolean expNegative = false; + boolean parsedE = false; + + if (src.is(i + n, '-')) { + expNegative = true; + n++; + } + else if (src.is(i + n, '+')) n++; + + while (src.is(i + n, Parsing::isDigit)) { + parsedE = true; + exponent *= 10; + + if (expNegative) exponent -= src.at(i + n++) - '0'; + else exponent += src.at(i + n++) - '0'; + } + + if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent"); + } + + if (!parsedAny) { + if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus"); + return ParseRes.failed(); + } + else if (negative) return ParseRes.res(-(whole + fract) * NumberStatement.power(10, exponent), n); + else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n); + } + public static ParseRes parseFloat(Source src, int i, boolean withMinus) { + var n = skipEmpty(src, i); + + double whole = 0; + double fract = 0; + long exponent = 0; + boolean parsedAny = false; + boolean negative = false; + + if (withMinus && src.is(i + n, "-")) { + negative = true; + n++; + } + + while (src.is(i + n, Parsing::isDigit)) { + parsedAny = true; + whole *= 10; + whole += src.at(i + n++) - '0'; + } + + if (src.is(i + n, '.')) { + parsedAny = true; + n++; + + while (src.is(i + n, Parsing::isDigit)) { + fract += src.at(i + n++) - '0'; + fract /= 10; + } + } + + if (src.is(i + n, 'e') || src.is(i + n, 'E')) { + n++; + parsedAny = true; + boolean expNegative = false; + boolean parsedE = false; + + if (src.is(i + n, '-')) { + expNegative = true; + n++; + } + else if (src.is(i + n, '+')) n++; + + while (src.is(i + n, Parsing::isDigit)) { + parsedE = true; + exponent *= 10; + + if (expNegative) exponent -= src.at(i + n++) - '0'; + else exponent += src.at(i + n++) - '0'; + } + + if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent"); + } + + if (!parsedAny) { + if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus"); + return ParseRes.failed(); + } + else if (negative) return ParseRes.res(-(whole + fract) * NumberStatement.power(10, exponent), n); + else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n); + } + public static ParseRes parseInt(Source src, int i, String alphabet, boolean withMinus) { + var n = skipEmpty(src, i); + + double result = 0; + boolean parsedAny = false; + boolean negative = false; + + if (withMinus && src.is(i + n, "-")) { + negative = true; + n++; + } + + if (alphabet == null && src.is(i + n, "0x") || src.is(i + n, "0X")) { + n += 2; + + var res = parseHex(src, i); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal"); + n += res.n; + + if (negative) return ParseRes.res(-res.result, n); + else return ParseRes.res(res.result, n); + } + + while (true) { + var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n))); + if (digit < 0) break; + + parsedAny = true; + result += digit; + result *= alphabet.length(); + } + + if (!parsedAny) { + if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus"); + return ParseRes.failed(); + } + else if (negative) return ParseRes.res(-result, n); + else return ParseRes.res(-result, n); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Source.java b/src/java/me/topchetoeu/jscript/common/parsing/Source.java similarity index 92% rename from src/java/me/topchetoeu/jscript/compilation/parsing/Source.java rename to src/java/me/topchetoeu/jscript/common/parsing/Source.java index bcfc56e..6fa9095 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Source.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Source.java @@ -1,10 +1,7 @@ -package me.topchetoeu.jscript.compilation.parsing; +package me.topchetoeu.jscript.common.parsing; import java.util.function.Predicate; -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Location; - public class Source { public final Filename filename; public final String src; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java b/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java similarity index 89% rename from src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java rename to src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java index 8eafffa..a2050d4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/SourceLocation.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java @@ -1,7 +1,4 @@ -package me.topchetoeu.jscript.compilation.parsing; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Location; +package me.topchetoeu.jscript.common.parsing; public class SourceLocation extends Location { private int[] lineStarts; diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java index 53a557a..be19a2b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java @@ -1,11 +1,7 @@ package me.topchetoeu.jscript.compilation; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.compilation.parsing.Operator; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.common.parsing.Location; public abstract class AssignableStatement extends Statement { public abstract Statement toAssign(Statement val, Operation operation); @@ -13,38 +9,53 @@ public abstract class AssignableStatement extends Statement { protected AssignableStatement(Location loc) { super(loc); } + // private static final Map operations = Map.ofEntries( + // Map.entry("*=", Operation.MULTIPLY), + // Map.entry("/=", Operation.DIVIDE), + // Map.entry("%=", Operation.MODULO), + // Map.entry("-=", Operation.SUBTRACT), + // Map.entry("+=", Operation.ADD), + // Map.entry(">>=", Operation.SHIFT_RIGHT), + // Map.entry("<<=", Operation.SHIFT_LEFT), + // Map.entry(">>>=", Operation.USHIFT_RIGHT), + // Map.entry("&=", Operation.AND), + // Map.entry("^=", Operation.XOR), + // Map.entry("|=", Operation.OR) + // ); + // private static final List operatorsByLength = operations.keySet().stream().sorted().collect(Collectors.toList()); - public static ParseRes parse(Source src, int i, Statement prev, int precedence) { - if (precedence > 2) return ParseRes.failed(); - var n = Parsing.skipEmpty(src, i); + // public static ParseRes parse(Source src, int i, Statement prev, int precedence) { + // if (precedence > 2) return ParseRes.failed(); - for (var op : Operator.opsByLength) { - if (!op.assignable || !src.is(i + n, op.readable + "=")) continue; - n += op.readable.length() + 1; + // var n = Parsing.skipEmpty(src, i); - if (!(prev instanceof AssignableStatement)) { - return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); - } + // for (var op : operatorsByLength) { + // if (!src.is(i + n, op)) continue; + // n += op.length() + 1; - var res = Parsing.parseValue(src, i + n, 2); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s=' operator", op.readable)); - n += res.n; + // if (!(prev instanceof AssignableStatement)) { + // return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); + // } - return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, op.operation), n); - } + // var res = Parsing.parseValue(src, i + n, 2); + // if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s=' operator", op)); + // n += res.n; - if (!src.is(i + n, "=")) return ParseRes.failed(); - n++; + // return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operations.get(op)), n); + // } - if (!(prev instanceof AssignableStatement)) { - return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); - } + // if (!src.is(i + n, "=")) return ParseRes.failed(); + // n++; - var res = Parsing.parseValue(src, i + n, 2); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '=' operator"); - n += res.n; + // if (!(prev instanceof AssignableStatement)) { + // return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); + // } - return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n); - } + // var res = Parsing.parseValue(src, i + n, 2); + // if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '=' operator"); + // n += res.n; + + // return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n); + // } } diff --git a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java index 00f6db7..f65e663 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -6,10 +6,10 @@ import java.util.Vector; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; public class CompileResult { diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java index 5adf30c..f2094c1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java @@ -5,11 +5,11 @@ import java.util.List; import java.util.Vector; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class CompoundStatement extends Statement { @@ -75,7 +75,7 @@ public class CompoundStatement extends Statement { if (!src.is(i + n, ",")) return ParseRes.failed(); n++; - var res = Parsing.parseValue(src, i + n, 2); + var res = ES5.parseExpression(src, i + n, 2); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma"); n += res.n; @@ -102,7 +102,7 @@ public class CompoundStatement extends Statement { continue; } - var res = Parsing.parseStatement(src, i + n); + var res = ES5.parseStatement(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement"); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/ES5.java b/src/java/me/topchetoeu/jscript/compilation/ES5.java new file mode 100644 index 0000000..b68d46c --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/ES5.java @@ -0,0 +1,302 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.control.BreakStatement; +import me.topchetoeu.jscript.compilation.control.ContinueStatement; +import me.topchetoeu.jscript.compilation.control.DebugStatement; +import me.topchetoeu.jscript.compilation.control.DeleteStatement; +import me.topchetoeu.jscript.compilation.control.DoWhileStatement; +import me.topchetoeu.jscript.compilation.control.ForInStatement; +import me.topchetoeu.jscript.compilation.control.ForOfStatement; +import me.topchetoeu.jscript.compilation.control.ForStatement; +import me.topchetoeu.jscript.compilation.control.IfStatement; +import me.topchetoeu.jscript.compilation.control.ReturnStatement; +import me.topchetoeu.jscript.compilation.control.SwitchStatement; +import me.topchetoeu.jscript.compilation.control.ThrowStatement; +import me.topchetoeu.jscript.compilation.control.TryStatement; +import me.topchetoeu.jscript.compilation.control.WhileStatement; +import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; +import me.topchetoeu.jscript.compilation.values.ArrayStatement; +import me.topchetoeu.jscript.compilation.values.FunctionStatement; +import me.topchetoeu.jscript.compilation.values.GlobalThisStatement; +import me.topchetoeu.jscript.compilation.values.ObjectStatement; +import me.topchetoeu.jscript.compilation.values.RegexStatement; +import me.topchetoeu.jscript.compilation.values.VariableStatement; +import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; +import me.topchetoeu.jscript.compilation.values.constants.NullStatement; +import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; +import me.topchetoeu.jscript.compilation.values.constants.StringStatement; +import me.topchetoeu.jscript.compilation.values.operations.CallStatement; +import me.topchetoeu.jscript.compilation.values.operations.ChangeStatement; +import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement; +import me.topchetoeu.jscript.compilation.values.operations.IndexStatement; +import me.topchetoeu.jscript.compilation.values.operations.OperationStatement; +import me.topchetoeu.jscript.compilation.values.operations.TypeofStatement; +import me.topchetoeu.jscript.compilation.values.operations.VariableIndexStatement; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; + +public class ES5 { + static final Set reserved = Set.of( + "true", "false", "void", "null", "this", "if", "else", "try", "catch", + "finally", "for", "do", "while", "switch", "case", "default", "new", + "function", "var", "return", "throw", "typeof", "delete", "break", + "continue", "debugger", "implements", "interface", "package", "private", + "protected", "public", "static" + ); + + public static ParseRes parseParens(Source src, int i) { + int n = 0; + + var openParen = Parsing.parseOperator(src, i + n, "("); + if (!openParen.isSuccess()) return openParen.chainError(); + n += openParen.n; + + var res = ES5.parseExpression(src, i + n, 0); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens"); + n += res.n; + + var closeParen = Parsing.parseOperator(src, i + n, ")"); + if (!closeParen.isSuccess()) return closeParen.chainError(src.loc(i + n), "Expected a closing paren"); + n += closeParen.n; + + return ParseRes.res(res.result, n); + } + + public static ParseRes parseSimple(Source src, int i, boolean statement) { + return ParseRes.first(src, i, + (a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b), + (a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false), + ES5::parseLiteral, + StringStatement::parse, + RegexStatement::parse, + NumberStatement::parse, + ChangeStatement::parsePrefixDecrease, + ChangeStatement::parsePrefixIncrease, + OperationStatement::parsePrefix, + ArrayStatement::parse, + ES5::parseParens, + CallStatement::parseNew, + TypeofStatement::parse, + DiscardStatement::parse, + DeleteStatement::parse, + VariableStatement::parse + ); + } + + public static ParseRes parseLiteral(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var id = Parsing.parseIdentifier(src, i); + if (!id.isSuccess()) return id.chainError(); + n += id.n; + + if (id.result.equals("true")) return ParseRes.res(new BoolStatement(loc, true), n); + if (id.result.equals("false")) return ParseRes.res(new BoolStatement(loc, false), n); + if (id.result.equals("undefined")) return ParseRes.res(new DiscardStatement(loc, null), n); + if (id.result.equals("null")) return ParseRes.res(new NullStatement(loc), n); + if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), n); + if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), n); + if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), n); + + return ParseRes.failed(); + } + + public static ParseRes parseExpression(Source src, int i, int precedence, boolean statement) { + var n = Parsing.skipEmpty(src, i); + Statement prev = null; + + while (true) { + if (prev == null) { + var res = parseSimple(src, i + n, statement); + if (res.isSuccess()) { + n += res.n; + prev = res.result; + } + else if (res.isError()) return res.chainError(); + else break; + } + else { + var _prev = prev; + ParseRes res = ParseRes.first(src, i + n, + (s, j) -> OperationStatement.parseInstanceof(s, j, _prev, precedence), + (s, j) -> OperationStatement.parseIn(s, j, _prev, precedence), + (s, j) -> ChangeStatement.parsePostfixIncrease(s, j, _prev, precedence), + (s, j) -> ChangeStatement.parsePostfixDecrease(s, j, _prev, precedence), + (s, j) -> OperationStatement.parseOperator(s, j, _prev, precedence), + (s, j) -> IfStatement.parseTernary(s, j, _prev, precedence), + (s, j) -> IndexStatement.parseMember(s, j, _prev, precedence), + (s, j) -> IndexStatement.parseIndex(s, j, _prev, precedence), + (s, j) -> CallStatement.parseCall(s, j, _prev, precedence), + (s, j) -> CompoundStatement.parseComma(s, j, _prev, precedence) + ); + + if (res.isSuccess()) { + n += res.n; + prev = res.result; + continue; + } + else if (res.isError()) return res.chainError(); + + break; + } + } + + if (prev == null) return ParseRes.failed(); + else return ParseRes.res(prev, n); + } + + public static ParseRes parseExpression(Source src, int i, int precedence) { + return parseExpression(src, i, precedence, false); + } + + public static ParseRes parseExpressionStatement(Source src, int i) { + var res = parseExpression(src, i, 0, true); + if (!res.isSuccess()) return res.chainError(); + + var end = ES5.parseStatementEnd(src, i + res.n); + if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement"); + + return res.addN(end.n); + } + + public static ParseRes parseStatement(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + + if (src.is(i + n, ";")) return ParseRes.res(new DiscardStatement(src.loc(i+ n), null), n + 1); + if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed."); + + ParseRes res = ParseRes.first(src, i + n, + VariableDeclareStatement::parse, + ReturnStatement::parse, + ThrowStatement::parse, + ContinueStatement::parse, + BreakStatement::parse, + DebugStatement::parse, + IfStatement::parse, + WhileStatement::parse, + SwitchStatement::parse, + ForStatement::parse, + ForInStatement::parse, + ForOfStatement::parse, + DoWhileStatement::parse, + TryStatement::parse, + CompoundStatement::parse, + (s, j) -> FunctionStatement.parseFunction(s, j, true), + ES5::parseExpressionStatement + ); + return res.addN(n); + } + + public static Statement[] parse(Filename filename, String raw) { + var src = new Source(filename, raw); + var list = new ArrayList(); + int i = 0; + + while (true) { + if (i >= src.size()) break; + + var res = parseStatement(src, i); + + if (res.isError()) throw new SyntaxException(src.loc(i), res.error); + else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax"); + + i += res.n; + + list.add(res.result); + } + + return list.toArray(Statement[]::new); + } + + public static ParseRes parseStatementEnd(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + if (i >= src.size()) return ParseRes.res(true, n + 1); + + for (var j = i; j < i + n; j++) { + if (src.is(j, '\n')) return ParseRes.res(true, n); + } + + if (src.is(i + n, ';')) return ParseRes.res(true, n + 1); + if (src.is(i + n, '}')) return ParseRes.res(true, n); + + return ParseRes.failed(); + } + + public static boolean checkVarName(String name) { + return !ES5.reserved.contains(name); + } + + public static ParseRes> parseParamList(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + + var openParen = Parsing.parseOperator(src, i + n, "("); + if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list."); + n += openParen.n; + + var args = new ArrayList(); + + var closeParen = Parsing.parseOperator(src, i + n, ")"); + n += closeParen.n; + + if (!closeParen.isSuccess()) { + while (true) { + var argRes = Parsing.parseIdentifier(src, i + n); + if (argRes.isSuccess()) { + args.add(argRes.result); + n += argRes.n; + n += Parsing.skipEmpty(src, i); + + if (src.is(i + n, ",")) { + n++; + n += Parsing.skipEmpty(src, i + n); + } + if (src.is(i + n, ")")) { + n++; + break; + } + } + else return ParseRes.error(src.loc(i + n), "Expected an argument, or a closing brace."); + } + } + + return ParseRes.res(args, n); + } + + public static CompileResult compile(Statement ...statements) { + var target = new CompileResult(new LocalScopeRecord()); + var stm = new CompoundStatement(null, true, statements); + + target.scope.define("this"); + target.scope.define("arguments"); + + try { + stm.compile(target, true); + FunctionStatement.checkBreakAndCont(target, 0); + } + catch (SyntaxException e) { + target = new CompileResult(new LocalScopeRecord()); + + target.scope.define("this"); + target.scope.define("arguments"); + + target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); + } + + target.add(Instruction.ret()).setLocation(stm.loc()); + + return target; + } + + public static CompileResult compile(Filename filename, String raw) { + return ES5.compile(ES5.parse(filename, raw)); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/Statement.java b/src/java/me/topchetoeu/jscript/compilation/Statement.java index de9d23b..c9c14c3 100644 --- a/src/java/me/topchetoeu/jscript/compilation/Statement.java +++ b/src/java/me/topchetoeu/jscript/compilation/Statement.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.compilation; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; public abstract class Statement { private Location _loc; diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java index dd10c58..ef5b7aa 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java @@ -4,11 +4,11 @@ import java.util.ArrayList; import java.util.List; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class VariableDeclareStatement extends Statement { @@ -63,7 +63,7 @@ public class VariableDeclareStatement extends Statement { var res = new ArrayList(); - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new VariableDeclareStatement(loc, res), n); @@ -75,7 +75,7 @@ public class VariableDeclareStatement extends Statement { if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name"); n += name.n; - if (!Parsing.checkVarName(name.result)) { + if (!ES5.checkVarName(name.result)) { return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result)); } @@ -85,7 +85,7 @@ public class VariableDeclareStatement extends Statement { if (src.is(i + n, "=")) { n++; - var valRes = Parsing.parseValue(src, i + n, 2); + var valRes = ES5.parseExpression(src, i + n, 2); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='"); n += valRes.n; @@ -100,7 +100,7 @@ public class VariableDeclareStatement extends Statement { continue; } - end = Parsing.parseStatementEnd(src, i + n); + end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java index 5a62233..7cf3cad 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java @@ -1,12 +1,13 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class BreakStatement extends Statement { public final String label; @@ -28,7 +29,7 @@ public class BreakStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed(); n += 5; - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new BreakStatement(loc, null), n); @@ -38,7 +39,7 @@ public class BreakStatement extends Statement { if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); n += label.n; - end = Parsing.parseStatementEnd(src, i + n); + end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new BreakStatement(loc, label.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java index 281e11c..8403f33 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java @@ -1,12 +1,13 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ContinueStatement extends Statement { public final String label; @@ -28,7 +29,7 @@ public class ContinueStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed(); n += 8; - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ContinueStatement(loc, null), n); @@ -38,7 +39,7 @@ public class ContinueStatement extends Statement { if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); n += label.n; - end = Parsing.parseStatementEnd(src, i + n); + end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ContinueStatement(loc, label.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java index 4150118..b2fb08e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java @@ -1,12 +1,13 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class DebugStatement extends Statement { @Override public void compile(CompileResult target, boolean pollute) { @@ -25,7 +26,7 @@ public class DebugStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed(); n += 8; - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new DebugStatement(loc), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java index 52a8c7d..cc09617 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java @@ -1,15 +1,16 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; -import me.topchetoeu.jscript.compilation.values.IndexStatement; import me.topchetoeu.jscript.compilation.values.VariableStatement; import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; +import me.topchetoeu.jscript.compilation.values.operations.IndexStatement; public class DeleteStatement extends Statement { public final Statement key; @@ -31,7 +32,7 @@ public class DeleteStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed(); n += 6; - var valRes = Parsing.parseValue(src, i + n, 15); + var valRes = ES5.parseExpression(src, i + n, 15); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'"); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java index 1d64dd8..bb9a42c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java @@ -1,13 +1,14 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class DoWhileStatement extends Statement { public final Statement condition, body; @@ -47,7 +48,7 @@ public class DoWhileStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed(); n += 2; - var bodyRes = Parsing.parseStatement(src, i + n); + var bodyRes = ES5.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body."); n += bodyRes.n; @@ -58,7 +59,7 @@ public class DoWhileStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); n++; - var condRes = Parsing.parseValue(src, i + n, 0); + var condRes = ES5.parseExpression(src, i + n, 0); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition."); n += condRes.n; n += Parsing.skipEmpty(src, i + n); @@ -66,7 +67,7 @@ public class DoWhileStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition."); n++; - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java index bbf4e03..8868a52 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java @@ -1,14 +1,15 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ForInStatement extends Statement { public final String varName; @@ -90,7 +91,7 @@ public class ForInStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration"); n += 2; - var obj = Parsing.parseValue(src, i + n, 0); + var obj = ES5.parseExpression(src, i + n, 0); if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); n += obj.n; n += Parsing.skipEmpty(src, i + n); @@ -98,7 +99,7 @@ public class ForInStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); n++; - var bodyRes = Parsing.parseStatement(src, i + n); + var bodyRes = ES5.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java index b56c3d9..3072384 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java @@ -1,13 +1,14 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ForOfStatement extends Statement { public final String varName; @@ -102,7 +103,7 @@ public class ForOfStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "fo")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); n += 2; - var obj = Parsing.parseValue(src, i + n, 0); + var obj = ES5.parseExpression(src, i + n, 0); if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); n += obj.n; n += Parsing.skipEmpty(src, i + n); @@ -110,7 +111,7 @@ public class ForOfStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); n++; - var bodyRes = Parsing.parseStatement(src, i + n); + var bodyRes = ES5.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); n += bodyRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java index 8dbfec7..2ab96b0 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java @@ -1,15 +1,16 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.VariableDeclareStatement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; -import me.topchetoeu.jscript.compilation.values.DiscardStatement; +import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement; public class ForStatement extends Statement { public final Statement declaration, assignment, condition, body; @@ -57,7 +58,7 @@ public class ForStatement extends Statement { private static ParseRes parseCondition(Source src, int i) { var n = Parsing.skipEmpty(src, i); - var res = Parsing.parseValue(src, i + n, 0); + var res = ES5.parseExpression(src, i + n, 0); if (!res.isSuccess()) return res.chainError(); n += res.n; n += Parsing.skipEmpty(src, i + n); @@ -66,7 +67,7 @@ public class ForStatement extends Statement { else return ParseRes.res(res.result, n + 1); } private static ParseRes parseUpdater(Source src, int i) { - return Parsing.parseValue(src, i, 0); + return ES5.parseExpression(src, i, 0); } public static ParseRes parse(Source src, int i) { @@ -107,7 +108,7 @@ public class ForStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater"); n++; - var body = Parsing.parseStatement(src, i + n); + var body = ES5.parseStatement(src, i + n); if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body."); n += body.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java index 5c0fcdb..a751ddd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java @@ -1,13 +1,14 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class IfStatement extends Statement { public final Statement condition, body, elseBody; @@ -58,7 +59,7 @@ public class IfStatement extends Statement { var loc = src.loc(i + n); n++; - var a = Parsing.parseValue(src, i + n, 2); + var a = ES5.parseExpression(src, i + n, 2); if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator."); n += a.n; n += Parsing.skipEmpty(src, i); @@ -66,7 +67,7 @@ public class IfStatement extends Statement { if (!src.is(i + n, ":")) return ParseRes.failed(); n++; - var b = Parsing.parseValue(src, i + n, 2); + var b = ES5.parseExpression(src, i + n, 2); if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator."); n += b.n; @@ -83,7 +84,7 @@ public class IfStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'."); n++; - var condRes = Parsing.parseValue(src, i + n, 0); + var condRes = ES5.parseExpression(src, i + n, 0); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition."); n += condRes.n; n += Parsing.skipEmpty(src, i + n); @@ -91,7 +92,7 @@ public class IfStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition."); n++; - var res = Parsing.parseStatement(src, i + n); + var res = ES5.parseStatement(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body."); n += res.n; @@ -99,7 +100,7 @@ public class IfStatement extends Statement { if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); n += elseKw.n; - var elseRes = Parsing.parseStatement(src, i + n); + var elseRes = ES5.parseStatement(src, i + n); if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body."); n += elseRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java index 84dd2fa..d5201d1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java @@ -1,12 +1,13 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ReturnStatement extends Statement { public final Statement value; @@ -30,21 +31,21 @@ public class ReturnStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed(); n += 6; - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ReturnStatement(loc, null), n); } - var val = Parsing.parseValue(src, i + n, 0); - if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); + var val = ES5.parseExpression(src, i + n, 0); + if (val.isError()) return val.chainError(); n += val.n; - end = Parsing.parseStatementEnd(src, i + n); + end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ReturnStatement(loc, val.result), n); } - else return end.chainError(src.loc(i + n), "Expected end of statement"); + else return end.chainError(src.loc(i + n), "Expected end of statement or a return value"); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java index 6d44178..2e738ce 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java @@ -4,15 +4,16 @@ import java.util.ArrayList; import java.util.HashMap; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class SwitchStatement extends Statement { public static class SwitchCase { @@ -89,7 +90,7 @@ public class SwitchStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed(); n += 4; - var valRes = Parsing.parseValue(src, i + n, 0); + var valRes = ES5.parseExpression(src, i + n, 0); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'"); n += valRes.n; @@ -121,7 +122,7 @@ public class SwitchStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'"); n++; - var valRes = Parsing.parseValue(src, i + n, 0); + var valRes = ES5.parseExpression(src, i + n, 0); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value"); n += valRes.n; n += Parsing.skipEmpty(src, i + n); @@ -166,7 +167,7 @@ public class SwitchStatement extends Statement { } if (caseRes.isError()) return caseRes.chainError(); - var stm = Parsing.parseStatement(src, i + n); + var stm = ES5.parseStatement(src, i + n); if (stm.isSuccess()) { n += stm.n; statements.add(stm.result); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java index 0993321..ddfce87 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java @@ -1,12 +1,13 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ThrowStatement extends Statement { public final Statement value; @@ -29,17 +30,17 @@ public class ThrowStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed(); n += 5; - var end = Parsing.parseStatementEnd(src, i + n); + var end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ThrowStatement(loc, null), n); } - var val = Parsing.parseValue(src, i + n, 0); + var val = ES5.parseExpression(src, i + n, 0); if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); n += val.n; - end = Parsing.parseStatementEnd(src, i + n); + end = ES5.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ThrowStatement(loc, val.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java index 78120b0..c5b46e1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java @@ -1,14 +1,14 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class TryStatement extends Statement { public final Statement tryBody; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java index 2aa1cdb..a636b92 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java @@ -1,14 +1,15 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class WhileStatement extends Statement { public final Statement condition, body; @@ -82,7 +83,7 @@ public class WhileStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); n++; - var condRes = Parsing.parseValue(src, i + n, 0); + var condRes = ES5.parseExpression(src, i + n, 0); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition."); n += condRes.n; n += Parsing.skipEmpty(src, i + n); @@ -90,7 +91,7 @@ public class WhileStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after while condition."); n++; - var res = Parsing.parseStatement(src, i + n); + var res = ES5.parseStatement(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body."); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java deleted file mode 100644 index acfb9c9..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Operator.java +++ /dev/null @@ -1,48 +0,0 @@ -package me.topchetoeu.jscript.compilation.parsing; - -import java.util.Arrays; -import java.util.LinkedHashSet; - -import me.topchetoeu.jscript.common.Operation; - -public enum Operator { - MULTIPLY("*", Operation.MULTIPLY, 13, true), - DIVIDE("/", Operation.DIVIDE, 12, true), - MODULO("%", Operation.MODULO, 12, true), - SUBTRACT("-", Operation.SUBTRACT, 11, true), - ADD("+", Operation.ADD, 11, true), - SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10, true), - SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10, true), - USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10, true), - GREATER(">", Operation.GREATER, 9, false), - LESS("<", Operation.LESS, 9, false), - GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9, false), - LESS_EQUALS("<=", Operation.LESS_EQUALS, 9, false), - NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8, false), - LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8, false), - EQUALS("==", Operation.LOOSE_EQUALS, 8, false), - LOOSE_EQUALS("===", Operation.EQUALS, 8, false), - AND("&", Operation.AND, 7, true), - XOR("^", Operation.XOR, 6, true), - OR("|", Operation.OR, 5, true); - - public final String readable; - public final Operation operation; - public final int precedence; - public final boolean assignable; - - public static final LinkedHashSet opsByLength = new LinkedHashSet(); - - static { - var vals = Operator.values(); - Arrays.sort(vals, (a, b) -> b.readable.length() - a.readable.length()); - for (var el : vals) opsByLength.add(el); - } - - private Operator(String value, Operation funcName, int precedence, boolean assignable) { - this.readable = value; - this.operation = funcName; - this.precedence = precedence; - this.assignable = assignable; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java deleted file mode 100644 index c5b377a..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ /dev/null @@ -1,539 +0,0 @@ -package me.topchetoeu.jscript.compilation.parsing; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Set; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.compilation.*; -import me.topchetoeu.jscript.compilation.control.*; -import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; -import me.topchetoeu.jscript.compilation.values.*; -import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; -import me.topchetoeu.jscript.compilation.values.constants.NullStatement; -import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; -import me.topchetoeu.jscript.compilation.values.constants.StringStatement; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; - -// TODO: this has to be rewritten -// @SourceFile -public class Parsing { - public static final HashMap> functions = new HashMap<>(); - - private static final Set reserved = Set.of( - "true", "false", "void", "null", "this", "if", "else", "try", "catch", - "finally", "for", "do", "while", "switch", "case", "default", "new", - "function", "var", "return", "throw", "typeof", "delete", "break", - "continue", "debugger", "implements", "interface", "package", "private", - "protected", "public", "static", - - // Although ES5 allow these, we will comply to ES6 here - "const", "let", - - // These are allowed too, however our parser considers them keywords - // We allow yield and await, because they're part of the custom async and generator functions - "undefined", "arguments", "globalThis", "window", "self" - ); - - public static boolean isDigit(Character c) { - return c != null && c >= '0' && c <= '9'; - } - public static boolean isAny(char c, String alphabet) { - return alphabet.contains(Character.toString(c)); - } - - public static int fromHex(char c) { - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - if (c >= '0' && c <= '9') return c - '0'; - return -1; - } - - public static int skipEmpty(Source src, int i) { - int n = 0; - - while (n < src.size() && src.is(i + n, Character::isWhitespace)) n++; - - return n; - } - - public static ParseRes parseChar(Source src, int i) { - int n = 0; - - if (src.is(i + n, '\\')) { - n++; - char c = src.at(i + n++); - - if (c == 'b') return ParseRes.res('\b', n); - else if (c == 't') return ParseRes.res('\t', n); - else if (c == 'n') return ParseRes.res('\n', n); - else if (c == 'f') return ParseRes.res('\f', n); - else if (c == 'r') return ParseRes.res('\r', n); - else if (c == '0') { - if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed"); - else return ParseRes.res('\0', n); - } - else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed"); - else if (c == 'x') { - var newC = 0; - - for (var j = 0; j < 2; j++) { - if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence."); - - int val = fromHex(src.at(i + n)); - if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence."); - n++; - - newC = (newC << 4) | val; - } - - return ParseRes.res((char)newC, n); - } - else if (c == 'u') { - var newC = 0; - - for (var j = 0; j < 4; j++) { - if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence"); - - int val = fromHex(src.at(i + n)); - if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence"); - n++; - - newC = (newC << 4) | val; - } - - return ParseRes.res((char)newC, n); - } - else if (c == '\n') return ParseRes.res(null, n); - } - - return ParseRes.res(src.at(i + n), n + 1); - } - - public static ParseRes parseIdentifier(Source src, int i) { - var n = skipEmpty(src, i); - var res = new StringBuilder(); - var first = false; - - while (true) { - if (i + n > src.size()) break; - char c = src.at(i + n, '\0'); - - if (first && !Character.isLetterOrDigit(c) && c != '_' && c != '$') break; - if (!first && !Character.isLetter(c) && c != '_' && c != '$') break; - res.append(c); - n++; - } - - if (res.length() <= 0) return ParseRes.failed(); - else return ParseRes.res(res.toString(), n); - } - public static ParseRes parseIdentifier(Source src, int i, String test) { - var n = skipEmpty(src, i); - var res = new StringBuilder(); - var first = true; - - while (true) { - if (i + n > src.size()) break; - char c = src.at(i + n, '\0'); - - if (first && !Character.isLetterOrDigit(c) && c != '_' && c != '$') break; - if (!first && !Character.isLetter(c) && c != '_' && c != '$') break; - first = false; - res.append(c); - n++; - } - - if (res.length() <= 0) return ParseRes.failed(); - else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n); - else return ParseRes.failed(); - } - public static boolean isIdentifier(Source src, int i, String test) { - return parseIdentifier(src, i, test).isSuccess(); - } - - public static ParseRes parseOperator(Source src, int i, String op) { - var n = skipEmpty(src, i); - - if (src.is(i + n, op)) return ParseRes.res(op, n + op.length()); - else return ParseRes.failed(); - } - - public static ParseRes parseStatementEnd(Source src, int i) { - var n = skipEmpty(src, i); - if (i >= src.size()) return ParseRes.res(true, n + 1); - - for (var j = i; j < i + n; j++) { - if (src.is(j, '\n')) return ParseRes.res(true, n); - } - - if (src.is(i + n, ';')) return ParseRes.res(true, n + 1); - if (src.is(i + n, '}')) return ParseRes.res(true, n); - - return ParseRes.failed(); - } - public static boolean checkVarName(String name) { - return !reserved.contains(name); - } - - public static ParseRes> parseParamList(Source src, int i) { - var n = skipEmpty(src, i); - - var openParen = parseOperator(src, i + n, "("); - if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list."); - n += openParen.n; - - var args = new ArrayList(); - - var closeParen = parseOperator(src, i + n, ")"); - n += closeParen.n; - - if (!closeParen.isSuccess()) { - while (true) { - var argRes = parseIdentifier(src, i + n); - if (argRes.isSuccess()) { - args.add(argRes.result); - n++; - - n += skipEmpty(src, i); - - if (src.is(i + n, ",")) { - n++; - n += skipEmpty(src, i + n); - } - if (src.is(i + n, ")")) { - n++; - break; - } - } - else return ParseRes.error(src.loc(i + n), "Expected an argument, or a closing brace."); - } - } - - return ParseRes.res(args, n); - } - - public static ParseRes parseParens(Source src, int i) { - int n = 0; - - var openParen = parseOperator(src, i + n, "("); - if (!openParen.isSuccess()) return openParen.chainError(); - n += openParen.n; - - var res = parseValue(src, i + n, 0); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens"); - n += res.n; - - var closeParen = parseOperator(src, i + n, ")"); - if (!closeParen.isSuccess()) return closeParen.chainError(src.loc(i + n), "Expected a closing paren"); - n += closeParen.n; - - return ParseRes.res(res.result, n); - } - public static ParseRes parseSimple(Source src, int i, boolean statement) { - return ParseRes.first(src, i, - (a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b), - (a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false), - VariableStatement::parse, - Parsing::parseLiteral, - StringStatement::parse, - RegexStatement::parse, - NumberStatement::parse, - ChangeStatement::parsePrefixDecrease, - ChangeStatement::parsePrefixIncrease, - OperationStatement::parsePrefix, - ArrayStatement::parse, - Parsing::parseParens, - CallStatement::parseNew, - TypeofStatement::parse, - DiscardStatement::parse, - DeleteStatement::parse - ); - } - - public static ParseRes parseLiteral(Source src, int i) { - var n = skipEmpty(src, i); - var loc = src.loc(i + n); - - var id = parseIdentifier(src, i); - if (!id.isSuccess()) return id.chainError(); - n += id.n; - - if (id.result.equals("true")) return ParseRes.res(new BoolStatement(loc, true), n); - if (id.result.equals("false")) return ParseRes.res(new BoolStatement(loc, false), n); - if (id.result.equals("undefined")) return ParseRes.res(new DiscardStatement(loc, null), n); - if (id.result.equals("null")) return ParseRes.res(new NullStatement(loc), n); - if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), n); - if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), n); - if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), n); - - return ParseRes.failed(); - } - public static ParseRes parseValue(Source src, int i, int precedence, boolean statement) { - var n = skipEmpty(src, i); - Statement prev = null; - - while (true) { - if (prev == null) { - var res = parseSimple(src, i + n, statement); - if (res.isSuccess()) { - n += res.n; - prev = res.result; - } - else if (res.isError()) return res.chainError(); - else break; - } - else { - var _prev = prev; - ParseRes res = ParseRes.first(src, i + n, - (s, j) -> OperationStatement.parseInstanceof(s, j, _prev, precedence), - (s, j) -> OperationStatement.parseIn(s, j, _prev, precedence), - (s, j) -> LazyOrStatement.parse(s, j, _prev, precedence), - (s, j) -> LazyAndStatement.parse(s, j, _prev, precedence), - (s, j) -> ChangeStatement.parsePostfixIncrease(s, j, _prev, precedence), - (s, j) -> ChangeStatement.parsePostfixDecrease(s, j, _prev, precedence), - (s, j) -> AssignableStatement.parse(s, j, _prev, precedence), - (s, j) -> OperationStatement.parseOperator(s, j, _prev, precedence), - (s, j) -> IfStatement.parseTernary(s, j, _prev, precedence), - (s, j) -> IndexStatement.parseMember(s, j, _prev, precedence), - (s, j) -> IndexStatement.parseIndex(s, j, _prev, precedence), - (s, j) -> CallStatement.parseCall(s, j, _prev, precedence), - (s, j) -> CompoundStatement.parseComma(s, j, _prev, precedence) - ); - - if (res.isSuccess()) { - n += res.n; - prev = res.result; - continue; - } - else if (res.isError()) return res.chainError(); - - break; - } - } - - if (prev == null) return ParseRes.failed(); - else return ParseRes.res(prev, n); - } - public static ParseRes parseValue(Source src, int i, int precedence) { - return parseValue(src, i, precedence, false); - } - - public static ParseRes parseValueStatement(Source src, int i) { - var res = parseValue(src, i, 0, true); - if (!res.isSuccess()) return res.chainError(); - - var end = parseStatementEnd(src, i + res.n); - if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement"); - - return res.addN(end.n); - } - public static ParseRes parseStatement(Source src, int i) { - var n = skipEmpty(src, i); - - if (src.is(i + n, ";")) return ParseRes.res(new DiscardStatement(src.loc(i+ n), null), n + 1); - if (isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed."); - - ParseRes res = ParseRes.first(src, i + n, - VariableDeclareStatement::parse, - ReturnStatement::parse, - ThrowStatement::parse, - ContinueStatement::parse, - BreakStatement::parse, - DebugStatement::parse, - IfStatement::parse, - WhileStatement::parse, - SwitchStatement::parse, - ForStatement::parse, - ForInStatement::parse, - ForOfStatement::parse, - DoWhileStatement::parse, - TryStatement::parse, - CompoundStatement::parse, - (s, j) -> FunctionStatement.parseFunction(s, j, true), - Parsing::parseValueStatement - ); - return res.addN(n); - } - - public static Statement[] parse(Filename filename, String raw) { - var src = new Source(filename, raw); - var list = new ArrayList(); - int i = 0; - - while (true) { - if (i >= src.size()) break; - - var res = Parsing.parseStatement(src, i); - - if (res.isError()) throw new SyntaxException(src.loc(i), res.error); - else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax"); - - i += res.n; - - list.add(res.result); - } - - return list.toArray(Statement[]::new); - } - - public static CompileResult compile(Statement ...statements) { - var target = new CompileResult(new LocalScopeRecord()); - var stm = new CompoundStatement(null, true, statements); - - target.scope.define("this"); - target.scope.define("arguments"); - - try { - stm.compile(target, true); - FunctionStatement.checkBreakAndCont(target, 0); - } - catch (SyntaxException e) { - target = new CompileResult(new LocalScopeRecord()); - - target.scope.define("this"); - target.scope.define("arguments"); - - target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); - } - - target.add(Instruction.ret()).setLocation(stm.loc()); - - return target; - } - public static CompileResult compile(Filename filename, String raw) { - return compile(parse(filename, raw)); - } - - private static ParseRes parseHex(Source src, int i) { - int n = 0; - double res = 0; - - while (true) { - int digit = Parsing.fromHex(src.at(i + n, '\0')); - if (digit < 0) { - if (n <= 0) return ParseRes.failed(); - else return ParseRes.res(res, n); - } - n++; - - res *= 16; - res += digit; - } - } - private static ParseRes parseOct(Source src, int i) { - int n = 0; - double res = 0; - - while (true) { - int digit = src.at(i + n, '\0') - '0'; - if (digit < 0 || digit > 9) break; - if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit); - - if (digit < 0) { - if (n <= 0) return ParseRes.failed(); - else return ParseRes.res(res, n); - } - n++; - - res *= 8; - res += digit; - } - - return ParseRes.res(res, n); - } - - public static ParseRes parseString(Source src, int i) { - var n = skipEmpty(src, i); - - char quote; - - if (src.is(i + n, '\'')) quote = '\''; - else if (src.is(i + n, '"')) quote = '"'; - else return ParseRes.failed(); - n++; - - var res = new StringBuilder(); - - while (true) { - if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal"); - if (src.is(i + n, quote)) { - n++; - return ParseRes.res(res.toString(), n); - } - - var charRes = parseChar(src, i + n); - if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character"); - n += charRes.n; - - if (charRes.result != null) res.append(charRes.result); - } - } - public static ParseRes parseNumber(Source src, int i) { - var n = skipEmpty(src, i); - - double whole = 0; - double fract = 0; - long exponent = 0; - boolean parsedAny = false; - - if (src.is(i + n, "0x") || src.is(i + n, "0X")) { - n += 2; - - var res = parseHex(src, i); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal"); - else return res.addN(2); - } - else if (src.is(i + n, "0o") || src.is(i + n, "0O")) { - n += 2; - - var res = parseOct(src, i); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal"); - else return res.addN(2); - } - else if (src.is(i + n, '0')) { - n++; - parsedAny = true; - if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed"); - } - - while (src.is(i + n, Parsing::isDigit)) { - parsedAny = true; - whole *= 10; - whole += src.at(i + n++) - '0'; - } - - if (src.is(i + n, '.')) { - parsedAny = true; - n++; - - while (src.is(i + n, Parsing::isDigit)) { - fract += src.at(i + n++) - '0'; - fract /= 10; - } - } - - if (src.is(i + n, 'e') || src.is(i + n, 'E')) { - n++; - parsedAny = true; - boolean negative = src.is(i + n, '-'); - boolean parsedE = false; - if (negative) n++; - - while (src.is(i + n, Parsing::isDigit)) { - parsedE = true; - exponent *= 10; - - if (negative) exponent -= src.at(i + n++) - '0'; - else exponent += src.at(i + n++) - '0'; - } - - if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent"); - } - - if (!parsedAny) return ParseRes.failed(); - else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n); - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/RawToken.java b/src/java/me/topchetoeu/jscript/compilation/parsing/RawToken.java deleted file mode 100644 index e300d6b..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/RawToken.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.topchetoeu.jscript.compilation.parsing; - -public class RawToken { - public final String value; - public final TokenType type; - public final int line; - public final int start; - - public RawToken(String value, TokenType type, int line, int start) { - this.value = value; - this.type = type; - this.line = line; - this.start = start; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java deleted file mode 100644 index c399a9e..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java +++ /dev/null @@ -1,45 +0,0 @@ -package me.topchetoeu.jscript.compilation.parsing; - -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.compilation.parsing.ParseRes.State; - -public class TestRes { - public final State state; - public final String error; - public final int i; - - private TestRes(ParseRes.State state, String error, int i) { - this.i = i; - this.state = state; - this.error = error; - } - - public TestRes add(int n) { - return new TestRes(state, null, this.i + n); - } - public ParseRes transform() { - if (isSuccess()) throw new RuntimeException("Can't transform a TestRes that hasn't failed."); - else if (isError()) return ParseRes.error(null, error); - else return ParseRes.failed(); - } - - public boolean isSuccess() { return state.isSuccess(); } - public boolean isFailed() { return state.isFailed(); } - public boolean isError() { return state.isError(); } - - public static TestRes failed() { - return new TestRes(State.FAILED, null, 0); - } - public static TestRes error(Location loc, String error) { - if (loc != null) error = loc + ": " + error; - return new TestRes(State.ERROR, error, 0); - } - public static TestRes error(Location loc, String error, TestRes other) { - if (loc != null) error = loc + ": " + error; - if (!other.isError()) return new TestRes(State.ERROR, error, 0); - return new TestRes(State.ERROR, other.error, 0); - } - public static TestRes res(int i) { - return new TestRes(State.SUCCESS, null, i); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Token.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Token.java deleted file mode 100644 index a3f4f0a..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Token.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.topchetoeu.jscript.compilation.parsing; - -public class Token { - public final Object value; - public final String rawValue; - public final boolean isString; - public final boolean isRegex; - public final int line; - public final int start; - - private Token(int line, int start, Object value, String rawValue, boolean isString, boolean isRegex) { - this.value = value; - this.rawValue = rawValue; - this.line = line; - this.start = start; - this.isString = isString; - this.isRegex = isRegex; - } - private Token(int line, int start, Object value, String rawValue) { - this.value = value; - this.rawValue = rawValue; - this.line = line; - this.start = start; - this.isString = false; - this.isRegex = false; - } - - public boolean isString() { return isString; } - public boolean isRegex() { return isRegex; } - public boolean isNumber() { return value instanceof Number; } - public boolean isIdentifier() { return !isString && !isRegex && value instanceof String; } - public boolean isOperator() { return value instanceof Operator; } - - public boolean isIdentifier(String lit) { return !isString && !isRegex && value.equals(lit); } - public boolean isOperator(Operator op) { return value.equals(op); } - - public String string() { return (String)value; } - public String regex() { return (String)value; } - public double number() { return (double)value; } - public String identifier() { return (String)value; } - public Operator operator() { return (Operator)value; } - - public static Token regex(int line, int start, String val, String rawValue) { - return new Token(line, start, val, rawValue, false, true); - } - public static Token string(int line, int start, String val, String rawValue) { - return new Token(line, start, val, rawValue, true, false); - } - public static Token number(int line, int start, double val, String rawValue) { - return new Token(line, start, val, rawValue); - } - public static Token identifier(int line, int start, String val) { - return new Token(line, start, val, val); - } - public static Token operator(int line, int start, Operator val) { - return new Token(line, start, val, val.readable); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/TokenType.java b/src/java/me/topchetoeu/jscript/compilation/parsing/TokenType.java deleted file mode 100644 index a34e821..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/TokenType.java +++ /dev/null @@ -1,9 +0,0 @@ -package me.topchetoeu.jscript.compilation.parsing; - -enum TokenType { - REGEX, - STRING, - NUMBER, - LITERAL, - OPERATOR, -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java index 9d60587..b4ef1e6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java @@ -3,12 +3,13 @@ package me.topchetoeu.jscript.compilation.values; import java.util.ArrayList; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ArrayStatement extends Statement { public final Statement[] statements; @@ -21,8 +22,7 @@ public class ArrayStatement extends Statement { return true; } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadArr(statements.length)); for (var i = 0; i < statements.length; i++) { @@ -70,7 +70,7 @@ public class ArrayStatement extends Statement { } } - var res = Parsing.parseValue(src, i + n, 2); + var res = ES5.parseExpression(src, i + n, 2); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an array element."); n += res.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java deleted file mode 100644 index 4cb52ed..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/CallStatement.java +++ /dev/null @@ -1,101 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -import java.util.ArrayList; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; - -public class CallStatement extends Statement { - public final Statement func; - public final Statement[] args; - public final boolean isNew; - - @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { - if (isNew) func.compile(target, true); - else if (func instanceof IndexStatement) { - ((IndexStatement)func).compile(target, true, true); - } - else { - target.add(Instruction.pushUndefined()); - func.compile(target, true); - } - - for (var arg : args) arg.compile(target, true); - - if (isNew) target.add(Instruction.callNew(args.length)).setLocationAndDebug(loc(), type); - else target.add(Instruction.call(args.length)).setLocationAndDebug(loc(), type); - - if (!pollute) target.add(Instruction.discard()); - } - @Override public void compile(CompileResult target, boolean pollute) { - compile(target, pollute, BreakpointType.STEP_IN); - } - - public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) { - super(loc); - this.isNew = isNew; - this.func = func; - this.args = args; - } - - public static ParseRes parseCall(Source src, int i, Statement prev, int precedence) { - if (precedence > 17) return ParseRes.failed(); - - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!src.is(i + n, "(")) return ParseRes.failed(); - n++; - - var args = new ArrayList(); - boolean prevArg = false; - - while (true) { - var argRes = Parsing.parseValue(src, i + n, 2); - n += argRes.n; - n += Parsing.skipEmpty(src, i + n); - - if (argRes.isSuccess()) { - args.add(argRes.result); - prevArg = true; - } - else if (argRes.isError()) return argRes.chainError(); - else if (prevArg && src.is(i + n, ",")) { - prevArg = false; - n++; - } - else if (src.is(i + n, ")")) { - n++; - break; - } - else if (prevArg) return ParseRes.error(src.loc(i + n), "Expected a comma or a closing paren"); - else return ParseRes.error(src.loc(i + n), "Expected an expression or a closing paren"); - } - - return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); - } - public static ParseRes parseNew(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!Parsing.isIdentifier(src, i + n, "new")) return ParseRes.failed(); - n += 3; - - var valRes = Parsing.parseValue(src, i + n, 18); - if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword."); - n += valRes.n; - - var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0); - if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); - if (callRes.isError()) return callRes.chainError(); - n += callRes.n; - - return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n); - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index f037bd8..00fe37f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -1,15 +1,16 @@ package me.topchetoeu.jscript.compilation.values; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.Type; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class FunctionStatement extends Statement { @@ -39,7 +40,7 @@ public class FunctionStatement extends Statement { } } - private CompileResult compileBody(CompileResult target, boolean pollute, BreakpointType bp) { + private CompileResult compileBody(CompileResult target, String name, boolean pollute, BreakpointType bp) { for (var i = 0; i < args.length; i++) { for (var j = 0; j < i; j++) { if (args[i].equals(args[j])) { @@ -72,7 +73,7 @@ public class FunctionStatement extends Statement { subtarget.add(Instruction.ret()).setLocation(end); checkBreakAndCont(subtarget, 0); - if (pollute) target.add(Instruction.loadFunc(target.children.size(), subtarget.scope.getCaptures())); + if (pollute) target.add(Instruction.loadFunc(target.children.size(), name, subtarget.scope.getCaptures())); return target.addChild(subtarget); } @@ -80,16 +81,8 @@ public class FunctionStatement extends Statement { if (this.varName != null) name = this.varName; var hasVar = this.varName != null && statement; - var hasName = name != null; - compileBody(target, pollute || hasVar || hasName, bp); - - if (hasName) { - if (pollute || hasVar) target.add(Instruction.dup()); - target.add(Instruction.pushValue("name")); - target.add(Instruction.pushValue(name)); - target.add(Instruction.storeMember()); - } + compileBody(target, name, pollute || hasVar, bp); if (hasVar) { var key = target.scope.getKey(this.varName); @@ -140,12 +133,12 @@ public class FunctionStatement extends Statement { n += nameRes.n; n += Parsing.skipEmpty(src, i + n); - var args = Parsing.parseParamList(src, i + n); + var args = ES5.parseParamList(src, i + n); if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); n += args.n; var res = CompoundStatement.parse(src, i + n); - if (!res.isSuccess()) res.chainError(src.loc(i + n), "Expected a compound statement for function."); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a compound statement for function."); n += res.n; return ParseRes.res(new FunctionStatement( diff --git a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java index b43cfc2..da8aa43 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.compilation.values; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java index 91dbb6f..aeefb24 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java @@ -5,13 +5,14 @@ import java.util.LinkedHashMap; import java.util.Map; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class ObjectStatement extends Statement { public static class ObjProp { @@ -82,7 +83,7 @@ public class ObjectStatement extends Statement { var res = ParseRes.first(src, i + n, Parsing::parseIdentifier, Parsing::parseString, - Parsing::parseNumber + (s, j) -> Parsing.parseNumber(s, j, false) ); n += res.n; @@ -102,7 +103,7 @@ public class ObjectStatement extends Statement { if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); n += name.n; - var params = Parsing.parseParamList(src, i + n); + var params = ES5.parseParamList(src, i + n); if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); n += params.n; @@ -153,7 +154,7 @@ public class ObjectStatement extends Statement { if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon"); n++; - var valRes = Parsing.parseValue(src, i + n, 2); + var valRes = ES5.parseExpression(src, i + n, 2); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list"); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java deleted file mode 100644 index 2c98e42..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/OperationStatement.java +++ /dev/null @@ -1,110 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.Operator; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; - -public class OperationStatement extends Statement { - public final Statement[] args; - public final Operation operation; - - @Override public boolean pure() { - for (var el : args) { - if (!el.pure()) return false; - } - - return true; - } - - @Override public void compile(CompileResult target, boolean pollute) { - for (var arg : args) { - arg.compile(target, true); - } - - if (pollute) target.add(Instruction.operation(operation)); - else target.add(Instruction.discard()); - } - - public OperationStatement(Location loc, Operation operation, Statement ...args) { - super(loc); - this.operation = operation; - this.args = args; - } - - public static ParseRes parsePrefix(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - Operation operation = null; - String op; - - if (src.is(i + n, op = "+")) operation = Operation.POS; - else if (src.is(i + n, op = "-")) operation = Operation.NEG; - else if (src.is(i + n, op = "~")) operation = Operation.INVERSE; - else if (src.is(i + n, op = "!")) operation = Operation.NOT; - else return ParseRes.failed(); - - n++; - - var res = Parsing.parseValue(src, i + n, 14); - - if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); - else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op)); - } - public static ParseRes parseInstanceof(Source src, int i, Statement prev, int precedence) { - if (precedence > 9) return ParseRes.failed(); - - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var kw = Parsing.parseIdentifier(src, i + n, "instanceof"); - if (!kw.isSuccess()) return kw.chainError(); - n += kw.n; - - var valRes = Parsing.parseValue(src, i + n, 10); - if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'instanceof'."); - n += valRes.n; - - return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n); - } - public static ParseRes parseIn(Source src, int i, Statement prev, int precedence) { - if (precedence > 9) return ParseRes.failed(); - - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var kw = Parsing.parseIdentifier(src, i + n, "in"); - if (!kw.isSuccess()) return kw.chainError(); - n += kw.n; - - var valRes = Parsing.parseValue(src, i + n, 10); - if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'in'."); - n += valRes.n; - - return ParseRes.res(new OperationStatement(loc, Operation.IN, valRes.result, prev), n); - } - public static ParseRes parseOperator(Source src, int i, Statement prev, int precedence) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - for (var op : Operator.opsByLength) { - if (!src.is(i + n, op.readable)) continue; - if (op.precedence < precedence) return ParseRes.failed(); - n += op.readable.length(); - - var res = Parsing.parseValue(src, i + n, op.precedence + 1); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s' operator.", op.readable)); - n += res.n; - - return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n); - } - - return ParseRes.failed(); - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java index 8b240d0..5bc7a78 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java @@ -1,12 +1,12 @@ package me.topchetoeu.jscript.compilation.values; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class RegexStatement extends Statement { public final String pattern, flags; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java index 53933bb..0f2f7ba 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java @@ -1,14 +1,16 @@ package me.topchetoeu.jscript.compilation.values; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.compilation.values.operations.VariableAssignStatement; public class VariableStatement extends AssignableStatement { public final String name; @@ -40,7 +42,7 @@ public class VariableStatement extends AssignableStatement { if (!literal.isSuccess()) return literal.chainError(); n += literal.n; - if (!Parsing.checkVarName(literal.result)) { + if (!ES5.checkVarName(literal.result)) { if (literal.result.equals("await")) return ParseRes.error(src.loc(i + n), "'await' expressions are not supported."); if (literal.result.equals("const")) return ParseRes.error(src.loc(i + n), "'const' declarations are not supported."); if (literal.result.equals("let")) return ParseRes.error(src.loc(i + n), "'let' declarations are not supported."); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java index 51c6db7..fa45815 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.compilation.values.constants; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java index 7238ce6..afb0dcd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.compilation.values.constants; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java index 2640cd3..a626e86 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java @@ -1,12 +1,12 @@ package me.topchetoeu.jscript.compilation.values.constants; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class NumberStatement extends Statement { public final double value; @@ -35,7 +35,7 @@ public class NumberStatement extends Statement { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var res = Parsing.parseNumber(src, i + n); + var res = Parsing.parseNumber(src, i + n, false); if (res.isSuccess()) return ParseRes.res(new NumberStatement(loc, res.result), n + res.n); else return res.chainError(); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java index 02fad21..2cb4d8c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java @@ -1,12 +1,12 @@ package me.topchetoeu.jscript.compilation.values.constants; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class StringStatement extends Statement { public final String value; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java new file mode 100644 index 0000000..e9a08b3 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java @@ -0,0 +1,180 @@ +package me.topchetoeu.jscript.compilation.values.operations; + +import java.util.ArrayList; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.json.JSON; +import me.topchetoeu.jscript.common.json.JSONElement; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.values.ArrayStatement; +import me.topchetoeu.jscript.compilation.values.ObjectStatement; +import me.topchetoeu.jscript.compilation.values.VariableStatement; +import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; +import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; +import me.topchetoeu.jscript.compilation.values.constants.StringStatement; + +public class CallStatement extends Statement { + public static boolean ATTACH_NAME = true; + + public final Statement func; + public final Statement[] args; + public final boolean isNew; + + private String generateName(Statement func, Statement index) { + String res = "(intermediate value)"; + boolean shouldParen = false; + + if (func instanceof ObjectStatement) { + var obj = (ObjectStatement)func; + + shouldParen = true; + + if (obj.getters.size() > 0 || obj.setters.size() > 0 || obj.map.size() > 0) res = "{}"; + else res = "{(intermediate value)}"; + } + else if (func instanceof StringStatement) { + res = JSON.stringify(JSONElement.string(((StringStatement)func).value)); + } + else if (func instanceof NumberStatement) { + res = JSON.stringify(JSONElement.number(((NumberStatement)func).value)); + } + else if (func instanceof BoolStatement) { + res = ((BoolStatement)func).value ? "true" : "false"; + } + else if (func instanceof VariableStatement) { + res = ((VariableStatement)func).name; + } + else if (func instanceof VariableIndexStatement) { + var i = ((VariableIndexStatement)func).index; + + if (i == 0) res = "this"; + else if (i == 1) res = "arguments"; + } + else if (func instanceof ArrayStatement) { + var els = new ArrayList(); + + for (var el : ((ArrayStatement)func).statements) { + if (el != null) els.add(generateName(el, null)); + else els.add("(intermediate value)"); + } + + res = "[" + String.join(",", els) + "]"; + } + + if (index == null) return res; + + if (shouldParen) res = "(" + res + ")"; + + if (index instanceof StringStatement) { + var val = ((StringStatement)index).value; + var bracket = JSON.stringify(JSONElement.string(val)); + + if (!bracket.substring(1, bracket.length() - 1).equals(val)) return res + "[" + bracket + "]"; + if (Parsing.parseIdentifier(new Source(null, val), 0).n != val.length()) return res + "[" + bracket + "]"; + + return res + "." + val; + } + + return res + "[" + generateName(index, null) + "]"; + } + + @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { + if (!isNew && func instanceof IndexStatement) { + var obj = ((IndexStatement)func).object; + var index = ((IndexStatement)func).index; + String name = ""; + + obj.compile(target, true); + index.compile(target, true); + for (var arg : args) arg.compile(target, true); + + if (ATTACH_NAME) name = generateName(obj, index); + + target.add(Instruction.callMember(args.length, name)).setLocationAndDebug(loc(), type); + } + else { + String name = ""; + + func.compile(target, true); + for (var arg : args) arg.compile(target, true); + + if (ATTACH_NAME) name = generateName(func, null); + + if (isNew) target.add(Instruction.callNew(args.length, name)).setLocationAndDebug(loc(), type); + else target.add(Instruction.call(args.length, name)).setLocationAndDebug(loc(), type); + } + if (!pollute) target.add(Instruction.discard()); + } + @Override public void compile(CompileResult target, boolean pollute) { + compile(target, pollute, BreakpointType.STEP_IN); + } + + public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) { + super(loc); + this.isNew = isNew; + this.func = func; + this.args = args; + } + + public static ParseRes parseCall(Source src, int i, Statement prev, int precedence) { + if (precedence > 17) return ParseRes.failed(); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "(")) return ParseRes.failed(); + n++; + + var args = new ArrayList(); + boolean prevArg = false; + + while (true) { + var argRes = ES5.parseExpression(src, i + n, 2); + n += argRes.n; + n += Parsing.skipEmpty(src, i + n); + + if (argRes.isSuccess()) { + args.add(argRes.result); + prevArg = true; + } + else if (argRes.isError()) return argRes.chainError(); + else if (prevArg && src.is(i + n, ",")) { + prevArg = false; + n++; + } + else if (src.is(i + n, ")")) { + n++; + break; + } + else if (prevArg) return ParseRes.error(src.loc(i + n), "Expected a comma or a closing paren"); + else return ParseRes.error(src.loc(i + n), "Expected an expression or a closing paren"); + } + + return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); + } + public static ParseRes parseNew(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!Parsing.isIdentifier(src, i + n, "new")) return ParseRes.failed(); + n += 3; + + var valRes = ES5.parseExpression(src, i + n, 18); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword."); + n += valRes.n; + + var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0); + if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); + if (callRes.isError()) return callRes.chainError(); + n += callRes.n; + + return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java similarity index 89% rename from src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java index e8cd584..66a3eca 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ChangeStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java @@ -1,14 +1,15 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; public class ChangeStatement extends Statement { @@ -39,7 +40,7 @@ public class ChangeStatement extends Statement { if (!src.is(i + n, "++")) return ParseRes.failed(); n += 2; - var res = Parsing.parseValue(src, i + n, 15); + var res = ES5.parseExpression(src, i + n, 15); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); @@ -52,7 +53,7 @@ public class ChangeStatement extends Statement { if (!src.is(i + n, "--")) return ParseRes.failed(); n += 2; - var res = Parsing.parseValue(src, i + n, 15); + var res = ES5.parseExpression(src, i + n, 15); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java similarity index 74% rename from src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java index 14041f6..ddcccda 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/DiscardStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java @@ -1,12 +1,13 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class DiscardStatement extends Statement { public final Statement value; @@ -30,7 +31,7 @@ public class DiscardStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "void")) return ParseRes.failed(); n += 4; - var valRes = Parsing.parseValue(src, i + n, 14); + var valRes = ES5.parseExpression(src, i + n, 14); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword."); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignStatement.java similarity index 92% rename from src/java/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignStatement.java index 1716336..fc11ac2 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignStatement.java @@ -1,9 +1,9 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java similarity index 87% rename from src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java index 0b4bf48..a0f8598 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/IndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java @@ -1,15 +1,16 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; import me.topchetoeu.jscript.compilation.values.constants.StringStatement; public class IndexStatement extends AssignableStatement { @@ -48,7 +49,7 @@ public class IndexStatement extends AssignableStatement { if (!src.is(i + n, "[")) return ParseRes.failed(); n++; - var valRes = Parsing.parseValue(src, i + n, 0); + var valRes = ES5.parseExpression(src, i + n, 0); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in index expression"); n += valRes.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java similarity index 79% rename from src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java index b556e07..8dabe29 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java @@ -1,12 +1,13 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class LazyAndStatement extends Statement { public final Statement first, second; @@ -38,7 +39,7 @@ public class LazyAndStatement extends Statement { var loc = src.loc(i + n); n += 2; - var res = Parsing.parseValue(src, i + n, 4); + var res = ES5.parseExpression(src, i + n, 4); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator."); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java similarity index 78% rename from src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java index 80ec4be..aff7f6f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java @@ -1,12 +1,13 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; public class LazyOrStatement extends Statement { public final Statement first, second; @@ -38,7 +39,7 @@ public class LazyOrStatement extends Statement { var loc = src.loc(i + n); n += 2; - var res = Parsing.parseValue(src, i + n, 4); + var res = ES5.parseExpression(src, i + n, 4); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator."); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java new file mode 100644 index 0000000..55b4e6a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java @@ -0,0 +1,234 @@ +package me.topchetoeu.jscript.compilation.values.operations; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.AssignableStatement; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.Statement; + +public class OperationStatement extends Statement { + private static interface OperatorFactory { + String token(); + int precedence(); + ParseRes construct(Source src, int i, Statement prev); + } + + private static class NormalOperatorFactory implements OperatorFactory { + public final String token; + public final int precedence; + public final Operation operation; + + @Override public int precedence() { return precedence; } + @Override public String token() { return token; } + @Override public ParseRes construct(Source src, int i, Statement prev) { + var loc = src.loc(i); + + var other = ES5.parseExpression(src, i, precedence + 1); + if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token)); + return ParseRes.res(new OperationStatement(loc, operation, prev, (Statement)other.result), other.n); + } + + public NormalOperatorFactory(String token, int precedence, Operation operation) { + this.token = token; + this.precedence = precedence; + this.operation = operation; + } + } + private static class AssignmentOperatorFactory implements OperatorFactory { + public final String token; + public final int precedence; + public final Operation operation; + + @Override public int precedence() { return precedence; } + @Override public String token() { return token; } + @Override public ParseRes construct(Source src, int i, Statement prev) { + var loc = src.loc(i); + + if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token)); + + var other = ES5.parseExpression(src, i, precedence); + if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token)); + return ParseRes.res(((AssignableStatement)prev).toAssign(other.result, operation), other.n); + } + + public AssignmentOperatorFactory(String token, int precedence, Operation operation) { + this.token = token; + this.precedence = precedence; + this.operation = operation; + } + } + private static class LazyAndFactory implements OperatorFactory { + @Override public int precedence() { return 4; } + @Override public String token() { return "&&"; } + @Override public ParseRes construct(Source src, int i, Statement prev) { + var loc = src.loc(i); + + var other = ES5.parseExpression(src, i, 5); + if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '&&'"); + return ParseRes.res(new LazyAndStatement(loc, prev, (Statement)other.result), other.n); + } + } + private static class LazyOrFactory implements OperatorFactory { + @Override public int precedence() { return 5; } + @Override public String token() { return "||"; } + @Override public ParseRes construct(Source src, int i, Statement prev) { + var loc = src.loc(i); + + var other = ES5.parseExpression(src, i, 6); + if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '||'"); + return ParseRes.res(new LazyOrStatement(loc, prev, (Statement)other.result), other.n); + } + } + + public final Statement[] args; + public final Operation operation; + + @Override public boolean pure() { + for (var el : args) { + if (!el.pure()) return false; + } + + return true; + } + + @Override public void compile(CompileResult target, boolean pollute) { + for (var arg : args) { + arg.compile(target, true); + } + + if (pollute) target.add(Instruction.operation(operation)); + else target.add(Instruction.discard()); + } + + public OperationStatement(Location loc, Operation operation, Statement ...args) { + super(loc); + this.operation = operation; + this.args = args; + } + + private static final Map factories = Set.of( + new NormalOperatorFactory("*", 13, Operation.MULTIPLY), + new NormalOperatorFactory("/", 12, Operation.DIVIDE), + new NormalOperatorFactory("%", 12, Operation.MODULO), + new NormalOperatorFactory("-", 11, Operation.SUBTRACT), + new NormalOperatorFactory("+", 11, Operation.ADD), + new NormalOperatorFactory(">>", 10, Operation.SHIFT_RIGHT), + new NormalOperatorFactory("<<", 10, Operation.SHIFT_LEFT), + new NormalOperatorFactory(">>>", 10, Operation.USHIFT_RIGHT), + new NormalOperatorFactory(">", 9, Operation.GREATER), + new NormalOperatorFactory("<", 9, Operation.LESS), + new NormalOperatorFactory(">=", 9, Operation.GREATER_EQUALS), + new NormalOperatorFactory("<=", 9, Operation.LESS_EQUALS), + new NormalOperatorFactory("!=", 8, Operation.LOOSE_NOT_EQUALS), + new NormalOperatorFactory("!==", 8, Operation.NOT_EQUALS), + new NormalOperatorFactory("==", 8, Operation.LOOSE_EQUALS), + new NormalOperatorFactory("===", 8, Operation.EQUALS), + new NormalOperatorFactory("&", 7, Operation.AND), + new NormalOperatorFactory("^", 6, Operation.XOR), + new NormalOperatorFactory("|", 5, Operation.OR), + + new AssignmentOperatorFactory("=", 2, null), + new AssignmentOperatorFactory("*=", 2, Operation.MULTIPLY), + new AssignmentOperatorFactory("/=", 2, Operation.DIVIDE), + new AssignmentOperatorFactory("%=", 2, Operation.MODULO), + new AssignmentOperatorFactory("-=", 2, Operation.SUBTRACT), + new AssignmentOperatorFactory("+=", 2, Operation.ADD), + new AssignmentOperatorFactory(">>=", 2, Operation.SHIFT_RIGHT), + new AssignmentOperatorFactory("<<=", 2, Operation.SHIFT_LEFT), + new AssignmentOperatorFactory(">>>=", 2, Operation.USHIFT_RIGHT), + new AssignmentOperatorFactory("&=", 2, Operation.AND), + new AssignmentOperatorFactory("^=", 2, Operation.XOR), + new AssignmentOperatorFactory("|=", 2, Operation.OR), + + new LazyAndFactory(), + new LazyOrFactory() + ).stream().collect(Collectors.toMap(v -> v.token(), v -> v)); + + private static final List operatorsByLength = factories.keySet().stream().sorted((a, b) -> -a.compareTo(b)).collect(Collectors.toList()); + + public static ParseRes parsePrefix(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + Operation operation = null; + String op; + + if (src.is(i + n, op = "+")) operation = Operation.POS; + else if (src.is(i + n, op = "-")) operation = Operation.NEG; + else if (src.is(i + n, op = "~")) operation = Operation.INVERSE; + else if (src.is(i + n, op = "!")) operation = Operation.NOT; + else return ParseRes.failed(); + + n++; + + var res = ES5.parseExpression(src, i + n, 14); + + if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); + else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op)); + } + public static ParseRes parseInstanceof(Source src, int i, Statement prev, int precedence) { + if (precedence > 9) return ParseRes.failed(); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var kw = Parsing.parseIdentifier(src, i + n, "instanceof"); + if (!kw.isSuccess()) return kw.chainError(); + n += kw.n; + + var valRes = ES5.parseExpression(src, i + n, 10); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'instanceof'."); + n += valRes.n; + + return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n); + } + public static ParseRes parseIn(Source src, int i, Statement prev, int precedence) { + if (precedence > 9) return ParseRes.failed(); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var kw = Parsing.parseIdentifier(src, i + n, "in"); + if (!kw.isSuccess()) return kw.chainError(); + n += kw.n; + + var valRes = ES5.parseExpression(src, i + n, 10); + if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'in'."); + n += valRes.n; + + return ParseRes.res(new OperationStatement(loc, Operation.IN, valRes.result, prev), n); + } + public static ParseRes parseOperator(Source src, int i, Statement prev, int precedence) { + var n = Parsing.skipEmpty(src, i); + + for (var token : operatorsByLength) { + var factory = factories.get(token); + + if (!src.is(i + n, token)) continue; + if (factory.precedence() < precedence) ParseRes.failed(); + + n += token.length(); + n += Parsing.skipEmpty(src, i + n); + + var res = factory.construct(src, i + n, prev); + return res.addN(n); + // var res = Parsing.parseValue(src, i + n, prec + 1); + // if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s' operator.", token)); + // n += res.n; + + // return ParseRes.res(new OperationStatement(loc, factories.get(token), prev, res.result), n); + } + + return ParseRes.failed(); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java similarity index 76% rename from src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java index 89ddc98..8aaa578 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/TypeofStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java @@ -1,12 +1,14 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.ES5; import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.compilation.parsing.Source; +import me.topchetoeu.jscript.compilation.values.VariableStatement; public class TypeofStatement extends Statement { public final Statement value; @@ -40,7 +42,7 @@ public class TypeofStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "typeof")) return ParseRes.failed(); n += 6; - var valRes = Parsing.parseValue(src, i + n, 15); + var valRes = ES5.parseExpression(src, i + n, 15); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword."); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignStatement.java similarity index 86% rename from src/java/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignStatement.java index 31bf6c9..a9e7167 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignStatement.java @@ -1,10 +1,11 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.values.FunctionStatement; public class VariableAssignStatement extends Statement { public final String name; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableIndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexStatement.java similarity index 82% rename from src/java/me/topchetoeu/jscript/compilation/values/VariableIndexStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexStatement.java index db3e4f0..f2f53bc 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableIndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexStatement.java @@ -1,7 +1,7 @@ -package me.topchetoeu.jscript.compilation.values; +package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Statement; diff --git a/src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java b/src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java new file mode 100644 index 0000000..df5a7e3 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java @@ -0,0 +1,13 @@ +package me.topchetoeu.jscript.runtime; + +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; + +public class ArgumentsValue extends ArrayValue { + public final Frame frame; + + public ArgumentsValue(Frame frame, Value... args) { + super(args); + this.frame = frame; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/Engine.java b/src/java/me/topchetoeu/jscript/runtime/Engine.java index 9e2d6d1..1b83179 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Engine.java +++ b/src/java/me/topchetoeu/jscript/runtime/Engine.java @@ -1,18 +1,19 @@ package me.topchetoeu.jscript.runtime; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.concurrent.PriorityBlockingQueue; +import java.util.function.Supplier; -import me.topchetoeu.jscript.common.ResultRunnable; -import me.topchetoeu.jscript.common.events.DataNotifier; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; public class Engine implements EventLoop { private static class Task implements Comparable> { - public final ResultRunnable runnable; - public final DataNotifier notifier = new DataNotifier<>(); + public final Supplier runnable; + public final CompletableFuture notifier = new CompletableFuture(); public final boolean micro; - public Task(ResultRunnable runnable, boolean micro) { + public Task(Supplier runnable, boolean micro) { this.runnable = runnable; this.micro = micro; } @@ -26,8 +27,7 @@ public class Engine implements EventLoop { private PriorityBlockingQueue> tasks = new PriorityBlockingQueue<>(); private Thread thread; - @Override - public DataNotifier pushMsg(ResultRunnable runnable, boolean micro) { + @Override public Future pushMsg(Supplier runnable, boolean micro) { var msg = new Task(runnable, micro); tasks.add(msg); return msg.notifier; @@ -40,15 +40,15 @@ public class Engine implements EventLoop { var task = tasks.take(); try { - ((Task)task).notifier.next(task.runnable.run()); + ((Task)task).notifier.complete(task.runnable.get()); } catch (RuntimeException e) { if (e instanceof InterruptException) throw e; - task.notifier.error(e); + task.notifier.completeExceptionally(e); } } catch (InterruptedException | InterruptException e) { - for (var msg : tasks) msg.notifier.error(new InterruptException(e)); + for (var msg : tasks) msg.notifier.cancel(false); break; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index adbc6ff..3d48364 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -1,9 +1,10 @@ package me.topchetoeu.jscript.runtime; +import java.util.concurrent.Future; +import java.util.function.Supplier; + import me.topchetoeu.jscript.common.Compiler; -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.ResultRunnable; -import me.topchetoeu.jscript.common.events.DataNotifier; +import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; @@ -16,21 +17,21 @@ public interface EventLoop { public static EventLoop get(Environment ext) { if (ext.hasNotNull(KEY)) return ext.get(KEY); else return new EventLoop() { - @Override public DataNotifier pushMsg(ResultRunnable runnable, boolean micro) { + @Override public Future pushMsg(Supplier runnable, boolean micro) { throw EngineException.ofError("No event loop attached to environment."); } }; } - public DataNotifier pushMsg(ResultRunnable runnable, boolean micro); - public default DataNotifier pushMsg(Runnable runnable, boolean micro) { + public Future pushMsg(Supplier runnable, boolean micro); + public default Future pushMsg(Runnable runnable, boolean micro) { return pushMsg(() -> { runnable.run(); return null; }, micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { + public default Future pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { return pushMsg(() -> func.call(env, thisArg, args), micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { - return pushMsg(() -> Compiler.compile(env, filename, raw).call(env, thisArg, args), micro); + public default Future pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { + return pushMsg(() -> Compiler.compileFunc(env, filename, raw).call(env, thisArg, args), micro); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index c2a40c7..4741223 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -13,11 +13,11 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.scope.LocalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; -import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; @@ -101,6 +101,7 @@ public class Frame { public final LocalScope scope; public final Object thisArg; public final Object[] args; + public final boolean isNew; public final Stack tryStack = new Stack<>(); public final CodeFunction function; public final Environment env; @@ -356,14 +357,14 @@ public class Frame { */ public ObjectValue getValStackScope() { return new ObjectValue() { - @Override public Member getOwnMember(Environment env, Value key) { + @Override public Member getOwnMember(Environment env, KeyCache key) { var res = super.getOwnMember(env, key); if (res != null) return res; - var f = key.toNumber(env).value; - var i = (int)f; + var num = key.toNumber(env); + var i = key.toInt(env); - if (i < 0 || i >= stackPtr) return null; + if (num != i || i < 0 || i >= stackPtr) return null; else return new FieldMember(false, true, true) { @Override public Value get(Environment env, Value self) { return stack[i]; } @Override public boolean set(Environment env, Value val, Value self) { @@ -391,12 +392,13 @@ public class Frame { }; } - public Frame(Environment env, Value thisArg, Value[] args, CodeFunction func) { + public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) { this.env = env; - this.args = args.clone(); + this.args = args; + this.isNew = isNew; this.scope = new LocalScope(func.body.localsN, func.captures); this.scope.get(0).set(thisArg); - this.scope.get(1).value = new ArrayValue(args); + this.scope.get(1).value = new ArgumentsValue(this, args); this.thisArg = thisArg; this.function = func; diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index bd25878..67ce302 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -35,9 +35,18 @@ public class InstructionRunner { private static Value execCall(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); - var thisArg = frame.pop(); - frame.push(func.call(env, thisArg, callArgs)); + frame.push(func.call(env, false, instr.get(1), VoidValue.UNDEFINED, callArgs)); + + frame.codePtr++; + return null; + } + private static Value execCallMember(Environment env, Instruction instr, Frame frame) { + var callArgs = frame.take(instr.get(0)); + var index = frame.pop(); + var obj = frame.pop(); + + frame.push(obj.getMember(env, index).call(env, false, instr.get(1), obj, callArgs)); frame.codePtr++; return null; @@ -46,7 +55,7 @@ public class InstructionRunner { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); - frame.push(funcObj.callNew(env, callArgs)); + frame.push(funcObj.callNew(env, instr.get(1), callArgs)); frame.codePtr++; return null; @@ -61,7 +70,7 @@ public class InstructionRunner { private static Value execDefProp(Environment env, Instruction instr, Frame frame) { var setterVal = frame.pop(); var getterVal = frame.pop(); - var name = frame.pop(); + var key = frame.pop(); var obj = frame.pop(); FunctionValue getter, setter; @@ -74,7 +83,7 @@ public class InstructionRunner { else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal; else throw EngineException.ofType("Setter must be a function or undefined."); - obj.defineOwnMember(env, name, new PropertyMember(getter, setter, true, true)); + obj.defineOwnMember(env, key, new PropertyMember(getter, setter, true, true)); frame.push(obj); frame.codePtr++; @@ -90,7 +99,7 @@ public class InstructionRunner { for (var el : members) { var obj = new ObjectValue(); - obj.defineOwnMember(env, new StringValue("value"), FieldMember.of(new StringValue(el))); + obj.defineOwnMember(env, "value", FieldMember.of(new StringValue(el))); frame.push(obj); } @@ -147,7 +156,9 @@ public class InstructionRunner { return null; } private static Value execLoadObj(Environment env, Instruction instr, Frame frame) { - frame.push(new ObjectValue()); + var obj = new ObjectValue(); + obj.setPrototype(Environment.OBJECT_PROTO); + frame.push(obj); frame.codePtr++; return null; } @@ -165,13 +176,14 @@ public class InstructionRunner { } private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { int id = instr.get(0); - var captures = new ValueVariable[instr.params.length - 1]; + String name = instr.get(1); + var captures = new ValueVariable[instr.params.length - 2]; - for (var i = 1; i < instr.params.length; i++) { - captures[i - 1] = frame.scope.get(instr.get(i)); + for (var i = 2; i < instr.params.length; i++) { + captures[i - 2] = frame.scope.get(instr.get(i)); } - var func = new CodeFunction(env, "", frame.function.body.children[id], captures); + var func = new CodeFunction(env, name, frame.function.body.children[id], captures); frame.push(func); @@ -240,7 +252,7 @@ public class InstructionRunner { return null; } private static Value execJmpIf(Environment env, Instruction instr, Frame frame) { - if (frame.pop().toBoolean().value) { + if (frame.pop().toBoolean()) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; } @@ -248,7 +260,7 @@ public class InstructionRunner { return null; } private static Value execJmpIfNot(Environment env, Instruction instr, Frame frame) { - if (frame.pop().not().value) { + if (!frame.pop().toBoolean()) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; } @@ -306,6 +318,7 @@ public class InstructionRunner { case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); case CALL: return execCall(env, instr, frame); case CALL_NEW: return execCallNew(env, instr, frame); + case CALL_MEMBER: return execCallMember(env, instr, frame); case TRY_START: return execTryStart(env, instr, frame); case TRY_END: return execTryEnd(env, instr, frame); diff --git a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java index 84b390a..ab4c9fd 100644 --- a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java +++ b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java @@ -27,7 +27,7 @@ public class JSONConverter { var res = new ObjectValue(); for (var el : val.map().entrySet()) { - res.defineOwnMember(null, new StringValue(el.getKey()), FieldMember.of(toJs(el.getValue()))); + res.defineOwnMember(null, el.getKey(), FieldMember.of(toJs(el.getValue()))); } return res; diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 4381f4f..551d4b6 100644 --- a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -3,12 +3,14 @@ package me.topchetoeu.jscript.runtime; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import me.topchetoeu.jscript.common.Compiler; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Reading; -import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; @@ -16,6 +18,7 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class SimpleRepl { @@ -32,13 +35,16 @@ public class SimpleRepl { try { var file = Path.of(arg); var raw = Files.readString(file); - var res = engine.pushMsg( - false, environment, - Filename.fromFile(file.toFile()), - raw, null - ).await(); - System.err.println(res.toReadable(environment)); + try { + var res = engine.pushMsg( + false, environment, + Filename.fromFile(file.toFile()), raw, null + ).get(); + + System.err.println(res.toReadable(environment)); + } + catch (ExecutionException e) { throw e.getCause(); } } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } } @@ -48,9 +54,16 @@ public class SimpleRepl { var raw = Reading.readline(); if (raw == null) break; - var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); - var res = engine.pushMsg(false, environment, func, VoidValue.UNDEFINED).await(); - System.err.println(res.toReadable(environment)); + + try { + var res = engine.pushMsg( + false, environment, + new Filename("jscript", "repl/" + i + ".js"), raw, + VoidValue.UNDEFINED + ).get(); + System.err.println(res.toReadable(environment)); + } + catch (ExecutionException e) { throw e.getCause(); } } catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } } @@ -59,62 +72,36 @@ public class SimpleRepl { System.out.println(e.toString()); engine.thread().interrupt(); } - catch (RuntimeException ex) { - if (ex instanceof InterruptException) return; - else { - System.out.println("Internal error ocurred:"); - ex.printStackTrace(); - } + catch (CancellationException | InterruptedException e) { return; } + catch (Throwable ex) { + System.out.println("Internal error ocurred:"); + ex.printStackTrace(); } } private static void initEnv() { - // glob.define(null, false, new NativeFunction("go", args -> { - // try { - // var f = Path.of("do.js"); - // var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); - // return func.call(args.env); - // } - // catch (IOException e) { - // throw new EngineException("Couldn't open do.js"); - // } - // })); - - // var fs = new RootFilesystem(PermissionsProvider.get(environment)); - // fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); - // fs.protocols.put("file", new PhysicalFilesystem(".")); - // fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err)); - - // environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); - // environment.add(Filesystem.KEY, fs); - // environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); - // environment.add(Compiler.KEY, new JSCompiler(environment)); environment.add(EventLoop.KEY, engine); environment.add(GlobalScope.KEY, new GlobalScope()); - // environment.add(EventLoop.KEY, engine); - environment.add(Compiler.KEY, (filename, source) -> { - return Parsing.compile(filename, source).body(); - }); + environment.add(DebugContext.KEY, new DebugContext()); + environment.add(Compiler.KEY, Compiler.DEFAULT); var glob = GlobalScope.get(environment); + glob.define(null, false, new NativeFunction("exit", args -> { + Thread.currentThread().interrupt(); throw new InterruptException(); })); glob.define(null, false, new NativeFunction("log", args -> { for (var el : args.args) { - System.out.print(el.toReadable(args.env)); + if (el instanceof StringValue) System.out.print(((StringValue)el).value); + else System.out.print(el.toReadable(args.env)); } return null; })); } private static void initEngine() { - // var ctx = new DebugContext(); - // environment.add(DebugContext.KEY, ctx); - - // debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx)); engineTask = engine.start(); - // debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); } public static void main(String args[]) throws InterruptedException { diff --git a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java deleted file mode 100644 index f97fa15..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.topchetoeu.jscript.runtime; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; -import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; - -public interface WrapperProvider { - public ObjectValue getProto(Class obj); - public ObjectValue getNamespace(Class obj); - public FunctionValue getConstr(Class obj); - - public WrapperProvider fork(Environment env); -} diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java index a1c5e26..0ecc549 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java @@ -5,11 +5,11 @@ import java.util.HashMap; import java.util.List; import java.util.WeakHashMap; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.mapping.FunctionMap; +import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java index aad4c68..5bf3817 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java @@ -2,10 +2,10 @@ package me.topchetoeu.jscript.runtime.debug; import java.util.List; -import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.mapping.FunctionMap; +import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/ConvertException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/ConvertException.java deleted file mode 100644 index 72cb63a..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/ConvertException.java +++ /dev/null @@ -1,11 +0,0 @@ -package me.topchetoeu.jscript.runtime.exceptions; - -public class ConvertException extends RuntimeException { - public final String source, target; - - public ConvertException(String source, String target) { - super(String.format("Cannot convert '%s' to '%s'.", source, target)); - this.source = source; - this.target = target; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 975db01..7912f09 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -3,13 +3,14 @@ package me.topchetoeu.jscript.runtime.exceptions; import java.util.ArrayList; import java.util.List; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class EngineException extends RuntimeException { public static class StackElement { @@ -69,7 +70,19 @@ public class EngineException extends RuntimeException { ss.append(value.toString(env)).append('\n'); } catch (EngineException e) { - ss.append("[Error while stringifying]\n"); + var name = value.getMember(env, "name"); + var desc = value.getMember(env, "message"); + + if (name.isPrimitive() && desc.isPrimitive()) { + if (name instanceof VoidValue) ss.append("Error: "); + else ss.append(name.toString(env).value + ": "); + + if (desc instanceof VoidValue) ss.append("An error occurred"); + else ss.append(desc.toString(env).value); + + ss.append("\n"); + } + else ss.append("[Error while stringifying]\n"); } for (var line : stackTrace) { if (line.visible()) ss.append(" ").append(line.toString()).append("\n"); @@ -83,8 +96,8 @@ public class EngineException extends RuntimeException { var res = new ObjectValue(); res.setPrototype(proto); - if (name != null) res.defineOwnMember(Environment.empty(), new StringValue("name"), FieldMember.of(new StringValue(name))); - res.defineOwnMember(Environment.empty(), new StringValue("message"), FieldMember.of(new StringValue(msg))); + if (name != null) res.defineOwnMember(Environment.empty(), "name", FieldMember.of(new StringValue(name))); + res.defineOwnMember(Environment.empty(), "message", FieldMember.of(new StringValue(msg))); return res; } diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java index 23bd206..2db27e1 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.exceptions; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.parsing.Location; public class SyntaxException extends RuntimeException { public final Location loc; diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java index a571bac..88cd8fc 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java @@ -29,10 +29,10 @@ public class GlobalScope { } public void define(Environment ext, String name, Variable variable) { - object.defineOwnMember(ext, new StringValue(name), variable.toField(true, true)); + object.defineOwnMember(ext, name, variable.toField(true, true)); } public void define(Environment ext, boolean readonly, String name, Value val) { - object.defineOwnMember(ext, new StringValue(name), FieldMember.of(val, !readonly)); + object.defineOwnMember(ext, name, FieldMember.of(val, !readonly)); } public void define(Environment ext, boolean readonly, String ...names) { for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED)); @@ -42,12 +42,12 @@ public class GlobalScope { } public Value get(Environment env, String name) { - if (!object.hasMember(env, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - else return object.getMember(env, new StringValue(name)); + if (!object.hasMember(env, name, false)) throw EngineException.ofSyntax(name + " is not defined"); + else return object.getMember(env, name); } public void set(Environment ext, String name, Value val) { - if (!object.hasMember(ext, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - if (!object.setMember(ext, new StringValue(name), val)) throw EngineException.ofSyntax("The global '" + name + "' is read-only."); + if (!object.hasMember(ext, name, false)) throw EngineException.ofSyntax(name + " is not defined"); + if (!object.setMember(ext, name, val)) throw EngineException.ofSyntax("Assignment to constant variable"); } public Set keys() { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ConvertHint.java b/src/java/me/topchetoeu/jscript/runtime/values/ConvertHint.java deleted file mode 100644 index 1717f40..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ConvertHint.java +++ /dev/null @@ -1,6 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -public enum ConvertHint { - TOSTRING, - VALUEOF, -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java b/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java new file mode 100644 index 0000000..df1c4c0 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java @@ -0,0 +1,59 @@ +package me.topchetoeu.jscript.runtime.values; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; + +public class KeyCache { + public final Value value; + private Integer intCache; + private Double doubleCache; + private Boolean booleanCache; + private String stringCache; + + public String toString(Environment env) { + if (stringCache != null) return stringCache; + else return stringCache = value.toString(env).value; + } + public double toNumber(Environment env) { + if (doubleCache != null) return doubleCache; + else return doubleCache = value.toNumber(env).value; + } + public int toInt(Environment env) { + if (intCache != null) return intCache; + else return intCache = (int)toNumber(env); + } + public boolean toBoolean() { + if (booleanCache != null) return booleanCache; + else return booleanCache = value.toBoolean(); + } + public SymbolValue toSymbol() { + if (value instanceof SymbolValue) return (SymbolValue)value; + else return null; + } + public boolean isSymbol() { + return value instanceof SymbolValue; + } + + public KeyCache(Value value) { + this.value = value; + } + public KeyCache(String value) { + this.value = new StringValue(value); + this.stringCache = value; + this.booleanCache = !value.equals(""); + } + public KeyCache(int value) { + this.value = new NumberValue(value); + this.intCache = value; + this.doubleCache = (double)value; + this.booleanCache = value != 0; + } + public KeyCache(double value) { + this.value = new NumberValue(value); + this.intCache = (int)value; + this.doubleCache = value; + this.booleanCache = value != 0; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/java/me/topchetoeu/jscript/runtime/values/Member.java index 91de993..2dae69b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Member.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Member.java @@ -4,7 +4,6 @@ import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; -import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public interface Member { @@ -42,14 +41,14 @@ public interface Member { @Override public ObjectValue descriptor(Environment env, Value self) { var res = new ObjectValue(); - if (getter == null) res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(VoidValue.UNDEFINED)); - else res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(getter)); + if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(VoidValue.UNDEFINED)); + else res.defineOwnMember(env, "getter", FieldMember.of(getter)); - if (setter == null) res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(VoidValue.UNDEFINED)); - else res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(setter)); + if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(VoidValue.UNDEFINED)); + else res.defineOwnMember(env, "setter", FieldMember.of(setter)); - res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); - res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); + res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable))); + res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable))); return res; } @@ -98,10 +97,10 @@ public interface Member { @Override public ObjectValue descriptor(Environment env, Value self) { var res = new ObjectValue(); - res.defineOwnMember(env, new StringValue("value"), FieldMember.of(get(env, self))); - res.defineOwnMember(env, new StringValue("writable"), FieldMember.of(BoolValue.of(writable))); - res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); - res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); + res.defineOwnMember(env, "value", FieldMember.of(get(env, self))); + res.defineOwnMember(env, "writable", FieldMember.of(BoolValue.of(writable))); + res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable))); + res.defineOwnMember(env, "configurable", FieldMember.of(BoolValue.of(configurable))); return res; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index 480fd1e..d15e324 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -53,31 +53,41 @@ public abstract class Value { public abstract StringValue type(); public abstract boolean isPrimitive(); - public abstract BoolValue toBoolean(); public final boolean isNaN() { return this instanceof NumberValue && Double.isNaN(((NumberValue)this).value); } - public Value call(Environment env, Value self, Value ...args) { - throw EngineException.ofType("Tried to call a non-function value."); + public Value call(Environment env, boolean isNew, String name, Value self, Value ...args) { + if (name == null || name.equals("")) name = "(intermediate value)"; + + if (isNew) throw EngineException.ofType(name + " is not a constructor"); + else throw EngineException.ofType(name + " is not a function"); } - public final Value callNew(Environment env, Value ...args) { + public final Value callNew(Environment env, String name, Value ...args) { var res = new ObjectValue(); var proto = getMember(env, new StringValue("prototype")); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); else res.setPrototype(env, null); - var ret = this.call(env, res, args); + var ret = this.call(env, true, name, res, args); if (!ret.isPrimitive()) return ret; return res; } + public final Value call(Environment env, Value self, Value ...args) { + return call(env, false, "", self, args); + } + public final Value callNew(Environment env, Value ...args) { + return callNew(env, "", args); + } + public abstract Value toPrimitive(Environment env); public abstract NumberValue toNumber(Environment env); public abstract StringValue toString(Environment env); + public abstract boolean toBoolean(); public final int toInt(Environment env) { return (int)toNumber(env).value; } public final long toLong(Environment env) { return (long)toNumber(env).value; } @@ -134,11 +144,11 @@ public abstract class Value { } public CompareResult compare(Environment env, Value other) { - return toPrimitive(env).compare(env, other); - } + var a = this.toPrimitive(env); + var b = other.toPrimitive(env); - public final BoolValue not() { - return toBoolean().value ? BoolValue.FALSE : BoolValue.TRUE; + if (a instanceof StringValue && b instanceof StringValue) return a.compare(env, b); + else return a.toNumber(env).compare(env, b.toNumber(env)); } public final boolean isInstanceOf(Environment env, Value proto) { @@ -172,7 +182,7 @@ public abstract class Value { case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual()); case INVERSE: return args[0].bitwiseNot(env); - case NOT: return args[0].not(); + case NOT: return BoolValue.of(!args[0].toBoolean()); case POS: return args[0].toNumber(env); case NEG: return args[0].negative(env); @@ -187,16 +197,71 @@ public abstract class Value { } } - public abstract Member getOwnMember(Environment env, Value key); + public abstract Member getOwnMember(Environment env, KeyCache key); public abstract Map getOwnMembers(Environment env); public abstract Map getOwnSymbolMembers(Environment env); - public abstract boolean defineOwnMember(Environment env, Value key, Member member); - public abstract boolean deleteOwnMember(Environment env, Value key); + public abstract boolean defineOwnMember(Environment env, KeyCache key, Member member); + public abstract boolean deleteOwnMember(Environment env, KeyCache key); public abstract ObjectValue getPrototype(Environment env); public abstract boolean setPrototype(Environment env, ObjectValue val); - public final Value getMember(Environment env, Value key) { + public final Member getOwnMember(Environment env, Value key) { + return getOwnMember(env, new KeyCache(key)); + } + public final Member getOwnMember(Environment env, String key) { + return getOwnMember(env, new KeyCache(key)); + } + public final Member getOwnMember(Environment env, int key) { + return getOwnMember(env, new KeyCache(key)); + } + public final Member getOwnMember(Environment env, double key) { + return getOwnMember(env, new KeyCache(key)); + } + + public final boolean defineOwnMember(Environment env, Value key, Member member) { + return defineOwnMember(env, new KeyCache(key), member); + } + public final boolean defineOwnMember(Environment env, String key, Member member) { + return defineOwnMember(env, new KeyCache(key), member); + } + public final boolean defineOwnMember(Environment env, int key, Member member) { + return defineOwnMember(env, new KeyCache(key), member); + } + public final boolean defineOwnMember(Environment env, double key, Member member) { + return defineOwnMember(env, new KeyCache(key), member); + } + + public final boolean defineOwnMember(Environment env, KeyCache key, Value val) { + return defineOwnMember(env, key, FieldMember.of(val)); + } + public final boolean defineOwnMember(Environment env, Value key, Value val) { + return defineOwnMember(env, new KeyCache(key), FieldMember.of(val)); + } + public final boolean defineOwnMember(Environment env, String key, Value val) { + return defineOwnMember(env, new KeyCache(key), FieldMember.of(val)); + } + public final boolean defineOwnMember(Environment env, int key, Value val) { + return defineOwnMember(env, new KeyCache(key), FieldMember.of(val)); + } + public final boolean defineOwnMember(Environment env, double key, Value val) { + return defineOwnMember(env, new KeyCache(key), FieldMember.of(val)); + } + + public final boolean deleteOwnMember(Environment env, Value key) { + return deleteOwnMember(env, new KeyCache(key)); + } + public final boolean deleteOwnMember(Environment env, String key) { + return deleteOwnMember(env, new KeyCache(key)); + } + public final boolean deleteOwnMember(Environment env, int key) { + return deleteOwnMember(env, new KeyCache(key)); + } + public final boolean deleteOwnMember(Environment env, double key) { + return deleteOwnMember(env, new KeyCache(key)); + } + + public final Value getMember(Environment env, KeyCache key) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { var member = obj.getOwnMember(env, key); if (member != null) return member.get(env, obj); @@ -204,15 +269,51 @@ public abstract class Value { return VoidValue.UNDEFINED; } - public final boolean setMember(Environment env, Value key, Value val) { + public final Value getMember(Environment env, Value key) { + return getMember(env, new KeyCache(key)); + } + public final Value getMember(Environment env, String key) { + return getMember(env, new KeyCache(key)); + } + public final Value getMember(Environment env, int key) { + return getMember(env, new KeyCache(key)); + } + public final Value getMember(Environment env, double key) { + return getMember(env, new KeyCache(key)); + } + + public final boolean setMember(Environment env, KeyCache key, Value val) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { var member = obj.getOwnMember(env, key); - if (member != null) return member.set(env, val, obj); + if (member != null) { + if (member.set(env, val, obj)) { + if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env)); + return true; + } + else return false; + } } - return defineOwnMember(env, key, FieldMember.of(val)); + if (defineOwnMember(env, key, FieldMember.of(val))) { + if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env)); + return true; + } + else return false; } - public final boolean hasMember(Environment env, Value key, boolean own) { + public final boolean setMember(Environment env, Value key, Value val) { + return setMember(env, new KeyCache(key), val); + } + public final boolean setMember(Environment env, String key, Value val) { + return setMember(env, new KeyCache(key), val); + } + public final boolean setMember(Environment env, int key, Value val) { + return setMember(env, new KeyCache(key), val); + } + public final boolean setMember(Environment env, double key, Value val) { + return setMember(env, new KeyCache(key), val); + } + + public final boolean hasMember(Environment env, KeyCache key, boolean own) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { if (obj.getOwnMember(env, key) != null) return true; if (own) return false; @@ -220,10 +321,36 @@ public abstract class Value { return false; } - public final boolean deleteMember(Environment env, Value key) { + public final boolean hasMember(Environment env, Value key, boolean own) { + return hasMember(env, new KeyCache(key), own); + } + public final boolean hasMember(Environment env, String key, boolean own) { + return hasMember(env, new KeyCache(key), own); + } + public final boolean hasMember(Environment env, int key, boolean own) { + return hasMember(env, new KeyCache(key), own); + } + public final boolean hasMember(Environment env, double key, boolean own) { + return hasMember(env, new KeyCache(key), own); + } + + public final boolean deleteMember(Environment env, KeyCache key) { if (!hasMember(env, key, true)) return true; return deleteOwnMember(env, key); } + public final boolean deleteMember(Environment env, Value key) { + return deleteMember(env, new KeyCache(key)); + } + public final boolean deleteMember(Environment env, String key) { + return deleteMember(env, new KeyCache(key)); + } + public final boolean deleteMember(Environment env, int key) { + return deleteMember(env, new KeyCache(key)); + } + public final boolean deleteMember(Environment env, double key) { + return deleteMember(env, new KeyCache(key)); + } + public final Map getMembers(Environment env, boolean own, boolean onlyEnumerable) { var res = new LinkedHashMap(); var protos = new ArrayList(); @@ -277,7 +404,7 @@ public abstract class Value { return res; } public final ObjectValue getMemberDescriptor(Environment env, Value key) { - var member = getOwnMember(env, key); + var member = getOwnMember(env, new KeyCache(key)); if (member != null) return member.descriptor(env, this); else return null; @@ -394,7 +521,7 @@ public abstract class Value { var curr = supplier.call(env, VoidValue.UNDEFINED); if (curr == null) { supplier = null; value = null; } - if (curr.getMember(env, new StringValue("done")).toBoolean().value) { supplier = null; value = null; } + if (curr.getMember(env, new StringValue("done")).toBoolean()) { supplier = null; value = null; } else { this.value = curr.getMember(env, new StringValue("value")); consumed = false; @@ -425,8 +552,8 @@ public abstract class Value { return new NativeFunction("", args -> { var obj = new ObjectValue(); - if (!it.hasNext()) obj.defineOwnMember(args.env, new StringValue("done"), FieldMember.of(BoolValue.TRUE)); - else obj.defineOwnMember(args.env, new StringValue("value"), FieldMember.of(it.next())); + if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE)); + else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next())); return obj; }); @@ -526,11 +653,11 @@ public abstract class Value { } else if (this instanceof VoidValue) return ((VoidValue)this).name; else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value)); + else if (this instanceof SymbolValue) return this.toString(); else return this.toString(env).value; } public final String toReadable(Environment ext) { - if (this instanceof StringValue) return ((StringValue)this).value; return toReadable(ext, new HashSet<>(), 0); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old b/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old deleted file mode 100644 index a2482b7..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old +++ /dev/null @@ -1,765 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.lang.reflect.Array; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.common.Operation; -// import me.topchetoeu.jscript.lib.PromiseLib; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.ConvertException; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; -import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; -import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; -import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; -import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; - -public class Values { - public static enum CompareResult { - NOT_EQUAL, - EQUAL, - LESS, - GREATER; - - public boolean less() { return this == LESS; } - public boolean greater() { return this == GREATER; } - public boolean lessOrEqual() { return this == LESS || this == EQUAL; } - public boolean greaterOrEqual() { return this == GREATER || this == EQUAL; } - - public static CompareResult from(int cmp) { - if (cmp < 0) return LESS; - if (cmp > 0) return GREATER; - return EQUAL; - } - } - - public static final Object NULL = new Object(); - public static final Object NO_RETURN = new Object(); - - public static boolean isWrapper(Object val) { return val instanceof NativeWrapper; } - public static boolean isWrapper(Object val, Class clazz) { - if (!isWrapper(val)) return false; - var res = (NativeWrapper)val; - return res != null && clazz.isInstance(res.wrapped); - } - public static boolean isNan(Object val) { return val instanceof Number && Double.isNaN(number(val)); } - - public static double number(Object val) { - if (val instanceof Number) return ((Number)val).doubleValue(); - else return Double.NaN; - } - - @SuppressWarnings("unchecked") - public static T wrapper(Object val, Class clazz) { - if (isWrapper(val)) val = ((NativeWrapper)val).wrapped; - if (val != null && clazz.isInstance(val)) return (T)val; - else return null; - } - - public static String type(Object val) { - if (val == null) return "undefined"; - if (val instanceof String) return "string"; - if (val instanceof Number) return "number"; - if (val instanceof Boolean) return "boolean"; - if (val instanceof SymbolValue) return "symbol"; - if (val instanceof FunctionValue) return "function"; - return "object"; - } - - private static Object tryCallConvertFunc(Environment ext, Object obj, String name) { - var func = getMember(ext, obj, name); - - if (func instanceof FunctionValue) { - var res = Values.call(ext, func, obj); - if (isPrimitive(res)) return res; - } - - throw EngineException.ofType("Value couldn't be converted to a primitive."); - } - - public static boolean isPrimitive(Object obj) { - return - obj instanceof Number || - obj instanceof String || - obj instanceof Boolean || - obj instanceof SymbolValue || - obj == null || - obj == NULL; - } - - public static Object toPrimitive(Environment ext, Object obj, ConvertHint hint) { - obj = normalize(ext, obj); - if (isPrimitive(obj)) return obj; - - var first = hint == ConvertHint.VALUEOF ? "valueOf" : "toString"; - var second = hint == ConvertHint.VALUEOF ? "toString" : "valueOf"; - - if (ext != null) { - try { return tryCallConvertFunc(ext, obj, first); } - catch (EngineException unused) { return tryCallConvertFunc(ext, obj, second); } - } - - throw EngineException.ofType("Value couldn't be converted to a primitive."); - } - public static boolean toBoolean(Object obj) { - if (obj == NULL || obj == null) return false; - if (obj instanceof Number && (number(obj) == 0 || Double.isNaN(number(obj)))) return false; - if (obj instanceof String && ((String)obj).equals("")) return false; - if (obj instanceof Boolean) return (Boolean)obj; - return true; - } - public static double toNumber(Environment ext, Object obj) { - var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); - - if (val instanceof Number) return number(val); - if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0; - if (val instanceof String) { - try { return Double.parseDouble((String)val); } - catch (NumberFormatException e) { return Double.NaN; } - } - return Double.NaN; - } - public static String toString(Environment ext, Object obj) { - var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); - - if (val == null) return "undefined"; - if (val == NULL) return "null"; - - if (val instanceof Number) { - var d = number(val); - if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; - if (d == Double.POSITIVE_INFINITY) return "Infinity"; - if (Double.isNaN(d)) return "NaN"; - return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); - } - if (val instanceof Boolean) return (Boolean)val ? "true" : "false"; - if (val instanceof String) return (String)val; - if (val instanceof SymbolValue) return val.toString(); - - return "Unknown value"; - } - - public static Object add(Environment ext, Object a, Object b) { - if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b); - else return toNumber(ext, a) + toNumber(ext, b); - } - public static double subtract(Environment ext, Object a, Object b) { - return toNumber(ext, a) - toNumber(ext, b); - } - public static double multiply(Environment ext, Object a, Object b) { - return toNumber(ext, a) * toNumber(ext, b); - } - public static double divide(Environment ext, Object a, Object b) { - return toNumber(ext, a) / toNumber(ext, b); - } - public static double modulo(Environment ext, Object a, Object b) { - return toNumber(ext, a) % toNumber(ext, b); - } - - public static double negative(Environment ext, Object obj) { - return -toNumber(ext, obj); - } - - public static int and(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) & (int)toNumber(ext, b); - } - public static int or(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) | (int)toNumber(ext, b); - } - public static int xor(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) ^ (int)toNumber(ext, b); - } - public static int bitwiseNot(Environment ext, Object obj) { - return ~(int)toNumber(ext, obj); - } - - public static int shiftLeft(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) << (int)toNumber(ext, b); - } - public static int shiftRight(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) >> (int)toNumber(ext, b); - } - public static long unsignedShiftRight(Environment ext, Object a, Object b) { - long _a = (long)toNumber(ext, a); - long _b = (long)toNumber(ext, b); - - if (_a < 0) _a += 0x100000000l; - if (_b < 0) _b += 0x100000000l; - return _a >>> _b; - } - - public static CompareResult compare(Environment ext, Object a, Object b) { - a = toPrimitive(ext, a, ConvertHint.VALUEOF); - b = toPrimitive(ext, b, ConvertHint.VALUEOF); - - if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b)); - - var _a = toNumber(ext, a); - var _b = toNumber(ext, b); - - if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL; - - return CompareResult.from(Double.compare(_a, _b)); - } - - public static boolean not(Object obj) { - return !toBoolean(obj); - } - - public static boolean isInstanceOf(Environment ext, Object obj, Object proto) { - if (obj == null || obj == NULL || proto == null || proto == NULL) return false; - var val = getPrototype(ext, obj); - - while (val != null) { - if (val.equals(proto)) return true; - val = val.getPrototype(ext); - } - - return false; - } - - public static Object operation(Environment ext, Operation op, Object ...args) { - switch (op) { - case ADD: return add(ext, args[0], args[1]); - case SUBTRACT: return subtract(ext, args[0], args[1]); - case DIVIDE: return divide(ext, args[0], args[1]); - case MULTIPLY: return multiply(ext, args[0], args[1]); - case MODULO: return modulo(ext, args[0], args[1]); - - case AND: return and(ext, args[0], args[1]); - case OR: return or(ext, args[0], args[1]); - case XOR: return xor(ext, args[0], args[1]); - - case EQUALS: return strictEquals(ext, args[0], args[1]); - case NOT_EQUALS: return !strictEquals(ext, args[0], args[1]); - case LOOSE_EQUALS: return looseEqual(ext, args[0], args[1]); - case LOOSE_NOT_EQUALS: return !looseEqual(ext, args[0], args[1]); - - case GREATER: return compare(ext, args[0], args[1]).greater(); - case GREATER_EQUALS: return compare(ext, args[0], args[1]).greaterOrEqual(); - case LESS: return compare(ext, args[0], args[1]).less(); - case LESS_EQUALS: return compare(ext, args[0], args[1]).lessOrEqual(); - - case INVERSE: return bitwiseNot(ext, args[0]); - case NOT: return not(args[0]); - case POS: return toNumber(ext, args[0]); - case NEG: return negative(ext, args[0]); - - case SHIFT_LEFT: return shiftLeft(ext, args[0], args[1]); - case SHIFT_RIGHT: return shiftRight(ext, args[0], args[1]); - case USHIFT_RIGHT: return unsignedShiftRight(ext, args[0], args[1]); - - case IN: return hasMember(ext, args[1], args[0], false); - case INSTANCEOF: { - var proto = getMember(ext, args[1], "prototype"); - return isInstanceOf(ext, args[0], proto); - } - - default: return null; - } - } - - public static Object getMember(Environment ctx, Object obj, Object key) { - obj = normalize(ctx, obj); key = normalize(ctx, key); - if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); - if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj); - - if (obj instanceof String && key instanceof Number) { - var i = number(key); - var s = (String)obj; - if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) { - return s.charAt((int)i) + ""; - } - } - - var proto = getPrototype(ctx, obj); - - if (proto == null) return "__proto__".equals(key) ? NULL : null; - else if (key != null && "__proto__".equals(key)) return proto; - else return proto.getMember(ctx, key, obj); - } - public static Object getMemberPath(Environment ctx, Object obj, Object ...path) { - var res = obj; - for (var key : path) res = getMember(ctx, res, key); - return res; - } - public static boolean setMember(Environment ctx, Object obj, Object key, Object val) { - obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); - if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); - if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); - if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false); - - var proto = getPrototype(ctx, obj); - return proto.setMember(ctx, key, val, obj, true); - } - public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) { - if (obj == null || obj == NULL) return false; - obj = normalize(ctx, obj); key = normalize(ctx, key); - - if ("__proto__".equals(key)) return true; - if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own); - - if (obj instanceof String && key instanceof Number) { - var i = number(key); - var s = (String)obj; - if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true; - } - - if (own) return false; - - var proto = getPrototype(ctx, obj); - return proto != null && proto.hasMember(ctx, key, own); - } - public static boolean deleteMember(Environment ext, Object obj, Object key) { - if (obj == null || obj == NULL) return false; - obj = normalize(ext, obj); key = normalize(ext, key); - - if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key); - else return false; - } - public static ObjectValue getPrototype(Environment ext, Object obj) { - if (obj == null || obj == NULL) return null; - obj = normalize(ext, obj); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext); - if (ext == null) return null; - - if (obj instanceof String) return ext.get(Environment.STRING_PROTO); - else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO); - else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO); - else if (obj instanceof SymbolValue) return ext.get(Environment.SYMBOL_PROTO); - - return null; - } - public static boolean setPrototype(Environment ext, Object obj, Object proto) { - obj = normalize(ext, obj); - return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto); - } - public static void makePrototypeChain(Environment ext, Object... chain) { - for(var i = 1; i < chain.length; i++) { - setPrototype(ext, chain[i], chain[i - 1]); - } - } - public static List getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) { - List res = new ArrayList<>(); - - if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable); - if (obj instanceof String) { - for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i); - } - - if (!own) { - var proto = getPrototype(ext, obj); - - while (proto != null) { - res.addAll(proto.keys(includeNonEnumerable)); - proto = getPrototype(ext, proto); - } - } - - - return res; - } - public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) { - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key); - else if (obj instanceof String && key instanceof Number) { - var i = ((Number)key).intValue(); - var _i = ((Number)key).doubleValue(); - if (i - _i != 0) return null; - if (i < 0 || i >= ((String)obj).length()) return null; - - return new ObjectValue(ext, Map.of( - "value", ((String)obj).charAt(i) + "", - "writable", false, - "enumerable", true, - "configurable", false - )); - } - else return null; - } - - public static Object call(Environment ext, Object func, Object thisArg, Object ...args) { - if (!(func instanceof FunctionValue)) throw EngineException.ofType("Tried to call a non-function value."); - return ((FunctionValue)func).call(ext, thisArg, args); - } - public static Object callNew(Environment ext, Object func, Object ...args) { - var res = new ObjectValue(); - try { - var proto = Values.getMember(ext, func, "prototype"); - setPrototype(ext, res, proto); - - var ret = call(ext, func, res, args); - - if (!isPrimitive(ret)) return ret; - return res; - } - catch (IllegalArgumentException e) { - throw EngineException.ofType("Tried to call new on an invalid constructor."); - } - } - - public static boolean strictEquals(Environment ext, Object a, Object b) { - a = normalize(ext, a); - b = normalize(ext, b); - - if (a == null || b == null) return a == null && b == null; - if (isNan(a) || isNan(b)) return false; - if (a instanceof Number && number(a) == -0.) a = 0.; - if (b instanceof Number && number(b) == -0.) b = 0.; - - return a == b || a.equals(b); - } - public static boolean looseEqual(Environment ext, Object a, Object b) { - a = normalize(ext, a); b = normalize(ext, b); - - // In loose equality, null is equivalent to undefined - if (a == NULL) a = null; - if (b == NULL) b = null; - - if (a == null || b == null) return a == null && b == null; - // If both are objects, just compare their references - if (!isPrimitive(a) && !isPrimitive(b)) return a == b; - - // Convert values to primitives - a = toPrimitive(ext, a, ConvertHint.VALUEOF); - b = toPrimitive(ext, b, ConvertHint.VALUEOF); - - // Compare symbols by reference - if (a instanceof SymbolValue || b instanceof SymbolValue) return a == b; - if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); - if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); - - // Default to strings - return toString(ext, a).equals(toString(ext, b)); - } - - public static Object normalize(Environment ext, Object val) { - if (val instanceof Number) return number(val); - if (isPrimitive(val) || val instanceof ObjectValue) return val; - if (val instanceof Character) return val + ""; - - if (val instanceof Map) { - var res = new ObjectValue(); - - for (var entry : ((Map)val).entrySet()) { - res.defineProperty(ext, entry.getKey(), entry.getValue()); - } - - return res; - } - - if (val instanceof Iterable) { - var res = new ArrayValue(); - - for (var entry : ((Iterable)val)) { - res.set(ext, res.size(), entry); - } - - return res; - } - - if (val instanceof Class) { - if (ext == null) return null; - else return NativeWrapperProvider.get(ext).getConstr((Class)val); - } - - return NativeWrapper.of(ext, val); - } - - @SuppressWarnings("unchecked") - public static T convert(Environment ext, Object obj, Class clazz) { - if (clazz == Void.class) return null; - - if (obj instanceof NativeWrapper) { - var res = ((NativeWrapper)obj).wrapped; - if (clazz.isInstance(res)) return (T)res; - } - - if (clazz == null || clazz == Object.class) return (T)obj; - - if (obj instanceof ArrayValue) { - if (clazz.isAssignableFrom(ArrayList.class)) { - var raw = ((ArrayValue)obj).toArray(); - var res = new ArrayList<>(); - for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - return (T)new ArrayList<>(res); - } - if (clazz.isAssignableFrom(HashSet.class)) { - var raw = ((ArrayValue)obj).toArray(); - var res = new HashSet<>(); - for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - return (T)new HashSet<>(res); - } - if (clazz.isArray()) { - var raw = ((ArrayValue)obj).toArray(); - Object res = Array.newInstance(clazz.getComponentType(), raw.length); - for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); - return (T)res; - } - } - - if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { - var res = new HashMap<>(); - for (var el : ((ObjectValue)obj).values.entrySet()) res.put( - convert(ext, el.getKey(), null), - convert(ext, el.getValue(), null) - ); - return (T)res; - } - - if (clazz == String.class) return (T)toString(ext, obj); - if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj); - if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, obj); - if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, obj); - if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, obj); - if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, obj); - if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, obj); - if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, obj); - - if (clazz == Character.class || clazz == char.class) { - if (obj instanceof Number) return (T)(Character)(char)number(obj); - else { - var res = toString(ext, obj); - if (res.length() == 0) throw new ConvertException("\"\"", "Character"); - else return (T)(Character)res.charAt(0); - } - } - - if (obj == null) return null; - if (clazz.isInstance(obj)) return (T)obj; - if (clazz.isAssignableFrom(NativeWrapper.class)) { - return (T)NativeWrapper.of(ext, obj); - } - - throw new ConvertException(type(obj), clazz.getSimpleName()); - } - - public static Iterable fromJSIterator(Environment ext, Object obj) { - return () -> { - try { - var symbol = SymbolValue.get("Symbol.iterator"); - - var iteratorFunc = getMember(ext, obj, symbol); - if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); - var iterator = iteratorFunc instanceof FunctionValue ? - ((FunctionValue)iteratorFunc).call(ext, obj, obj) : - iteratorFunc; - var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next"); - - if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator(); - - return new Iterator() { - private Object value = null; - public boolean consumed = true; - private FunctionValue next = (FunctionValue)nextFunc; - - private void loadNext() { - if (next == null) value = null; - else if (consumed) { - var curr = next.call(ext, iterator); - if (curr == null) { next = null; value = null; } - if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; } - else { - this.value = Values.getMember(ext, curr, "value"); - consumed = false; - } - } - } - - @Override - public boolean hasNext() { - loadNext(); - return next != null; - } - @Override - public Object next() { - loadNext(); - var res = value; - value = null; - consumed = true; - return res; - } - }; - } - catch (IllegalArgumentException | NullPointerException e) { - return Collections.emptyIterator(); - } - }; - } - - public static ObjectValue toJSIterator(Environment ext, Iterator it) { - var res = new ObjectValue(); - - try { - var key = getMember(ext, getMember(ext, ext.get(Environment.SYMBOL_PROTO), "constructor"), "iterator"); - res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); - } - catch (IllegalArgumentException | NullPointerException e) { } - - res.defineProperty(ext, "next", new NativeFunction("", args -> { - if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); - else { - var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; - } - })); - - return res; - } - - public static ObjectValue toJSIterator(Environment ext, Iterable it) { - return toJSIterator(ext, it.iterator()); - } - - public static ObjectValue toJSAsyncIterator(Environment ext, Iterator it) { - var res = new ObjectValue(); - - try { - var key = getMemberPath(ext, ext.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator"); - res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); - } - catch (IllegalArgumentException | NullPointerException e) { } - - res.defineProperty(ext, "next", new NativeFunction("", args -> { - return PromiseLib.await(args.env, () -> { - if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); - else { - var obj = new ObjectValue(); - object.defineProperty(args.env, "value", it.next()); - return object; - } - }); - })); - - return res; - } - - private static boolean isEmptyFunc(ObjectValue val) { - if (!(val instanceof FunctionValue)) return false; - if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false; - var proto = val.values.get("prototype"); - if (!(proto instanceof ObjectValue)) return false; - var protoObj = (ObjectValue)proto; - if (protoObj.values.get("constructor") != val) return false; - if (protoObj.values.size() + protoObj.properties.size() != 1) return false; - return true; - } - private static String toReadable(Environment ext, Object val, HashSet passed, int tab) { - if (tab == 0 && val instanceof String) return (String)val; - - if (passed.contains(val)) return "[circular]"; - - var printed = true; - var res = new StringBuilder(); - var dbg = DebugContext.get(ext); - - if (val instanceof FunctionValue) { - res.append(val.toString()); - var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null; - - if (loc != null) res.append(" @ " + loc); - } - else if (val instanceof ArrayValue) { - res.append("["); - var obj = ((ArrayValue)val); - for (int i = 0; i < obj.size(); i++) { - if (i != 0) res.append(", "); - else res.append(" "); - if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab)); - else res.append(""); - } - res.append(" ] "); - } - else if (val instanceof NativeWrapper) { - var obj = ((NativeWrapper)val).wrapped; - res.append("Native " + obj.toString() + " "); - } - else printed = false; - - if (val instanceof ObjectValue) { - if (tab > 3) { - return "{...}"; - } - - passed.add(val); - - var obj = (ObjectValue)val; - if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) { - if (!printed) res.append("{}\n"); - } - else { - res.append("{\n"); - - for (var el : obj.values.entrySet()) { - for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ext, el.getKey(), passed, tab + 1)); - res.append(": "); - res.append(toReadable(ext, el.getValue(), passed, tab + 1)); - res.append(",\n"); - } - for (var el : obj.properties.entrySet()) { - for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ext, el.getKey(), passed, tab + 1)); - res.append(": [prop],\n"); - } - - for (int i = 0; i < tab; i++) res.append(" "); - res.append("}"); - } - - passed.remove(val); - } - else if (val == null) return "undefined"; - else if (val == Values.NULL) return "null"; - else if (val instanceof String) return "'" + val + "'"; - else return Values.toString(ext, val); - - return res.toString(); - } - - public static String toReadable(Environment ext, Object val) { - return toReadable(ext, val, new HashSet<>(), 0); - } - public static String errorToReadable(RuntimeException err, String prefix) { - prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix; - if (err instanceof EngineException) { - var ee = ((EngineException)err); - try { - return prefix + " " + ee.toString(ee.env); - } - catch (EngineException ex) { - return prefix + " " + toReadable(ee.env, ee.value); - } - } - else if (err instanceof SyntaxException) { - return prefix + " SyntaxError " + ((SyntaxException)err).msg; - } - else if (err.getCause() instanceof InterruptedException) return ""; - else { - var str = new ByteArrayOutputStream(); - err.printStackTrace(new PrintStream(str)); - - return prefix + " internal error " + str.toString(); - } - } - public static void printValue(Environment ext, Object val) { - System.out.print(toReadable(ext, val)); - } - public static void printError(RuntimeException err, String prefix) { - System.out.println(errorToReadable(err, prefix)); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java index 7acb9e1..d6f9604 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java @@ -9,6 +9,7 @@ public class Arguments { public final Value self; public final Value[] args; public final Environment env; + public final boolean isNew; public int n() { return args.length; @@ -31,9 +32,10 @@ public class Arguments { else return get(i); } - public Arguments(Environment env, Value thisArg, Value... args) { + public Arguments(Environment env, boolean isNew, Value thisArg, Value... args) { this.env = env; this.args = args; this.self = thisArg; + this.isNew = isNew; } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index daf35f9..6c70cb3 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -11,8 +11,8 @@ public class CodeFunction extends FunctionValue { public final ValueVariable[] captures; public Environment env; - @Override public Value call(Environment env, Value thisArg, Value ...args) { - var frame = new Frame(env, thisArg, args, this); + @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { + var frame = new Frame(env, isNew, thisArg, args, this); frame.onPush(); try { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index 83f0770..8e4c971 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; @@ -13,8 +14,12 @@ public abstract class FunctionValue extends ObjectValue { public int length; public Value prototype = new ObjectValue(); - private final FieldMember nameField = new FieldMember(false, true, true) { + public boolean enableCall = true; + public boolean enableNew = true; + + private final FieldMember nameField = new FieldMember(true, false, false) { @Override public Value get(Environment env, Value self) { + if (name == null) return new StringValue(""); return new StringValue(name); } @Override public boolean set(Environment env, Value val, Value self) { @@ -22,7 +27,7 @@ public abstract class FunctionValue extends ObjectValue { return true; } }; - private final FieldMember lengthField = new FieldMember(false, true, false) { + private final FieldMember lengthField = new FieldMember(true, false, false) { @Override public Value get(Environment env, Value self) { return new NumberValue(length); } @@ -30,7 +35,7 @@ public abstract class FunctionValue extends ObjectValue { return false; } }; - private final FieldMember prototypeField = new FieldMember(false, true, true) { + private final FieldMember prototypeField = new FieldMember(false, false, true) { @Override public Value get(Environment env, Value self) { return prototype; } @@ -40,28 +45,40 @@ public abstract class FunctionValue extends ObjectValue { } }; + protected abstract Value onCall(Environment ext, boolean isNew, String name, Value thisArg, Value ...args); + @Override public String toString() { return String.format("function %s(...)", name); } - @Override public abstract Value call(Environment ext, Value thisArg, Value ...args); + @Override public Value call(Environment ext, boolean isNew, String name, Value thisArg, Value ...args) { + if (isNew && !enableNew) super.call(ext, isNew, name, thisArg, args); + if (!isNew && !enableCall) super.call(ext, isNew, name, thisArg, args); - @Override public Member getOwnMember(Environment env, Value key) { - var el = key.toString(env).value; - - if (el.equals("length")) return lengthField; - if (el.equals("name")) return nameField; - if (el.equals("prototype")) return prototypeField; - - return super.getOwnMember(env, key); + return onCall(ext, isNew, name, thisArg, args); } - @Override public boolean deleteOwnMember(Environment env, Value key) { - if (!super.deleteOwnMember(env, key)) return false; - var el = key.toString(env).value; + @Override public Member getOwnMember(Environment env, KeyCache key) { + switch (key.toString(env)) { + case "length": return lengthField; + case "name": return nameField; + case "prototype": return prototypeField; + default: return super.getOwnMember(env, key); + } + } + @Override public boolean deleteOwnMember(Environment env, KeyCache key) { + switch (key.toString(env)) { + case "length": + length = 0; + return true; + case "name": + name = ""; + return true; + case "prototype": + return false; + default: return super.deleteOwnMember(env, key); + } + } - if (el.equals("length")) return false; - if (el.equals("name")) return false; - if (el.equals("prototype")) return false; - - return true; + public void setName(String val) { + if (this.name == null || this.name.equals("")) this.name = val; } public FunctionValue(String name, int length) { @@ -71,7 +88,7 @@ public abstract class FunctionValue extends ObjectValue { this.length = length; this.name = name; - prototype.defineOwnMember(Environment.empty(), new StringValue("constructor"), FieldMember.of(this)); + prototype.defineOwnMember(null, "constructor", FieldMember.of(this)); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index 895552d..d671efa 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -10,8 +10,8 @@ public class NativeFunction extends FunctionValue { public final NativeFunctionRunner action; - @Override public Value call(Environment env, Value self, Value ...args) { - return action.run(new Arguments(env, self, args)); + @Override public Value onCall(Environment env, boolean isNew, String name, Value self, Value ...args) { + return action.run(new Arguments(env, isNew, self, args)); } public NativeFunction(String name, NativeFunctionRunner action) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java index 542837e..f754cbc 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java @@ -8,6 +8,7 @@ import java.util.LinkedHashMap; import java.util.Map; import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Value; @@ -19,6 +20,16 @@ public class ArrayValue extends ObjectValue implements Iterable { private Value[] values; private int size; + private final FieldMember lengthField = new FieldMember(false, false, true) { + @Override public Value get(Environment env, Value self) { + return new NumberValue(size); + } + @Override public boolean set(Environment env, Value val, Value self) { + size = val.toInt(env); + return true; + } + }; + private class IndexField extends FieldMember { private int i; private ArrayValue arr; @@ -44,7 +55,7 @@ public class ArrayValue extends ObjectValue implements Iterable { var arr = new Value[index]; System.arraycopy(values, 0, arr, 0, values.length); - return arr; + return values = arr; } public int size() { return size; } @@ -95,11 +106,11 @@ public class ArrayValue extends ObjectValue implements Iterable { } public void copyTo(Value[] arr, int sourceStart, int destStart, int count) { - var nullFill = values.length - destStart + count; + var nullFill = Math.max(0, arr.length - size - destStart); count -= nullFill; System.arraycopy(values, sourceStart, arr, destStart, count); - Arrays.fill(arr, count, nullFill, null); + Arrays.fill(arr, count, nullFill + count, null); } public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) { if (arr == this) { @@ -138,36 +149,37 @@ public class ArrayValue extends ObjectValue implements Iterable { }); } - @Override public Member getOwnMember(Environment env, Value key) { + @Override public Member getOwnMember(Environment env, KeyCache key) { var res = super.getOwnMember(env, key); if (res != null) return res; var num = key.toNumber(env); - var i = num.toInt(env); + var i = key.toInt(env); - if (i == num.value && i >= 0 && i < size) return new IndexField(i, this); + if (i == num && i >= 0 && i < size && has(i)) return new IndexField(i, this); + else if (key.toString(env).equals("length")) return lengthField; else return null; } - @Override public boolean defineOwnMember(Environment env, Value key, Member member) { + @Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) { if (!(member instanceof FieldMember) || hasMember(env, key, true)) return super.defineOwnMember(env, key, member); if (!extensible) return false; var num = key.toNumber(env); - var i = num.toInt(env); + var i = key.toInt(env); - if (i == num.value && i >= 0) { + if (i == num && i >= 0) { set(i, ((FieldMember)member).get(env, this)); return true; } else return super.defineOwnMember(env, key, member); } - @Override public boolean deleteOwnMember(Environment env, Value key) { + @Override public boolean deleteOwnMember(Environment env, KeyCache key) { if (!super.deleteOwnMember(env, key)) return false; var num = key.toNumber(env); - var i = num.toInt(env); + var i = key.toInt(env); - if (i == num.value && i >= 0 && i < size) return super.deleteOwnMember(env, key); + if (i == num && i >= 0 && i < size) return super.deleteOwnMember(env, key); else return true; } @@ -175,9 +187,12 @@ public class ArrayValue extends ObjectValue implements Iterable { var res = new LinkedHashMap(); for (var i = 0; i < size; i++) { - res.put(i + "", getOwnMember(env, new NumberValue(i))); + var member = getOwnMember(env, i); + if (member != null) res.put(i + "", member); } + res.put("length", lengthField); + res.putAll(super.getOwnMembers(env)); return res; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java index efc2000..0fb7528 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -7,10 +7,10 @@ import java.util.Map; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; -import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; @@ -66,7 +66,7 @@ public class ObjectValue extends Value { throw EngineException.ofType("Value couldn't be converted to a primitive."); } @Override public StringValue toString(Environment env) { return toPrimitive(env).toString(env); } - @Override public BoolValue toBoolean() { return BoolValue.TRUE; } + @Override public boolean toBoolean() { return true; } @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } @Override public StringValue type() { return typeString; } @@ -76,33 +76,29 @@ public class ObjectValue extends Value { extensible = false; } - @Override public Member getOwnMember(Environment env, Value key) { - if (key instanceof SymbolValue) return symbolMembers.get(key); - else return members.get(key.toString(env).value); + @Override public Member getOwnMember(Environment env, KeyCache key) { + if (key.isSymbol()) return symbolMembers.get(key.toSymbol()); + else return members.get(key.toString(env)); } - @Override public boolean defineOwnMember(Environment env, Value key, Member member) { - if (!(key instanceof SymbolValue)) key = key.toString(env); - + @Override public boolean defineOwnMember(Environment env, KeyCache key, Member member) { var old = getOwnMember(env, key); if (old != null && old.configure(env, member, this)) return true; if (old != null && !old.configurable()) return false; - if (key instanceof SymbolValue) symbolMembers.put((SymbolValue)key, member); - else members.put(key.toString(env).value, member); + if (key.isSymbol()) symbolMembers.put(key.toSymbol(), member); + else members.put(key.toString(env), member); return true; } - @Override public boolean deleteOwnMember(Environment env, Value key) { + @Override public boolean deleteOwnMember(Environment env, KeyCache key) { if (!extensible) return false; - if (!(key instanceof SymbolValue)) key = key.toString(env); - var member = getOwnMember(env, key); if (member == null) return true; if (member.configurable()) return false; - if (key instanceof SymbolValue) symbolMembers.remove(key); - else members.remove(key.toString(env).value); + if (key.isSymbol()) symbolMembers.remove(key.toSymbol()); + else members.remove(key.toString(env)); return true; } @@ -114,7 +110,7 @@ public class ObjectValue extends Value { } @Override public ObjectValue getPrototype(Environment env) { - if (prototype == null) return null; + if (prototype == null || env == null) return null; else return prototype.get(env); } @Override public final boolean setPrototype(Environment env, ObjectValue val) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java index bfd3a91..38ba524 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java @@ -4,7 +4,6 @@ import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; -import me.topchetoeu.jscript.runtime.values.primitives.StringValue; public class ScopeValue extends ObjectValue { private class VariableField extends FieldMember { @@ -29,7 +28,7 @@ public class ScopeValue extends ObjectValue { public ScopeValue(ValueVariable[] variables, String[] names) { this.variables = variables; for (var i = 0; i < names.length && i < variables.length; i++) { - defineOwnMember(Environment.empty(), new StringValue(i + ""), new VariableField(i)); + defineOwnMember(Environment.empty(), i, new VariableField(i)); } } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java index 5e9d0c0..f4d32a6 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -13,7 +13,7 @@ public final class BoolValue extends PrimitiveValue { @Override public StringValue type() { return typeString; } - @Override public BoolValue toBoolean() { return this; } + @Override public boolean toBoolean() { return value; } @Override public NumberValue toNumber(Environment ext) { return value ? new NumberValue(1) : new NumberValue(0); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java index 608516a..abcd8e1 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java @@ -1,7 +1,9 @@ package me.topchetoeu.jscript.runtime.values.primitives; -import java.math.BigDecimal; - +import me.topchetoeu.jscript.common.json.JSON; +import me.topchetoeu.jscript.common.json.JSONElement; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -14,21 +16,19 @@ public final class NumberValue extends PrimitiveValue { @Override public StringValue type() { return typeString; } - @Override public BoolValue toBoolean() { return BoolValue.of(value != 0); } + @Override public boolean toBoolean() { return value != 0; } @Override public NumberValue toNumber(Environment ext) { return this; } @Override public StringValue toString(Environment ext) { return new StringValue(toString()); } - @Override public String toString() { - var d = value; - if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; - if (d == Double.POSITIVE_INFINITY) return "Infinity"; - if (Double.isNaN(d)) return "NaN"; - return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); - } + @Override public String toString() { return JSON.stringify(JSONElement.number(value)); } @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.NUMBER_PROTO); } + @Override public CompareResult compare(Environment env, Value other) { + if (other instanceof NumberValue) return CompareResult.from(Double.compare(value, ((NumberValue)other).value)); + else return super.compare(env, other); + } @Override public boolean strictEquals(Environment ext, Value other) { other = other.toPrimitive(ext); if (other instanceof NumberValue) return value == ((NumberValue)other).value; @@ -39,42 +39,22 @@ public final class NumberValue extends PrimitiveValue { this.value = value; } - public static double parseFloat(String val, boolean tolerant, String alphabet) { - val = val.trim(); + public static NumberValue parseInt(String str, int radix, boolean relaxed) { + if (radix < 2 || radix > 36) return new NumberValue(Double.NaN); - int res = 0; - - for (int i = 0; i >= val.length(); i++) { - var c = alphabet.indexOf(val.charAt(i)); - - if (c < 0) { - if (tolerant) return res; - else return Double.NaN; - } - - res *= alphabet.length(); - res += c; + str = str.trim(); + var res = Parsing.parseInt(new Source(null, str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true); + if (res.isSuccess()) { + if (relaxed || res.n == str.length()) return new NumberValue(res.result); } - - return res; + return new NumberValue(Double.NaN); } - public static double parseInt(String val, boolean tolerant, String alphabet) { - val = val.trim(); - - int res = 0; - - for (int i = 0; i >= val.length(); i++) { - var c = alphabet.indexOf(val.charAt(i)); - - if (c < 0) { - if (tolerant) return res; - else return Double.NaN; - } - - res *= alphabet.length(); - res += c; + public static NumberValue parseFloat(String str, boolean relaxed) { + str = str.trim(); + var res = Parsing.parseFloat(new Source(null, str), 0, true); + if (res.isSuccess()) { + if (relaxed || res.n == str.length()) return new NumberValue(res.result); } - - return res; + return new NumberValue(Double.NaN); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java index 0a1085a..ccbe0ae 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java @@ -3,19 +3,20 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.Map; import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public abstract class PrimitiveValue extends Value { - @Override public final boolean defineOwnMember(Environment env, Value key, Member member) { return false; } - @Override public final boolean deleteOwnMember(Environment env, Value key) { return false; } + @Override public final boolean defineOwnMember(Environment env, KeyCache key, Member member) { return false; } + @Override public final boolean deleteOwnMember(Environment env, KeyCache key) { return false; } @Override public final boolean isPrimitive() { return true; } @Override public final Value toPrimitive(Environment env) { return this; } @Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } - @Override public Member getOwnMember(Environment env, Value key) { return null; } + @Override public Member getOwnMember(Environment env, KeyCache key) { return null; } @Override public Map getOwnMembers(Environment env) { return Map.of(); } @Override public Map getOwnSymbolMembers(Environment env) { return Map.of(); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index 9ba16fe..315738e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -1,8 +1,12 @@ package me.topchetoeu.jscript.runtime.values.primitives; +import java.util.Map; import java.util.Objects; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -12,10 +16,14 @@ public final class StringValue extends PrimitiveValue { @Override public StringValue type() { return typeString; } - @Override public BoolValue toBoolean() { return BoolValue.of(!value.equals("")); } + @Override public boolean toBoolean() { return !value.equals(""); } @Override public NumberValue toNumber(Environment ext) { - try { return new NumberValue(Double.parseDouble(value)); } - catch (NumberFormatException e) { return new NumberValue(Double.NaN); } + var val = value.trim(); + if (val.equals("")) return new NumberValue(0); + var res = Parsing.parseNumber(new Source(null, val), 0, true); + + if (res.isSuccess() && res.n == val.length()) return new NumberValue(res.result); + else return new NumberValue(Double.NaN); } @Override public StringValue toString(Environment ext) { return this; } @@ -23,11 +31,20 @@ public final class StringValue extends PrimitiveValue { return new StringValue(value + other.toString(ext).value); } + @Override public CompareResult compare(Environment env, Value other) { + if (other instanceof StringValue) return CompareResult.from(value.compareTo(((StringValue)other).value)); + else return super.compare(env, other); + } @Override public boolean strictEquals(Environment ext, Value other) { return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value); } @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); } + @Override public Map getOwnMembers(Environment env) { + // TODO Auto-generated method stub + return super.getOwnMembers(env); + } + public StringValue(String value) { this.value = value; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java index 0f87e15..cc9b954 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -13,14 +13,18 @@ public final class SymbolValue extends PrimitiveValue { public final String value; + public Value key() { + return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : VoidValue.UNDEFINED; + } + @Override public StringValue type() { return typeString; } - @Override public BoolValue toBoolean() { return BoolValue.TRUE; } + @Override public boolean toBoolean() { return false; } @Override public StringValue toString(Environment env) { - return new StringValue(toString()); + throw EngineException.ofType("Cannot convert a Symbol value to a string"); } @Override public NumberValue toNumber(Environment env) { - throw EngineException.ofType("Can't convert symbol to number"); + throw EngineException.ofType("Cannot convert a Symbol value to a number"); } @Override public boolean strictEquals(Environment ext, Value other) { @@ -29,8 +33,8 @@ public final class SymbolValue extends PrimitiveValue { @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); } @Override public String toString() { - if (value == null) return "Symbol"; - else return "@@" + value; + if (value == null) return "Symbol()"; + else return "Symbol(" + value + ")"; } public SymbolValue(String value) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java index 7f10906..5f20d05 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -4,13 +4,14 @@ import java.util.Map; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class VoidValue extends PrimitiveValue { public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); - public static final VoidValue NULL = new VoidValue("null", new StringValue("null")); + public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); private final StringValue namestring; @@ -18,7 +19,7 @@ public final class VoidValue extends PrimitiveValue { public final StringValue typeString; @Override public StringValue type() { return typeString; } - @Override public BoolValue toBoolean() { return BoolValue.FALSE; } + @Override public boolean toBoolean() { return false; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } @Override public StringValue toString(Environment ext) { return namestring; } @@ -34,8 +35,8 @@ public final class VoidValue extends PrimitiveValue { } @Override public ObjectValue getPrototype(Environment env) { return null; } - @Override public Member getOwnMember(Environment env, Value key) { - throw EngineException.ofError(String.format("Cannot read properties of %s (reading %s)", name, key.toString(env).value)); + @Override public Member getOwnMember(Environment env, KeyCache key) { + throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env))); } @Override public Map getOwnMembers(Environment env) { throw EngineException.ofError(String.format("Cannot read properties of %s (listing all members)", name)); @@ -44,6 +45,10 @@ public final class VoidValue extends PrimitiveValue { throw EngineException.ofError(String.format("Cannot read properties of %s (listing all symbol members)", name)); } + // @Override public Value call(Environment env, Value self, Value... args) { + // throw EngineException.ofType(String.format("Tried to call a value of %s", name)); + // } + public VoidValue(String name, StringValue type) { this.name = name; this.typeString = type; -- 2.45.2 From 4048d6ef1ce48d21d6ca797cd102f855409f7f46 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:48:30 +0300 Subject: [PATCH 06/48] refactor: rename ES5 to JavaScript --- .../jscript/compilation/AssignableStatement.java | 6 +----- .../jscript/compilation/CompoundStatement.java | 4 ++-- .../compilation/{ES5.java => JavaScript.java} | 16 ++++++++-------- .../compilation/VariableDeclareStatement.java | 8 ++++---- .../compilation/control/BreakStatement.java | 6 +++--- .../compilation/control/ContinueStatement.java | 6 +++--- .../compilation/control/DebugStatement.java | 4 ++-- .../compilation/control/DeleteStatement.java | 4 ++-- .../compilation/control/DoWhileStatement.java | 8 ++++---- .../compilation/control/ForInStatement.java | 6 +++--- .../compilation/control/ForOfStatement.java | 6 +++--- .../compilation/control/ForStatement.java | 8 ++++---- .../jscript/compilation/control/IfStatement.java | 12 ++++++------ .../compilation/control/ReturnStatement.java | 8 ++++---- .../compilation/control/SwitchStatement.java | 8 ++++---- .../compilation/control/ThrowStatement.java | 8 ++++---- .../compilation/control/WhileStatement.java | 6 +++--- .../compilation/values/ArrayStatement.java | 4 ++-- .../compilation/values/FunctionStatement.java | 4 ++-- .../compilation/values/ObjectStatement.java | 6 +++--- .../compilation/values/VariableStatement.java | 6 +++--- .../values/operations/CallStatement.java | 6 +++--- .../values/operations/ChangeStatement.java | 6 +++--- .../values/operations/DiscardStatement.java | 4 ++-- .../values/operations/IndexStatement.java | 6 +++--- .../values/operations/LazyAndStatement.java | 4 ++-- .../values/operations/LazyOrStatement.java | 4 ++-- .../values/operations/OperationStatement.java | 16 ++++++++-------- .../values/operations/TypeofStatement.java | 4 ++-- 29 files changed, 95 insertions(+), 99 deletions(-) rename src/java/me/topchetoeu/jscript/compilation/{ES5.java => JavaScript.java} (96%) diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java index be19a2b..a0e2175 100644 --- a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java @@ -1,14 +1,10 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.parsing.Location; -public abstract class AssignableStatement extends Statement { +public interface AssignableStatement { public abstract Statement toAssign(Statement val, Operation operation); - protected AssignableStatement(Location loc) { - super(loc); - } // private static final Map operations = Map.ofEntries( // Map.entry("*=", Operation.MULTIPLY), // Map.entry("/=", Operation.DIVIDE), diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java index f2094c1..ca04696 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java @@ -75,7 +75,7 @@ public class CompoundStatement extends Statement { if (!src.is(i + n, ",")) return ParseRes.failed(); n++; - var res = ES5.parseExpression(src, i + n, 2); + var res = JavaScript.parseExpression(src, i + n, 2); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma"); n += res.n; @@ -102,7 +102,7 @@ public class CompoundStatement extends Statement { continue; } - var res = ES5.parseStatement(src, i + n); + var res = JavaScript.parseStatement(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement"); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/ES5.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java similarity index 96% rename from src/java/me/topchetoeu/jscript/compilation/ES5.java rename to src/java/me/topchetoeu/jscript/compilation/JavaScript.java index b68d46c..a07e92b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/ES5.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -43,7 +43,7 @@ import me.topchetoeu.jscript.compilation.values.operations.TypeofStatement; import me.topchetoeu.jscript.compilation.values.operations.VariableIndexStatement; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -public class ES5 { +public class JavaScript { static final Set reserved = Set.of( "true", "false", "void", "null", "this", "if", "else", "try", "catch", "finally", "for", "do", "while", "switch", "case", "default", "new", @@ -59,7 +59,7 @@ public class ES5 { if (!openParen.isSuccess()) return openParen.chainError(); n += openParen.n; - var res = ES5.parseExpression(src, i + n, 0); + var res = JavaScript.parseExpression(src, i + n, 0); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens"); n += res.n; @@ -74,7 +74,7 @@ public class ES5 { return ParseRes.first(src, i, (a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b), (a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false), - ES5::parseLiteral, + JavaScript::parseLiteral, StringStatement::parse, RegexStatement::parse, NumberStatement::parse, @@ -82,7 +82,7 @@ public class ES5 { ChangeStatement::parsePrefixIncrease, OperationStatement::parsePrefix, ArrayStatement::parse, - ES5::parseParens, + JavaScript::parseParens, CallStatement::parseNew, TypeofStatement::parse, DiscardStatement::parse, @@ -162,7 +162,7 @@ public class ES5 { var res = parseExpression(src, i, 0, true); if (!res.isSuccess()) return res.chainError(); - var end = ES5.parseStatementEnd(src, i + res.n); + var end = JavaScript.parseStatementEnd(src, i + res.n); if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement"); return res.addN(end.n); @@ -191,7 +191,7 @@ public class ES5 { TryStatement::parse, CompoundStatement::parse, (s, j) -> FunctionStatement.parseFunction(s, j, true), - ES5::parseExpressionStatement + JavaScript::parseExpressionStatement ); return res.addN(n); } @@ -232,7 +232,7 @@ public class ES5 { } public static boolean checkVarName(String name) { - return !ES5.reserved.contains(name); + return !JavaScript.reserved.contains(name); } public static ParseRes> parseParamList(Source src, int i) { @@ -297,6 +297,6 @@ public class ES5 { } public static CompileResult compile(Filename filename, String raw) { - return ES5.compile(ES5.parse(filename, raw)); + return JavaScript.compile(JavaScript.parse(filename, raw)); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java index ef5b7aa..603a3c8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java @@ -63,7 +63,7 @@ public class VariableDeclareStatement extends Statement { var res = new ArrayList(); - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new VariableDeclareStatement(loc, res), n); @@ -75,7 +75,7 @@ public class VariableDeclareStatement extends Statement { if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name"); n += name.n; - if (!ES5.checkVarName(name.result)) { + if (!JavaScript.checkVarName(name.result)) { return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result)); } @@ -85,7 +85,7 @@ public class VariableDeclareStatement extends Statement { if (src.is(i + n, "=")) { n++; - var valRes = ES5.parseExpression(src, i + n, 2); + var valRes = JavaScript.parseExpression(src, i + n, 2); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='"); n += valRes.n; @@ -100,7 +100,7 @@ public class VariableDeclareStatement extends Statement { continue; } - end = ES5.parseStatementEnd(src, i + n); + end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java index 7cf3cad..d7d1758 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class BreakStatement extends Statement { @@ -29,7 +29,7 @@ public class BreakStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed(); n += 5; - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new BreakStatement(loc, null), n); @@ -39,7 +39,7 @@ public class BreakStatement extends Statement { if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); n += label.n; - end = ES5.parseStatementEnd(src, i + n); + end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new BreakStatement(loc, label.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java index 8403f33..88f1551 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ContinueStatement extends Statement { @@ -29,7 +29,7 @@ public class ContinueStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed(); n += 8; - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ContinueStatement(loc, null), n); @@ -39,7 +39,7 @@ public class ContinueStatement extends Statement { if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement"); n += label.n; - end = ES5.parseStatementEnd(src, i + n); + end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ContinueStatement(loc, label.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java index b2fb08e..58d9fa9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class DebugStatement extends Statement { @@ -26,7 +26,7 @@ public class DebugStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed(); n += 8; - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new DebugStatement(loc), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java index cc09617..4731feb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.values.VariableStatement; import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; @@ -32,7 +32,7 @@ public class DeleteStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed(); n += 6; - var valRes = ES5.parseExpression(src, i + n, 15); + var valRes = JavaScript.parseExpression(src, i + n, 15); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'"); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java index bb9a42c..94cc1c5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java @@ -7,7 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class DoWhileStatement extends Statement { @@ -48,7 +48,7 @@ public class DoWhileStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed(); n += 2; - var bodyRes = ES5.parseStatement(src, i + n); + var bodyRes = JavaScript.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body."); n += bodyRes.n; @@ -59,7 +59,7 @@ public class DoWhileStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); n++; - var condRes = ES5.parseExpression(src, i + n, 0); + var condRes = JavaScript.parseExpression(src, i + n, 0); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition."); n += condRes.n; n += Parsing.skipEmpty(src, i + n); @@ -67,7 +67,7 @@ public class DoWhileStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition."); n++; - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java index 8868a52..2cb0a5d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java @@ -8,7 +8,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ForInStatement extends Statement { @@ -91,7 +91,7 @@ public class ForInStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration"); n += 2; - var obj = ES5.parseExpression(src, i + n, 0); + var obj = JavaScript.parseExpression(src, i + n, 0); if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); n += obj.n; n += Parsing.skipEmpty(src, i + n); @@ -99,7 +99,7 @@ public class ForInStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); n++; - var bodyRes = ES5.parseStatement(src, i + n); + var bodyRes = JavaScript.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java index 3072384..fe64e92 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java @@ -7,7 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ForOfStatement extends Statement { @@ -103,7 +103,7 @@ public class ForOfStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "fo")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); n += 2; - var obj = ES5.parseExpression(src, i + n, 0); + var obj = JavaScript.parseExpression(src, i + n, 0); if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); n += obj.n; n += Parsing.skipEmpty(src, i + n); @@ -111,7 +111,7 @@ public class ForOfStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); n++; - var bodyRes = ES5.parseStatement(src, i + n); + var bodyRes = JavaScript.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); n += bodyRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java index 2ab96b0..d9135ba 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java @@ -7,7 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.VariableDeclareStatement; import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement; @@ -58,7 +58,7 @@ public class ForStatement extends Statement { private static ParseRes parseCondition(Source src, int i) { var n = Parsing.skipEmpty(src, i); - var res = ES5.parseExpression(src, i + n, 0); + var res = JavaScript.parseExpression(src, i + n, 0); if (!res.isSuccess()) return res.chainError(); n += res.n; n += Parsing.skipEmpty(src, i + n); @@ -67,7 +67,7 @@ public class ForStatement extends Statement { else return ParseRes.res(res.result, n + 1); } private static ParseRes parseUpdater(Source src, int i) { - return ES5.parseExpression(src, i, 0); + return JavaScript.parseExpression(src, i, 0); } public static ParseRes parse(Source src, int i) { @@ -108,7 +108,7 @@ public class ForStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater"); n++; - var body = ES5.parseStatement(src, i + n); + var body = JavaScript.parseStatement(src, i + n); if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body."); n += body.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java index a751ddd..992b89c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java @@ -7,7 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class IfStatement extends Statement { @@ -59,7 +59,7 @@ public class IfStatement extends Statement { var loc = src.loc(i + n); n++; - var a = ES5.parseExpression(src, i + n, 2); + var a = JavaScript.parseExpression(src, i + n, 2); if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator."); n += a.n; n += Parsing.skipEmpty(src, i); @@ -67,7 +67,7 @@ public class IfStatement extends Statement { if (!src.is(i + n, ":")) return ParseRes.failed(); n++; - var b = ES5.parseExpression(src, i + n, 2); + var b = JavaScript.parseExpression(src, i + n, 2); if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator."); n += b.n; @@ -84,7 +84,7 @@ public class IfStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'."); n++; - var condRes = ES5.parseExpression(src, i + n, 0); + var condRes = JavaScript.parseExpression(src, i + n, 0); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition."); n += condRes.n; n += Parsing.skipEmpty(src, i + n); @@ -92,7 +92,7 @@ public class IfStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition."); n++; - var res = ES5.parseStatement(src, i + n); + var res = JavaScript.parseStatement(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body."); n += res.n; @@ -100,7 +100,7 @@ public class IfStatement extends Statement { if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); n += elseKw.n; - var elseRes = ES5.parseStatement(src, i + n); + var elseRes = JavaScript.parseStatement(src, i + n); if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body."); n += elseRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java index d5201d1..2769d0a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ReturnStatement extends Statement { @@ -31,17 +31,17 @@ public class ReturnStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed(); n += 6; - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ReturnStatement(loc, null), n); } - var val = ES5.parseExpression(src, i + n, 0); + var val = JavaScript.parseExpression(src, i + n, 0); if (val.isError()) return val.chainError(); n += val.n; - end = ES5.parseStatementEnd(src, i + n); + end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ReturnStatement(loc, val.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java index 2e738ce..bc11ca1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java @@ -12,7 +12,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class SwitchStatement extends Statement { @@ -90,7 +90,7 @@ public class SwitchStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed(); n += 4; - var valRes = ES5.parseExpression(src, i + n, 0); + var valRes = JavaScript.parseExpression(src, i + n, 0); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'"); n += valRes.n; @@ -122,7 +122,7 @@ public class SwitchStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'"); n++; - var valRes = ES5.parseExpression(src, i + n, 0); + var valRes = JavaScript.parseExpression(src, i + n, 0); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value"); n += valRes.n; n += Parsing.skipEmpty(src, i + n); @@ -167,7 +167,7 @@ public class SwitchStatement extends Statement { } if (caseRes.isError()) return caseRes.chainError(); - var stm = ES5.parseStatement(src, i + n); + var stm = JavaScript.parseStatement(src, i + n); if (stm.isSuccess()) { n += stm.n; statements.add(stm.result); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java index ddfce87..a122473 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ThrowStatement extends Statement { @@ -30,17 +30,17 @@ public class ThrowStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed(); n += 5; - var end = ES5.parseStatementEnd(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ThrowStatement(loc, null), n); } - var val = ES5.parseExpression(src, i + n, 0); + var val = JavaScript.parseExpression(src, i + n, 0); if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value"); n += val.n; - end = ES5.parseStatementEnd(src, i + n); + end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; return ParseRes.res(new ThrowStatement(loc, val.result), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java index a636b92..1393f6f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java @@ -8,7 +8,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class WhileStatement extends Statement { @@ -83,7 +83,7 @@ public class WhileStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); n++; - var condRes = ES5.parseExpression(src, i + n, 0); + var condRes = JavaScript.parseExpression(src, i + n, 0); if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition."); n += condRes.n; n += Parsing.skipEmpty(src, i + n); @@ -91,7 +91,7 @@ public class WhileStatement extends Statement { if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after while condition."); n++; - var res = ES5.parseStatement(src, i + n); + var res = JavaScript.parseStatement(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body."); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java index b4ef1e6..015311c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java @@ -8,7 +8,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ArrayStatement extends Statement { @@ -70,7 +70,7 @@ public class ArrayStatement extends Statement { } } - var res = ES5.parseExpression(src, i + n, 2); + var res = JavaScript.parseExpression(src, i + n, 2); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an array element."); n += res.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index 00fe37f..2dbdd7f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -9,7 +9,7 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; @@ -133,7 +133,7 @@ public class FunctionStatement extends Statement { n += nameRes.n; n += Parsing.skipEmpty(src, i + n); - var args = ES5.parseParamList(src, i + n); + var args = JavaScript.parseParamList(src, i + n); if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); n += args.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java index aeefb24..e9d954d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java @@ -11,7 +11,7 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundStatement; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class ObjectStatement extends Statement { @@ -103,7 +103,7 @@ public class ObjectStatement extends Statement { if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); n += name.n; - var params = ES5.parseParamList(src, i + n); + var params = JavaScript.parseParamList(src, i + n); if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); n += params.n; @@ -154,7 +154,7 @@ public class ObjectStatement extends Statement { if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected a colon"); n++; - var valRes = ES5.parseExpression(src, i + n, 2); + var valRes = JavaScript.parseExpression(src, i + n, 2); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in array list"); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java index 0f2f7ba..177394e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java @@ -8,11 +8,11 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.values.operations.VariableAssignStatement; -public class VariableStatement extends AssignableStatement { +public class VariableStatement extends Statement implements AssignableStatement { public final String name; @Override public boolean pure() { return false; } @@ -42,7 +42,7 @@ public class VariableStatement extends AssignableStatement { if (!literal.isSuccess()) return literal.chainError(); n += literal.n; - if (!ES5.checkVarName(literal.result)) { + if (!JavaScript.checkVarName(literal.result)) { if (literal.result.equals("await")) return ParseRes.error(src.loc(i + n), "'await' expressions are not supported."); if (literal.result.equals("const")) return ParseRes.error(src.loc(i + n), "'const' declarations are not supported."); if (literal.result.equals("let")) return ParseRes.error(src.loc(i + n), "'let' declarations are not supported."); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java index e9a08b3..ad022e1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java @@ -11,7 +11,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.values.ArrayStatement; import me.topchetoeu.jscript.compilation.values.ObjectStatement; @@ -136,7 +136,7 @@ public class CallStatement extends Statement { boolean prevArg = false; while (true) { - var argRes = ES5.parseExpression(src, i + n, 2); + var argRes = JavaScript.parseExpression(src, i + n, 2); n += argRes.n; n += Parsing.skipEmpty(src, i + n); @@ -166,7 +166,7 @@ public class CallStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "new")) return ParseRes.failed(); n += 3; - var valRes = ES5.parseExpression(src, i + n, 18); + var valRes = JavaScript.parseExpression(src, i + n, 18); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword."); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java index 66a3eca..33f1033 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java @@ -8,7 +8,7 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; @@ -40,7 +40,7 @@ public class ChangeStatement extends Statement { if (!src.is(i + n, "++")) return ParseRes.failed(); n += 2; - var res = ES5.parseExpression(src, i + n, 15); + var res = JavaScript.parseExpression(src, i + n, 15); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); @@ -53,7 +53,7 @@ public class ChangeStatement extends Statement { if (!src.is(i + n, "--")) return ParseRes.failed(); n += 2; - var res = ES5.parseExpression(src, i + n, 15); + var res = JavaScript.parseExpression(src, i + n, 15); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java index ddcccda..513c29c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class DiscardStatement extends Statement { @@ -31,7 +31,7 @@ public class DiscardStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "void")) return ParseRes.failed(); n += 4; - var valRes = ES5.parseExpression(src, i + n, 14); + var valRes = JavaScript.parseExpression(src, i + n, 14); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword."); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java index a0f8598..aae2d10 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java @@ -9,11 +9,11 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.values.constants.StringStatement; -public class IndexStatement extends AssignableStatement { +public class IndexStatement extends Statement implements AssignableStatement { public final Statement object; public final Statement index; @@ -49,7 +49,7 @@ public class IndexStatement extends AssignableStatement { if (!src.is(i + n, "[")) return ParseRes.failed(); n++; - var valRes = ES5.parseExpression(src, i + n, 0); + var valRes = JavaScript.parseExpression(src, i + n, 0); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value in index expression"); n += valRes.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java index 8dabe29..3da73bd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class LazyAndStatement extends Statement { @@ -39,7 +39,7 @@ public class LazyAndStatement extends Statement { var loc = src.loc(i + n); n += 2; - var res = ES5.parseExpression(src, i + n, 4); + var res = JavaScript.parseExpression(src, i + n, 4); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator."); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java index aff7f6f..0ce04b5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class LazyOrStatement extends Statement { @@ -39,7 +39,7 @@ public class LazyOrStatement extends Statement { var loc = src.loc(i + n); n += 2; - var res = ES5.parseExpression(src, i + n, 4); + var res = JavaScript.parseExpression(src, i + n, 4); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator."); n += res.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java index 55b4e6a..a45afa5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java @@ -13,7 +13,7 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; public class OperationStatement extends Statement { @@ -33,7 +33,7 @@ public class OperationStatement extends Statement { @Override public ParseRes construct(Source src, int i, Statement prev) { var loc = src.loc(i); - var other = ES5.parseExpression(src, i, precedence + 1); + var other = JavaScript.parseExpression(src, i, precedence + 1); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token)); return ParseRes.res(new OperationStatement(loc, operation, prev, (Statement)other.result), other.n); } @@ -56,7 +56,7 @@ public class OperationStatement extends Statement { if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token)); - var other = ES5.parseExpression(src, i, precedence); + var other = JavaScript.parseExpression(src, i, precedence); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token)); return ParseRes.res(((AssignableStatement)prev).toAssign(other.result, operation), other.n); } @@ -73,7 +73,7 @@ public class OperationStatement extends Statement { @Override public ParseRes construct(Source src, int i, Statement prev) { var loc = src.loc(i); - var other = ES5.parseExpression(src, i, 5); + var other = JavaScript.parseExpression(src, i, 5); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '&&'"); return ParseRes.res(new LazyAndStatement(loc, prev, (Statement)other.result), other.n); } @@ -84,7 +84,7 @@ public class OperationStatement extends Statement { @Override public ParseRes construct(Source src, int i, Statement prev) { var loc = src.loc(i); - var other = ES5.parseExpression(src, i, 6); + var other = JavaScript.parseExpression(src, i, 6); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '||'"); return ParseRes.res(new LazyOrStatement(loc, prev, (Statement)other.result), other.n); } @@ -171,7 +171,7 @@ public class OperationStatement extends Statement { n++; - var res = ES5.parseExpression(src, i + n, 14); + var res = JavaScript.parseExpression(src, i + n, 14); if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op)); @@ -186,7 +186,7 @@ public class OperationStatement extends Statement { if (!kw.isSuccess()) return kw.chainError(); n += kw.n; - var valRes = ES5.parseExpression(src, i + n, 10); + var valRes = JavaScript.parseExpression(src, i + n, 10); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'instanceof'."); n += valRes.n; @@ -202,7 +202,7 @@ public class OperationStatement extends Statement { if (!kw.isSuccess()) return kw.chainError(); n += kw.n; - var valRes = ES5.parseExpression(src, i + n, 10); + var valRes = JavaScript.parseExpression(src, i + n, 10); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'in'."); n += valRes.n; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java index 8aaa578..c073b57 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.values.VariableStatement; @@ -42,7 +42,7 @@ public class TypeofStatement extends Statement { if (!Parsing.isIdentifier(src, i + n, "typeof")) return ParseRes.failed(); n += 6; - var valRes = ES5.parseExpression(src, i + n, 15); + var valRes = JavaScript.parseExpression(src, i + n, 15); if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword."); n += valRes.n; -- 2.45.2 From 62aba62a4190643d13b126fea98dfcf6d576a14f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:48:48 +0300 Subject: [PATCH 07/48] refactor: make Environment more reusable --- src/assets/lib/index.js | 319 ++++++++++++++++++ .../topchetoeu/jscript/common/Compiler.java | 10 +- .../common/environment/Environment.java | 195 +++++++++++ .../jscript/common/environment/Key.java | 7 + .../jscript/common/environment/MultiKey.java | 7 + .../topchetoeu/jscript/runtime/EventLoop.java | 6 +- .../me/topchetoeu/jscript/runtime/Frame.java | 11 +- .../jscript/runtime/InstructionRunner.java | 19 +- .../jscript/runtime/JSONConverter.java | 8 +- .../jscript/runtime/SimpleRepl.java | 260 +++++++++++++- .../jscript/runtime/debug/DebugContext.java | 8 +- .../jscript/runtime/debug/DebugHandler.java | 2 +- .../runtime/environment/Environment.java | 144 -------- .../jscript/runtime/environment/Key.java | 5 - .../runtime/exceptions/EngineException.java | 14 +- .../jscript/runtime/scope/GlobalScope.java | 9 +- .../jscript/runtime/scope/ValueVariable.java | 2 +- .../jscript/runtime/scope/Variable.java | 2 +- .../jscript/runtime/values/KeyCache.java | 2 +- .../jscript/runtime/values/Member.java | 9 +- .../jscript/runtime/values/Value.java | 99 ++---- .../runtime/values/functions/Arguments.java | 5 +- .../values/functions/CodeFunction.java | 2 +- .../values/functions/FunctionValue.java | 4 +- .../values/functions/NativeFunction.java | 2 +- .../runtime/values/objects/ArrayValue.java | 6 +- .../runtime/values/objects/ObjectValue.java | 4 +- .../runtime/values/objects/ScopeValue.java | 2 +- .../runtime/values/primitives/BoolValue.java | 4 +- .../values/primitives/NumberValue.java | 4 +- .../values/primitives/PrimitiveValue.java | 2 +- .../values/primitives/StringValue.java | 4 +- .../values/primitives/SymbolValue.java | 6 +- .../runtime/values/primitives/VoidValue.java | 13 +- 34 files changed, 886 insertions(+), 310 deletions(-) create mode 100644 src/assets/lib/index.js create mode 100644 src/java/me/topchetoeu/jscript/common/environment/Environment.java create mode 100644 src/java/me/topchetoeu/jscript/common/environment/Key.java create mode 100644 src/java/me/topchetoeu/jscript/common/environment/MultiKey.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/environment/Environment.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/environment/Key.java diff --git a/src/assets/lib/index.js b/src/assets/lib/index.js new file mode 100644 index 0000000..6176ce2 --- /dev/null +++ b/src/assets/lib/index.js @@ -0,0 +1,319 @@ +(function(target, primordials) { + var makeSymbol = primordials.symbol.makeSymbol; + var getSymbol = primordials.symbol.getSymbol; + var getSymbolKey = primordials.symbol.getSymbolKey; + var getSymbolDescription = primordials.symbol.getSymbolDescription; + + var parseInt = primordials.number.parseInt; + var parseFloat = primordials.number.parseFloat; + var isNaN = primordials.number.isNaN; + var NaN = primordials.number.NaN; + var Infinity = primordials.number.Infinity; + + var fromCharCode = primordials.string.fromCharCode; + var fromCodePoint = primordials.string.fromCodePoint; + var stringBuild = primordials.string.stringBuild; + + var defineProperty = primordials.object.defineProperty; + var defineField = primordials.object.defineField; + var getOwnMember = primordials.object.getMember; + var getOwnSymbolMember = primordials.object.getOwnSymbolMember; + var getOwnMembers = primordials.object.getOwnMembers; + var getOwnSymbolMembers = primordials.object.getOwnSymbolMembers; + var getPrototype = primordials.object.getPrototype; + var setPrototype = primordials.object.setPrototype; + + var invokeType = primordials.function.invokeType; + var setConstructable = primordials.function.setConstructable; + var setCallable = primordials.function.setCallable; + var invoke = primordials.function.invoke; + + var setGlobalPrototype = primordials.setGlobalPrototype; + var compile = primordials.compile; + + var json = primordials.json; + + var valueKey = makeSymbol("Primitive.value"); + + function unwrapThis(self, type, constr, name, arg, defaultVal) { + if (arg == null) arg = "this"; + if (typeof self === type) return self; + if (self instanceof constr && valueKey in self) self = self[valueKey]; + if (typeof self === type) return self; + if (arguments.length > 5) return defaultVal; + + throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); + } + + function wrapIndex(i, len) { + } + + var Symbol = function(name) { + if (arguments.length === 0) return makeSymbol(""); + else return makeSymbol(name + ""); + }; + setConstructable(Symbol, false); + + defineField(Symbol, "for", true, false, true, function(name) { + return getSymbol(name + ""); + }); + defineField(Symbol, "keyFor", true, false, true, function(symbol) { + return getSymbolKey(unwrapThis(symbol, "symbol", Symbol, "Symbol.keyFor")); + }); + + defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); + defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); + defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); + defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); + defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); + defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); + defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); + defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); + defineField(Symbol, "prototype", false, false, false, {}); + + defineProperty(Symbol.prototype, "description", false, true, function () { + return getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); + }, undefined); + defineField(Symbol.prototype, "toString", true, false, true, function() { + return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; + }); + defineField(Symbol.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); + }); + + target.Symbol = Symbol; + + var Number = function(value) { + if (invokeType(arguments) === "call") { + if (arguments.length === 0) return 0; + else return +value; + } + + this[valueKey] = target.Number(value); + }; + + defineField(Number, "isFinite", true, false, true, function(value) { + value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined); + + if (value === undefined || isNaN(value)) return false; + if (value === Infinity || value === -Infinity) return false; + + return true; + }); + defineField(Number, "isInteger", true, false, true, function(value) { + value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined); + if (value === undefined) return false; + return parseInt(value) === value; + }); + defineField(Number, "isNaN", true, false, true, function(value) { + return isNaN(value); + }); + defineField(Number, "isSafeInteger", true, false, true, function(value) { + value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined); + if (value === undefined || parseInt(value) !== value) return false; + return value >= -9007199254740991 && value <= 9007199254740991; + }); + defineField(Number, "parseFloat", true, false, true, function(value) { + value = 0 + value; + return parseFloat(value); + }); + defineField(Number, "parseInt", true, false, true, function(value, radix) { + value = 0 + value; + radix = +radix; + if (isNaN(radix)) radix = 10; + + return parseInt(value, radix); + }); + + defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); + defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); + defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); + defineField(Number, "POSITIVE_INFINITY", false, false, false, +Infinity); + defineField(Number, "NEGATIVE_INFINITY", false, false, false, -Infinity); + defineField(Number, "NaN", false, false, false, NaN); + defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); + defineField(Number, "MIN_VALUE", false, false, false, 5e-324); + defineField(Number, "prototype", false, false, false, {}); + + defineField(Number.prototype, "toString", true, false, true); + defineField(Number.prototype, "toString", true, false, true, function() { + return "" + unwrapThis(this, "number", Number, "Number.prototype.toString"); + }); + defineField(Number.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "number", Number, "Number.prototype.toString"); + }); + + target.Number = Number; + + var String = function(value) { + if (invokeType(arguments) === "call") { + if (arguments.length === 0) return ""; + else return value + ""; + } + + this[valueKey] = target.String(value); + }; + + defineField(String, "fromCharCode", true, false, true, function() { + var res = []; + res[arguments.length] = 0; + + for (var i = 0; i < arguments.length; i++) { + res[res.length] = fromCharCode(+arguments[i]); + } + + return stringBuild(res); + }); + defineField(String, "fromCodePoint", true, false, true, function(value) { + var res = []; + res[arguments.length] = 0; + + for (var i = 0; i < arguments.length; i++) { + res[res.length] = fromCodePoint(+arguments[i]); + } + + return stringBuild(res); + }); + + defineField(String, "prototype", false, false, false, {}); + + defineField(String.prototype, "at", true, false, true, function(index) { + return "" + unwrapThis(this, "string", String, "String.prototype.at"); + }); + defineField(String.prototype, "toString", true, false, true, function() { + return unwrapThis(this, "string", String, "String.prototype.toString"); + }); + defineField(String.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "string", String, "String.prototype.valueOf"); + }); + + target.String = String; + + var Boolean = function(value) { + if (invokeType(arguments) === "call") { + if (arguments.length === 0) return false; + else return !!value; + } + + this[valueKey] = target.Boolean(value); + }; + + defineField(Boolean, "prototype", false, false, false, {}); + + defineField(Boolean.prototype, "toString", true, false, true, function() { + return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString"); + }); + defineField(Boolean.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf"); + }); + + target.Boolean = Boolean; + + var Object = function(value) { + if (typeof value === 'object' && value !== null) return value; + + if (typeof value === 'string') return new String(value); + if (typeof value === 'number') return new Number(value); + if (typeof value === 'boolean') return new Boolean(value); + if (typeof value === 'symbol') { + var res = {}; + setPrototype(res, Symbol.prototype); + res[valueKey] = value; + return res; + } + + var target = this; + if (target === undefined || target === null || typeof target !== 'object') target = {}; + + this[valueKey] = target.Object(value); + }; + + defineField(Object, "prototype", false, false, false, setPrototype({}, null)); + + defineField(Object.prototype, "toString", true, false, true, function() { + if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; + else if (typeof this === "number" || this instanceof Number) return "[object Number]"; + else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; + else if (typeof this === "string" || this instanceof String) return "[object String]"; + else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; + else if (typeof this === "function") return "[object Function]"; + else return "[object Object]"; + }); + defineField(Object.prototype, "valueOf", true, false, true, function() { + return this; + }); + + target.Boolean = Boolean; + + var Function = function() { + if (invokeType(arguments) === "new") return Function(value); + + var res = ["return function ("]; + + for (var i = 0; i < arguments.length - 1; i++) { + if (i > 0) res[res.length] = ","; + res[res.length] = arguments[i]; + } + res[res.length] = "){"; + res[res.length] = String(arguments[arguments.length - 1]); + res[res.length] = "}"; + + log(res); + + return compile(stringBuild(res))(); + }; + + defineField(Function, "compile", true, false, true, function(src, options) { + if (options == null) options = {}; + if (src == null) src = ""; + + if (options.globals == null) options.globals = []; + if (options.wrap == null) options.wrap = true; + + var res = []; + + if (options.wrap) res[res.length] = "return (function() {\n"; + if (options.globals.length > 0) { + res[res.length] = "var "; + + for (var i = 0; i < options.globals.length; i++) { + if (i > 0) res[res.length] = ","; + res[res.length] = options.globals[i]; + } + + res[res.length] = ";(function(g){"; + + for (var i = 0; i < options.globals.length; i++) { + var name = options.globals[i]; + res[res.length] = name; + res[res.length] = "=g["; + res[res.length] = json.stringify(name); + res[res.length] = "];"; + } + + res[res.length] = "})(arguments[0] || {});\n"; + } + + res[res.length] = src; + if (options.wrap) res[res.length] = "\n})(arguments[0])"; + + return compile(stringBuild(res)); + }); + defineField(Function, "prototype", false, false, false, setPrototype({}, null)); + + defineField(Function.prototype, "toString", true, false, true, function() { + if (this.name !== "") return "function " + this.name + "(...) { ... }"; + else return "function (...) { ... }"; + }); + defineField(Function.prototype, "valueOf", true, false, true, function() { + return this; + }); + + target.Function = Function; + + setGlobalPrototype("string", String.prototype); + setGlobalPrototype("number", Number.prototype); + setGlobalPrototype("boolean", Boolean.prototype); + setGlobalPrototype("symbol", Symbol.prototype); + setGlobalPrototype("object", Object.prototype); +})(arguments[0], arguments[1]); \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/common/Compiler.java b/src/java/me/topchetoeu/jscript/common/Compiler.java index 812530a..5944988 100644 --- a/src/java/me/topchetoeu/jscript/common/Compiler.java +++ b/src/java/me/topchetoeu/jscript/common/Compiler.java @@ -1,18 +1,18 @@ package me.topchetoeu.jscript.common; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.ES5; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { public static final Compiler DEFAULT = (env, filename, raw) -> { - var res = ES5.compile(filename, raw); + var res = JavaScript.compile(filename, raw); var body = res.body(); DebugContext.get(env).onSource(filename, raw); registerFunc(env, body, res); @@ -20,7 +20,7 @@ public interface Compiler { return body; }; - public Key KEY = new Key<>(); + public Key KEY = Key.of(); public FunctionBody compile(Environment env, Filename filename, String source); diff --git a/src/java/me/topchetoeu/jscript/common/environment/Environment.java b/src/java/me/topchetoeu/jscript/common/environment/Environment.java new file mode 100644 index 0000000..7bb724e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/common/environment/Environment.java @@ -0,0 +1,195 @@ +package me.topchetoeu.jscript.common.environment; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.function.Supplier; + +public class Environment { + public final Environment parent; + private final Map, Object> map = new HashMap<>(); + private final Set> hidden = new HashSet<>(); + + private final Map, Set> multi = new HashMap<>(); + private final Map, Set> multiHidden = new HashMap<>(); + + @SuppressWarnings("unchecked") + private Set getAll(MultiKey key, boolean forceClone) { + Set parent = null, child = null; + boolean cloned = false; + + if (this.parent != null && !hidden.contains(key)) { + parent = this.parent.getAll(key, false); + if (parent.size() == 0) parent = null; + else if (multiHidden.containsKey(key)) { + parent = new HashSet<>(parent); + parent.removeAll(multiHidden.get(key)); + cloned = true; + } + } + if (multi.containsKey(key)) { + child = (Set)multi.get(key); + if (child.size() == 0) child = null; + } + + if (!forceClone) { + if (parent == null && child == null) return Set.of(); + if (parent == null && child != null) return child; + if (parent != null && child == null) return parent; + } + + if (!cloned) parent = new HashSet<>(); + parent.addAll(child); + return parent; + } + private T getMulti(MultiKey key) { + return key.of(getAll(key, false)); + } + private boolean hasMulti(MultiKey key) { + return getAll(key, false).size() > 0; + } + + @SuppressWarnings("all") + private Environment addMulti(MultiKey key, T value) { + if (!multi.containsKey(key)) { + if (hidden.contains(key)) { + multiHidden.put((MultiKey)key, (Set)parent.getAll(key, true)); + hidden.remove(key); + } + + multi.put((MultiKey)key, new HashSet<>()); + } + + multi.get(key).add(value); + return this; + } + + @SuppressWarnings("unchecked") + public T get(Key key) { + if (key instanceof MultiKey) return getMulti((MultiKey)key); + + if (map.containsKey(key)) return (T)map.get(key); + else if (!hidden.contains(key) && parent != null) return parent.get(key); + else return null; + } + public boolean has(Key key) { + if (key instanceof MultiKey) return hasMulti((MultiKey)key); + + if (map.containsKey(key)) return true; + else if (!hidden.contains(key) && parent != null) return parent.has(key); + else return false; + } + + public boolean hasNotNull(Key key) { + return get(key) != null; + } + + public T get(Key key, T defaultVal) { + if (has(key)) return get(key); + else return defaultVal; + } + public T get(Key key, Supplier defaultVal) { + if (has(key)) return get(key); + else return defaultVal.get(); + } + + @SuppressWarnings("unchecked") + public Environment add(Key key, T val) { + if (key instanceof MultiKey) return add(key, val); + + map.put((Key)key, val); + hidden.remove(key); + return this; + } + public Environment add(Key key) { + return add(key, null); + } + @SuppressWarnings("all") + public Environment addAll(Map, ?> map, boolean iterableAsMulti) { + for (var pair : map.entrySet()) { + if (iterableAsMulti && pair.getKey() instanceof MultiKey && pair.getValue() instanceof Iterable) { + for (var val : (Iterable)pair.getValue()) { + addMulti((MultiKey)pair.getKey(), val); + } + } + else add((Key)pair.getKey(), pair.getValue()); + } + map.putAll((Map)map); + hidden.removeAll(map.keySet()); + return this; + } + public Environment addAll(Map, ?> map) { + return addAll(map, true); + } + // public Environment addAll(Environment env) { + // this.map.putAll(env.map); + // this.hidden.removeAll(env.map.keySet()); + + // for (var el : env.multi.entrySet()) { + // for (var val : el.getValue()) { + // add(el.getKey(), val); + // } + // } + + // return this; + // } + + @SuppressWarnings("unchecked") + public Environment remove(Key key) { + map.remove(key); + multi.remove(key); + multiHidden.remove(key); + hidden.add((Key)key); + return this; + } + @SuppressWarnings("all") + public Environment remove(MultiKey key, T val) { + if (multi.containsKey(key)) { + multi.get(key).remove(val); + multiHidden.get(key).add(val); + + if (multi.get(key).size() == 0) { + multi.remove(key); + multiHidden.remove(key); + hidden.add((Key)key); + } + } + + return this; + } + + public Environment init(Key key, T val) { + if (!has(key)) this.add(key, val); + return this; + } + public Environment init(Key key, Supplier val) { + if (!has(key)) this.add(key, val.get()); + return this; + } + + public Environment child() { + return new Environment(this); + } + + public Environment(Environment parent) { + this.parent = parent; + } + public Environment() { + this.parent = null; + } + + public static Environment wrap(Environment env) { + if (env == null) return empty(); + else return env; + } + + public static Environment empty() { + return new Environment(); + } + + public static int nextId() { + return new Random().nextInt(); + } +} diff --git a/src/java/me/topchetoeu/jscript/common/environment/Key.java b/src/java/me/topchetoeu/jscript/common/environment/Key.java new file mode 100644 index 0000000..1bb96b3 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/common/environment/Key.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript.common.environment; + +public interface Key { + public static Key of() { + return new Key<>() { }; + } +} diff --git a/src/java/me/topchetoeu/jscript/common/environment/MultiKey.java b/src/java/me/topchetoeu/jscript/common/environment/MultiKey.java new file mode 100644 index 0000000..79e8404 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/common/environment/MultiKey.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript.common.environment; + +import java.util.Set; + +public interface MultiKey extends Key { + public T of(Set values); +} diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index 3d48364..0227682 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -4,15 +4,15 @@ import java.util.concurrent.Future; import java.util.function.Supplier; import me.topchetoeu.jscript.common.Compiler; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.parsing.Filename; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; public interface EventLoop { - public static final Key KEY = new Key<>(); + public static final Key KEY = Key.of(); public static EventLoop get(Environment ext) { if (ext.hasNotNull(KEY)) return ext.get(KEY); diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 4741223..cc56651 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -6,9 +6,9 @@ import java.util.Map; import java.util.Stack; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.scope.LocalScope; @@ -20,10 +20,9 @@ import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; -import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class Frame { - public static final Key KEY = new Key<>(); + public static final Key KEY = Key.of(); public static enum TryState { TRY, @@ -127,7 +126,7 @@ public class Frame { else return stack[stackPtr - 1 - offset]; } public Value pop() { - if (stackPtr == 0) return VoidValue.UNDEFINED; + if (stackPtr == 0) return Value.UNDEFINED; return stack[--stackPtr]; } public Value[] take(int n) { @@ -138,7 +137,7 @@ public class Frame { int copyN = stackPtr - srcI; Value[] res = new Value[n]; - Arrays.fill(res, VoidValue.UNDEFINED); + Arrays.fill(res, Value.UNDEFINED); System.arraycopy(stack, srcI, res, dstI, copyN); stackPtr -= copyN; diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 67ce302..4b0db70 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -5,7 +5,7 @@ import java.util.Collections; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; @@ -19,7 +19,6 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; -import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class InstructionRunner { private static Value execReturn(Environment env, Instruction instr, Frame frame) { @@ -36,7 +35,7 @@ public class InstructionRunner { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); - frame.push(func.call(env, false, instr.get(1), VoidValue.UNDEFINED, callArgs)); + frame.push(func.call(env, false, instr.get(1), Value.UNDEFINED, callArgs)); frame.codePtr++; return null; @@ -75,11 +74,11 @@ public class InstructionRunner { FunctionValue getter, setter; - if (getterVal == VoidValue.UNDEFINED) getter = null; + if (getterVal == Value.UNDEFINED) getter = null; else if (getterVal instanceof FunctionValue) getter = (FunctionValue)getterVal; else throw EngineException.ofType("Getter must be a function or undefined."); - if (setterVal == VoidValue.UNDEFINED) setter = null; + if (setterVal == Value.UNDEFINED) setter = null; else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal; else throw EngineException.ofType("Setter must be a function or undefined."); @@ -135,8 +134,8 @@ public class InstructionRunner { } private static Value execLoadValue(Environment env, Instruction instr, Frame frame) { switch (instr.type) { - case PUSH_UNDEFINED: frame.push(VoidValue.UNDEFINED); break; - case PUSH_NULL: frame.push(VoidValue.NULL); break; + case PUSH_UNDEFINED: frame.push(Value.UNDEFINED); break; + case PUSH_NULL: frame.push(Value.NULL); break; case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); break; case PUSH_NUMBER: frame.push(new NumberValue(instr.get(0))); break; case PUSH_STRING: frame.push(new StringValue(instr.get(0))); break; @@ -157,7 +156,7 @@ public class InstructionRunner { } private static Value execLoadObj(Environment env, Instruction instr, Frame frame) { var obj = new ObjectValue(); - obj.setPrototype(Environment.OBJECT_PROTO); + obj.setPrototype(Value.OBJECT_PROTO); frame.push(obj); frame.codePtr++; return null; @@ -204,8 +203,8 @@ public class InstructionRunner { return null; } private static Value execLoadRegEx(Environment env, Instruction instr, Frame frame) { - if (env.hasNotNull(Environment.REGEX_CONSTR)) { - frame.push(env.get(Environment.REGEX_CONSTR).callNew(env, instr.get(0), instr.get(1))); + if (env.hasNotNull(Value.REGEX_CONSTR)) { + frame.push(env.get(Value.REGEX_CONSTR).callNew(env, instr.get(0), instr.get(1))); } else { throw EngineException.ofSyntax("Regex is not supported."); diff --git a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java index ab4c9fd..b895dea 100644 --- a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java +++ b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java @@ -3,10 +3,10 @@ package me.topchetoeu.jscript.runtime; import java.util.HashSet; import java.util.stream.Collectors; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.common.json.JSONList; import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Value; @@ -32,8 +32,8 @@ public class JSONConverter { return res; } - if (val.isNull()) return VoidValue.NULL; - return VoidValue.UNDEFINED; + if (val.isNull()) return Value.NULL; + return Value.UNDEFINED; } public static JSONElement fromJs(Environment ext, Value val) { @@ -46,7 +46,7 @@ public class JSONConverter { if (val instanceof BoolValue) return JSONElement.bool(((BoolValue)val).value); if (val instanceof NumberValue) return JSONElement.number(((NumberValue)val).value); if (val instanceof StringValue) return JSONElement.string(((StringValue)val).value); - if (val == VoidValue.NULL) return JSONElement.NULL; + if (val == Value.NULL) return JSONElement.NULL; if (val instanceof VoidValue) return null; if (val instanceof ArrayValue) { diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 551d4b6..18a4278 100644 --- a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -9,16 +9,25 @@ import java.util.concurrent.ExecutionException; import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Reading; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class SimpleRepl { @@ -31,6 +40,11 @@ public class SimpleRepl { private static void reader() { try { + try { + try { initGlobals(); } catch (ExecutionException e) { throw e.getCause(); } + } + catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } + for (var arg : args) { try { var file = Path.of(arg); @@ -59,7 +73,7 @@ public class SimpleRepl { var res = engine.pushMsg( false, environment, new Filename("jscript", "repl/" + i + ".js"), raw, - VoidValue.UNDEFINED + Value.UNDEFINED ).get(); System.err.println(res.toReadable(environment)); } @@ -79,10 +93,240 @@ public class SimpleRepl { } } + private static ObjectValue symbolPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env).value))); + res.defineOwnMember(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env).value))); + res.defineOwnMember(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key())); + res.defineOwnMember(env, "getSymbolDescriptor", new NativeFunction(args -> new StringValue(((SymbolValue)args.get(0)).value))); + + return res; + } + + private static ObjectValue numberPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "parseInt", new NativeFunction(args -> { + var radix = args.get(1).toInt(env); + + if (radix != 10 && args.get(0) instanceof NumberValue) { + return new NumberValue(args.get(0).toNumber(env).value - args.get(0).toNumber(env).value % 1); + } + else { + return NumberValue.parseInt(args.get(0).toString(), radix, false); + } + })); + res.defineOwnMember(env, "parseFloat", new NativeFunction(args -> { + if (args.get(0) instanceof NumberValue) { + return args.get(0); + } + else return NumberValue.parseFloat(args.get(0).toString(), false); + })); + res.defineOwnMember(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN()))); + res.defineOwnMember(env, "NaN", new NumberValue(Double.NaN)); + res.defineOwnMember(env, "Infinity", new NumberValue(Double.POSITIVE_INFINITY)); + + return res; + } + + private static ObjectValue stringPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "stringBuild", new NativeFunction(args -> { + var parts = ((ArrayValue)args.get(0)).toArray(); + var sb = new StringBuilder(); + + for (var i = 0; i < parts.length; i++) { + sb.append(((StringValue)parts[i]).value); + } + + return new StringValue(sb.toString()); + })); + + res.defineOwnMember(env, "fromCharCode", new NativeFunction(args -> { + var parts = ((ArrayValue)args.get(0)).toArray(); + var sb = new StringBuilder(); + + for (var i = 0; i < parts.length; i++) { + sb.append(((StringValue)parts[i]).value); + } + + return new StringValue(sb.toString()); + })); + + return res; + } + + private static ObjectValue objectPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "defineField", new NativeFunction(args -> { + var obj = (ObjectValue)args.get(0); + var key = args.get(1); + var writable = args.get(2).toBoolean(); + var enumerable = args.get(3).toBoolean(); + var configurable = args.get(4).toBoolean(); + var value = args.get(5); + + obj.defineOwnMember(args.env, key, FieldMember.of(value, enumerable, configurable, writable)); + + return Value.UNDEFINED; + })); + res.defineOwnMember(env, "defineProperty", new NativeFunction(args -> { + var obj = (ObjectValue)args.get(0); + var key = args.get(1); + var enumerable = args.get(2).toBoolean(); + var configurable = args.get(3).toBoolean(); + var getter = args.get(4) instanceof VoidValue ? null : (FunctionValue)args.get(4); + var setter = args.get(5) instanceof VoidValue ? null : (FunctionValue)args.get(5); + + obj.defineOwnMember(args.env, key, new PropertyMember(getter, setter, configurable, enumerable)); + + return Value.UNDEFINED; + })); + res.defineOwnMember(env, "getPrototype", new NativeFunction(args -> { + return args.get(0).getPrototype(env); + })); + res.defineOwnMember(env, "setPrototype", new NativeFunction(args -> { + var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1); + args.get(0).setPrototype(env, proto); + return args.get(0); + })); + return res; + } + + private static ObjectValue functionPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "setCallable", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + func.enableCall = args.get(1).toBoolean(); + return Value.UNDEFINED; + })); + res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + func.enableNew = args.get(1).toBoolean(); + return Value.UNDEFINED; + })); + res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { + if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new"); + else return new StringValue("call"); + })); + res.defineOwnMember(env, "invoke", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + var self = args.get(1); + var funcArgs = (ArrayValue)args.get(2); + + return func.call(env, self, funcArgs.toArray()); + })); + + return res; + } + + private static ObjectValue jsonPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "stringify", new NativeFunction(args -> { + return new StringValue(JSON.stringify(JSONConverter.fromJs(env, args.get(0)))); + })); + res.defineOwnMember(env, "parse", new NativeFunction(args -> { + return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env).value)); + })); + res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + func.enableNew = args.get(1).toBoolean(); + return Value.UNDEFINED; + })); + res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { + if (((ArgumentsValue)args.get(0)).frame.isNew) return new StringValue("new"); + else return new StringValue("call"); + })); + res.defineOwnMember(env, "invoke", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + var self = args.get(1); + var funcArgs = (ArrayValue)args.get(2); + + return func.call(env, self, funcArgs.toArray()); + })); + + return res; + } + + private static ObjectValue primordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnMember(env, "symbol", symbolPrimordials(env)); + res.defineOwnMember(env, "number", numberPrimordials(env)); + res.defineOwnMember(env, "string", stringPrimordials(env)); + res.defineOwnMember(env, "object", objectPrimordials(env)); + res.defineOwnMember(env, "function", functionPrimordials(env)); + res.defineOwnMember(env, "json", jsonPrimordials(env)); + + int[] i = new int[1]; + + res.defineOwnMember(env, "setGlobalPrototype", new NativeFunction(args -> { + var type = args.get(0).toString(env).value; + var obj = (ObjectValue)args.get(1); + + switch (type) { + case "string": + args.env.add(Value.STRING_PROTO, obj); + break; + case "number": + args.env.add(Value.NUMBER_PROTO, obj); + break; + case "boolean": + args.env.add(Value.BOOL_PROTO, obj); + break; + case "symbol": + args.env.add(Value.SYMBOL_PROTO, obj); + break; + case "object": + args.env.add(Value.OBJECT_PROTO, obj); + break; + } + + return Value.UNDEFINED; + })); + res.defineOwnMember(env, "compile", new NativeFunction(args -> { + return Compiler.compileFunc(env, new Filename("jscript", "func" + i[0]++ + ".js"), args.get(0).toString(env).value); + })); + return res; + } + private static void initEnv() { + // glob.define(null, false, new NativeFunction("go", args -> { + // try { + // var f = Path.of("do.js"); + // var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); + // return func.call(args.env); + // } + // catch (IOException e) { + // throw new EngineException("Couldn't open do.js"); + // } + // })); + + // var fs = new RootFilesystem(PermissionsProvider.get(environment)); + // fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); + // fs.protocols.put("file", new PhysicalFilesystem(".")); + // fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err)); + + // environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); + // environment.add(Filesystem.KEY, fs); + // environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); + // environment.add(Compiler.KEY, new JSCompiler(environment)); environment.add(EventLoop.KEY, engine); environment.add(GlobalScope.KEY, new GlobalScope()); environment.add(DebugContext.KEY, new DebugContext()); + // environment.add(EventLoop.KEY, engine); environment.add(Compiler.KEY, Compiler.DEFAULT); var glob = GlobalScope.get(environment); @@ -101,7 +345,19 @@ public class SimpleRepl { })); } private static void initEngine() { + // var ctx = new DebugContext(); + // environment.add(DebugContext.KEY, ctx); + + // debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx)); engineTask = engine.start(); + // debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); + } + private static void initGlobals() throws InterruptedException, ExecutionException { + EventLoop.get(environment).pushMsg( + false, environment, + Filename.parse("jscript://init.js"), Reading.resourceToString("lib/index.js"), + Value.UNDEFINED, GlobalScope.get(environment).object, primordials(environment) + ).get(); } public static void main(String args[]) throws InterruptedException { diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java index 0ecc549..df26c27 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java @@ -7,19 +7,19 @@ import java.util.WeakHashMap; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; public class DebugContext { - public static final Key KEY = new Key<>(); - public static final Key IGNORE = new Key<>(); + public static final Key KEY = Key.of(); + public static final Key IGNORE = Key.of(); private HashMap sources; private WeakHashMap maps; diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java index 5bf3817..2d67c6f 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java @@ -4,10 +4,10 @@ import java.util.List; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; public interface DebugHandler { diff --git a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java deleted file mode 100644 index 11e10e6..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java +++ /dev/null @@ -1,144 +0,0 @@ -package me.topchetoeu.jscript.runtime.environment; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.function.Supplier; - -import me.topchetoeu.jscript.common.Compiler; -import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; -import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; - -public class Environment { - public static final Key COMPILE_FUNC = new Key<>(); - - public static final Key REGEX_CONSTR = new Key<>(); - public static final Key MAX_STACK_COUNT = new Key<>(); - public static final Key HIDE_STACK = new Key<>(); - - public static final Key OBJECT_PROTO = new Key<>(); - public static final Key FUNCTION_PROTO = new Key<>(); - public static final Key ARRAY_PROTO = new Key<>(); - public static final Key BOOL_PROTO = new Key<>(); - public static final Key NUMBER_PROTO = new Key<>(); - public static final Key STRING_PROTO = new Key<>(); - public static final Key SYMBOL_PROTO = new Key<>(); - public static final Key ERROR_PROTO = new Key<>(); - public static final Key SYNTAX_ERR_PROTO = new Key<>(); - public static final Key TYPE_ERR_PROTO = new Key<>(); - public static final Key RANGE_ERR_PROTO = new Key<>(); - - public final Environment parent; - private final Map, Object> map = new HashMap<>(); - private final Set> hidden = new HashSet<>(); - - @SuppressWarnings("unchecked") - public T get(Key key) { - if (map.containsKey(key)) return (T)map.get(key); - else if (!hidden.contains(key) && parent != null) return parent.get(key); - else return null; - } - public boolean has(Key key) { - if (map.containsKey(key)) return true; - else if (!hidden.contains(key) && parent != null) return parent.has(key); - else return false; - } - - @SuppressWarnings("all") - public Set> keys() { - if (parent != null) { - if (map.size() == 0) return (Set)map.keySet(); - - var res = new HashSet(); - res.addAll(parent.keys()); - res.addAll(map.keySet()); - return res; - } - else return (Set)map.keySet(); - } - - public boolean hasNotNull(Key key) { - return get(key) != null; - } - - public T get(Key key, T defaultVal) { - if (has(key)) return get(key); - else return defaultVal; - } - public T get(Key key, Supplier defaultVal) { - if (has(key)) return get(key); - else return defaultVal.get(); - } - - @SuppressWarnings("unchecked") - public Environment add(Key key, T val) { - map.put((Key)key, val); - hidden.remove(key); - return this; - } - @SuppressWarnings("unchecked") - public Environment add(Key key) { - map.put((Key)(Key)key, null); - hidden.remove(key); - return this; - } - @SuppressWarnings("all") - public Environment addAll(Map, ?> map) { - map.putAll((Map)map); - hidden.removeAll(map.keySet()); - return this; - } - public Environment addAll(Environment env) { - this.map.putAll(env.map); - this.hidden.removeAll(env.map.keySet()); - return this; - } - - @SuppressWarnings("unchecked") - public Environment remove(Key key) { - map.remove((Key)key); - hidden.add((Key)key); - return this; - } - - public Environment init(Key key, T val) { - if (!has(key)) this.add(key, val); - return this; - } - public Environment init(Key key, Supplier val) { - if (!has(key)) this.add(key, val.get()); - return this; - } - - public Environment child() { - return new Environment(this); - } - - public Environment(Environment parent) { - this.parent = parent; - } - public Environment() { - this.parent = null; - } - - public static Environment wrap(Environment ext) { - if (ext == null) return empty(); - else return ext; - } - - // public static Environment chain(int id, Environment ...envs) { - // var res = new Environment(); - // for (var env : envs) res.addAll(env); - // return res; - // } - - public static Environment empty() { - return new Environment(); - } - - public static int nextId() { - return new Random().nextInt(); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/environment/Key.java b/src/java/me/topchetoeu/jscript/runtime/environment/Key.java deleted file mode 100644 index 67a5156..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/environment/Key.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.runtime.environment; - -public class Key { - -} diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 7912f09..1c0706d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -3,8 +3,8 @@ package me.topchetoeu.jscript.runtime.exceptions; import java.util.ArrayList; import java.util.List; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -19,7 +19,7 @@ public class EngineException extends RuntimeException { public final Environment ext; public boolean visible() { - return ext == null || !ext.get(Environment.HIDE_STACK, false); + return ext == null || !ext.get(Value.HIDE_STACK, false); } public String toString() { var res = ""; @@ -109,18 +109,18 @@ public class EngineException extends RuntimeException { } public static EngineException ofError(String name, String msg) { - return new EngineException(err(name, msg, env -> env.get(Environment.ERROR_PROTO))); + return new EngineException(err(name, msg, env -> env.get(Value.ERROR_PROTO))); } public static EngineException ofError(String msg) { - return new EngineException(err(null, msg, env -> env.get(Environment.ERROR_PROTO))); + return new EngineException(err(null, msg, env -> env.get(Value.ERROR_PROTO))); } public static EngineException ofSyntax(String msg) { - return new EngineException(err(null, msg, env -> env.get(Environment.SYNTAX_ERR_PROTO))); + return new EngineException(err(null, msg, env -> env.get(Value.SYNTAX_ERR_PROTO))); } public static EngineException ofType(String msg) { - return new EngineException(err(null, msg, env -> env.get(Environment.TYPE_ERR_PROTO))); + return new EngineException(err(null, msg, env -> env.get(Value.TYPE_ERR_PROTO))); } public static EngineException ofRange(String msg) { - return new EngineException(err(null, msg, env -> env.get(Environment.RANGE_ERR_PROTO))); + return new EngineException(err(null, msg, env -> env.get(Value.RANGE_ERR_PROTO))); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java index 88cd8fc..4092749 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java @@ -3,18 +3,17 @@ package me.topchetoeu.jscript.runtime.scope; import java.util.HashSet; import java.util.Set; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; -import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class GlobalScope { - public static final Key KEY = new Key<>(); + public static final Key KEY = Key.of(); public final ObjectValue object; @@ -35,7 +34,7 @@ public class GlobalScope { object.defineOwnMember(ext, name, FieldMember.of(val, !readonly)); } public void define(Environment ext, boolean readonly, String ...names) { - for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED)); + for (var name : names) define(ext, name, new ValueVariable(readonly, Value.UNDEFINED)); } public void define(Environment ext, boolean readonly, FunctionValue val) { define(ext, readonly, val.name, val); diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java index a4b4a43..22e2329 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.scope; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; public class ValueVariable implements Variable { diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java index 85f5f5b..43393a1 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.scope; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java b/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java index df1c4c0..c1d4840 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/java/me/topchetoeu/jscript/runtime/values/Member.java index 2dae69b..8f99012 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Member.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Member.java @@ -1,10 +1,9 @@ package me.topchetoeu.jscript.runtime.values; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; -import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public interface Member { public static final class PropertyMember implements Member { @@ -15,7 +14,7 @@ public interface Member { @Override public Value get(Environment env, Value self) { if (getter != null) return getter.call(env, self); - else return VoidValue.UNDEFINED; + else return Value.UNDEFINED; } @Override public boolean set(Environment env, Value val, Value self) { if (setter == null) return false; @@ -41,10 +40,10 @@ public interface Member { @Override public ObjectValue descriptor(Environment env, Value self) { var res = new ObjectValue(); - if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(VoidValue.UNDEFINED)); + if (getter == null) res.defineOwnMember(env, "getter", FieldMember.of(Value.UNDEFINED)); else res.defineOwnMember(env, "getter", FieldMember.of(getter)); - if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(VoidValue.UNDEFINED)); + if (setter == null) res.defineOwnMember(env, "setter", FieldMember.of(Value.UNDEFINED)); else res.defineOwnMember(env, "setter", FieldMember.of(setter)); res.defineOwnMember(env, "enumerable", FieldMember.of(BoolValue.of(enumerable))); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index d15e324..9282791 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -10,11 +10,12 @@ import java.util.LinkedHashMap; import java.util.Map; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.runtime.EventLoop; import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; @@ -48,8 +49,24 @@ public abstract class Value { } } - public static final Object NULL = new Object(); - public static final Object NO_RETURN = new Object(); + public static final Key REGEX_CONSTR = Key.of(); + + public static final Key MAX_STACK_COUNT = Key.of(); + public static final Key HIDE_STACK = Key.of(); + public static final Key OBJECT_PROTO = Key.of(); + public static final Key FUNCTION_PROTO = Key.of(); + public static final Key ARRAY_PROTO = Key.of(); + public static final Key BOOL_PROTO = Key.of(); + public static final Key NUMBER_PROTO = Key.of(); + public static final Key STRING_PROTO = Key.of(); + public static final Key SYMBOL_PROTO = Key.of(); + public static final Key ERROR_PROTO = Key.of(); + public static final Key SYNTAX_ERR_PROTO = Key.of(); + public static final Key TYPE_ERR_PROTO = Key.of(); + public static final Key RANGE_ERR_PROTO = Key.of(); + + public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); + public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); public abstract StringValue type(); public abstract boolean isPrimitive(); @@ -267,7 +284,7 @@ public abstract class Value { if (member != null) return member.get(env, obj); } - return VoidValue.UNDEFINED; + return Value.UNDEFINED; } public final Value getMember(Environment env, Value key) { return getMember(env, new KeyCache(key)); @@ -437,74 +454,6 @@ public abstract class Value { return a.toString(env).strictEquals(env, b.toString(env)); } - // @SuppressWarnings("unchecked") - // public static T convert(Environment ext, Class clazz) { - // if (clazz == Void.class) return null; - - // if (this instanceof NativeWrapper) { - // var res = ((NativeWrapper)this).wrapped; - // if (clazz.isInstance(res)) return (T)res; - // } - - // if (clazz == null || clazz == Object.class) return (T)this; - - // if (this instanceof ArrayValue) { - // if (clazz.isAssignableFrom(ArrayList.class)) { - // var raw = ((ArrayValue)this).toArray(); - // var res = new ArrayList<>(); - // for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - // return (T)new ArrayList<>(res); - // } - // if (clazz.isAssignableFrom(HashSet.class)) { - // var raw = ((ArrayValue)this).toArray(); - // var res = new HashSet<>(); - // for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - // return (T)new HashSet<>(res); - // } - // if (clazz.isArray()) { - // var raw = ((ArrayValue)this).toArray(); - // Object res = Array.newInstance(clazz.getComponentType(), raw.length); - // for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); - // return (T)res; - // } - // } - - // if (this instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { - // var res = new HashMap<>(); - // for (var el : ((ObjectValue)this).values.entrySet()) res.put( - // convert(ext, el.getKey(), null), - // convert(ext, el.getValue(), null) - // ); - // return (T)res; - // } - - // if (clazz == String.class) return (T)toString(ext, this); - // if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(this); - // if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, this); - // if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, this); - // if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, this); - // if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, this); - // if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, this); - // if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, this); - - // if (clazz == Character.class || clazz == char.class) { - // if (this instanceof Number) return (T)(Character)(char)number(this); - // else { - // var res = toString(ext, this); - // if (res.length() == 0) throw new ConvertException("\"\"", "Character"); - // else return (T)(Character)res.charAt(0); - // } - // } - - // if (this == null) return null; - // if (clazz.isInstance(this)) return (T)this; - // if (clazz.isAssignableFrom(NativeWrapper.class)) { - // return (T)NativeWrapper.of(ext, this); - // } - - // throw new ConvertException(type(this), clazz.getSimpleName()); - // } - public Iterable toIterable(Environment env) { return () -> { if (!(this instanceof FunctionValue)) return Collections.emptyIterator(); @@ -518,7 +467,7 @@ public abstract class Value { private void loadNext() { if (supplier == null) value = null; else if (consumed) { - var curr = supplier.call(env, VoidValue.UNDEFINED); + var curr = supplier.call(env, Value.UNDEFINED); if (curr == null) { supplier = null; value = null; } if (curr.getMember(env, new StringValue("done")).toBoolean()) { supplier = null; value = null; } @@ -561,12 +510,12 @@ public abstract class Value { public void callWith(Environment env, Iterable it) { for (var el : it) { - this.call(env, VoidValue.UNDEFINED, el); + this.call(env, Value.UNDEFINED, el); } } public void callWithAsync(Environment env, Iterable it, boolean async) { for (var el : it) { - env.get(EventLoop.KEY).pushMsg(() -> this.call(env, VoidValue.UNDEFINED, el), true); + env.get(EventLoop.KEY).pushMsg(() -> this.call(env, Value.UNDEFINED, el), true); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java index d6f9604..1a7f276 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java @@ -1,9 +1,8 @@ package me.topchetoeu.jscript.runtime.values.functions; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; -import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class Arguments { public final Value self; @@ -23,7 +22,7 @@ public class Arguments { return get(-1); } public Value get(int i) { - if (i >= args.length || i < -1) return VoidValue.UNDEFINED; + if (i >= args.length || i < -1) return Value.UNDEFINED; else if (i == -1) return self; else return args[i]; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index 6c70cb3..b462e5f 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -1,8 +1,8 @@ package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.FunctionBody; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.Value; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index 8e4c971..543a6d1 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values.functions; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; @@ -82,7 +82,7 @@ public abstract class FunctionValue extends ObjectValue { } public FunctionValue(String name, int length) { - setPrototype(Environment.FUNCTION_PROTO); + setPrototype(FUNCTION_PROTO); if (name == null) name = ""; this.length = length; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index d671efa..4615f8d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values.functions; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; public class NativeFunction extends FunctionValue { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java index f754cbc..23299ce 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java @@ -7,7 +7,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; @@ -73,7 +73,7 @@ public class ArrayValue extends ObjectValue implements Iterable { if (i < 0 || i >= size) return null; var res = values[i]; - if (res == null) return VoidValue.UNDEFINED; + if (res == null) return Value.UNDEFINED; else return res; } public void set(int i, Value val) { @@ -217,7 +217,7 @@ public class ArrayValue extends ObjectValue implements Iterable { this(16); } public ArrayValue(int cap) { - setPrototype(env -> env.get(Environment.ARRAY_PROTO)); + setPrototype(env -> env.get(ARRAY_PROTO)); values = new Value[Math.min(cap, 16)]; size = 0; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java index 0fb7528..f1c81b8 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -4,8 +4,8 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java index 38ba524..0d4ae0e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values.objects; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java index f4d32a6..a869252 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.runtime.values.primitives; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -20,7 +20,7 @@ public final class BoolValue extends PrimitiveValue { @Override public StringValue toString(Environment ext) { return new StringValue(value ? "true" : "false"); } @Override public ObjectValue getPrototype(Environment env) { - return env.get(Environment.BOOL_PROTO); + return env.get(BOOL_PROTO); } @Override public boolean strictEquals(Environment ext, Value other) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java index abcd8e1..bfd4aac 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java @@ -1,10 +1,10 @@ package me.topchetoeu.jscript.runtime.values.primitives; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -22,7 +22,7 @@ public final class NumberValue extends PrimitiveValue { @Override public String toString() { return JSON.stringify(JSONElement.number(value)); } @Override public ObjectValue getPrototype(Environment env) { - return env.get(Environment.NUMBER_PROTO); + return env.get(NUMBER_PROTO); } @Override public CompareResult compare(Environment env, Value other) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java index ccbe0ae..4d18f7a 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.Map; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index 315738e..0da0f7c 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -3,9 +3,9 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.Map; import java.util.Objects; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -38,7 +38,7 @@ public final class StringValue extends PrimitiveValue { @Override public boolean strictEquals(Environment ext, Value other) { return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value); } - @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); } + @Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); } @Override public Map getOwnMembers(Environment env) { // TODO Auto-generated method stub diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java index cc9b954..f6cabf8 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.HashMap; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; @@ -14,7 +14,7 @@ public final class SymbolValue extends PrimitiveValue { public final String value; public Value key() { - return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : VoidValue.UNDEFINED; + return registry.containsKey(value) && registry.get(value) == this ? new StringValue(value) : Value.UNDEFINED; } @Override public StringValue type() { return typeString; } @@ -30,7 +30,7 @@ public final class SymbolValue extends PrimitiveValue { @Override public boolean strictEquals(Environment ext, Value other) { return other == this; } - @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); } + @Override public ObjectValue getPrototype(Environment env) { return env.get(SYMBOL_PROTO); } @Override public String toString() { if (value == null) return "Symbol()"; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java index 5f20d05..5c37052 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.Map; -import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; @@ -10,10 +10,7 @@ import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class VoidValue extends PrimitiveValue { - public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); - public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); - - private final StringValue namestring; + private final StringValue nameString; public final String name; public final StringValue typeString; @@ -21,12 +18,12 @@ public final class VoidValue extends PrimitiveValue { @Override public StringValue type() { return typeString; } @Override public boolean toBoolean() { return false; } @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } - @Override public StringValue toString(Environment ext) { return namestring; } + @Override public StringValue toString(Environment ext) { return nameString; } @Override public Value add(Environment ext, Value other) { if (!other.isPrimitive()) other = other.toPrimitive(ext); - if (other instanceof StringValue) return namestring.add(ext, other); + if (other instanceof StringValue) return nameString.add(ext, other); else return NumberValue.NAN; } @@ -52,6 +49,6 @@ public final class VoidValue extends PrimitiveValue { public VoidValue(String name, StringValue type) { this.name = name; this.typeString = type; - this.namestring = new StringValue(name); + this.nameString = new StringValue(name); } } -- 2.45.2 From a45f4109d8c31f27916dd3bf83c2ea3a3e3bc3f6 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:00:20 +0300 Subject: [PATCH 08/48] feat: add environment in Source --- .../topchetoeu/jscript/common/Compiler.java | 2 +- .../topchetoeu/jscript/common/json/JSON.java | 2 +- .../jscript/common/parsing/ParseRes.java | 4 -- .../jscript/common/parsing/Parser.java | 5 ++ .../jscript/common/parsing/Parsing.java | 2 - .../jscript/common/parsing/Source.java | 11 +++- .../compilation/AssignableStatement.java | 50 ---------------- .../jscript/compilation/ExpressionParser.java | 5 ++ .../jscript/compilation/JavaScript.java | 59 ++++++++++--------- .../values/operations/CallStatement.java | 2 +- .../values/primitives/NumberValue.java | 4 +- .../values/primitives/StringValue.java | 2 +- 12 files changed, 56 insertions(+), 92 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/common/parsing/Parser.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java diff --git a/src/java/me/topchetoeu/jscript/common/Compiler.java b/src/java/me/topchetoeu/jscript/common/Compiler.java index 5944988..1d6e7d2 100644 --- a/src/java/me/topchetoeu/jscript/common/Compiler.java +++ b/src/java/me/topchetoeu/jscript/common/Compiler.java @@ -12,7 +12,7 @@ import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { public static final Compiler DEFAULT = (env, filename, raw) -> { - var res = JavaScript.compile(filename, raw); + var res = JavaScript.compile(env, filename, raw); var body = res.body(); DebugContext.get(env).onSource(filename, raw); registerFunc(env, body, res); diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index 74434ba..e93f3f3 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -101,7 +101,7 @@ public class JSON { public static JSONElement parse(Filename filename, String raw) { if (filename == null) filename = new Filename("jscript", "json"); - var res = parseValue(new Source(filename, raw), 0); + var res = parseValue(new Source(null, filename, raw), 0); if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given."); else if (res.isError()) throw new SyntaxException(null, res.error); else return JSONElement.of(res.result); diff --git a/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java b/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java index d50d289..c1af040 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java @@ -1,10 +1,6 @@ package me.topchetoeu.jscript.common.parsing; public class ParseRes { - public static interface Parser { - public ParseRes parse(Source src, int i); - } - public static enum State { SUCCESS, FAILED, diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parser.java b/src/java/me/topchetoeu/jscript/common/parsing/Parser.java new file mode 100644 index 0000000..d40f4f4 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/common/parsing/Parser.java @@ -0,0 +1,5 @@ +package me.topchetoeu.jscript.common.parsing; + +public interface Parser { + public ParseRes parse(Source src, int i); +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java index a0a5f27..62e2cdd 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java @@ -3,8 +3,6 @@ package me.topchetoeu.jscript.common.parsing; import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -// TODO: this has to be rewritten -// @SourceFile public class Parsing { public static boolean isDigit(Character c) { return c != null && c >= '0' && c <= '9'; diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Source.java b/src/java/me/topchetoeu/jscript/common/parsing/Source.java index 6fa9095..d04c1e4 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/Source.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Source.java @@ -2,7 +2,10 @@ package me.topchetoeu.jscript.common.parsing; import java.util.function.Predicate; +import me.topchetoeu.jscript.common.environment.Environment; + public class Source { + public final Environment env; public final Filename filename; public final String src; @@ -41,7 +44,10 @@ public class Source { return src.substring(start, end); } - public Source(Filename filename, String src) { + public Source(Environment env, Filename filename, String src) { + if (env == null) this.env = new Environment(); + else this.env = env; + this.filename = filename; this.src = src; @@ -63,4 +69,7 @@ public class Source { System.arraycopy(lineStarts, 0, newArr, 0, n); lineStarts = newArr; } + public Source(String src) { + this(null, null, src); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java index a0e2175..40f9930 100644 --- a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java @@ -4,54 +4,4 @@ import me.topchetoeu.jscript.common.Operation; public interface AssignableStatement { public abstract Statement toAssign(Statement val, Operation operation); - - // private static final Map operations = Map.ofEntries( - // Map.entry("*=", Operation.MULTIPLY), - // Map.entry("/=", Operation.DIVIDE), - // Map.entry("%=", Operation.MODULO), - // Map.entry("-=", Operation.SUBTRACT), - // Map.entry("+=", Operation.ADD), - // Map.entry(">>=", Operation.SHIFT_RIGHT), - // Map.entry("<<=", Operation.SHIFT_LEFT), - // Map.entry(">>>=", Operation.USHIFT_RIGHT), - // Map.entry("&=", Operation.AND), - // Map.entry("^=", Operation.XOR), - // Map.entry("|=", Operation.OR) - // ); - // private static final List operatorsByLength = operations.keySet().stream().sorted().collect(Collectors.toList()); - - - // public static ParseRes parse(Source src, int i, Statement prev, int precedence) { - // if (precedence > 2) return ParseRes.failed(); - - // var n = Parsing.skipEmpty(src, i); - - // for (var op : operatorsByLength) { - // if (!src.is(i + n, op)) continue; - // n += op.length() + 1; - - // if (!(prev instanceof AssignableStatement)) { - // return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); - // } - - // var res = Parsing.parseValue(src, i + n, 2); - // if (!res.isSuccess()) return res.chainError(src.loc(i + n), String.format("Expected a value after the '%s=' operator", op)); - // n += res.n; - - // return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operations.get(op)), n); - // } - - // if (!src.is(i + n, "=")) return ParseRes.failed(); - // n++; - - // if (!(prev instanceof AssignableStatement)) { - // return ParseRes.error(src.loc(i + n), "Invalid expression on left hand side of assign operator"); - // } - - // var res = Parsing.parseValue(src, i + n, 2); - // if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '=' operator"); - // n += res.n; - - // return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, null), n); - // } } diff --git a/src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java b/src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java new file mode 100644 index 0000000..cfbfafc --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java @@ -0,0 +1,5 @@ +package me.topchetoeu.jscript.compilation; + +public class ExpressionParser { + +} diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index a07e92b..949a3ea 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Set; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -72,8 +73,8 @@ public class JavaScript { public static ParseRes parseSimple(Source src, int i, boolean statement) { return ParseRes.first(src, i, - (a, b) -> statement ? ParseRes.failed() : ObjectStatement.parse(a, b), - (a, b) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(a, b, false), + (s, j) -> statement ? ParseRes.failed() : ObjectStatement.parse(s, j), + (s, j) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(s, j, false), JavaScript::parseLiteral, StringStatement::parse, RegexStatement::parse, @@ -196,27 +197,6 @@ public class JavaScript { return res.addN(n); } - public static Statement[] parse(Filename filename, String raw) { - var src = new Source(filename, raw); - var list = new ArrayList(); - int i = 0; - - while (true) { - if (i >= src.size()) break; - - var res = parseStatement(src, i); - - if (res.isError()) throw new SyntaxException(src.loc(i), res.error); - else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax"); - - i += res.n; - - list.add(res.result); - } - - return list.toArray(Statement[]::new); - } - public static ParseRes parseStatementEnd(Source src, int i) { var n = Parsing.skipEmpty(src, i); if (i >= src.size()) return ParseRes.res(true, n + 1); @@ -231,10 +211,6 @@ public class JavaScript { return ParseRes.failed(); } - public static boolean checkVarName(String name) { - return !JavaScript.reserved.contains(name); - } - public static ParseRes> parseParamList(Source src, int i) { var n = Parsing.skipEmpty(src, i); @@ -271,6 +247,31 @@ public class JavaScript { return ParseRes.res(args, n); } + public static Statement[] parse(Environment env, Filename filename, String raw) { + var src = new Source(env, filename, raw); + var list = new ArrayList(); + int i = 0; + + while (true) { + if (i >= src.size()) break; + + var res = parseStatement(src, i); + + if (res.isError()) throw new SyntaxException(src.loc(i), res.error); + else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax"); + + i += res.n; + + list.add(res.result); + } + + return list.toArray(Statement[]::new); + } + + public static boolean checkVarName(String name) { + return !JavaScript.reserved.contains(name); + } + public static CompileResult compile(Statement ...statements) { var target = new CompileResult(new LocalScopeRecord()); var stm = new CompoundStatement(null, true, statements); @@ -296,7 +297,7 @@ public class JavaScript { return target; } - public static CompileResult compile(Filename filename, String raw) { - return JavaScript.compile(JavaScript.parse(filename, raw)); + public static CompileResult compile(Environment env, Filename filename, String raw) { + return JavaScript.compile(JavaScript.parse(env, filename, raw)); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java index ad022e1..306c61c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java @@ -77,7 +77,7 @@ public class CallStatement extends Statement { var bracket = JSON.stringify(JSONElement.string(val)); if (!bracket.substring(1, bracket.length() - 1).equals(val)) return res + "[" + bracket + "]"; - if (Parsing.parseIdentifier(new Source(null, val), 0).n != val.length()) return res + "[" + bracket + "]"; + if (Parsing.parseIdentifier(new Source(val), 0).n != val.length()) return res + "[" + bracket + "]"; return res + "." + val; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java index bfd4aac..09c2029 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java @@ -43,7 +43,7 @@ public final class NumberValue extends PrimitiveValue { if (radix < 2 || radix > 36) return new NumberValue(Double.NaN); str = str.trim(); - var res = Parsing.parseInt(new Source(null, str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true); + var res = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true); if (res.isSuccess()) { if (relaxed || res.n == str.length()) return new NumberValue(res.result); } @@ -51,7 +51,7 @@ public final class NumberValue extends PrimitiveValue { } public static NumberValue parseFloat(String str, boolean relaxed) { str = str.trim(); - var res = Parsing.parseFloat(new Source(null, str), 0, true); + var res = Parsing.parseFloat(new Source(str), 0, true); if (res.isSuccess()) { if (relaxed || res.n == str.length()) return new NumberValue(res.result); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index 0da0f7c..ec87847 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -20,7 +20,7 @@ public final class StringValue extends PrimitiveValue { @Override public NumberValue toNumber(Environment ext) { var val = value.trim(); if (val.equals("")) return new NumberValue(0); - var res = Parsing.parseNumber(new Source(null, val), 0, true); + var res = Parsing.parseNumber(new Source(val), 0, true); if (res.isSuccess() && res.n == val.length()) return new NumberValue(res.result); else return new NumberValue(Double.NaN); -- 2.45.2 From 89ba921b4ad27fe1564f4b73eaeb8389408a4e43 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:30:13 +0300 Subject: [PATCH 09/48] refactor: rename statements to nodes --- .../jscript/common/parsing/Parsing.java | 10 +- .../jscript/compilation/AssignableNode.java | 7 + .../compilation/AssignableStatement.java | 7 - ...mpoundStatement.java => CompoundNode.java} | 24 +-- ...ressionParser.java => ExpressionNode.java} | 2 +- .../jscript/compilation/JavaScript.java | 190 +++++++++--------- .../compilation/{Statement.java => Node.java} | 4 +- ...taxStatement.java => ThrowSyntaxNode.java} | 4 +- ...tatement.java => VariableDeclareNode.java} | 20 +- .../{BreakStatement.java => BreakNode.java} | 12 +- ...ntinueStatement.java => ContinueNode.java} | 12 +- .../{DebugStatement.java => DebugNode.java} | 10 +- .../{DeleteStatement.java => DeleteNode.java} | 28 +-- ...DoWhileStatement.java => DoWhileNode.java} | 16 +- .../{ForInStatement.java => ForInNode.java} | 16 +- .../{ForStatement.java => ForNode.java} | 42 ++-- .../{ForOfStatement.java => ForOfNode.java} | 16 +- .../control/{IfStatement.java => IfNode.java} | 18 +- .../{ReturnStatement.java => ReturnNode.java} | 14 +- .../{SwitchStatement.java => SwitchNode.java} | 30 +-- .../{ThrowStatement.java => ThrowNode.java} | 14 +- .../{TryStatement.java => TryNode.java} | 26 +-- .../{WhileStatement.java => WhileNode.java} | 14 +- .../{ArrayStatement.java => ArrayNode.java} | 14 +- ...nctionStatement.java => FunctionNode.java} | 24 +-- ...ThisStatement.java => GlobalThisNode.java} | 6 +- .../{ObjectStatement.java => ObjectNode.java} | 41 ++-- .../{RegexStatement.java => RegexNode.java} | 10 +- ...riableStatement.java => VariableNode.java} | 18 +- .../{BoolStatement.java => BoolNode.java} | 6 +- .../values/constants/ConstantStatements.java | 4 - .../{NullStatement.java => NullNode.java} | 6 +- .../{NumberStatement.java => NumberNode.java} | 10 +- .../{StringStatement.java => StringNode.java} | 10 +- .../{CallStatement.java => CallNode.java} | 76 +++---- .../{ChangeStatement.java => ChangeNode.java} | 38 ++-- ...DiscardStatement.java => DiscardNode.java} | 12 +- ...ignStatement.java => IndexAssignNode.java} | 12 +- .../{IndexStatement.java => IndexNode.java} | 26 +-- ...LazyAndStatement.java => LazyAndNode.java} | 12 +- .../{LazyOrStatement.java => LazyOrNode.java} | 12 +- ...ationStatement.java => OperationNode.java} | 44 ++-- .../{TypeofStatement.java => TypeofNode.java} | 18 +- ...Statement.java => VariableAssignNode.java} | 14 +- ...xStatement.java => VariableIndexNode.java} | 6 +- 45 files changed, 475 insertions(+), 480 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/compilation/AssignableNode.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java rename src/java/me/topchetoeu/jscript/compilation/{CompoundStatement.java => CompoundNode.java} (73%) rename src/java/me/topchetoeu/jscript/compilation/{ExpressionParser.java => ExpressionNode.java} (61%) rename src/java/me/topchetoeu/jscript/compilation/{Statement.java => Node.java} (88%) rename src/java/me/topchetoeu/jscript/compilation/{ThrowSyntaxStatement.java => ThrowSyntaxNode.java} (75%) rename src/java/me/topchetoeu/jscript/compilation/{VariableDeclareStatement.java => VariableDeclareNode.java} (81%) rename src/java/me/topchetoeu/jscript/compilation/control/{BreakStatement.java => BreakNode.java} (79%) rename src/java/me/topchetoeu/jscript/compilation/control/{ContinueStatement.java => ContinueNode.java} (78%) rename src/java/me/topchetoeu/jscript/compilation/control/{DebugStatement.java => DebugNode.java} (79%) rename src/java/me/topchetoeu/jscript/compilation/control/{DeleteStatement.java => DeleteNode.java} (62%) rename src/java/me/topchetoeu/jscript/compilation/control/{DoWhileStatement.java => DoWhileNode.java} (80%) rename src/java/me/topchetoeu/jscript/compilation/control/{ForInStatement.java => ForInNode.java} (85%) rename src/java/me/topchetoeu/jscript/compilation/control/{ForStatement.java => ForNode.java} (70%) rename src/java/me/topchetoeu/jscript/compilation/control/{ForOfStatement.java => ForOfNode.java} (86%) rename src/java/me/topchetoeu/jscript/compilation/control/{IfStatement.java => IfNode.java} (83%) rename src/java/me/topchetoeu/jscript/compilation/control/{ReturnStatement.java => ReturnNode.java} (77%) rename src/java/me/topchetoeu/jscript/compilation/control/{SwitchStatement.java => SwitchNode.java} (87%) rename src/java/me/topchetoeu/jscript/compilation/control/{ThrowStatement.java => ThrowNode.java} (77%) rename src/java/me/topchetoeu/jscript/compilation/control/{TryStatement.java => TryNode.java} (81%) rename src/java/me/topchetoeu/jscript/compilation/control/{WhileStatement.java => WhileNode.java} (87%) rename src/java/me/topchetoeu/jscript/compilation/values/{ArrayStatement.java => ArrayNode.java} (84%) rename src/java/me/topchetoeu/jscript/compilation/values/{FunctionStatement.java => FunctionNode.java} (84%) rename src/java/me/topchetoeu/jscript/compilation/values/{GlobalThisStatement.java => GlobalThisNode.java} (74%) rename src/java/me/topchetoeu/jscript/compilation/values/{ObjectStatement.java => ObjectNode.java} (77%) rename src/java/me/topchetoeu/jscript/compilation/values/{RegexStatement.java => RegexNode.java} (86%) rename src/java/me/topchetoeu/jscript/compilation/values/{VariableStatement.java => VariableNode.java} (75%) rename src/java/me/topchetoeu/jscript/compilation/values/constants/{BoolStatement.java => BoolNode.java} (76%) delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java rename src/java/me/topchetoeu/jscript/compilation/values/constants/{NullStatement.java => NullNode.java} (72%) rename src/java/me/topchetoeu/jscript/compilation/values/constants/{NumberStatement.java => NumberNode.java} (77%) rename src/java/me/topchetoeu/jscript/compilation/values/constants/{StringStatement.java => StringNode.java} (71%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{CallStatement.java => CallNode.java} (67%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{ChangeStatement.java => ChangeNode.java} (54%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{DiscardStatement.java => DiscardNode.java} (76%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{IndexAssignStatement.java => IndexAssignNode.java} (79%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{IndexStatement.java => IndexNode.java} (71%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{LazyAndStatement.java => LazyAndNode.java} (77%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{LazyOrStatement.java => LazyOrNode.java} (77%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{OperationStatement.java => OperationNode.java} (80%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{TypeofStatement.java => TypeofNode.java} (72%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{VariableAssignStatement.java => VariableAssignNode.java} (66%) rename src/java/me/topchetoeu/jscript/compilation/values/operations/{VariableIndexStatement.java => VariableIndexNode.java} (75%) diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java index 62e2cdd..07047d4 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java @@ -1,6 +1,6 @@ package me.topchetoeu.jscript.common.parsing; -import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; +import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class Parsing { @@ -276,8 +276,8 @@ public class Parsing { if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus"); return ParseRes.failed(); } - else if (negative) return ParseRes.res(-(whole + fract) * NumberStatement.power(10, exponent), n); - else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n); + else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n); + else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n); } public static ParseRes parseFloat(Source src, int i, boolean withMinus) { var n = skipEmpty(src, i); @@ -336,8 +336,8 @@ public class Parsing { if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus"); return ParseRes.failed(); } - else if (negative) return ParseRes.res(-(whole + fract) * NumberStatement.power(10, exponent), n); - else return ParseRes.res((whole + fract) * NumberStatement.power(10, exponent), n); + else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n); + else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n); } public static ParseRes parseInt(Source src, int i, String alphabet, boolean withMinus) { var n = skipEmpty(src, i); diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableNode.java b/src/java/me/topchetoeu/jscript/compilation/AssignableNode.java new file mode 100644 index 0000000..e684c44 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/AssignableNode.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript.compilation; + +import me.topchetoeu.jscript.common.Operation; + +public interface AssignableNode { + public abstract Node toAssign(Node val, Operation operation); +} diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java deleted file mode 100644 index 40f9930..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -import me.topchetoeu.jscript.common.Operation; - -public interface AssignableStatement { - public abstract Statement toAssign(Statement val, Operation operation); -} diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java similarity index 73% rename from src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java rename to src/java/me/topchetoeu/jscript/compilation/CompoundNode.java index ca04696..305e6fd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -10,10 +10,10 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.values.FunctionStatement; +import me.topchetoeu.jscript.compilation.values.FunctionNode; -public class CompoundStatement extends Statement { - public final Statement[] statements; +public class CompoundNode extends Node { + public final Node[] statements; public final boolean separateFuncs; public Location end; @@ -32,9 +32,9 @@ public class CompoundStatement extends Statement { @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { - List statements = new Vector(); + List statements = new Vector(); if (separateFuncs) for (var stm : this.statements) { - if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) { + if (stm instanceof FunctionNode && ((FunctionNode)stm).statement) { stm.compile(target, false); } else statements.add(stm); @@ -55,18 +55,18 @@ public class CompoundStatement extends Statement { } } - public CompoundStatement setEnd(Location loc) { + public CompoundNode setEnd(Location loc) { this.end = loc; return this; } - public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) { + public CompoundNode(Location loc, boolean separateFuncs, Node ...statements) { super(loc); this.separateFuncs = separateFuncs; this.statements = statements; } - public static ParseRes parseComma(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseComma(Source src, int i, Node prev, int precedence) { if (precedence > 1) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -79,16 +79,16 @@ public class CompoundStatement extends Statement { if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma"); n += res.n; - return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n); + return ParseRes.res(new CompoundNode(loc, false, prev, res.result), n); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); if (!src.is(i + n, "{")) return ParseRes.failed(); n++; - var statements = new ArrayList(); + var statements = new ArrayList(); while (true) { n += Parsing.skipEmpty(src, i + n); @@ -109,6 +109,6 @@ public class CompoundStatement extends Statement { statements.add(res.result); } - return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(src.loc(i + n - 1)), n); + return ParseRes.res(new CompoundNode(loc, true, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java b/src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java similarity index 61% rename from src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java rename to src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java index cfbfafc..b8b8e0b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/ExpressionParser.java +++ b/src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java @@ -1,5 +1,5 @@ package me.topchetoeu.jscript.compilation; -public class ExpressionParser { +public class ExpressionNode { } diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index 949a3ea..2434885 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -10,38 +10,38 @@ import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.control.BreakStatement; -import me.topchetoeu.jscript.compilation.control.ContinueStatement; -import me.topchetoeu.jscript.compilation.control.DebugStatement; -import me.topchetoeu.jscript.compilation.control.DeleteStatement; -import me.topchetoeu.jscript.compilation.control.DoWhileStatement; -import me.topchetoeu.jscript.compilation.control.ForInStatement; -import me.topchetoeu.jscript.compilation.control.ForOfStatement; -import me.topchetoeu.jscript.compilation.control.ForStatement; -import me.topchetoeu.jscript.compilation.control.IfStatement; -import me.topchetoeu.jscript.compilation.control.ReturnStatement; -import me.topchetoeu.jscript.compilation.control.SwitchStatement; -import me.topchetoeu.jscript.compilation.control.ThrowStatement; -import me.topchetoeu.jscript.compilation.control.TryStatement; -import me.topchetoeu.jscript.compilation.control.WhileStatement; +import me.topchetoeu.jscript.compilation.control.BreakNode; +import me.topchetoeu.jscript.compilation.control.ContinueNode; +import me.topchetoeu.jscript.compilation.control.DebugNode; +import me.topchetoeu.jscript.compilation.control.DeleteNode; +import me.topchetoeu.jscript.compilation.control.DoWhileNode; +import me.topchetoeu.jscript.compilation.control.ForInNode; +import me.topchetoeu.jscript.compilation.control.ForOfNode; +import me.topchetoeu.jscript.compilation.control.ForNode; +import me.topchetoeu.jscript.compilation.control.IfNode; +import me.topchetoeu.jscript.compilation.control.ReturnNode; +import me.topchetoeu.jscript.compilation.control.SwitchNode; +import me.topchetoeu.jscript.compilation.control.ThrowNode; +import me.topchetoeu.jscript.compilation.control.TryNode; +import me.topchetoeu.jscript.compilation.control.WhileNode; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; -import me.topchetoeu.jscript.compilation.values.ArrayStatement; -import me.topchetoeu.jscript.compilation.values.FunctionStatement; -import me.topchetoeu.jscript.compilation.values.GlobalThisStatement; -import me.topchetoeu.jscript.compilation.values.ObjectStatement; -import me.topchetoeu.jscript.compilation.values.RegexStatement; -import me.topchetoeu.jscript.compilation.values.VariableStatement; -import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; -import me.topchetoeu.jscript.compilation.values.constants.NullStatement; -import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; -import me.topchetoeu.jscript.compilation.values.constants.StringStatement; -import me.topchetoeu.jscript.compilation.values.operations.CallStatement; -import me.topchetoeu.jscript.compilation.values.operations.ChangeStatement; -import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement; -import me.topchetoeu.jscript.compilation.values.operations.IndexStatement; -import me.topchetoeu.jscript.compilation.values.operations.OperationStatement; -import me.topchetoeu.jscript.compilation.values.operations.TypeofStatement; -import me.topchetoeu.jscript.compilation.values.operations.VariableIndexStatement; +import me.topchetoeu.jscript.compilation.values.ArrayNode; +import me.topchetoeu.jscript.compilation.values.FunctionNode; +import me.topchetoeu.jscript.compilation.values.GlobalThisNode; +import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.RegexNode; +import me.topchetoeu.jscript.compilation.values.VariableNode; +import me.topchetoeu.jscript.compilation.values.constants.BoolNode; +import me.topchetoeu.jscript.compilation.values.constants.NullNode; +import me.topchetoeu.jscript.compilation.values.constants.NumberNode; +import me.topchetoeu.jscript.compilation.values.constants.StringNode; +import me.topchetoeu.jscript.compilation.values.operations.CallNode; +import me.topchetoeu.jscript.compilation.values.operations.ChangeNode; +import me.topchetoeu.jscript.compilation.values.operations.DiscardNode; +import me.topchetoeu.jscript.compilation.values.operations.IndexNode; +import me.topchetoeu.jscript.compilation.values.operations.OperationNode; +import me.topchetoeu.jscript.compilation.values.operations.TypeofNode; +import me.topchetoeu.jscript.compilation.values.operations.VariableIndexNode; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class JavaScript { @@ -53,7 +53,7 @@ public class JavaScript { "protected", "public", "static" ); - public static ParseRes parseParens(Source src, int i) { + public static ParseRes parseParens(Source src, int i) { int n = 0; var openParen = Parsing.parseOperator(src, i + n, "("); @@ -71,28 +71,28 @@ public class JavaScript { return ParseRes.res(res.result, n); } - public static ParseRes parseSimple(Source src, int i, boolean statement) { + public static ParseRes parseSimple(Source src, int i, boolean statement) { return ParseRes.first(src, i, - (s, j) -> statement ? ParseRes.failed() : ObjectStatement.parse(s, j), - (s, j) -> statement ? ParseRes.failed() : FunctionStatement.parseFunction(s, j, false), + (s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j), + (s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false), JavaScript::parseLiteral, - StringStatement::parse, - RegexStatement::parse, - NumberStatement::parse, - ChangeStatement::parsePrefixDecrease, - ChangeStatement::parsePrefixIncrease, - OperationStatement::parsePrefix, - ArrayStatement::parse, + StringNode::parse, + RegexNode::parse, + NumberNode::parse, + ChangeNode::parsePrefixDecrease, + ChangeNode::parsePrefixIncrease, + OperationNode::parsePrefix, + ArrayNode::parse, JavaScript::parseParens, - CallStatement::parseNew, - TypeofStatement::parse, - DiscardStatement::parse, - DeleteStatement::parse, - VariableStatement::parse + CallNode::parseNew, + TypeofNode::parse, + DiscardNode::parse, + DeleteNode::parse, + VariableNode::parse ); } - public static ParseRes parseLiteral(Source src, int i) { + public static ParseRes parseLiteral(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -100,20 +100,20 @@ public class JavaScript { if (!id.isSuccess()) return id.chainError(); n += id.n; - if (id.result.equals("true")) return ParseRes.res(new BoolStatement(loc, true), n); - if (id.result.equals("false")) return ParseRes.res(new BoolStatement(loc, false), n); - if (id.result.equals("undefined")) return ParseRes.res(new DiscardStatement(loc, null), n); - if (id.result.equals("null")) return ParseRes.res(new NullStatement(loc), n); - if (id.result.equals("this")) return ParseRes.res(new VariableIndexStatement(loc, 0), n); - if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexStatement(loc, 1), n); - if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisStatement(loc), n); + if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), n); + if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); + if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n); + if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); + if (id.result.equals("this")) return ParseRes.res(new VariableIndexNode(loc, 0), n); + if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexNode(loc, 1), n); + if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); return ParseRes.failed(); } - public static ParseRes parseExpression(Source src, int i, int precedence, boolean statement) { + public static ParseRes parseExpression(Source src, int i, int precedence, boolean statement) { var n = Parsing.skipEmpty(src, i); - Statement prev = null; + Node prev = null; while (true) { if (prev == null) { @@ -127,17 +127,17 @@ public class JavaScript { } else { var _prev = prev; - ParseRes res = ParseRes.first(src, i + n, - (s, j) -> OperationStatement.parseInstanceof(s, j, _prev, precedence), - (s, j) -> OperationStatement.parseIn(s, j, _prev, precedence), - (s, j) -> ChangeStatement.parsePostfixIncrease(s, j, _prev, precedence), - (s, j) -> ChangeStatement.parsePostfixDecrease(s, j, _prev, precedence), - (s, j) -> OperationStatement.parseOperator(s, j, _prev, precedence), - (s, j) -> IfStatement.parseTernary(s, j, _prev, precedence), - (s, j) -> IndexStatement.parseMember(s, j, _prev, precedence), - (s, j) -> IndexStatement.parseIndex(s, j, _prev, precedence), - (s, j) -> CallStatement.parseCall(s, j, _prev, precedence), - (s, j) -> CompoundStatement.parseComma(s, j, _prev, precedence) + ParseRes res = ParseRes.first(src, i + n, + (s, j) -> OperationNode.parseInstanceof(s, j, _prev, precedence), + (s, j) -> OperationNode.parseIn(s, j, _prev, precedence), + (s, j) -> ChangeNode.parsePostfixIncrease(s, j, _prev, precedence), + (s, j) -> ChangeNode.parsePostfixDecrease(s, j, _prev, precedence), + (s, j) -> OperationNode.parseOperator(s, j, _prev, precedence), + (s, j) -> IfNode.parseTernary(s, j, _prev, precedence), + (s, j) -> IndexNode.parseMember(s, j, _prev, precedence), + (s, j) -> IndexNode.parseIndex(s, j, _prev, precedence), + (s, j) -> CallNode.parseCall(s, j, _prev, precedence), + (s, j) -> CompoundNode.parseComma(s, j, _prev, precedence) ); if (res.isSuccess()) { @@ -155,11 +155,11 @@ public class JavaScript { else return ParseRes.res(prev, n); } - public static ParseRes parseExpression(Source src, int i, int precedence) { + public static ParseRes parseExpression(Source src, int i, int precedence) { return parseExpression(src, i, precedence, false); } - public static ParseRes parseExpressionStatement(Source src, int i) { + public static ParseRes parseExpressionStatement(Source src, int i) { var res = parseExpression(src, i, 0, true); if (!res.isSuccess()) return res.chainError(); @@ -169,29 +169,29 @@ public class JavaScript { return res.addN(end.n); } - public static ParseRes parseStatement(Source src, int i) { + public static ParseRes parseStatement(Source src, int i) { var n = Parsing.skipEmpty(src, i); - if (src.is(i + n, ";")) return ParseRes.res(new DiscardStatement(src.loc(i+ n), null), n + 1); + if (src.is(i + n, ";")) return ParseRes.res(new DiscardNode(src.loc(i+ n), null), n + 1); if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed."); - ParseRes res = ParseRes.first(src, i + n, - VariableDeclareStatement::parse, - ReturnStatement::parse, - ThrowStatement::parse, - ContinueStatement::parse, - BreakStatement::parse, - DebugStatement::parse, - IfStatement::parse, - WhileStatement::parse, - SwitchStatement::parse, - ForStatement::parse, - ForInStatement::parse, - ForOfStatement::parse, - DoWhileStatement::parse, - TryStatement::parse, - CompoundStatement::parse, - (s, j) -> FunctionStatement.parseFunction(s, j, true), + ParseRes res = ParseRes.first(src, i + n, + VariableDeclareNode::parse, + ReturnNode::parse, + ThrowNode::parse, + ContinueNode::parse, + BreakNode::parse, + DebugNode::parse, + IfNode::parse, + WhileNode::parse, + SwitchNode::parse, + ForNode::parse, + ForInNode::parse, + ForOfNode::parse, + DoWhileNode::parse, + TryNode::parse, + CompoundNode::parse, + (s, j) -> FunctionNode.parseFunction(s, j, true), JavaScript::parseExpressionStatement ); return res.addN(n); @@ -247,9 +247,9 @@ public class JavaScript { return ParseRes.res(args, n); } - public static Statement[] parse(Environment env, Filename filename, String raw) { + public static Node[] parse(Environment env, Filename filename, String raw) { var src = new Source(env, filename, raw); - var list = new ArrayList(); + var list = new ArrayList(); int i = 0; while (true) { @@ -265,23 +265,23 @@ public class JavaScript { list.add(res.result); } - return list.toArray(Statement[]::new); + return list.toArray(Node[]::new); } public static boolean checkVarName(String name) { return !JavaScript.reserved.contains(name); } - public static CompileResult compile(Statement ...statements) { + public static CompileResult compile(Node ...statements) { var target = new CompileResult(new LocalScopeRecord()); - var stm = new CompoundStatement(null, true, statements); + var stm = new CompoundNode(null, true, statements); target.scope.define("this"); target.scope.define("arguments"); try { stm.compile(target, true); - FunctionStatement.checkBreakAndCont(target, 0); + FunctionNode.checkBreakAndCont(target, 0); } catch (SyntaxException e) { target = new CompileResult(new LocalScopeRecord()); diff --git a/src/java/me/topchetoeu/jscript/compilation/Statement.java b/src/java/me/topchetoeu/jscript/compilation/Node.java similarity index 88% rename from src/java/me/topchetoeu/jscript/compilation/Statement.java rename to src/java/me/topchetoeu/jscript/compilation/Node.java index c9c14c3..cf2c2fb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/Statement.java +++ b/src/java/me/topchetoeu/jscript/compilation/Node.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; -public abstract class Statement { +public abstract class Node { private Location _loc; public boolean pure() { return false; } @@ -21,7 +21,7 @@ public abstract class Statement { public Location loc() { return _loc; } public void setLoc(Location loc) { _loc = loc; } - protected Statement(Location loc) { + protected Node(Location loc) { this._loc = loc; } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxStatement.java b/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java similarity index 75% rename from src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxStatement.java rename to src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java index 523e10a..b2d29b5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -public class ThrowSyntaxStatement extends Statement { +public class ThrowSyntaxNode extends Node { public final String name; @Override @@ -11,7 +11,7 @@ public class ThrowSyntaxStatement extends Statement { target.add(Instruction.throwSyntax(name)); } - public ThrowSyntaxStatement(SyntaxException e) { + public ThrowSyntaxNode(SyntaxException e) { super(e.loc); this.name = e.msg; } diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java similarity index 81% rename from src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java rename to src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 603a3c8..4d0ba04 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -9,15 +9,15 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.values.FunctionStatement; +import me.topchetoeu.jscript.compilation.values.FunctionNode; -public class VariableDeclareStatement extends Statement { +public class VariableDeclareNode extends Node { public static class Pair { public final String name; - public final Statement value; + public final Node value; public final Location location; - public Pair(String name, Statement value, Location location) { + public Pair(String name, Node value, Location location) { this.name = name; this.value = value; this.location = location; @@ -41,7 +41,7 @@ public class VariableDeclareStatement extends Statement { if (key instanceof String) target.add(Instruction.makeVar((String)key)); if (entry.value != null) { - FunctionStatement.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER); + FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER); target.add(Instruction.storeVar(key)); } } @@ -49,12 +49,12 @@ public class VariableDeclareStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public VariableDeclareStatement(Location loc, List values) { + public VariableDeclareNode(Location loc, List values) { super(loc); this.values = values; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -66,7 +66,7 @@ public class VariableDeclareStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new VariableDeclareStatement(loc, res), n); + return ParseRes.res(new VariableDeclareNode(loc, res), n); } while (true) { @@ -79,7 +79,7 @@ public class VariableDeclareStatement extends Statement { return ParseRes.error(src.loc(i + n), String.format("Unexpected identifier '%s'", name.result)); } - Statement val = null; + Node val = null; n += Parsing.skipEmpty(src, i + n); if (src.is(i + n, "=")) { @@ -104,7 +104,7 @@ public class VariableDeclareStatement extends Statement { if (end.isSuccess()) { n += end.n; - return ParseRes.res(new VariableDeclareStatement(loc, res), n); + return ParseRes.res(new VariableDeclareNode(loc, res), n); } else return end.chainError(src.loc(i + n), "Expected a comma or end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java similarity index 79% rename from src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java index d7d1758..f3f95fd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java @@ -7,9 +7,9 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class BreakStatement extends Statement { +public class BreakNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { @@ -17,12 +17,12 @@ public class BreakStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public BreakStatement(Location loc, String label) { + public BreakNode(Location loc, String label) { super(loc); this.label = label; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -32,7 +32,7 @@ public class BreakStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new BreakStatement(loc, null), n); + return ParseRes.res(new BreakNode(loc, null), n); } var label = Parsing.parseIdentifier(src, i + n); @@ -42,7 +42,7 @@ public class BreakStatement extends Statement { end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new BreakStatement(loc, label.result), n); + return ParseRes.res(new BreakNode(loc, label.result), n); } else return end.chainError(src.loc(i + n), "Expected end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java similarity index 78% rename from src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java index 88f1551..b3b5761 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java @@ -7,9 +7,9 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ContinueStatement extends Statement { +public class ContinueNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { @@ -17,12 +17,12 @@ public class ContinueStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public ContinueStatement(Location loc, String label) { + public ContinueNode(Location loc, String label) { super(loc); this.label = label; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -32,7 +32,7 @@ public class ContinueStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new ContinueStatement(loc, null), n); + return ParseRes.res(new ContinueNode(loc, null), n); } var label = Parsing.parseIdentifier(src, i + n); @@ -42,7 +42,7 @@ public class ContinueStatement extends Statement { end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new ContinueStatement(loc, label.result), n); + return ParseRes.res(new ContinueNode(loc, label.result), n); } else return end.chainError(src.loc(i + n), "Expected end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DebugNode.java similarity index 79% rename from src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/DebugNode.java index 58d9fa9..9464e9d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DebugStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DebugNode.java @@ -7,19 +7,19 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class DebugStatement extends Statement { +public class DebugNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.debug()); if (pollute) target.add(Instruction.pushUndefined()); } - public DebugStatement(Location loc) { + public DebugNode(Location loc) { super(loc); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -29,7 +29,7 @@ public class DebugStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new DebugStatement(loc), n); + return ParseRes.res(new DebugNode(loc), n); } else return end.chainError(src.loc(i + n), "Expected end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java similarity index 62% rename from src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java index 4731feb..57071e6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DeleteStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java @@ -7,14 +7,14 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.VariableStatement; -import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; -import me.topchetoeu.jscript.compilation.values.operations.IndexStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.VariableNode; +import me.topchetoeu.jscript.compilation.values.constants.BoolNode; +import me.topchetoeu.jscript.compilation.values.operations.IndexNode; -public class DeleteStatement extends Statement { - public final Statement key; - public final Statement value; +public class DeleteNode extends Node { + public final Node key; + public final Node value; @Override public void compile(CompileResult target, boolean pollute) { @@ -25,7 +25,7 @@ public class DeleteStatement extends Statement { if (pollute) target.add(Instruction.pushValue(true)); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -36,17 +36,17 @@ public class DeleteStatement extends Statement { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'"); n += valRes.n; - if (valRes.result instanceof IndexStatement) { - var index = (IndexStatement)valRes.result; - return ParseRes.res(new DeleteStatement(loc, index.index, index.object), n); + if (valRes.result instanceof IndexNode) { + var index = (IndexNode)valRes.result; + return ParseRes.res(new DeleteNode(loc, index.index, index.object), n); } - else if (valRes.result instanceof VariableStatement) { + else if (valRes.result instanceof VariableNode) { return ParseRes.error(src.loc(i + n), "A variable may not be deleted"); } - else return ParseRes.res(new BoolStatement(loc, true), n); + else return ParseRes.res(new BoolNode(loc, true), n); } - public DeleteStatement(Location loc, Statement key, Statement value) { + public DeleteNode(Location loc, Node key, Node value) { super(loc); this.key = key; this.value = value; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java similarity index 80% rename from src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java index 94cc1c5..616cd6a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java @@ -8,10 +8,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class DoWhileStatement extends Statement { - public final Statement condition, body; +public class DoWhileNode extends Node { + public final Node condition, body; public final String label; @Override @@ -27,22 +27,22 @@ public class DoWhileStatement extends Statement { condition.compile(target, true, BreakpointType.STEP_OVER); int end = target.size(); - WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1); + WhileNode.replaceBreaks(target, label, start, mid - 1, mid, end + 1); target.add(Instruction.jmpIf(start - end)); } - public DoWhileStatement(Location loc, String label, Statement condition, Statement body) { + public DoWhileNode(Location loc, String label, Node condition, Node body) { super(loc); this.label = label; this.condition = condition; this.body = body; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var labelRes = WhileStatement.parseLabel(src, i + n); + var labelRes = WhileNode.parseLabel(src, i + n); n += labelRes.n; if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed(); @@ -70,7 +70,7 @@ public class DoWhileStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new DoWhileStatement(loc, labelRes.result, condRes.result, bodyRes.result), n); + return ParseRes.res(new DoWhileNode(loc, labelRes.result, condRes.result, bodyRes.result), n); } else return end.chainError(src.loc(i + n), "Expected end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java similarity index 85% rename from src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index 2cb0a5d..ae89d7c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -9,12 +9,12 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ForInStatement extends Statement { +public class ForInNode extends Node { public final String varName; public final boolean isDeclaration; - public final Statement object, body; + public final Node object, body; public final String label; public final Location varLocation; @@ -45,7 +45,7 @@ public class ForInStatement extends Statement { int end = target.size(); - WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1); + WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); target.add(Instruction.jmp(start - end)); target.add(Instruction.discard()); @@ -53,7 +53,7 @@ public class ForInStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement object, Statement body) { + public ForInNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) { super(loc); this.varLocation = varLocation; this.label = label; @@ -63,11 +63,11 @@ public class ForInStatement extends Statement { this.body = body; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var label = WhileStatement.parseLabel(src, i + n); + var label = WhileNode.parseLabel(src, i + n); n += label.n; n += Parsing.skipEmpty(src, i + n); @@ -103,6 +103,6 @@ public class ForInStatement extends Statement { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; - return ParseRes.res(new ForInStatement(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForInNode(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java similarity index 70% rename from src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/ForNode.java index d9135ba..44e28fc 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -8,12 +8,12 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.VariableDeclareStatement; -import me.topchetoeu.jscript.compilation.values.operations.DiscardStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.VariableDeclareNode; +import me.topchetoeu.jscript.compilation.values.operations.DiscardNode; -public class ForStatement extends Statement { - public final Statement declaration, assignment, condition, body; +public class ForNode extends Node { + public final Node declaration, assignment, condition, body; public final String label; @Override @@ -33,14 +33,14 @@ public class ForStatement extends Statement { assignment.compile(target, false, BreakpointType.STEP_OVER); int end = target.size(); - WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1); + WhileNode.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1); target.add(Instruction.jmp(start - end)); target.set(mid, Instruction.jmpIfNot(end - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } - public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) { + public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) { super(loc); this.label = label; this.declaration = declaration; @@ -49,13 +49,13 @@ public class ForStatement extends Statement { this.body = body; } - private static ParseRes parseSemicolon(Source src, int i) { + private static ParseRes parseSemicolon(Source src, int i) { var n = Parsing.skipEmpty(src, i); if (!src.is(i + n, ";")) return ParseRes.failed(); - else return ParseRes.res(new DiscardStatement(src.loc(i), null), n + 1); + else return ParseRes.res(new DiscardNode(src.loc(i), null), n + 1); } - private static ParseRes parseCondition(Source src, int i) { + private static ParseRes parseCondition(Source src, int i) { var n = Parsing.skipEmpty(src, i); var res = JavaScript.parseExpression(src, i + n, 0); @@ -66,15 +66,15 @@ public class ForStatement extends Statement { if (!src.is(i + n, ";")) return ParseRes.error(src.loc(i + n), "Expected a semicolon"); else return ParseRes.res(res.result, n + 1); } - private static ParseRes parseUpdater(Source src, int i) { + private static ParseRes parseUpdater(Source src, int i) { return JavaScript.parseExpression(src, i, 0); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var labelRes = WhileStatement.parseLabel(src, i + n); + var labelRes = WhileNode.parseLabel(src, i + n); n += labelRes.n; n += Parsing.skipEmpty(src, i + n); @@ -85,17 +85,17 @@ public class ForStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'for'"); n++; - ParseRes decl = ParseRes.first(src, i + n, - ForStatement::parseSemicolon, - VariableDeclareStatement::parse, - ForStatement::parseCondition + ParseRes decl = ParseRes.first(src, i + n, + ForNode::parseSemicolon, + VariableDeclareNode::parse, + ForNode::parseCondition ); if (!decl.isSuccess()) return decl.chainError(src.loc(i + n), "Expected a declaration or an expression"); n += decl.n; - ParseRes cond = ParseRes.first(src, i + n, - ForStatement::parseSemicolon, - ForStatement::parseCondition + ParseRes cond = ParseRes.first(src, i + n, + ForNode::parseSemicolon, + ForNode::parseCondition ); if (!cond.isSuccess()) return cond.chainError(src.loc(i + n), "Expected a condition"); n += cond.n; @@ -112,6 +112,6 @@ public class ForStatement extends Statement { if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body."); n += body.n; - return ParseRes.res(new ForStatement(loc, labelRes.result, decl.result, cond.result, update.result, body.result), n); + return ParseRes.res(new ForNode(loc, labelRes.result, decl.result, cond.result, update.result, body.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java similarity index 86% rename from src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index fe64e92..ce214ce 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -8,12 +8,12 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ForOfStatement extends Statement { +public class ForOfNode extends Node { public final String varName; public final boolean isDeclaration; - public final Statement iterable, body; + public final Node iterable, body; public final String label; public final Location varLocation; @@ -56,7 +56,7 @@ public class ForOfStatement extends Statement { int end = target.size(); - WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1); + WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); target.add(Instruction.jmp(start - end)); target.add(Instruction.discard()); @@ -65,7 +65,7 @@ public class ForOfStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public ForOfStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement object, Statement body) { + public ForOfNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) { super(loc); this.varLocation = varLocation; this.label = label; @@ -75,11 +75,11 @@ public class ForOfStatement extends Statement { this.body = body; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var label = WhileStatement.parseLabel(src, i + n); + var label = WhileNode.parseLabel(src, i + n); n += label.n; n += Parsing.skipEmpty(src, i + n); @@ -115,6 +115,6 @@ public class ForOfStatement extends Statement { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); n += bodyRes.n; - return ParseRes.res(new ForOfStatement(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java similarity index 83% rename from src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/IfNode.java index 992b89c..f22c2dd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -8,10 +8,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class IfStatement extends Statement { - public final Statement condition, body, elseBody; +public class IfNode extends Node { + public final Node condition, body, elseBody; @Override public void declare(CompileResult target) { @@ -43,14 +43,14 @@ public class IfStatement extends Statement { compile(target, pollute, BreakpointType.STEP_IN); } - public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) { + public IfNode(Location loc, Node condition, Node body, Node elseBody) { super(loc); this.condition = condition; this.body = body; this.elseBody = elseBody; } - public static ParseRes parseTernary(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseTernary(Source src, int i, Node prev, int precedence) { if (precedence > 2) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -71,9 +71,9 @@ public class IfStatement extends Statement { if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator."); n += b.n; - return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n); + return ParseRes.res(new IfNode(loc, prev, a.result, b.result), n); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -97,14 +97,14 @@ public class IfStatement extends Statement { n += res.n; var elseKw = Parsing.parseIdentifier(src, i + n, "else"); - if (!elseKw.isSuccess()) return ParseRes.res(new IfStatement(loc, condRes.result, res.result, null), n); + if (!elseKw.isSuccess()) return ParseRes.res(new IfNode(loc, condRes.result, res.result, null), n); n += elseKw.n; var elseRes = JavaScript.parseStatement(src, i + n); if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body."); n += elseRes.n; - return ParseRes.res(new IfStatement(loc, condRes.result, res.result, elseRes.result), n); + return ParseRes.res(new IfNode(loc, condRes.result, res.result, elseRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java similarity index 77% rename from src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java index 2769d0a..19582ae 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ReturnStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java @@ -7,10 +7,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ReturnStatement extends Statement { - public final Statement value; +public class ReturnNode extends Node { + public final Node value; @Override public void compile(CompileResult target, boolean pollute) { @@ -19,12 +19,12 @@ public class ReturnStatement extends Statement { target.add(Instruction.ret()).setLocation(loc()); } - public ReturnStatement(Location loc, Statement value) { + public ReturnNode(Location loc, Node value) { super(loc); this.value = value; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -34,7 +34,7 @@ public class ReturnStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new ReturnStatement(loc, null), n); + return ParseRes.res(new ReturnNode(loc, null), n); } var val = JavaScript.parseExpression(src, i + n, 0); @@ -44,7 +44,7 @@ public class ReturnStatement extends Statement { end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new ReturnStatement(loc, val.result), n); + return ParseRes.res(new ReturnNode(loc, val.result), n); } else return end.chainError(src.loc(i + n), "Expected end of statement or a return value"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java similarity index 87% rename from src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index bc11ca1..0d50aab 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -13,22 +13,22 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class SwitchStatement extends Statement { +public class SwitchNode extends Node { public static class SwitchCase { - public final Statement value; + public final Node value; public final int statementI; - public SwitchCase(Statement value, int statementI) { + public SwitchCase(Node value, int statementI) { this.value = value; this.statementI = statementI; } } - public final Statement value; + public final Node value; public final SwitchCase[] cases; - public final Statement[] body; + public final Node[] body; public final int defaultI; @Override public void declare(CompileResult target) { @@ -76,7 +76,7 @@ public class SwitchStatement extends Statement { } - public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) { + public SwitchNode(Location loc, Node value, int defaultI, SwitchCase[] cases, Node[] body) { super(loc); this.value = value; this.defaultI = defaultI; @@ -84,7 +84,7 @@ public class SwitchStatement extends Statement { this.body = body; } - private static ParseRes parseSwitchCase(Source src, int i) { + private static ParseRes parseSwitchCase(Source src, int i) { var n = Parsing.skipEmpty(src, i); if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed(); @@ -112,7 +112,7 @@ public class SwitchStatement extends Statement { return ParseRes.res(null, n); } @SuppressWarnings("unused") - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -135,7 +135,7 @@ public class SwitchStatement extends Statement { n++; n += Parsing.skipEmpty(src, i + n); - var statements = new ArrayList(); + var statements = new ArrayList(); var cases = new ArrayList(); var defaultI = -1; @@ -151,9 +151,9 @@ public class SwitchStatement extends Statement { continue; } - ParseRes caseRes = ParseRes.first(src, i + n, - SwitchStatement::parseDefaultCase, - SwitchStatement::parseSwitchCase + ParseRes caseRes = ParseRes.first(src, i + n, + SwitchNode::parseDefaultCase, + SwitchNode::parseSwitchCase ); // Parsing::parseStatement @@ -176,10 +176,10 @@ public class SwitchStatement extends Statement { else stm.chainError(src.loc(i + n), "Expected a statement, 'case' or 'default'"); } - return ParseRes.res(new SwitchStatement( + return ParseRes.res(new SwitchNode( loc, valRes.result, defaultI, cases.toArray(SwitchCase[]::new), - statements.toArray(Statement[]::new) + statements.toArray(Node[]::new) ), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java similarity index 77% rename from src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java index a122473..3552adc 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ThrowStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java @@ -7,10 +7,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ThrowStatement extends Statement { - public final Statement value; +public class ThrowNode extends Node { + public final Node value; @Override public void compile(CompileResult target, boolean pollute) { @@ -18,12 +18,12 @@ public class ThrowStatement extends Statement { target.add(Instruction.throwInstr()).setLocation(loc()); } - public ThrowStatement(Location loc, Statement value) { + public ThrowNode(Location loc, Node value) { super(loc); this.value = value; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -33,7 +33,7 @@ public class ThrowStatement extends Statement { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new ThrowStatement(loc, null), n); + return ParseRes.res(new ThrowNode(loc, null), n); } var val = JavaScript.parseExpression(src, i + n, 0); @@ -43,7 +43,7 @@ public class ThrowStatement extends Statement { end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new ThrowStatement(loc, val.result), n); + return ParseRes.res(new ThrowNode(loc, val.result), n); } else return end.chainError(src.loc(i + n), "Expected end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java similarity index 81% rename from src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/TryNode.java index c5b46e1..e9b34a2 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/TryStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -7,13 +7,13 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundStatement; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.CompoundNode; +import me.topchetoeu.jscript.compilation.Node; -public class TryStatement extends Statement { - public final Statement tryBody; - public final Statement catchBody; - public final Statement finallyBody; +public class TryNode extends Node { + public final Node tryBody; + public final Node catchBody; + public final Node finallyBody; public final String name; @Override public void declare(CompileResult target) { @@ -50,7 +50,7 @@ public class TryStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) { + public TryNode(Location loc, Node tryBody, Node catchBody, Node finallyBody, String name) { super(loc); this.tryBody = tryBody; this.catchBody = catchBody; @@ -58,20 +58,20 @@ public class TryStatement extends Statement { this.name = name; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); if (!Parsing.isIdentifier(src, i + n, "try")) return ParseRes.failed(); n += 3; - var tryBody = CompoundStatement.parse(src, i + n); + var tryBody = CompoundNode.parse(src, i + n); if (!tryBody.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a try body"); n += tryBody.n; n += Parsing.skipEmpty(src, i + n); String name = null; - Statement catchBody = null, finallyBody = null; + Node catchBody = null, finallyBody = null; if (Parsing.isIdentifier(src, i + n, "catch")) { n += 5; @@ -88,7 +88,7 @@ public class TryStatement extends Statement { n++; } - var bodyRes = CompoundStatement.parse(src, i + n); + var bodyRes = CompoundNode.parse(src, i + n); if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a catch body"); n += bodyRes.n; n += Parsing.skipEmpty(src, i + n); @@ -99,7 +99,7 @@ public class TryStatement extends Statement { if (Parsing.isIdentifier(src, i + n, "finally")) { n += 7; - var bodyRes = CompoundStatement.parse(src, i + n); + var bodyRes = CompoundNode.parse(src, i + n); if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a finally body"); n += bodyRes.n; n += Parsing.skipEmpty(src, i + n); @@ -108,6 +108,6 @@ public class TryStatement extends Statement { if (finallyBody == null && catchBody == null) ParseRes.error(src.loc(i + n), "Expected catch or finally"); - return ParseRes.res(new TryStatement(loc, tryBody.result, catchBody, finallyBody, name), n); + return ParseRes.res(new TryNode(loc, tryBody.result, catchBody, finallyBody, name), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java similarity index 87% rename from src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java rename to src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index 1393f6f..484818b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -9,10 +9,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class WhileStatement extends Statement { - public final Statement condition, body; +public class WhileNode extends Node { + public final Node condition, body; public final String label; @Override @@ -49,7 +49,7 @@ public class WhileStatement extends Statement { return ParseRes.res(nameRes.result, n); } - public WhileStatement(Location loc, String label, Statement condition, Statement body) { + public WhileNode(Location loc, String label, Node condition, Node body) { super(loc); this.label = label; this.condition = condition; @@ -68,11 +68,11 @@ public class WhileStatement extends Statement { } } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var labelRes = WhileStatement.parseLabel(src, i + n); + var labelRes = WhileNode.parseLabel(src, i + n); n += labelRes.n; n += Parsing.skipEmpty(src, i + n); @@ -95,6 +95,6 @@ public class WhileStatement extends Statement { if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body."); n += res.n; - return ParseRes.res(new WhileStatement(loc, labelRes.result, condRes.result, res.result), n); + return ParseRes.res(new WhileNode(loc, labelRes.result, condRes.result, res.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java similarity index 84% rename from src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java index 015311c..a2f2fe2 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java @@ -9,10 +9,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ArrayStatement extends Statement { - public final Statement[] statements; +public class ArrayNode extends Node { + public final Node[] statements; @Override public boolean pure() { for (var stm : statements) { @@ -38,19 +38,19 @@ public class ArrayStatement extends Statement { if (!pollute) target.add(Instruction.discard()); } - public ArrayStatement(Location loc, Statement[] statements) { + public ArrayNode(Location loc, Node[] statements) { super(loc); this.statements = statements; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); if (!src.is(i + n, "[")) return ParseRes.failed(); n++; - var values = new ArrayList(); + var values = new ArrayList(); loop: while (true) { n += Parsing.skipEmpty(src, i + n); @@ -84,6 +84,6 @@ public class ArrayStatement extends Statement { } } - return ParseRes.res(new ArrayStatement(loc, values.toArray(Statement[]::new)), n); + return ParseRes.res(new ArrayNode(loc, values.toArray(Node[]::new)), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java similarity index 84% rename from src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java index 2dbdd7f..2a73052 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java @@ -8,13 +8,13 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundStatement; +import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -public class FunctionStatement extends Statement { - public final CompoundStatement body; +public class FunctionNode extends Node { + public final CompoundNode body; public final String varName; public final String[] args; public final boolean statement; @@ -101,7 +101,7 @@ public class FunctionStatement extends Statement { compile(target, pollute, (String)null, BreakpointType.NONE); } - public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) { + public FunctionNode(Location loc, Location end, String varName, String[] args, boolean statement, CompoundNode body) { super(loc); this.end = end; @@ -112,16 +112,16 @@ public class FunctionStatement extends Statement { this.body = body; } - public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name) { - if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name); + public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) { + if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name); else stm.compile(target, pollute); } - public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name, BreakpointType bp) { - if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name, bp); + public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name, BreakpointType bp) { + if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name, bp); else stm.compile(target, pollute, bp); } - public static ParseRes parseFunction(Source src, int i, boolean statement) { + public static ParseRes parseFunction(Source src, int i, boolean statement) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -137,11 +137,11 @@ public class FunctionStatement extends Statement { if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); n += args.n; - var res = CompoundStatement.parse(src, i + n); + var res = CompoundNode.parse(src, i + n); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a compound statement for function."); n += res.n; - return ParseRes.res(new FunctionStatement( + return ParseRes.res(new FunctionNode( loc, src.loc(i + n - 1), nameRes.result, args.result.toArray(String[]::new), statement, res.result diff --git a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java similarity index 74% rename from src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java index da8aa43..d80658b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java @@ -3,9 +3,9 @@ package me.topchetoeu.jscript.compilation.values; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class GlobalThisStatement extends Statement { +public class GlobalThisNode extends Node { @Override public boolean pure() { return true; } @Override @@ -13,7 +13,7 @@ public class GlobalThisStatement extends Statement { if (pollute) target.add(Instruction.loadGlob()); } - public GlobalThisStatement(Location loc) { + public GlobalThisNode(Location loc) { super(loc); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java similarity index 77% rename from src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index e9d954d..257ed81 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -10,26 +10,26 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundStatement; +import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class ObjectStatement extends Statement { +public class ObjectNode extends Node { public static class ObjProp { public final String name; public final String access; - public final FunctionStatement func; + public final FunctionNode func; - public ObjProp(String name, String access, FunctionStatement func) { + public ObjProp(String name, String access, FunctionNode func) { this.name = name; this.access = access; this.func = func; } } - public final Map map; - public final Map getters; - public final Map setters; + public final Map map; + public final Map getters; + public final Map setters; @Override public boolean pure() { for (var el : map.values()) { @@ -39,15 +39,14 @@ public class ObjectStatement extends Statement { return true; } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadObj()); for (var el : map.entrySet()) { target.add(Instruction.dup()); target.add(Instruction.pushValue(el.getKey())); var val = el.getValue(); - FunctionStatement.compileWithName(val, target, true, el.getKey().toString()); + FunctionNode.compileWithName(val, target, true, el.getKey().toString()); target.add(Instruction.storeMember()); } @@ -70,7 +69,7 @@ public class ObjectStatement extends Statement { if (!pollute) target.add(Instruction.discard()); } - public ObjectStatement(Location loc, Map map, Map getters, Map setters) { + public ObjectNode(Location loc, Map map, Map getters, Map setters) { super(loc); this.map = map; this.getters = getters; @@ -90,7 +89,7 @@ public class ObjectStatement extends Statement { if (!res.isSuccess()) return res.chainError(); return ParseRes.res(res.result.toString(), n); } - private static ParseRes parseObjectProp(Source src, int i) { + private static ParseRes parseObjectProp(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -107,7 +106,7 @@ public class ObjectStatement extends Statement { if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); n += params.n; - var body = CompoundStatement.parse(src, i + n); + var body = CompoundNode.parse(src, i + n); if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor."); n += body.n; @@ -115,11 +114,11 @@ public class ObjectStatement extends Statement { return ParseRes.res(new ObjProp( name.result, access.result, - new FunctionStatement(loc, end, access + " " + name.result.toString(), params.result.toArray(String[]::new), false, body.result) + new FunctionNode(loc, end, access + " " + name.result.toString(), params.result.toArray(String[]::new), false, body.result) ), n); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -127,13 +126,13 @@ public class ObjectStatement extends Statement { n++; n += Parsing.skipEmpty(src, i + n); - var values = new LinkedHashMap(); - var getters = new LinkedHashMap(); - var setters = new LinkedHashMap(); + var values = new LinkedHashMap(); + var getters = new LinkedHashMap(); + var setters = new LinkedHashMap(); if (src.is(i + n, "}")) { n++; - return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); + return ParseRes.res(new ObjectNode(loc, values, getters, setters), n); } while (true) { @@ -180,7 +179,7 @@ public class ObjectStatement extends Statement { else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); } - return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n); + return ParseRes.res(new ObjectNode(loc, values, getters, setters), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java similarity index 86% rename from src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java index 5bc7a78..11d00b5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/RegexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java @@ -6,9 +6,9 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class RegexStatement extends Statement { +public class RegexNode extends Node { public final String pattern, flags; // Not really pure, since a function is called, but can be ignored. @@ -21,7 +21,7 @@ public class RegexStatement extends Statement { } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); if (!src.is(i + n, '/')) return ParseRes.failed(); @@ -69,10 +69,10 @@ public class RegexStatement extends Statement { n++; } - return ParseRes.res(new RegexStatement(loc, source.toString(), flags.toString()), n); + return ParseRes.res(new RegexNode(loc, source.toString(), flags.toString()), n); } - public RegexStatement(Location loc, String pattern, String flags) { + public RegexNode(Location loc, String pattern, String flags) { super(loc); this.pattern = pattern; this.flags = flags; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java similarity index 75% rename from src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 177394e..8f4d614 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -6,20 +6,20 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.AssignableStatement; +import me.topchetoeu.jscript.compilation.AssignableNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.operations.VariableAssignStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.operations.VariableAssignNode; -public class VariableStatement extends Statement implements AssignableStatement { +public class VariableNode extends Node implements AssignableNode { public final String name; @Override public boolean pure() { return false; } @Override - public Statement toAssign(Statement val, Operation operation) { - return new VariableAssignStatement(loc(), name, val, operation); + public Node toAssign(Node val, Operation operation) { + return new VariableAssignNode(loc(), name, val, operation); } @Override @@ -29,12 +29,12 @@ public class VariableStatement extends Statement implements AssignableStatement if (!pollute) target.add(Instruction.discard()); } - public VariableStatement(Location loc, String name) { + public VariableNode(Location loc, String name) { super(loc); this.name = name; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -49,6 +49,6 @@ public class VariableStatement extends Statement implements AssignableStatement return ParseRes.error(src.loc(i + n), String.format("Unexpected keyword '%s'.", literal.result)); } - return ParseRes.res(new VariableStatement(loc, literal.result), n); + return ParseRes.res(new VariableNode(loc, literal.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java similarity index 76% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java index fa45815..a15b16a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java @@ -3,9 +3,9 @@ package me.topchetoeu.jscript.compilation.values.constants; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class BoolStatement extends Statement { +public class BoolNode extends Node { public final boolean value; @Override public boolean pure() { return true; } @@ -14,7 +14,7 @@ public class BoolStatement extends Statement { if (pollute) target.add(Instruction.pushValue(value)); } - public BoolStatement(Location loc, boolean value) { + public BoolNode(Location loc, boolean value) { super(loc); this.value = value; } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java deleted file mode 100644 index b21545f..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/ConstantStatements.java +++ /dev/null @@ -1,4 +0,0 @@ -package me.topchetoeu.jscript.compilation.values.constants; - -public class ConstantStatements { -} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java similarity index 72% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java index afb0dcd..e8c4d54 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java @@ -3,14 +3,14 @@ package me.topchetoeu.jscript.compilation.values.constants; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class NullStatement extends Statement { +public class NullNode extends Node { @Override public boolean pure() { return true; } @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.pushNull()); } - public NullStatement(Location loc) { super(loc); } + public NullNode(Location loc) { super(loc); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java similarity index 77% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java index a626e86..246bf68 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java @@ -6,9 +6,9 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class NumberStatement extends Statement { +public class NumberNode extends Node { public final double value; @Override public boolean pure() { return true; } @@ -17,7 +17,7 @@ public class NumberStatement extends Statement { if (pollute) target.add(Instruction.pushValue(value)); } - public NumberStatement(Location loc, double value) { + public NumberNode(Location loc, double value) { super(loc); this.value = value; } @@ -31,12 +31,12 @@ public class NumberStatement extends Statement { else return a * power(a * a, b / 2); } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); var res = Parsing.parseNumber(src, i + n, false); - if (res.isSuccess()) return ParseRes.res(new NumberStatement(loc, res.result), n + res.n); + if (res.isSuccess()) return ParseRes.res(new NumberNode(loc, res.result), n + res.n); else return res.chainError(); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java similarity index 71% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java index 2cb4d8c..b288b12 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java @@ -6,9 +6,9 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class StringStatement extends Statement { +public class StringNode extends Node { public final String value; @Override public boolean pure() { return true; } @@ -17,17 +17,17 @@ public class StringStatement extends Statement { if (pollute) target.add(Instruction.pushValue(value)); } - public StringStatement(Location loc, String value) { + public StringNode(Location loc, String value) { super(loc); this.value = value; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); var res = Parsing.parseString(src, i + n); - if (res.isSuccess()) return ParseRes.res(new StringStatement(loc, res.result), n + res.n); + if (res.isSuccess()) return ParseRes.res(new StringNode(loc, res.result), n + res.n); else return res.chainError(); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java similarity index 67% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index 306c61c..b4635fb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -12,55 +12,55 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.ArrayStatement; -import me.topchetoeu.jscript.compilation.values.ObjectStatement; -import me.topchetoeu.jscript.compilation.values.VariableStatement; -import me.topchetoeu.jscript.compilation.values.constants.BoolStatement; -import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; -import me.topchetoeu.jscript.compilation.values.constants.StringStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.ArrayNode; +import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.VariableNode; +import me.topchetoeu.jscript.compilation.values.constants.BoolNode; +import me.topchetoeu.jscript.compilation.values.constants.NumberNode; +import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class CallStatement extends Statement { +public class CallNode extends Node { public static boolean ATTACH_NAME = true; - public final Statement func; - public final Statement[] args; + public final Node func; + public final Node[] args; public final boolean isNew; - private String generateName(Statement func, Statement index) { + private String generateName(Node func, Node index) { String res = "(intermediate value)"; boolean shouldParen = false; - if (func instanceof ObjectStatement) { - var obj = (ObjectStatement)func; + if (func instanceof ObjectNode) { + var obj = (ObjectNode)func; shouldParen = true; if (obj.getters.size() > 0 || obj.setters.size() > 0 || obj.map.size() > 0) res = "{}"; else res = "{(intermediate value)}"; } - else if (func instanceof StringStatement) { - res = JSON.stringify(JSONElement.string(((StringStatement)func).value)); + else if (func instanceof StringNode) { + res = JSON.stringify(JSONElement.string(((StringNode)func).value)); } - else if (func instanceof NumberStatement) { - res = JSON.stringify(JSONElement.number(((NumberStatement)func).value)); + else if (func instanceof NumberNode) { + res = JSON.stringify(JSONElement.number(((NumberNode)func).value)); } - else if (func instanceof BoolStatement) { - res = ((BoolStatement)func).value ? "true" : "false"; + else if (func instanceof BoolNode) { + res = ((BoolNode)func).value ? "true" : "false"; } - else if (func instanceof VariableStatement) { - res = ((VariableStatement)func).name; + else if (func instanceof VariableNode) { + res = ((VariableNode)func).name; } - else if (func instanceof VariableIndexStatement) { - var i = ((VariableIndexStatement)func).index; + else if (func instanceof VariableIndexNode) { + var i = ((VariableIndexNode)func).index; if (i == 0) res = "this"; else if (i == 1) res = "arguments"; } - else if (func instanceof ArrayStatement) { + else if (func instanceof ArrayNode) { var els = new ArrayList(); - for (var el : ((ArrayStatement)func).statements) { + for (var el : ((ArrayNode)func).statements) { if (el != null) els.add(generateName(el, null)); else els.add("(intermediate value)"); } @@ -72,8 +72,8 @@ public class CallStatement extends Statement { if (shouldParen) res = "(" + res + ")"; - if (index instanceof StringStatement) { - var val = ((StringStatement)index).value; + if (index instanceof StringNode) { + var val = ((StringNode)index).value; var bracket = JSON.stringify(JSONElement.string(val)); if (!bracket.substring(1, bracket.length() - 1).equals(val)) return res + "[" + bracket + "]"; @@ -86,9 +86,9 @@ public class CallStatement extends Statement { } @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { - if (!isNew && func instanceof IndexStatement) { - var obj = ((IndexStatement)func).object; - var index = ((IndexStatement)func).index; + if (!isNew && func instanceof IndexNode) { + var obj = ((IndexNode)func).object; + var index = ((IndexNode)func).index; String name = ""; obj.compile(target, true); @@ -116,14 +116,14 @@ public class CallStatement extends Statement { compile(target, pollute, BreakpointType.STEP_IN); } - public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) { + public CallNode(Location loc, boolean isNew, Node func, Node ...args) { super(loc); this.isNew = isNew; this.func = func; this.args = args; } - public static ParseRes parseCall(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseCall(Source src, int i, Node prev, int precedence) { if (precedence > 17) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -132,7 +132,7 @@ public class CallStatement extends Statement { if (!src.is(i + n, "(")) return ParseRes.failed(); n++; - var args = new ArrayList(); + var args = new ArrayList(); boolean prevArg = false; while (true) { @@ -157,9 +157,9 @@ public class CallStatement extends Statement { else return ParseRes.error(src.loc(i + n), "Expected an expression or a closing paren"); } - return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n); + return ParseRes.res(new CallNode(loc, false, prev, args.toArray(Node[]::new)), n); } - public static ParseRes parseNew(Source src, int i) { + public static ParseRes parseNew(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -170,11 +170,11 @@ public class CallStatement extends Statement { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'new' keyword."); n += valRes.n; - var callRes = CallStatement.parseCall(src, i + n, valRes.result, 0); - if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n); + var callRes = CallNode.parseCall(src, i + n, valRes.result, 0); + if (callRes.isFailed()) return ParseRes.res(new CallNode(loc, true, valRes.result), n); if (callRes.isError()) return callRes.chainError(); n += callRes.n; - return ParseRes.res(new CallStatement(loc, true, callRes.result.func, callRes.result.args), n); + return ParseRes.res(new CallNode(loc, true, callRes.result.func, callRes.result.args), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java similarity index 54% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java index 33f1033..e8b403a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java @@ -6,19 +6,19 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.AssignableStatement; +import me.topchetoeu.jscript.compilation.AssignableNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.constants.NumberStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.constants.NumberNode; -public class ChangeStatement extends Statement { - public final AssignableStatement value; +public class ChangeNode extends Node { + public final AssignableNode value; public final double addAmount; public final boolean postfix; @Override public void compile(CompileResult target, boolean pollute) { - value.toAssign(new NumberStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true); + value.toAssign(new NumberNode(loc(), -addAmount), Operation.SUBTRACT).compile(target, true); if (!pollute) target.add(Instruction.discard()); else if (postfix) { target.add(Instruction.pushValue(addAmount)); @@ -26,14 +26,14 @@ public class ChangeStatement extends Statement { } } - public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) { + public ChangeNode(Location loc, AssignableNode value, double addAmount, boolean postfix) { super(loc); this.value = value; this.addAmount = addAmount; this.postfix = postfix; } - public static ParseRes parsePrefixIncrease(Source src, int i) { + public static ParseRes parsePrefixIncrease(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -42,11 +42,11 @@ public class ChangeStatement extends Statement { var res = JavaScript.parseExpression(src, i + n, 15); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); - else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); + else if (!(res.result instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, 1, false), n + res.n); + return ParseRes.res(new ChangeNode(loc, (AssignableNode)res.result, 1, false), n + res.n); } - public static ParseRes parsePrefixDecrease(Source src, int i) { + public static ParseRes parsePrefixDecrease(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -55,33 +55,33 @@ public class ChangeStatement extends Statement { var res = JavaScript.parseExpression(src, i + n, 15); if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected assignable value after prefix operator."); - else if (!(res.result instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); + else if (!(res.result instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value after prefix operator."); - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, -1, false), n + res.n); + return ParseRes.res(new ChangeNode(loc, (AssignableNode)res.result, -1, false), n + res.n); } - public static ParseRes parsePostfixIncrease(Source src, int i, Statement prev, int precedence) { + public static ParseRes parsePostfixIncrease(Source src, int i, Node prev, int precedence) { if (precedence > 15) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); if (!src.is(i + n, "++")) return ParseRes.failed(); - if (!(prev instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator."); + if (!(prev instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator."); n += 2; - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, 1, true), n); + return ParseRes.res(new ChangeNode(loc, (AssignableNode)prev, 1, true), n); } - public static ParseRes parsePostfixDecrease(Source src, int i, Statement prev, int precedence) { + public static ParseRes parsePostfixDecrease(Source src, int i, Node prev, int precedence) { if (precedence > 15) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); if (!src.is(i + n, "--")) return ParseRes.failed(); - if (!(prev instanceof AssignableStatement)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator."); + if (!(prev instanceof AssignableNode)) return ParseRes.error(src.loc(i + n), "Expected assignable value before suffix operator."); n += 2; - return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, -1, true), n); + return ParseRes.res(new ChangeNode(loc, (AssignableNode)prev, -1, true), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java similarity index 76% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java index 513c29c..063f503 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java @@ -7,10 +7,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class DiscardStatement extends Statement { - public final Statement value; +public class DiscardNode extends Node { + public final Node value; @Override public boolean pure() { return value.pure(); } @@ -19,12 +19,12 @@ public class DiscardStatement extends Statement { if (pollute) target.add(Instruction.pushUndefined()); } - public DiscardStatement(Location loc, Statement val) { + public DiscardNode(Location loc, Node val) { super(loc); this.value = val; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -35,6 +35,6 @@ public class DiscardStatement extends Statement { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'void' keyword."); n += valRes.n; - return ParseRes.res(new DiscardStatement(loc, valRes.result), n); + return ParseRes.res(new DiscardNode(loc, valRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java similarity index 79% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java index fc11ac2..2b5c68e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java @@ -5,12 +5,12 @@ import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class IndexAssignStatement extends Statement { - public final Statement object; - public final Statement index; - public final Statement value; +public class IndexAssignNode extends Node { + public final Node object; + public final Node index; + public final Node value; public final Operation operation; @Override @@ -35,7 +35,7 @@ public class IndexAssignStatement extends Statement { } } - public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) { + public IndexAssignNode(Location loc, Node object, Node index, Node value, Operation operation) { super(loc); this.object = object; this.index = index; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java similarity index 71% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java index aae2d10..f8c1387 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java @@ -7,19 +7,19 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.AssignableStatement; +import me.topchetoeu.jscript.compilation.AssignableNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.constants.StringStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class IndexStatement extends Statement implements AssignableStatement { - public final Statement object; - public final Statement index; +public class IndexNode extends Node implements AssignableNode { + public final Node object; + public final Node index; @Override - public Statement toAssign(Statement val, Operation operation) { - return new IndexAssignStatement(loc(), object, index, val, operation); + public Node toAssign(Node val, Operation operation) { + return new IndexAssignNode(loc(), object, index, val, operation); } public void compile(CompileResult target, boolean dupObj, boolean pollute) { object.compile(target, true); @@ -34,13 +34,13 @@ public class IndexStatement extends Statement implements AssignableStatement { compile(target, false, pollute); } - public IndexStatement(Location loc, Statement object, Statement index) { + public IndexNode(Location loc, Node object, Node index) { super(loc); this.object = object; this.index = index; } - public static ParseRes parseIndex(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseIndex(Source src, int i, Node prev, int precedence) { if (precedence > 18) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -57,9 +57,9 @@ public class IndexStatement extends Statement implements AssignableStatement { if (!src.is(i + n, "]")) return ParseRes.error(src.loc(i + n), "Expected a closing bracket"); n++; - return ParseRes.res(new IndexStatement(loc, prev, valRes.result), n); + return ParseRes.res(new IndexNode(loc, prev, valRes.result), n); } - public static ParseRes parseMember(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseMember(Source src, int i, Node prev, int precedence) { if (precedence > 18) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -72,6 +72,6 @@ public class IndexStatement extends Statement implements AssignableStatement { if (!literal.isSuccess()) return literal.chainError(src.loc(i + n), "Expected an identifier after member access."); n += literal.n; - return ParseRes.res(new IndexStatement(loc, prev, new StringStatement(loc, literal.result)), n); + return ParseRes.res(new IndexNode(loc, prev, new StringNode(loc, literal.result)), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java similarity index 77% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java index 3da73bd..71294b3 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java @@ -7,10 +7,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class LazyAndStatement extends Statement { - public final Statement first, second; +public class LazyAndNode extends Node { + public final Node first, second; @Override public boolean pure() { return first.pure() && second.pure(); } @@ -24,14 +24,14 @@ public class LazyAndStatement extends Statement { target.set(start, Instruction.jmpIfNot(target.size() - start)); } - public LazyAndStatement(Location loc, Statement first, Statement second) { + public LazyAndNode(Location loc, Node first, Node second) { super(loc); this.first = first; this.second = second; } - public static ParseRes parse(Source src, int i, Statement prev, int precedence) { + public static ParseRes parse(Source src, int i, Node prev, int precedence) { if (precedence < 4) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -43,6 +43,6 @@ public class LazyAndStatement extends Statement { if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '&&' operator."); n += res.n; - return ParseRes.res(new LazyAndStatement(loc, prev, res.result), n); + return ParseRes.res(new LazyAndNode(loc, prev, res.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java similarity index 77% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java index 0ce04b5..225961e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java @@ -7,10 +7,10 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class LazyOrStatement extends Statement { - public final Statement first, second; +public class LazyOrNode extends Node { + public final Node first, second; @Override public boolean pure() { return first.pure() && second.pure(); } @@ -24,14 +24,14 @@ public class LazyOrStatement extends Statement { target.set(start, Instruction.jmpIf(target.size() - start)); } - public LazyOrStatement(Location loc, Statement first, Statement second) { + public LazyOrNode(Location loc, Node first, Node second) { super(loc); this.first = first; this.second = second; } - public static ParseRes parse(Source src, int i, Statement prev, int precedence) { + public static ParseRes parse(Source src, int i, Node prev, int precedence) { if (precedence < 3) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -43,6 +43,6 @@ public class LazyOrStatement extends Statement { if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the '||' operator."); n += res.n; - return ParseRes.res(new LazyOrStatement(loc, prev, res.result), n); + return ParseRes.res(new LazyOrNode(loc, prev, res.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java similarity index 80% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java index a45afa5..d59951b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java @@ -11,16 +11,16 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.AssignableStatement; +import me.topchetoeu.jscript.compilation.AssignableNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class OperationStatement extends Statement { +public class OperationNode extends Node { private static interface OperatorFactory { String token(); int precedence(); - ParseRes construct(Source src, int i, Statement prev); + ParseRes construct(Source src, int i, Node prev); } private static class NormalOperatorFactory implements OperatorFactory { @@ -30,12 +30,12 @@ public class OperationStatement extends Statement { @Override public int precedence() { return precedence; } @Override public String token() { return token; } - @Override public ParseRes construct(Source src, int i, Statement prev) { + @Override public ParseRes construct(Source src, int i, Node prev) { var loc = src.loc(i); var other = JavaScript.parseExpression(src, i, precedence + 1); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token)); - return ParseRes.res(new OperationStatement(loc, operation, prev, (Statement)other.result), other.n); + return ParseRes.res(new OperationNode(loc, operation, prev, (Node)other.result), other.n); } public NormalOperatorFactory(String token, int precedence, Operation operation) { @@ -51,14 +51,14 @@ public class OperationStatement extends Statement { @Override public int precedence() { return precedence; } @Override public String token() { return token; } - @Override public ParseRes construct(Source src, int i, Statement prev) { + @Override public ParseRes construct(Source src, int i, Node prev) { var loc = src.loc(i); - if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token)); + if (!(prev instanceof AssignableNode)) return ParseRes.error(loc, String.format("Expected an assignable expression before '%s'", token)); var other = JavaScript.parseExpression(src, i, precedence); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), String.format("Expected a value after '%s'", token)); - return ParseRes.res(((AssignableStatement)prev).toAssign(other.result, operation), other.n); + return ParseRes.res(((AssignableNode)prev).toAssign(other.result, operation), other.n); } public AssignmentOperatorFactory(String token, int precedence, Operation operation) { @@ -70,27 +70,27 @@ public class OperationStatement extends Statement { private static class LazyAndFactory implements OperatorFactory { @Override public int precedence() { return 4; } @Override public String token() { return "&&"; } - @Override public ParseRes construct(Source src, int i, Statement prev) { + @Override public ParseRes construct(Source src, int i, Node prev) { var loc = src.loc(i); var other = JavaScript.parseExpression(src, i, 5); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '&&'"); - return ParseRes.res(new LazyAndStatement(loc, prev, (Statement)other.result), other.n); + return ParseRes.res(new LazyAndNode(loc, prev, (Node)other.result), other.n); } } private static class LazyOrFactory implements OperatorFactory { @Override public int precedence() { return 5; } @Override public String token() { return "||"; } - @Override public ParseRes construct(Source src, int i, Statement prev) { + @Override public ParseRes construct(Source src, int i, Node prev) { var loc = src.loc(i); var other = JavaScript.parseExpression(src, i, 6); if (!other.isSuccess()) return other.chainError(src.loc(i + other.n), "Expected a value after '||'"); - return ParseRes.res(new LazyOrStatement(loc, prev, (Statement)other.result), other.n); + return ParseRes.res(new LazyOrNode(loc, prev, (Node)other.result), other.n); } } - public final Statement[] args; + public final Node[] args; public final Operation operation; @Override public boolean pure() { @@ -110,7 +110,7 @@ public class OperationStatement extends Statement { else target.add(Instruction.discard()); } - public OperationStatement(Location loc, Operation operation, Statement ...args) { + public OperationNode(Location loc, Operation operation, Node ...args) { super(loc); this.operation = operation; this.args = args; @@ -156,7 +156,7 @@ public class OperationStatement extends Statement { private static final List operatorsByLength = factories.keySet().stream().sorted((a, b) -> -a.compareTo(b)).collect(Collectors.toList()); - public static ParseRes parsePrefix(Source src, int i) { + public static ParseRes parsePrefix(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -173,10 +173,10 @@ public class OperationStatement extends Statement { var res = JavaScript.parseExpression(src, i + n, 14); - if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n); + if (res.isSuccess()) return ParseRes.res(new OperationNode(loc, operation, res.result), n + res.n); else return res.chainError(src.loc(i + n), String.format("Expected a value after the unary operator '%s'.", op)); } - public static ParseRes parseInstanceof(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseInstanceof(Source src, int i, Node prev, int precedence) { if (precedence > 9) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -190,9 +190,9 @@ public class OperationStatement extends Statement { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'instanceof'."); n += valRes.n; - return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n); + return ParseRes.res(new OperationNode(loc, Operation.INSTANCEOF, prev, valRes.result), n); } - public static ParseRes parseIn(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseIn(Source src, int i, Node prev, int precedence) { if (precedence > 9) return ParseRes.failed(); var n = Parsing.skipEmpty(src, i); @@ -206,9 +206,9 @@ public class OperationStatement extends Statement { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'in'."); n += valRes.n; - return ParseRes.res(new OperationStatement(loc, Operation.IN, valRes.result, prev), n); + return ParseRes.res(new OperationNode(loc, Operation.IN, valRes.result, prev), n); } - public static ParseRes parseOperator(Source src, int i, Statement prev, int precedence) { + public static ParseRes parseOperator(Source src, int i, Node prev, int precedence) { var n = Parsing.skipEmpty(src, i); for (var token : operatorsByLength) { diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java similarity index 72% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java index c073b57..1eabc75 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java @@ -7,11 +7,11 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.VariableStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.VariableNode; -public class TypeofStatement extends Statement { - public final Statement value; +public class TypeofNode extends Node { + public final Node value; // Not really pure, since a variable from the global scope could be accessed, // which could lead to code execution, that would get omitted @@ -19,8 +19,8 @@ public class TypeofStatement extends Statement { @Override public void compile(CompileResult target, boolean pollute) { - if (value instanceof VariableStatement) { - var i = target.scope.getKey(((VariableStatement)value).name); + if (value instanceof VariableNode) { + var i = target.scope.getKey(((VariableNode)value).name); if (i instanceof String) { target.add(Instruction.typeof((String)i)); return; @@ -30,12 +30,12 @@ public class TypeofStatement extends Statement { target.add(Instruction.typeof()); } - public TypeofStatement(Location loc, Statement value) { + public TypeofNode(Location loc, Node value) { super(loc); this.value = value; } - public static ParseRes parse(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -46,6 +46,6 @@ public class TypeofStatement extends Statement { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'typeof' keyword."); n += valRes.n; - return ParseRes.res(new TypeofStatement(loc, valRes.result), n); + return ParseRes.res(new TypeofNode(loc, valRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java similarity index 66% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java index a9e7167..f51f92d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java @@ -4,12 +4,12 @@ import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; -import me.topchetoeu.jscript.compilation.values.FunctionStatement; +import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.FunctionNode; -public class VariableAssignStatement extends Statement { +public class VariableAssignNode extends Node { public final String name; - public final Statement value; + public final Node value; public final Operation operation; @Override public boolean pure() { return false; } @@ -19,17 +19,17 @@ public class VariableAssignStatement extends Statement { var i = target.scope.getKey(name); if (operation != null) { target.add(Instruction.loadVar(i)); - FunctionStatement.compileWithName(value, target, true, name); + FunctionNode.compileWithName(value, target, true, name); target.add(Instruction.operation(operation)); target.add(Instruction.storeVar(i, pollute)); } else { - FunctionStatement.compileWithName(value, target, true, name); + FunctionNode.compileWithName(value, target, true, name); target.add(Instruction.storeVar(i, pollute)); } } - public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) { + public VariableAssignNode(Location loc, String name, Node val, Operation operation) { super(loc); this.name = name; this.value = val; diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexStatement.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java similarity index 75% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexStatement.java rename to src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java index f2f53bc..a5dbb48 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexStatement.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java @@ -3,9 +3,9 @@ package me.topchetoeu.jscript.compilation.values.operations; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Statement; +import me.topchetoeu.jscript.compilation.Node; -public class VariableIndexStatement extends Statement { +public class VariableIndexNode extends Node { public final int index; @Override public boolean pure() { return true; } @@ -15,7 +15,7 @@ public class VariableIndexStatement extends Statement { if (pollute) target.add(Instruction.loadVar(index)); } - public VariableIndexStatement(Location loc, int i) { + public VariableIndexNode(Location loc, int i) { super(loc); this.index = i; } -- 2.45.2 From 4a5e5a71afb5dc727409c1caaec1b1998d70f4df Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:02:25 +0300 Subject: [PATCH 10/48] feat: Create new scope system for ES6+ support --- .../compilation/scope/FunctionScope.java | 63 +++++++ .../compilation/scope/GlobalScope.java | 18 ++ .../jscript/compilation/scope/LocalScope.java | 46 +++++ .../compilation/scope/LocalScopeRecord.java | 77 -------- .../jscript/compilation/scope/Scope.java | 68 +++++++ .../compilation/scope/ScopeRecord.java | 7 - .../compilation/scope/VariableDescriptor.java | 19 ++ .../compilation/scope/VariableList.java | 176 ++++++++++++++++++ 8 files changed, 390 insertions(+), 84 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/LocalScopeRecord.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/Scope.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/ScopeRecord.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java new file mode 100644 index 0000000..3318f17 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -0,0 +1,63 @@ +package me.topchetoeu.jscript.compilation.scope; + +import java.util.HashMap; + +import me.topchetoeu.jscript.common.parsing.Location; + +public class FunctionScope extends Scope { + private final VariableList captures = new VariableList(); + private final VariableList locals = new VariableList(captures); + private HashMap childToParent = new HashMap<>(); + + private void removeCapture(String name) { + var res = captures.remove(name); + if (res != null) childToParent.remove(res); + } + + @Override public VariableDescriptor define(String name, boolean readonly, Location loc) { + var old = locals.get(name); + if (old != null) return old; + + removeCapture(name); + return locals.add(name, readonly); + } + @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { + if (locals.has(name)) throw alreadyDefinedErr(loc, name); + else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes"); + else return parent.defineStrict(name, readonly, loc); + } + + @Override public VariableDescriptor get(String name, boolean capture) { + if (locals.has(name)) return locals.get(name); + if (captures.has(name)) return captures.get(name); + + var parentVar = parent.get(name, true); + var childVar = captures.add(parentVar); + + childToParent.put(childVar, parentVar); + + return childVar; + } + + public int localsCount() { + return locals.size(); + } + public int offset() { + return captures.size() + locals.size(); + } + + public int[] getCaptureIndices() { + var res = new int[captures.size()]; + var i = 0; + + for (var el : captures) { + assert childToParent.containsKey(el); + res[i] = childToParent.get(el).index(); + } + + return res; + } + + public FunctionScope() { super(); } + public FunctionScope(Scope parent) { super(parent); } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java new file mode 100644 index 0000000..75ba7d0 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java @@ -0,0 +1,18 @@ +package me.topchetoeu.jscript.compilation.scope; + +import me.topchetoeu.jscript.common.parsing.Location; + +public final class GlobalScope extends Scope { + @Override public VariableDescriptor define(String name, boolean readonly, Location loc) { + return null; + } + @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { + return null; + } + @Override public VariableDescriptor get(String name, boolean capture) { + return null; + } + @Override public int offset() { + return 0; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java new file mode 100644 index 0000000..0ee6630 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java @@ -0,0 +1,46 @@ +package me.topchetoeu.jscript.compilation.scope; + +import me.topchetoeu.jscript.common.parsing.Location; + +public class LocalScope extends Scope { + private final VariableList locals = new VariableList(); + + @Override public int offset() { + if (parent != null) return parent.offset() + locals.size(); + else return locals.size(); + } + + @Override public VariableDescriptor define(String name, boolean readonly, Location loc) { + if (locals.has(name)) throw alreadyDefinedErr(loc, name); + + return parent.define(name, readonly, loc); + } + @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { + if (locals.has(name)) throw alreadyDefinedErr(loc, name); + return locals.add(name, readonly); + } + + @Override public VariableDescriptor get(String name, boolean capture) { + var res = locals.get(name); + + if (res != null) return res; + if (parent != null) return parent.get(name, capture); + + return null; + } + + @Override public boolean end() { + if (!super.end()) return false; + + this.locals.freeze(); + return true; + } + + public Iterable all() { + return () -> locals.iterator(); + } + + public LocalScope(Scope parent) { + super(parent); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScopeRecord.java b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScopeRecord.java deleted file mode 100644 index 070168d..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScopeRecord.java +++ /dev/null @@ -1,77 +0,0 @@ -package me.topchetoeu.jscript.compilation.scope; - -import java.util.ArrayList; - -public class LocalScopeRecord implements ScopeRecord { - public final LocalScopeRecord parent; - - private final ArrayList captures = new ArrayList<>(); - private final ArrayList locals = new ArrayList<>(); - - public String[] captures() { - return captures.toArray(String[]::new); - } - public String[] locals() { - return locals.toArray(String[]::new); - } - - public LocalScopeRecord child() { - return new LocalScopeRecord(this); - } - - public int localsCount() { - return locals.size(); - } - public int capturesCount() { - return captures.size(); - } - - public int[] getCaptures() { - var buff = new int[captures.size()]; - var i = 0; - - for (var name : captures) { - var index = parent.getKey(name); - if (index instanceof Integer) buff[i++] = (int)index; - } - - var res = new int[i]; - System.arraycopy(buff, 0, res, 0, i); - - return res; - } - - public Object getKey(String name) { - var capI = captures.indexOf(name); - var locI = locals.lastIndexOf(name); - if (locI >= 0) return locI; - if (capI >= 0) return ~capI; - if (parent != null) { - var res = parent.getKey(name); - if (res != null && res instanceof Integer) { - captures.add(name); - return -captures.size(); - } - } - - return 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); - } - - public LocalScopeRecord() { - this.parent = null; - } - public LocalScopeRecord(LocalScopeRecord parent) { - this.parent = parent; - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java new file mode 100644 index 0000000..6631abd --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -0,0 +1,68 @@ +package me.topchetoeu.jscript.compilation.scope; + +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; + +public abstract class Scope { + public final Scope parent; + private boolean active = true; + private Scope child; + + protected final SyntaxException alreadyDefinedErr(Location loc, String name) { + return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name)); + } + + /** + * Defines an ES5-style variable + * @returns The index supplier of the variable if it is a local, or null if it is a global + * @throws SyntaxException If an ES2015-style variable with the same name exists anywhere from the current function to the current scope + * @throws RuntimeException If the scope is finalized or has an active child + */ + public abstract VariableDescriptor define(String name, boolean readonly, Location loc); + /** + * Defines an ES2015-style variable + * @param readonly True if const, false if let + * @return The index supplier of the variable + * @throws SyntaxException If any variable with the same name exists in the current scope + * @throws RuntimeException If the scope is finalized or has an active child + */ + public abstract VariableDescriptor defineStrict(String name, boolean readonly, Location loc); + /** + * Gets the index supplier of the given variable name, or null if it is a global + * + * @param capture Used to signal to the scope that the variable is going to be captured. + * Not passing this could lead to a local variable being optimized out as an ES5-style variable, + * which could break the semantics of a capture + */ + public abstract VariableDescriptor get(String name, boolean capture); + /** + * Gets the index offset from this scope to its children + */ + public abstract int offset(); + + public boolean end() { + if (!active) return false; + + this.active = false; + if (this.parent != null) { + assert this.parent.child == this; + this.parent.child = this; + } + + return true; + } + + public final boolean active() { return active; } + public final Scope child() { return child; } + + public Scope() { + this.parent = null; + } + public Scope(Scope parent) { + if (!parent.active) throw new RuntimeException("Parent is not active"); + if (parent.child != null) throw new RuntimeException("Parent has an active child"); + + this.parent = parent; + this.parent.child = this; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/ScopeRecord.java b/src/java/me/topchetoeu/jscript/compilation/scope/ScopeRecord.java deleted file mode 100644 index fe08968..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/scope/ScopeRecord.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.compilation.scope; - -public interface ScopeRecord { - public Object getKey(String name); - public Object define(String name); - public LocalScopeRecord child(); -} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java b/src/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java new file mode 100644 index 0000000..29b3bb5 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java @@ -0,0 +1,19 @@ +package me.topchetoeu.jscript.compilation.scope; + +public abstract class VariableDescriptor { + public final boolean readonly; + public final String name; + + public abstract int index(); + + public VariableDescriptor(String name, boolean readonly) { + this.name = name; + this.readonly = readonly; + } + + public static VariableDescriptor of(String name, boolean readonly, int i) { + return new VariableDescriptor(name, readonly) { + @Override public int index() { return i; } + }; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java new file mode 100644 index 0000000..2fca3a8 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -0,0 +1,176 @@ +package me.topchetoeu.jscript.compilation.scope; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.function.IntSupplier; + +public class VariableList implements Iterable { + private class ListVar extends VariableDescriptor { + private ListVar next; + private ListVar prev; + + @Override public int index() { + throw new RuntimeException("The index of a variable may not be retrieved until the scope has been finalized"); + // var res = 0; + // if (offset != null) res = offset.getAsInt(); + + // for (var it = prev; it != null; it = it.prev) { + // res++; + // } + + // return res; + } + + public ListVar(String name, boolean readonly, ListVar next, ListVar prev) { + super(name, readonly); + + this.next = next; + this.prev = prev; + } + } + + private ListVar first, last; + + private HashMap map = new HashMap<>(); + + private HashMap frozenMap = null; + private ArrayList frozenList = null; + + private final IntSupplier offset; + + public boolean frozen() { + if (frozenMap != null) { + assert frozenList != null; + assert frozenMap != null; + assert map == null; + assert first == null; + assert last == null; + + return true; + } + else { + assert frozenList == null; + assert frozenMap == null; + assert map != null; + + return false; + } + } + + public VariableDescriptor add(VariableDescriptor val) { + return add(val.name, val.readonly); + } + public VariableDescriptor add(String name, boolean readonly) { + if (frozen()) throw new RuntimeException("The scope has been frozen"); + if (map.containsKey(name)) return map.get(name); + + var res = new ListVar(name, readonly, null, last); + last.next = res; + last = res; + map.put(name, res); + + return res; + } + public VariableDescriptor remove(String name) { + if (frozen()) throw new RuntimeException("The scope has been frozen"); + + var el = map.get(name); + if (el == null) return null; + + el.prev.next = el.next; + el.next.prev = el.prev; + + el.next = null; + el.prev = null; + + return el; + } + + public VariableDescriptor get(String name) { + return map.get(name); + } + public VariableDescriptor get(int i) { + if (frozen()) { + if (i < 0 || i >= frozenList.size()) return null; + return frozenList.get(i); + } + else { + if (i < 0 || i >= map.size()) return null; + + if (i < map.size() / 2) { + var it = first; + for (var j = 0; j < i; it = it.next, j++); + return it; + } + else { + var it = last; + for (var j = map.size() - 1; j >= i; it = it.prev, j--); + return it; + } + } + } + + public boolean has(String name) { + return this.get(name) != null; + } + + public int size() { + if (frozen()) return frozenList.size(); + else return map.size(); + } + + public void freeze() { + if (frozen()) return; + + frozenMap = new HashMap<>(); + frozenList = new ArrayList<>(); + + var i = 0; + if (offset != null) i = offset.getAsInt(); + + for (var it = first; it != null; it = it.next) { + frozenMap.put(it.name, VariableDescriptor.of(it.name, it.readonly, i++)); + } + } + + @Override public Iterator iterator() { + if (frozen()) return frozenList.iterator(); + else return new Iterator() { + private ListVar curr = first; + + @Override public boolean hasNext() { + return curr != null; + } + @Override public VariableDescriptor next() { + if (curr == null) return null; + + var res = curr; + curr = curr.next; + return res; + } + }; + } + + public VariableDescriptor[] toArray() { + var res = new VariableDescriptor[size()]; + var i = 0; + + for (var el : this) res[i++] = el; + + return res; + } + + public VariableList(IntSupplier offset) { + this.offset = offset; + } + public VariableList(int offset) { + this.offset = () -> offset; + } + public VariableList(VariableList prev) { + this.offset = prev::size; + } + public VariableList() { + this.offset = null; + } +} -- 2.45.2 From 6481e992faff2fed32bad876c648abb639232f3c Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:05:41 +0300 Subject: [PATCH 11/48] feat: implement "has" function for scopes --- .../jscript/compilation/scope/FunctionScope.java | 8 ++++++++ .../topchetoeu/jscript/compilation/scope/GlobalScope.java | 5 +++++ .../topchetoeu/jscript/compilation/scope/LocalScope.java | 8 ++++++++ .../me/topchetoeu/jscript/compilation/scope/Scope.java | 1 + 4 files changed, 22 insertions(+) diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 3318f17..72f7468 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -39,6 +39,14 @@ public class FunctionScope extends Scope { return childVar; } + @Override public boolean has(String name) { + if (locals.has(name)) return true; + if (captures.has(name)) return true; + if (parent != null) return parent.has(name); + + return false; + } + public int localsCount() { return locals.size(); } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java index 75ba7d0..1f7e8a3 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java @@ -15,4 +15,9 @@ public final class GlobalScope extends Scope { @Override public int offset() { return 0; } + @Override public boolean has(String name) { + return false; + } + + public GlobalScope() { super(); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java index 0ee6630..9244fb6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java @@ -29,6 +29,13 @@ public class LocalScope extends Scope { return null; } + @Override public boolean has(String name) { + if (locals.has(name)) return true; + if (parent != null) return parent.has(name); + + return false; + } + @Override public boolean end() { if (!super.end()) return false; @@ -40,6 +47,7 @@ public class LocalScope extends Scope { return () -> locals.iterator(); } + public LocalScope(Scope parent) { super(parent); } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java index 6631abd..51cf155 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -35,6 +35,7 @@ public abstract class Scope { * which could break the semantics of a capture */ public abstract VariableDescriptor get(String name, boolean capture); + public abstract boolean has(String name); /** * Gets the index offset from this scope to its children */ -- 2.45.2 From 349d392269ab09848b834284e6298bbd17ac60df Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:44:51 +0300 Subject: [PATCH 12/48] major rewrite: clean up a lot of code and lay ground for ES6 support --- build.gradle | 24 +- src/assets/lib/index.js | 2 + .../jscript/common/FunctionBody.java | 5 +- .../jscript/common/Instruction.java | 279 ++++++++++-------- .../common/environment/Environment.java | 14 +- .../jscript/common/json/JSONElement.java | 3 +- .../jscript/common/json/JSONMap.java | 36 +-- .../jscript/common/mapping/FunctionMap.java | 6 +- .../jscript/common/parsing/Location.java | 6 +- .../jscript/common/parsing/ParseRes.java | 27 +- .../jscript/common/parsing/Parsing.java | 41 ++- .../common/parsing/SourceLocation.java | 30 +- .../jscript/compilation/CompileResult.java | 65 +++- .../jscript/compilation/CompoundNode.java | 46 ++- .../compilation/DeferredIntSupplier.java | 19 ++ .../jscript/compilation/ExpressionNode.java | 5 - .../jscript/compilation/FunctionNode.java | 130 ++++++++ .../compilation/FunctionStatementNode.java | 25 ++ .../compilation/FunctionValueNode.java | 19 ++ .../jscript/compilation/JavaScript.java | 53 ++-- .../jscript/compilation/LabelContext.java | 85 ++++++ .../topchetoeu/jscript/compilation/Node.java | 11 +- .../jscript/compilation/NodeChildren.java | 93 ++++++ .../topchetoeu/jscript/compilation/Path.java | 29 ++ .../jscript/compilation/ThrowSyntaxNode.java | 18 -- .../compilation/VariableDeclareNode.java | 25 +- .../compilation/control/BreakNode.java | 11 +- .../compilation/control/ContinueNode.java | 11 +- .../compilation/control/DeleteNode.java | 3 +- .../compilation/control/DoWhileNode.java | 31 +- .../compilation/control/ForInNode.java | 36 ++- .../jscript/compilation/control/ForNode.java | 40 ++- .../compilation/control/ForOfNode.java | 14 +- .../jscript/compilation/control/IfNode.java | 75 ++++- .../compilation/control/ReturnNode.java | 3 +- .../compilation/control/SwitchNode.java | 82 +++-- .../compilation/control/ThrowNode.java | 3 +- .../jscript/compilation/control/TryNode.java | 74 +++-- .../compilation/control/WhileNode.java | 86 +++--- .../compilation/scope/FunctionScope.java | 51 +++- .../compilation/scope/GlobalScope.java | 10 + .../jscript/compilation/scope/LocalScope.java | 14 +- .../jscript/compilation/scope/Scope.java | 6 +- .../compilation/scope/VariableList.java | 115 ++++++-- .../compilation/values/ArgumentsNode.java | 17 ++ .../jscript/compilation/values/ArrayNode.java | 9 +- .../compilation/values/FunctionNode.java | 150 ---------- .../compilation/values/GlobalThisNode.java | 6 +- .../compilation/values/ObjectNode.java | 19 +- .../jscript/compilation/values/RegexNode.java | 6 +- .../jscript/compilation/values/ThisNode.java | 17 ++ .../compilation/values/VariableNode.java | 57 +++- .../values/constants/BoolNode.java | 2 - .../values/constants/NullNode.java | 4 +- .../values/constants/NumberNode.java | 2 - .../values/constants/StringNode.java | 2 - .../values/operations/CallNode.java | 18 +- .../values/operations/DiscardNode.java | 10 +- .../values/operations/IndexAssignNode.java | 3 +- .../values/operations/IndexNode.java | 6 +- .../values/operations/LazyAndNode.java | 11 +- .../values/operations/LazyOrNode.java | 12 +- .../values/operations/OperationNode.java | 8 - .../values/operations/TypeofNode.java | 28 +- .../values/operations/VariableAssignNode.java | 15 +- .../values/operations/VariableIndexNode.java | 22 -- .../jscript/{common => runtime}/Compiler.java | 9 +- .../me/topchetoeu/jscript/runtime/Engine.java | 3 +- .../topchetoeu/jscript/runtime/EventLoop.java | 1 - .../me/topchetoeu/jscript/runtime/Frame.java | 89 ++++-- .../jscript/runtime/InstructionRunner.java | 120 +++++--- .../jscript/runtime/SimpleRepl.java | 11 +- .../runtime/exceptions/EngineException.java | 2 + .../runtime/exceptions/SyntaxException.java | 2 +- .../jscript/runtime/scope/GlobalScope.java | 74 ----- .../jscript/runtime/scope/LocalScope.java | 29 -- .../jscript/runtime/scope/ValueVariable.java | 25 -- .../jscript/runtime/scope/Variable.java | 24 -- .../jscript/runtime/values/Value.java | 60 +++- .../values/functions/CodeFunction.java | 5 +- .../runtime/values/objects/ArrayValue.java | 6 +- .../runtime/values/objects/ScopeValue.java | 10 +- 82 files changed, 1657 insertions(+), 998 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/FunctionNode.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/LabelContext.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/NodeChildren.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/Path.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/values/ThisNode.java delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java rename src/java/me/topchetoeu/jscript/{common => runtime}/Compiler.java (84%) delete mode 100644 src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/scope/LocalScope.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java delete mode 100644 src/java/me/topchetoeu/jscript/runtime/scope/Variable.java diff --git a/build.gradle b/build.gradle index 187c46b..34358c7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,11 +2,27 @@ plugins { id "application" } +repositories { + mavenCentral() +} + +dependencies { + annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' + // Genuinely fuck Java + compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' +} + java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - toolchain.languageVersion = JavaLanguageVersion.of(11) - withSourcesJar() + toolchain.languageVersion = JavaLanguageVersion.of(17) +} + +configure([tasks.compileJava]) { + sourceCompatibility = 17 // for the IDE support + options.release = 11 + + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(17) + } } jar { diff --git a/src/assets/lib/index.js b/src/assets/lib/index.js index 6176ce2..96d0e60 100644 --- a/src/assets/lib/index.js +++ b/src/assets/lib/index.js @@ -1,3 +1,5 @@ +#! my special comment lol + (function(target, primordials) { var makeSymbol = primordials.symbol.makeSymbol; var getSymbol = primordials.symbol.getSymbol; diff --git a/src/java/me/topchetoeu/jscript/common/FunctionBody.java b/src/java/me/topchetoeu/jscript/common/FunctionBody.java index d0328a0..9d3cab1 100644 --- a/src/java/me/topchetoeu/jscript/common/FunctionBody.java +++ b/src/java/me/topchetoeu/jscript/common/FunctionBody.java @@ -3,12 +3,13 @@ package me.topchetoeu.jscript.common; public class FunctionBody { public final FunctionBody[] children; public final Instruction[] instructions; - public final int localsN, argsN; + public final int localsN, capturesN, argsN; - public FunctionBody(int localsN, int argsN, Instruction[] instructions, FunctionBody[] children) { + public FunctionBody(int localsN, int capturesN, int argsN, Instruction[] instructions, FunctionBody[] children) { this.children = children; this.argsN = argsN; this.localsN = localsN; + this.capturesN = capturesN; this.instructions = instructions; } } diff --git a/src/java/me/topchetoeu/jscript/common/Instruction.java b/src/java/me/topchetoeu/jscript/common/Instruction.java index edc0652..478fddc 100644 --- a/src/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/java/me/topchetoeu/jscript/common/Instruction.java @@ -1,8 +1,8 @@ package me.topchetoeu.jscript.common; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; +// import java.io.DataInputStream; +// import java.io.DataOutputStream; +// import java.io.IOException; import java.util.HashMap; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; @@ -35,20 +35,27 @@ public class Instruction { LOAD_FUNC(0x30), LOAD_ARR(0x31), LOAD_OBJ(0x32), - STORE_SELF_FUNC(0x33), + LOAD_GLOB(0x33), LOAD_REGEX(0x34), LOAD_VAR(0x40), LOAD_MEMBER(0x41), - LOAD_GLOB(0x42), - STORE_VAR(0x43), - STORE_MEMBER(0x44), + LOAD_ARGS(0x42), + LOAD_THIS(0x43), + STORE_VAR(0x48), + STORE_MEMBER(0x49), - MAKE_VAR(0x50), - DEF_PROP(0x51), - KEYS(0x52), - TYPEOF(0x53), - OPERATION(0x54); + DEF_PROP(0x50), + KEYS(0x51), + TYPEOF(0x52), + OPERATION(0x53), + + GLOB_GET(0x60), + GLOB_SET(0x61), + GLOB_DEF(0x62), + + STACK_ALLOC(0x70), + STACK_FREE(0x71); private static final HashMap types = new HashMap<>(); public final int numeric; @@ -108,123 +115,128 @@ public class Instruction { return params[i].equals(arg); } - public void write(DataOutputStream writer) throws IOException { - var rawType = type.numeric; + // public void write(DataOutputStream writer) throws IOException { + // var rawType = type.numeric; - switch (type) { - case KEYS: - case PUSH_BOOL: - case STORE_MEMBER: rawType |= (boolean)get(0) ? 128 : 0; break; - case STORE_VAR: rawType |= (boolean)get(1) ? 128 : 0; break; - case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break; - default: - } + // switch (type) { + // case KEYS: + // case PUSH_BOOL: + // case STORE_MEMBER: + // case GLOB_SET: + // rawType |= (boolean)get(0) ? 128 : 0; break; + // case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break; + // default: + // } - writer.writeByte(rawType); + // writer.writeByte(rawType); - switch (type) { - case CALL: - case CALL_NEW: - case CALL_MEMBER: - writer.writeInt(get(0)); - writer.writeUTF(get(1)); - break; - case DUP: writer.writeInt(get(0)); break; - case JMP: writer.writeInt(get(0)); break; - case JMP_IF: writer.writeInt(get(0)); break; - case JMP_IFN: writer.writeInt(get(0)); break; - case LOAD_ARR: writer.writeInt(get(0)); break; - case LOAD_FUNC: { - writer.writeInt(params.length - 1); + // switch (type) { + // case CALL: + // case CALL_NEW: + // case CALL_MEMBER: + // writer.writeInt(get(0)); + // writer.writeUTF(get(1)); + // break; + // case DUP: writer.writeInt(get(0)); break; + // case JMP: writer.writeInt(get(0)); break; + // case JMP_IF: writer.writeInt(get(0)); break; + // case JMP_IFN: writer.writeInt(get(0)); break; + // case LOAD_ARR: writer.writeInt(get(0)); break; + // case LOAD_FUNC: { + // writer.writeInt(params.length - 1); - for (var i = 0; i < params.length; i++) { - writer.writeInt(get(i + 1)); - } + // for (var i = 0; i < params.length; i++) { + // writer.writeInt(get(i + 1)); + // } - writer.writeInt(get(0)); - writer.writeUTF(get(0)); - break; - } - case LOAD_REGEX: writer.writeUTF(get(0)); break; - case LOAD_VAR: writer.writeInt(get(0)); break; - case MAKE_VAR: writer.writeUTF(get(0)); break; - case OPERATION: writer.writeByte(((Operation)get(0)).numeric); break; - case PUSH_NUMBER: writer.writeDouble(get(0)); break; - case PUSH_STRING: writer.writeUTF(get(0)); break; - case STORE_SELF_FUNC: writer.writeInt(get(0)); break; - case STORE_VAR: writer.writeInt(get(0)); break; - case THROW_SYNTAX: writer.writeUTF(get(0)); - case TRY_START: - writer.writeInt(get(0)); - writer.writeInt(get(1)); - writer.writeInt(get(2)); - break; - case TYPEOF: - if (params.length > 0) writer.writeUTF(get(0)); - break; - default: - } - } + // writer.writeInt(get(0)); + // writer.writeUTF(get(0)); + // break; + // } + // case LOAD_REGEX: writer.writeUTF(get(0)); break; + // case LOAD_VAR: writer.writeInt(get(0)); break; + // case GLOB_DEF: writer.writeUTF(get(0)); break; + // case GLOB_GET: writer.writeUTF(get(0)); break; + // case GLOB_SET: + // writer.writeUTF(get(0)); + // break; + // case OPERATION: writer.writeByte(((Operation)get(0)).numeric); break; + // case PUSH_NUMBER: writer.writeDouble(get(0)); break; + // case PUSH_STRING: writer.writeUTF(get(0)); break; + // case STORE_VAR: writer.writeInt(get(0)); break; + // case THROW_SYNTAX: writer.writeUTF(get(0)); + // case TRY_START: + // writer.writeInt(get(0)); + // writer.writeInt(get(1)); + // writer.writeInt(get(2)); + // break; + // case TYPEOF: + // if (params.length > 0) writer.writeUTF(get(0)); + // break; + // default: + // } + // } private Instruction(Type type, Object ...params) { this.type = type; this.params = params; } - public static Instruction read(DataInputStream stream) throws IOException { - var rawType = stream.readUnsignedByte(); - var type = Type.fromNumeric(rawType & 127); - var flag = (rawType & 128) != 0; + // public static Instruction read(DataInputStream stream) throws IOException { + // var rawType = stream.readUnsignedByte(); + // var type = Type.fromNumeric(rawType & 127); + // var flag = (rawType & 128) != 0; - switch (type) { - case CALL: return call(stream.readInt(), stream.readUTF()); - case CALL_NEW: return callNew(stream.readInt(), stream.readUTF()); - case CALL_MEMBER: return callNew(stream.readInt(), stream.readUTF()); - case DEF_PROP: return defProp(); - case DELETE: return delete(); - case DISCARD: return discard(); - case DUP: return dup(stream.readInt()); - case JMP: return jmp(stream.readInt()); - case JMP_IF: return jmpIf(stream.readInt()); - case JMP_IFN: return jmpIfNot(stream.readInt()); - case KEYS: return keys(flag); - case LOAD_ARR: return loadArr(stream.readInt()); - case LOAD_FUNC: { - var captures = new int[stream.readInt()]; + // switch (type) { + // case CALL: return call(stream.readInt(), stream.readUTF()); + // case CALL_NEW: return callNew(stream.readInt(), stream.readUTF()); + // case CALL_MEMBER: return callNew(stream.readInt(), stream.readUTF()); + // case DEF_PROP: return defProp(); + // case DELETE: return delete(); + // case DISCARD: return discard(); + // case DUP: return dup(stream.readInt()); + // case JMP: return jmp(stream.readInt()); + // case JMP_IF: return jmpIf(stream.readInt()); + // case JMP_IFN: return jmpIfNot(stream.readInt()); + // case KEYS: return keys(flag); + // case LOAD_ARR: return loadArr(stream.readInt()); + // case LOAD_FUNC: { + // var captures = new int[stream.readInt()]; - for (var i = 0; i < captures.length; i++) { - captures[i] = stream.readInt(); - } + // for (var i = 0; i < captures.length; i++) { + // captures[i] = stream.readInt(); + // } - return loadFunc(stream.readInt(), stream.readUTF(), captures); - } - case LOAD_GLOB: return loadGlob(); - case LOAD_MEMBER: return loadMember(); - case LOAD_OBJ: return loadObj(); - case LOAD_REGEX: return loadRegex(stream.readUTF(), null); - case LOAD_VAR: return loadVar(stream.readInt()); - case MAKE_VAR: return makeVar(stream.readUTF()); - case OPERATION: return operation(Operation.fromNumeric(stream.readUnsignedByte())); - case PUSH_NULL: return pushNull(); - case PUSH_UNDEFINED: return pushUndefined(); - case PUSH_BOOL: return pushValue(flag); - case PUSH_NUMBER: return pushValue(stream.readDouble()); - case PUSH_STRING: return pushValue(stream.readUTF()); - case RETURN: return ret(); - case STORE_MEMBER: return storeMember(flag); - case STORE_SELF_FUNC: return storeSelfFunc(stream.readInt()); - case STORE_VAR: return storeVar(stream.readInt(), flag); - case THROW: return throwInstr(); - case THROW_SYNTAX: return throwSyntax(stream.readUTF()); - case TRY_END: return tryEnd(); - case TRY_START: return tryStart(stream.readInt(), stream.readInt(), stream.readInt()); - case TYPEOF: return flag ? typeof(stream.readUTF()) : typeof(); - case NOP: - if (flag) return null; - else return nop(); - default: return null; - } - } + // return loadFunc(stream.readInt(), stream.readUTF(), captures); + // } + // case LOAD_GLOB: return loadGlob(); + // case LOAD_MEMBER: return loadMember(); + // case LOAD_OBJ: return loadObj(); + // case LOAD_REGEX: return loadRegex(stream.readUTF(), null); + // case LOAD_VAR: return loadVar(stream.readInt()); + // case GLOB_DEF: return globDef(stream.readUTF()); + // case GLOB_GET: return globGet(stream.readUTF()); + // case GLOB_SET: return globSet(stream.readUTF(), flag); + // case OPERATION: return operation(Operation.fromNumeric(stream.readUnsignedByte())); + // case PUSH_NULL: return pushNull(); + // case PUSH_UNDEFINED: return pushUndefined(); + // case PUSH_BOOL: return pushValue(flag); + // case PUSH_NUMBER: return pushValue(stream.readDouble()); + // case PUSH_STRING: return pushValue(stream.readUTF()); + // case RETURN: return ret(); + // case STORE_MEMBER: return storeMember(flag); + // case STORE_VAR: return storeVar(stream.readInt(), flag); + // case THROW: return throwInstr(); + // case THROW_SYNTAX: return throwSyntax(stream.readUTF()); + // case TRY_END: return tryEnd(); + // case TRY_START: return tryStart(stream.readInt(), stream.readInt(), stream.readInt(), stream.readInt()); + // case TYPEOF: return flag ? typeof(stream.readUTF()) : typeof(); + // case NOP: + // if (flag) return null; + // else return nop(); + // default: return null; + // } + // } public static Instruction tryStart(int catchStart, int finallyStart, int end) { return new Instruction(Type.TRY_START, catchStart, finallyStart, end); @@ -299,12 +311,26 @@ public class Instruction { return new Instruction(Type.PUSH_STRING, val); } - public static Instruction makeVar(String name) { - return new Instruction(Type.MAKE_VAR, name); + public static Instruction globDef(String name) { + return new Instruction(Type.GLOB_GET, name); } - public static Instruction loadVar(Object i) { + + public static Instruction globGet(String name) { + return new Instruction(Type.GLOB_GET, name); + } + public static Instruction globSet(String name, boolean keep, boolean define) { + return new Instruction(Type.GLOB_SET, name, keep, define); + } + + public static Instruction loadVar(int i) { return new Instruction(Type.LOAD_VAR, i); } + public static Instruction loadThis() { + return new Instruction(Type.LOAD_THIS); + } + public static Instruction loadArgs() { + return new Instruction(Type.LOAD_ARGS); + } public static Instruction loadGlob() { return new Instruction(Type.LOAD_GLOB); } @@ -337,13 +363,10 @@ public class Instruction { return new Instruction(Type.DUP, count); } - public static Instruction storeSelfFunc(int i) { - return new Instruction(Type.STORE_SELF_FUNC, i); - } - public static Instruction storeVar(Object i) { + public static Instruction storeVar(int i) { return new Instruction(Type.STORE_VAR, i, false); } - public static Instruction storeVar(Object i, boolean keep) { + public static Instruction storeVar(int i, boolean keep) { return new Instruction(Type.STORE_VAR, i, keep); } public static Instruction storeMember() { @@ -375,8 +398,14 @@ public class Instruction { return new Instruction(Type.OPERATION, op); } - @Override - public String toString() { + public static Instruction stackAlloc(int i) { + return new Instruction(Type.STACK_ALLOC, i); + } + public static Instruction stackFree(int i) { + return new Instruction(Type.STACK_FREE, i); + } + + @Override public String toString() { var res = type.toString(); for (int i = 0; i < params.length; i++) { diff --git a/src/java/me/topchetoeu/jscript/common/environment/Environment.java b/src/java/me/topchetoeu/jscript/common/environment/Environment.java index 7bb724e..6195fa2 100644 --- a/src/java/me/topchetoeu/jscript/common/environment/Environment.java +++ b/src/java/me/topchetoeu/jscript/common/environment/Environment.java @@ -160,13 +160,17 @@ public class Environment { return this; } - public Environment init(Key key, T val) { + public T init(Key key, T val) { if (!has(key)) this.add(key, val); - return this; + return val; } - public Environment init(Key key, Supplier val) { - if (!has(key)) this.add(key, val.get()); - return this; + public T initFrom(Key key, Supplier val) { + if (!has(key)) { + var res = val.get(); + this.add(key, res); + return res; + } + else return get(key); } public Environment child() { diff --git a/src/java/me/topchetoeu/jscript/common/json/JSONElement.java b/src/java/me/topchetoeu/jscript/common/json/JSONElement.java index 0b3af33..a8d8490 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSONElement.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSONElement.java @@ -69,8 +69,7 @@ public class JSONElement { return (boolean)value; } - @Override - public String toString() { + @Override public String toString() { if (isMap()) return "{...}"; if (isList()) return "[...]"; if (isString()) return (String)value; diff --git a/src/java/me/topchetoeu/jscript/common/json/JSONMap.java b/src/java/me/topchetoeu/jscript/common/json/JSONMap.java index ac0cb49..2e2f1f9 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSONMap.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSONMap.java @@ -116,32 +116,20 @@ public class JSONMap implements Map { public JSONMap set(String key, Map val) { elements.put(key, JSONElement.of(val)); return this; } public JSONMap set(String key, Collection val) { elements.put(key, JSONElement.of(val)); return this; } - @Override - public int size() { return elements.size(); } - @Override - public boolean isEmpty() { return elements.isEmpty(); } - @Override - public boolean containsKey(Object key) { return elements.containsKey(key); } - @Override - public boolean containsValue(Object value) { return elements.containsValue(value); } - @Override - public JSONElement get(Object key) { return elements.get(key); } - @Override - public JSONElement put(String key, JSONElement value) { return elements.put(key, value); } - @Override - public JSONElement remove(Object key) { return elements.remove(key); } - @Override - public void putAll(Map m) { elements.putAll(m); } + @Override public int size() { return elements.size(); } + @Override public boolean isEmpty() { return elements.isEmpty(); } + @Override public boolean containsKey(Object key) { return elements.containsKey(key); } + @Override public boolean containsValue(Object value) { return elements.containsValue(value); } + @Override public JSONElement get(Object key) { return elements.get(key); } + @Override public JSONElement put(String key, JSONElement value) { return elements.put(key, value); } + @Override public JSONElement remove(Object key) { return elements.remove(key); } + @Override public void putAll(Map m) { elements.putAll(m); } - @Override - public void clear() { elements.clear(); } + @Override public void clear() { elements.clear(); } - @Override - public Set keySet() { return elements.keySet(); } - @Override - public Collection values() { return elements.values(); } - @Override - public Set> entrySet() { return elements.entrySet(); } + @Override public Set keySet() { return elements.keySet(); } + @Override public Collection values() { return elements.values(); } + @Override public Set> entrySet() { return elements.entrySet(); } public JSONMap() { } public JSONMap(Map els) { diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index 5d54895..fea96ff 100644 --- a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; +import me.topchetoeu.jscript.compilation.scope.Scope; public class FunctionMap { public static class FunctionMapBuilder { @@ -53,8 +53,8 @@ public class FunctionMap { public FunctionMap build(String[] localNames, String[] captureNames) { return new FunctionMap(sourceMap, breakpoints, localNames, captureNames); } - public FunctionMap build(LocalScopeRecord scope) { - return new FunctionMap(sourceMap, breakpoints, scope.locals(), scope.captures()); + public FunctionMap build(Scope scope) { + return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]); } public FunctionMap build() { return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]); diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Location.java b/src/java/me/topchetoeu/jscript/common/parsing/Location.java index 1ef86e8..1d86866 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/Location.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Location.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.Objects; public abstract class Location implements Comparable { - public static final Location INTERNAL = Location.of("jscript://native"); + public static final Location INTERNAL = Location.of(new Filename("jscript", "native"), -1, -1); public abstract int line(); public abstract int start(); @@ -39,10 +39,10 @@ public abstract class Location implements Comparable { }; } - @Override public final int hashCode() { + @Override public int hashCode() { return Objects.hash(line(), start(), filename()); } - @Override public final boolean equals(Object obj) { + @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Location)) return false; var other = (Location)obj; diff --git a/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java b/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java index c1af040..7c1f553 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java @@ -12,28 +12,35 @@ public class ParseRes { } public final ParseRes.State state; + public final Location errorLocation; public final String error; public final T result; public final int n; - private ParseRes(ParseRes.State state, String error, T result, int readN) { + private ParseRes(ParseRes.State state, Location errorLocation, String error, T result, int readN) { this.result = result; this.n = readN; this.state = state; this.error = error; + this.errorLocation = errorLocation; } public ParseRes setN(int i) { if (!state.isSuccess()) return this; - return new ParseRes<>(state, null, result, i); + return new ParseRes<>(state, null, null, result, i); } public ParseRes addN(int n) { if (!state.isSuccess()) return this; - return new ParseRes<>(state, null, result, this.n + n); + return new ParseRes<>(state, null, null, result, this.n + n); } public ParseRes chainError() { if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed."); - return new ParseRes<>(state, error, null, 0); + return new ParseRes<>(state, errorLocation, error, null, 0); + } + @SuppressWarnings("unchecked") + public ParseRes chainError(Location loc, String error) { + if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0); + return (ParseRes) this; } public boolean isSuccess() { return state.isSuccess(); } @@ -41,19 +48,13 @@ public class ParseRes { public boolean isError() { return state.isError(); } public static ParseRes failed() { - return new ParseRes(State.FAILED, null, null, 0); + return new ParseRes(State.FAILED, null, null, null, 0); } public static ParseRes error(Location loc, String error) { - if (loc != null) error = loc + ": " + error; - return new ParseRes<>(State.ERROR, error, null, 0); - } - public ParseRes chainError(Location loc, String error) { - if (loc != null) error = loc + ": " + error; - if (!this.isError()) return new ParseRes<>(State.ERROR, error, null, 0); - return new ParseRes<>(State.ERROR, this.error, null, 0); + return new ParseRes<>(State.ERROR, loc, error, null, 0); } public static ParseRes res(T val, int i) { - return new ParseRes<>(State.SUCCESS, null, val, i); + return new ParseRes<>(State.SUCCESS, null, null, val, i); } @SafeVarargs diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java index 07047d4..925c3d3 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java @@ -19,9 +19,48 @@ public class Parsing { } public static int skipEmpty(Source src, int i) { + return skipEmpty(src, i, true); + } + + public static int skipEmpty(Source src, int i, boolean noComments) { int n = 0; - while (n < src.size() && src.is(i + n, Character::isWhitespace)) n++; + if (i == 0 && src.is(0, "#!")) { + while (!src.is(n, '\n')) n++; + n++; + } + + var isSingle = false; + var isMulti = false; + + while (i + n < src.size()) { + if (isSingle) { + if (src.is(i + n, '\n')) { + n++; + isSingle = false; + } + else n++; + } + else if (isMulti) { + if (src.is(i + n, "*/")) { + n += 2; + isMulti = false; + } + else n++; + } + else if (src.is(i + n, "//")) { + n += 2; + isSingle = true; + } + else if (src.is(i + n, "/*")) { + n += 2; + isMulti = true; + } + else if (src.is(i + n, Character::isWhitespace)) { + n++; + } + else break; + } return n; } diff --git a/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java b/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java index a2050d4..5819565 100644 --- a/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java +++ b/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java @@ -1,5 +1,7 @@ package me.topchetoeu.jscript.common.parsing; +import java.util.Objects; + public class SourceLocation extends Location { private int[] lineStarts; private int line; @@ -10,16 +12,16 @@ public class SourceLocation extends Location { private void update() { if (lineStarts == null) return; - int start = 0; - int end = lineStarts.length - 1; + int a = 0; + int b = lineStarts.length; while (true) { - if (start + 1 >= end) break; - var mid = -((-start - end) >> 1); + if (a + 1 >= b) break; + var mid = -((-a - b) >> 1); var el = lineStarts[mid]; - if (el < offset) start = mid; - else if (el > offset) end = mid; + if (el < offset) a = mid; + else if (el > offset) b = mid; else { this.line = mid; this.start = 0; @@ -28,8 +30,8 @@ public class SourceLocation extends Location { } } - this.line = start; - this.start = offset - lineStarts[start]; + this.line = a; + this.start = offset - lineStarts[a]; this.lineStarts = null; return; } @@ -44,6 +46,18 @@ public class SourceLocation extends Location { return start; } + @Override public int hashCode() { + return Objects.hash(offset); + } + @Override public int compareTo(Location other) { + if (other instanceof SourceLocation srcLoc) return Integer.compare(offset, srcLoc.offset); + else return super.compareTo(other); + } + @Override public boolean equals(Object obj) { + if (obj instanceof SourceLocation other) return this.offset == other.offset; + else return super.equals(obj); + } + public SourceLocation(Filename filename, int[] lineStarts, int offset) { this.filename = filename; this.lineStarts = lineStarts; diff --git a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java index f65e663..59ce8fd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -1,23 +1,27 @@ package me.topchetoeu.jscript.compilation; import java.util.List; +import java.util.ArrayList; import java.util.LinkedList; -import java.util.Vector; +import java.util.function.Supplier; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder; import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; +import me.topchetoeu.jscript.compilation.scope.LocalScope; +import me.topchetoeu.jscript.compilation.scope.Scope; -public class CompileResult { - public final Vector instructions = new Vector<>(); - public final List children = new LinkedList<>(); - public final FunctionMapBuilder map = FunctionMap.builder(); - public final LocalScopeRecord scope; +public final class CompileResult { + public final List> instructions; + public final List children; + public final FunctionMapBuilder map; + public final Environment env; public int length = 0; + public final Scope scope; public int temp() { instructions.add(null); @@ -25,16 +29,24 @@ public class CompileResult { } public CompileResult add(Instruction instr) { + instructions.add(() -> instr); + return this; + } + public CompileResult add(Supplier instr) { instructions.add(instr); return this; } public CompileResult set(int i, Instruction instr) { + instructions.set(i, () -> instr); + return this; + } + public CompileResult set(int i, Supplierinstr) { instructions.set(i, instr); return this; } - public Instruction get(int i) { - return instructions.get(i); - } + // public Instruction get(int i) { + // return instructions.get(i); + // } public int size() { return instructions.size(); } public void setDebug(Location loc, BreakpointType type) { @@ -61,6 +73,13 @@ public class CompileResult { return child; } + public Instruction[] instructions() { + var res = new Instruction[instructions.size()]; + var i = 0; + for (var suppl : instructions) res[i++] = suppl.get(); + return res; + } + public FunctionMap map() { return map.build(scope); } @@ -69,10 +88,32 @@ public class CompileResult { for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body(); - return new FunctionBody(scope.localsCount(), length, instructions.toArray(Instruction[]::new), builtChildren); + var instrRes = new Instruction[instructions.size()]; + var i = 0; + for (var suppl : instructions) instrRes[i++] = suppl.get(); + + return new FunctionBody( + scope.localsCount() + scope.allocCount(), scope.capturesCount(), length, + instrRes, builtChildren + ); } - public CompileResult(LocalScopeRecord scope) { + public CompileResult subtarget() { + return new CompileResult(new LocalScope(scope), this); + } + + public CompileResult(Environment env, Scope scope) { this.scope = scope; + instructions = new ArrayList<>(); + children = new LinkedList<>(); + map = FunctionMap.builder(); + this.env = env; + } + private CompileResult(Scope scope, CompileResult parent) { + this.scope = scope; + this.instructions = parent.instructions; + this.children = parent.children; + this.map = parent.map; + this.env = parent.env; } } diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java index 305e6fd..4db2815 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -2,7 +2,6 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; import java.util.List; -import java.util.Vector; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; @@ -10,36 +9,25 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.values.FunctionNode; + public class CompoundNode extends Node { public final Node[] statements; - public final boolean separateFuncs; public Location end; - @Override public boolean pure() { - for (var stm : statements) { - if (!stm.pure()) return false; - } - - return true; + @Override public void resolve(CompileResult target) { + for (var stm : statements) stm.resolve(target); } - @Override - public void declare(CompileResult target) { - for (var stm : statements) stm.declare(target); - } + @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { + List statements = new ArrayList(); - @Override - public void compile(CompileResult target, boolean pollute, BreakpointType type) { - List statements = new Vector(); - if (separateFuncs) for (var stm : this.statements) { - if (stm instanceof FunctionNode && ((FunctionNode)stm).statement) { + for (var stm : this.statements) { + if (stm instanceof FunctionStatementNode) { stm.compile(target, false); } else statements.add(stm); } - else statements = List.of(this.statements); var polluted = false; @@ -60,9 +48,8 @@ public class CompoundNode extends Node { return this; } - public CompoundNode(Location loc, boolean separateFuncs, Node ...statements) { + public CompoundNode(Location loc, Node ...statements) { super(loc); - this.separateFuncs = separateFuncs; this.statements = statements; } @@ -75,11 +62,18 @@ public class CompoundNode extends Node { if (!src.is(i + n, ",")) return ParseRes.failed(); n++; - var res = JavaScript.parseExpression(src, i + n, 2); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a value after the comma"); - n += res.n; + var curr = JavaScript.parseExpression(src, i + n, 2); + if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma"); + n += curr.n; - return ParseRes.res(new CompoundNode(loc, false, prev, res.result), n); + if (prev instanceof CompoundNode) { + var children = new ArrayList(); + children.addAll(List.of(((CompoundNode)prev).statements)); + children.add(curr.result); + + return ParseRes.res(new CompoundNode(loc, children.toArray(Node[]::new)), n); + } + else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n); } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); @@ -109,6 +103,6 @@ public class CompoundNode extends Node { statements.add(res.result); } - return ParseRes.res(new CompoundNode(loc, true, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n); + return ParseRes.res(new CompoundNode(loc, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java b/src/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java new file mode 100644 index 0000000..e0cb484 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java @@ -0,0 +1,19 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.function.IntSupplier; + +public final class DeferredIntSupplier implements IntSupplier { + private int value; + private boolean set; + + public void set(int val) { + if (set) throw new RuntimeException("A deferred int supplier may be set only once"); + value = val; + set = true; + } + + @Override public int getAsInt() { + if (!set) throw new RuntimeException("Deferred int supplier accessed too early"); + return value; + } +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java b/src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java deleted file mode 100644 index b8b8e0b..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/ExpressionNode.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -public class ExpressionNode { - -} diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java new file mode 100644 index 0000000..2b05291 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -0,0 +1,130 @@ +package me.topchetoeu.jscript.compilation; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.scope.FunctionScope; +import me.topchetoeu.jscript.compilation.scope.LocalScope; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; + +public abstract class FunctionNode extends Node { + public final CompoundNode body; + public final String[] args; + public final Location end; + + public abstract String name(); + + // @Override public void declare(CompileResult target) { + // if (varName != null && statement) target.scope.define(varName); + // } + + // public static void checkBreakAndCont(CompileResult target, int start) { + // for (int i = start; i < target.size(); i++) { + // if (target.get(i).type == Type.NOP) { + // if (target.get(i).is(0, "break") ) { + // throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop."); + // } + // if (target.get(i).is(0, "cont")) { + // throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop."); + // } + // } + // } + // } + + protected void compileLoadFunc(CompileResult target, int[] captures, String name) { + target.add(Instruction.loadFunc(target.children.size(), name, captures)); + } + + private CompileResult compileBody(CompileResult target, String name, boolean storeSelf, boolean pollute, BreakpointType bp) { + for (var i = 0; i < args.length; i++) { + for (var j = 0; j < i; j++) { + if (args[i].equals(args[j])) { + throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'."); + } + } + } + + var funcScope = new FunctionScope(storeSelf ? name : null, args, target.scope); + var subtarget = new CompileResult(target.env, new LocalScope(funcScope)); + + // compileStoreSelf(subtarget, pollute, bp); + + body.resolve(subtarget); + body.compile(subtarget, false); + + subtarget.length = args.length; + subtarget.scope.end(); + funcScope.end(); + subtarget.add(Instruction.ret()).setLocation(end); + + if (pollute) compileLoadFunc(target, funcScope.getCaptureIndices(), name); + + return target.addChild(subtarget); + } + + public void compile(CompileResult target, boolean pollute, boolean storeSelf, String name, BreakpointType bp) { + if (this.name() != null) name = this.name(); + + compileBody(target, name, storeSelf, pollute, bp); + } + public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); + public void compile(CompileResult target, boolean pollute, String name) { + compile(target, pollute, name, BreakpointType.NONE); + } + @Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) { + compile(target, pollute, (String)null, bp); + } + @Override public void compile(CompileResult target, boolean pollute) { + compile(target, pollute, (String)null, BreakpointType.NONE); + } + + public FunctionNode(Location loc, Location end, String[] args, CompoundNode body) { + super(loc); + + this.end = end; + this.args = args; + this.body = body; + } + + public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) { + if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name); + else stm.compile(target, pollute); + } + public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name, BreakpointType bp) { + if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name, bp); + else stm.compile(target, pollute, bp); + } + + public static ParseRes parseFunction(Source src, int i, boolean statement) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!Parsing.isIdentifier(src, i + n, "function")) return ParseRes.failed(); + n += 8; + + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess() && statement) return ParseRes.error(src.loc(i + n), "A statement function requires a name"); + n += name.n; + n += Parsing.skipEmpty(src, i + n); + + var args = JavaScript.parseParamList(src, i + n); + if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); + n += args.n; + + var body = CompoundNode.parse(src, i + n); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for function."); + n += body.n; + + if (statement) return ParseRes.res(new FunctionStatementNode( + loc, src.loc(i + n - 1), + args.result.toArray(String[]::new), body.result, name.result + ), n); + else return ParseRes.res(new FunctionValueNode( + loc, src.loc(i + n - 1), + args.result.toArray(String[]::new), body.result, name.result + ), n); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java new file mode 100644 index 0000000..1b404f3 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -0,0 +1,25 @@ +package me.topchetoeu.jscript.compilation; + +import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.values.VariableNode; + +public class FunctionStatementNode extends FunctionNode { + public final String name; + + @Override public String name() { return name; } + + @Override public void resolve(CompileResult target) { + target.scope.define(name, false, end); + } + + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { + compile(target, true, false, this.name, bp); + target.add(VariableNode.toSet(target, end, this.name, pollute, true)); + } + + public FunctionStatementNode(Location loc, Location end, String[] args, CompoundNode body, String name) { + super(loc, end, args, body); + this.name = name; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java new file mode 100644 index 0000000..c89d48d --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -0,0 +1,19 @@ +package me.topchetoeu.jscript.compilation; + +import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; + +public class FunctionValueNode extends FunctionNode { + public final String name; + + @Override public String name() { return name; } + + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { + compile(target, pollute, true, name, bp); + } + + public FunctionValueNode(Location loc, Location end, String[] args, CompoundNode body, String name) { + super(loc, end, args, body); + this.name = name; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index 2434885..75aaa83 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -16,7 +16,6 @@ import me.topchetoeu.jscript.compilation.control.DebugNode; import me.topchetoeu.jscript.compilation.control.DeleteNode; import me.topchetoeu.jscript.compilation.control.DoWhileNode; import me.topchetoeu.jscript.compilation.control.ForInNode; -import me.topchetoeu.jscript.compilation.control.ForOfNode; import me.topchetoeu.jscript.compilation.control.ForNode; import me.topchetoeu.jscript.compilation.control.IfNode; import me.topchetoeu.jscript.compilation.control.ReturnNode; @@ -24,12 +23,14 @@ import me.topchetoeu.jscript.compilation.control.SwitchNode; import me.topchetoeu.jscript.compilation.control.ThrowNode; import me.topchetoeu.jscript.compilation.control.TryNode; import me.topchetoeu.jscript.compilation.control.WhileNode; -import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; +import me.topchetoeu.jscript.compilation.scope.GlobalScope; +import me.topchetoeu.jscript.compilation.scope.LocalScope; +import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; -import me.topchetoeu.jscript.compilation.values.FunctionNode; import me.topchetoeu.jscript.compilation.values.GlobalThisNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; +import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.BoolNode; import me.topchetoeu.jscript.compilation.values.constants.NullNode; @@ -41,7 +42,6 @@ import me.topchetoeu.jscript.compilation.values.operations.DiscardNode; import me.topchetoeu.jscript.compilation.values.operations.IndexNode; import me.topchetoeu.jscript.compilation.values.operations.OperationNode; import me.topchetoeu.jscript.compilation.values.operations.TypeofNode; -import me.topchetoeu.jscript.compilation.values.operations.VariableIndexNode; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class JavaScript { @@ -104,8 +104,8 @@ public class JavaScript { if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); - if (id.result.equals("this")) return ParseRes.res(new VariableIndexNode(loc, 0), n); - if (id.result.equals("arguments")) return ParseRes.res(new VariableIndexNode(loc, 1), n); + if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); + if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); return ParseRes.failed(); @@ -187,7 +187,7 @@ public class JavaScript { SwitchNode::parse, ForNode::parse, ForInNode::parse, - ForOfNode::parse, + // ForOfNode::parse, DoWhileNode::parse, TryNode::parse, CompoundNode::parse, @@ -257,7 +257,7 @@ public class JavaScript { var res = parseStatement(src, i); - if (res.isError()) throw new SyntaxException(src.loc(i), res.error); + if (res.isError()) throw new SyntaxException(res.errorLocation, res.error); else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax"); i += res.n; @@ -272,22 +272,17 @@ public class JavaScript { return !JavaScript.reserved.contains(name); } - public static CompileResult compile(Node ...statements) { - var target = new CompileResult(new LocalScopeRecord()); - var stm = new CompoundNode(null, true, statements); - - target.scope.define("this"); - target.scope.define("arguments"); + public static CompileResult compile(Environment env, Node ...statements) { + var target = new CompileResult(env, new LocalScope(new GlobalScope())); + var stm = new CompoundNode(null, statements); try { + stm.resolve(target); stm.compile(target, true); - FunctionNode.checkBreakAndCont(target, 0); + // FunctionNode.checkBreakAndCont(target, 0); } catch (SyntaxException e) { - target = new CompileResult(new LocalScopeRecord()); - - target.scope.define("this"); - target.scope.define("arguments"); + target = new CompileResult(env, new LocalScope(new GlobalScope())); target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); } @@ -298,6 +293,24 @@ public class JavaScript { } public static CompileResult compile(Environment env, Filename filename, String raw) { - return JavaScript.compile(JavaScript.parse(env, filename, raw)); + return JavaScript.compile(env, JavaScript.parse(env, filename, raw)); + } + public static CompileResult compile(Filename filename, String raw) { + var env = new Environment(); + return JavaScript.compile(env, JavaScript.parse(env, filename, raw)); + } + + public static ParseRes parseLabel(Source src, int i) { + int n = Parsing.skipEmpty(src, i); + + var nameRes = Parsing.parseIdentifier(src, i + n); + if (!nameRes.isSuccess()) return nameRes.chainError(); + n += nameRes.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ":")) return ParseRes.failed(); + n++; + + return ParseRes.res(nameRes.result, n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/LabelContext.java b/src/java/me/topchetoeu/jscript/compilation/LabelContext.java new file mode 100644 index 0000000..5cfb1a3 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/LabelContext.java @@ -0,0 +1,85 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.function.IntSupplier; +import java.util.function.Supplier; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; + +public class LabelContext { + public static final Key BREAK_CTX = Key.of(); + public static final Key CONTINUE_CTX = Key.of(); + + private final LinkedList list = new LinkedList<>(); + private final HashMap map = new HashMap<>(); + + public IntSupplier get() { + return list.peekLast(); + } + public IntSupplier get(String name) { + return map.get(name); + } + + public Supplier getJump(int offset) { + var res = get(); + if (res == null) return null; + else return () -> Instruction.jmp(res.getAsInt() - offset); + } + public Supplier getJump(int offset, String name) { + var res = get(name); + if (res == null) return null; + else return () -> Instruction.jmp(res.getAsInt() - offset); + } + + public void push(IntSupplier jumpTarget) { + list.add(jumpTarget); + } + public void push(Location loc, String name, IntSupplier jumpTarget) { + if (name == null) return; + if (map.containsKey(name)) throw new SyntaxException(loc, String.format("Label '%s' has already been declared", name)); + map.put(name, jumpTarget); + } + + public void pushLoop(Location loc, String name, IntSupplier jumpTarget) { + push(jumpTarget); + push(loc, name, jumpTarget); + } + + public void pop() { + list.removeLast(); + } + public void pop(String name) { + if (name == null) return; + map.remove(name); + } + + public void popLoop(String name) { + pop(); + pop(name); + } + + public static LabelContext getBreak(Environment env) { + return env.initFrom(BREAK_CTX, () -> new LabelContext()); + } + public static LabelContext getCont(Environment env) { + return env.initFrom(CONTINUE_CTX, () -> new LabelContext()); + } + + public static void pushLoop(Environment env, Location loc, String name, IntSupplier breakTarget, int contTarget) { + LabelContext.getBreak(env).pushLoop(loc, name, breakTarget); + LabelContext.getCont(env).pushLoop(loc, name, () -> contTarget); + } + public static void pushLoop(Environment env, Location loc, String name, IntSupplier breakTarget, IntSupplier contTarget) { + LabelContext.getBreak(env).pushLoop(loc, name, breakTarget); + LabelContext.getCont(env).pushLoop(loc, name, contTarget); + } + public static void popLoop(Environment env, String name) { + LabelContext.getBreak(env).popLoop(name); + LabelContext.getCont(env).popLoop(name); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/Node.java b/src/java/me/topchetoeu/jscript/compilation/Node.java index cf2c2fb..7da1c3e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/Node.java +++ b/src/java/me/topchetoeu/jscript/compilation/Node.java @@ -4,10 +4,9 @@ import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; public abstract class Node { - private Location _loc; + private Location loc; - public boolean pure() { return false; } - public void declare(CompileResult target) { } + public void resolve(CompileResult target) {} public void compile(CompileResult target, boolean pollute, BreakpointType type) { int start = target.size(); @@ -18,10 +17,10 @@ public abstract class Node { compile(target, pollute, BreakpointType.NONE); } - public Location loc() { return _loc; } - public void setLoc(Location loc) { _loc = loc; } + public Location loc() { return loc; } + public void setLoc(Location loc) { this.loc = loc; } protected Node(Location loc) { - this._loc = loc; + this.loc = loc; } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/NodeChildren.java b/src/java/me/topchetoeu/jscript/compilation/NodeChildren.java new file mode 100644 index 0000000..920c6d0 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/NodeChildren.java @@ -0,0 +1,93 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.function.Function; + +public final class NodeChildren implements Iterable { + public static final class Slot { + private Node node; + private final Function replacer; + + public final void replace(Node node) { + this.node = this.replacer.apply(node); + } + + public Slot(Node nodes, Function replacer) { + this.node = nodes; + this.replacer = replacer; + } + } + + private final Slot[] slots; + + private NodeChildren(Slot[] slots) { + this.slots = slots; + } + + @Override public Iterator iterator() { + return new Iterator() { + private int i = 0; + private Slot[] arr = slots; + + @Override public boolean hasNext() { + if (arr == null) return false; + else if (i >= arr.length) { + arr = null; + return false; + } + else return true; + } + @Override public Node next() { + if (!hasNext()) return null; + return arr[i++].node; + } + }; + } + public Iterable slots() { + return () -> new Iterator() { + private int i = 0; + private Slot[] arr = slots; + + @Override public boolean hasNext() { + if (arr == null) return false; + else if (i >= arr.length) { + arr = null; + return false; + } + else return true; + } + @Override public Slot next() { + if (!hasNext()) return null; + return arr[i++]; + } + }; + } + + public static final class Builder { + private final ArrayList slots = new ArrayList<>(); + + public final Builder add(Slot ...children) { + for (var child : children) { + this.slots.add(child); + } + + return this; + } + public final Builder add(Iterable children) { + for (var child : children) { + this.slots.add(child); + } + + return this; + } + public final Builder add(Node child, Function replacer) { + slots.add(new Slot(child, replacer)); + return this; + } + + public final NodeChildren build() { + return new NodeChildren(slots.toArray(Slot[]::new)); + } + } +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/Path.java b/src/java/me/topchetoeu/jscript/compilation/Path.java new file mode 100644 index 0000000..9d5f897 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/Path.java @@ -0,0 +1,29 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.function.Predicate; + +public class Path { + public final Path parent; + public final Node node; + + public Path getParent(Predicate> predicate) { + for (Path it = this; it != null; it = it.parent) { + if (predicate.test(it)) return it; + } + + return null; + } + + public Path getParent(Class type, Predicate> predicate) { + for (Path it = this; it != null; it = it.parent) { + if (type.isInstance(it.node) && predicate.test(it)) return it; + } + + return null; + } + + public Path(Path parent, Node node) { + this.parent = parent; + this.node = node; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java b/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java deleted file mode 100644 index b2d29b5..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/ThrowSyntaxNode.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; - -public class ThrowSyntaxNode extends Node { - public final String name; - - @Override - public void compile(CompileResult target, boolean pollute) { - target.add(Instruction.throwSyntax(name)); - } - - public ThrowSyntaxNode(SyntaxException e) { - super(e.loc); - this.name = e.msg; - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 4d0ba04..84c7014 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -9,7 +9,7 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.values.FunctionNode; +import me.topchetoeu.jscript.compilation.values.VariableNode; public class VariableDeclareNode extends Node { public static class Pair { @@ -26,23 +26,26 @@ public class VariableDeclareNode extends Node { public final List values; - @Override - public void declare(CompileResult target) { - for (var key : values) { - target.scope.define(key.name); + @Override public void resolve(CompileResult target) { + for (var entry : values) { + target.scope.define(entry.name, false, entry.location); } } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { if (entry.name == null) continue; - var key = target.scope.getKey(entry.name); - - if (key instanceof String) target.add(Instruction.makeVar((String)key)); if (entry.value != null) { FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER); - target.add(Instruction.storeVar(key)); + target.add(VariableNode.toSet(target, entry.location, entry.name, false, true)); + } + else { + target.add(() -> { + var i = target.scope.get(entry.name, true); + + if (i == null) return Instruction.globDef(entry.name); + else return Instruction.nop(); + }); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java index f3f95fd..e052b30 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java @@ -7,13 +7,22 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class BreakNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { - target.add(Instruction.nop("break", label)); + var res = LabelContext.getBreak(target.env).getJump(target.size()); + if (res == null) { + if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); + else throw new SyntaxException(loc(), "Illegal break statement"); + } + target.add(res); + + // target.add(Instruction.nop("break", label)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java index b3b5761..be09e72 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java @@ -7,13 +7,22 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class ContinueNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { - target.add(Instruction.nop("cont", label)); + var res = LabelContext.getCont(target.env).getJump(target.size()); + if (res == null) { + if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); + else throw new SyntaxException(loc(), "Illegal continue statement"); + } + target.add(res); + + // () -> Instruction.nop("cont", label)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java b/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java index 57071e6..6df811d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java @@ -16,8 +16,7 @@ public class DeleteNode extends Node { public final Node key; public final Node value; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { value.compile(target, true); key.compile(target, true); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java index 616cd6a..c07a004 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java @@ -7,28 +7,35 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; public class DoWhileNode extends Node { public final Node condition, body; public final String label; - @Override - public void declare(CompileResult target) { - body.declare(target); + @Override public void resolve(CompileResult target) { + body.resolve(target); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { int start = target.size(); - body.compile(target, false, BreakpointType.STEP_OVER); - int mid = target.size(); - condition.compile(target, true, BreakpointType.STEP_OVER); - int end = target.size(); + var end = new DeferredIntSupplier(); + var mid = new DeferredIntSupplier(); - WhileNode.replaceBreaks(target, label, start, mid - 1, mid, end + 1); - target.add(Instruction.jmpIf(start - end)); + LabelContext.pushLoop(target.env, loc(), label, end, start); + body.compile(target, false, BreakpointType.STEP_OVER); + LabelContext.popLoop(target.env, label); + + mid.set(target.size()); + condition.compile(target, true, BreakpointType.STEP_OVER); + int endI = target.size(); + end.set(endI + 1); + + // WhileNode.replaceBreaks(target, label, start, mid - 1, mid, end + 1); + target.add(Instruction.jmpIf(start - endI)); } public DoWhileNode(Location loc, String label, Node condition, Node body) { @@ -42,7 +49,7 @@ public class DoWhileNode extends Node { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var labelRes = WhileNode.parseLabel(src, i + n); + var labelRes = JavaScript.parseLabel(src, i + n); n += labelRes.n; if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed(); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index ae89d7c..1ae1dfa 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -8,8 +8,11 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForInNode extends Node { public final String varName; @@ -18,16 +21,12 @@ public class ForInNode extends Node { public final String label; public final Location varLocation; - @Override public void declare(CompileResult target) { - body.declare(target); - if (isDeclaration) target.scope.define(varName); + @Override public void resolve(CompileResult target) { + body.resolve(target); + if (isDeclaration) target.scope.define(varName, false, loc()); } @Override public void compile(CompileResult target, boolean pollute) { - var key = target.scope.getKey(varName); - - if (key instanceof String) target.add(Instruction.makeVar((String)key)); - object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(true)); @@ -39,17 +38,28 @@ public class ForInNode extends Node { target.add(Instruction.pushValue("value")).setLocation(varLocation); target.add(Instruction.loadMember()).setLocation(varLocation); - target.add(Instruction.storeVar(key)).setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); + target.add(VariableNode.toSet(target, loc(), varName, pollute, isDeclaration)); + target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); + + var end = new DeferredIntSupplier(); + + LabelContext.pushLoop(target.env, loc(), label, end, start); + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); body.compile(target, false, BreakpointType.STEP_OVER); - int end = target.size(); + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + LabelContext.popLoop(target.env, label); - WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); + int endI = target.size(); - target.add(Instruction.jmp(start - end)); + // WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); + + target.add(Instruction.jmp(start - endI)); target.add(Instruction.discard()); - target.set(mid, Instruction.jmpIf(end - mid + 1)); + target.set(mid, Instruction.jmpIf(endI - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } @@ -67,7 +77,7 @@ public class ForInNode extends Node { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var label = WhileNode.parseLabel(src, i + n); + var label = JavaScript.parseLabel(src, i + n); n += label.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java index 44e28fc..bf361f4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -7,7 +7,9 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.VariableDeclareNode; import me.topchetoeu.jscript.compilation.values.operations.DiscardNode; @@ -16,27 +18,39 @@ public class ForNode extends Node { public final Node declaration, assignment, condition, body; public final String label; - @Override - public void declare(CompileResult target) { - declaration.declare(target); - body.declare(target); + @Override public void resolve(CompileResult target) { + declaration.resolve(target); + body.resolve(target); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { declaration.compile(target, false, BreakpointType.STEP_OVER); int start = target.size(); condition.compile(target, true, BreakpointType.STEP_OVER); int mid = target.temp(); - body.compile(target, false, BreakpointType.STEP_OVER); - int beforeAssign = target.size(); + + var end = new DeferredIntSupplier(); + LabelContext.pushLoop(target.env, loc(), label, end, start); + + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + + body.compile(subtarget, false, BreakpointType.STEP_OVER); + + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + + LabelContext.popLoop(target.env, label); + + // int beforeAssign = target.size(); assignment.compile(target, false, BreakpointType.STEP_OVER); - int end = target.size(); + int endI = target.size(); + end.set(endI); - WhileNode.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1); + // WhileNode.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1); - target.add(Instruction.jmp(start - end)); - target.set(mid, Instruction.jmpIfNot(end - mid + 1)); + target.add(Instruction.jmp(start - endI)); + target.set(mid, Instruction.jmpIfNot(endI - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } @@ -74,7 +88,7 @@ public class ForNode extends Node { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var labelRes = WhileNode.parseLabel(src, i + n); + var labelRes = JavaScript.parseLabel(src, i + n); n += labelRes.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index ce214ce..d963c5e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -17,17 +17,15 @@ public class ForOfNode extends Node { public final String label; public final Location varLocation; - @Override - public void declare(CompileResult target) { - body.declare(target); - if (isDeclaration) target.scope.define(varName); + @Override public void resolve(CompileResult target) { + body.resolve(target); + if (isDeclaration) target.scope.resolve(varName); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { var key = target.scope.getKey(varName); - if (key instanceof String) target.add(Instruction.makeVar((String)key)); + if (key instanceof String) target.add(Instruction.globDef((String)key)); iterable.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.dup()); @@ -79,7 +77,7 @@ public class ForOfNode extends Node { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var label = WhileNode.parseLabel(src, i + n); + var label = JavaScript.parseLabel(src, i + n); n += label.n; n += Parsing.skipEmpty(src, i + n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java index f22c2dd..e4045a4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -7,47 +7,87 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; public class IfNode extends Node { public final Node condition, body, elseBody; + public final String label; - @Override - public void declare(CompileResult target) { - body.declare(target); - if (elseBody != null) elseBody.declare(target); + @Override public void resolve(CompileResult target) { + body.resolve(target); + if (elseBody != null) elseBody.resolve(target); } @Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) { condition.compile(target, true, breakpoint); if (elseBody == null) { - int i = target.temp(); - body.compile(target, pollute, breakpoint); + int start = target.temp(); + var end = new DeferredIntSupplier(); + + LabelContext.getBreak(target.env).push(loc(), label, end); + + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + + body.compile(subtarget, false, BreakpointType.STEP_OVER); + + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + + LabelContext.getBreak(target.env).pop(label); + int endI = target.size(); - target.set(i, Instruction.jmpIfNot(endI - i)); + end.set(endI); + + target.set(start, Instruction.jmpIfNot(endI - start)); } else { int start = target.temp(); - body.compile(target, pollute, breakpoint); + var end = new DeferredIntSupplier(); + + LabelContext.getBreak(target.env).push(loc(), label, end); + + var bodyTarget = target.subtarget(); + bodyTarget.add(() -> Instruction.stackAlloc(bodyTarget.scope.allocCount())); + + body.compile(bodyTarget, false, BreakpointType.STEP_OVER); + + bodyTarget.scope.end(); + bodyTarget.add(Instruction.stackFree(bodyTarget.scope.allocCount())); + int mid = target.temp(); - elseBody.compile(target, pollute, breakpoint); - int end = target.size(); + + var elseTarget = target.subtarget(); + elseTarget.add(() -> Instruction.stackAlloc(elseTarget.scope.allocCount())); + + body.compile(elseTarget, false, BreakpointType.STEP_OVER); + + elseTarget.scope.end(); + elseTarget.add(Instruction.stackFree(elseTarget.scope.allocCount())); + + LabelContext.getBreak(target.env).pop(label); + + int endI = target.size(); + end.set(endI); target.set(start, Instruction.jmpIfNot(mid - start + 1)); - target.set(mid, Instruction.jmp(end - mid)); + target.set(mid, Instruction.jmp(endI - mid)); } } @Override public void compile(CompileResult target, boolean pollute) { compile(target, pollute, BreakpointType.STEP_IN); } - public IfNode(Location loc, Node condition, Node body, Node elseBody) { + public IfNode(Location loc, Node condition, Node body, Node elseBody, String label) { super(loc); this.condition = condition; this.body = body; this.elseBody = elseBody; + this.label = label; } public static ParseRes parseTernary(Source src, int i, Node prev, int precedence) { @@ -71,12 +111,16 @@ public class IfNode extends Node { if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator."); n += b.n; - return ParseRes.res(new IfNode(loc, prev, a.result, b.result), n); + return ParseRes.res(new IfNode(loc, prev, a.result, b.result, null), n); } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); + var label = JavaScript.parseLabel(src, i + n); + n += label.n; + n += Parsing.skipEmpty(src, i + n); + if (!Parsing.isIdentifier(src, i + n, "if")) return ParseRes.failed(); n += 2; n += Parsing.skipEmpty(src, i + n); @@ -97,14 +141,13 @@ public class IfNode extends Node { n += res.n; var elseKw = Parsing.parseIdentifier(src, i + n, "else"); - if (!elseKw.isSuccess()) return ParseRes.res(new IfNode(loc, condRes.result, res.result, null), n); + if (!elseKw.isSuccess()) return ParseRes.res(new IfNode(loc, condRes.result, res.result, null, label.result), n); n += elseKw.n; var elseRes = JavaScript.parseStatement(src, i + n); if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body."); n += elseRes.n; - return ParseRes.res(new IfNode(loc, condRes.result, res.result, elseRes.result), n); + return ParseRes.res(new IfNode(loc, condRes.result, res.result, elseRes.result, label.result), n); } - } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java index 19582ae..3caccea 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java @@ -12,8 +12,7 @@ import me.topchetoeu.jscript.compilation.Node; public class ReturnNode extends Node { public final Node value; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { if (value == null) target.add(Instruction.pushUndefined()); else value.compile(target, true); target.add(Instruction.ret()).setLocation(loc()); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index 0d50aab..311a1a9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -6,13 +6,14 @@ import java.util.HashMap; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; public class SwitchNode extends Node { @@ -30,9 +31,10 @@ public class SwitchNode extends Node { public final SwitchCase[] cases; public final Node[] body; public final int defaultI; + public final String label; - @Override public void declare(CompileResult target) { - for (var stm : body) stm.declare(target); + @Override public void resolve(CompileResult target) { + for (var stm : body) stm.resolve(target); } @Override public void compile(CompileResult target, boolean pollute) { @@ -41,43 +43,55 @@ public class SwitchNode extends Node { value.compile(target, true, BreakpointType.STEP_OVER); + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + + // TODO: create a jump map for (var ccase : cases) { - target.add(Instruction.dup()); - ccase.value.compile(target, true); - target.add(Instruction.operation(Operation.EQUALS)); - caseToStatement.put(target.temp(), ccase.statementI); + subtarget.add(Instruction.dup()); + ccase.value.compile(subtarget, true); + subtarget.add(Instruction.operation(Operation.EQUALS)); + caseToStatement.put(subtarget.temp(), ccase.statementI); } - int start = target.temp(); + int start = subtarget.temp(); + var end = new DeferredIntSupplier(); + LabelContext.getBreak(target.env).push(loc(), label, end); for (var stm : body) { - statementToIndex.put(statementToIndex.size(), target.size()); - stm.compile(target, false, BreakpointType.STEP_OVER); + statementToIndex.put(statementToIndex.size(), subtarget.size()); + stm.compile(subtarget, false, BreakpointType.STEP_OVER); } + LabelContext.getBreak(target.env).pop(label); - int end = target.size(); - target.add(Instruction.discard()); - if (pollute) target.add(Instruction.pushUndefined()); + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); - if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(end - start)); - else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start)); + int endI = subtarget.size(); + end.set(endI); + subtarget.add(Instruction.discard()); + if (pollute) subtarget.add(Instruction.pushUndefined()); - for (int i = start; i < end; i++) { - var instr = target.get(i); - if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) { - target.set(i, Instruction.jmp(end - i)); - } - } + if (defaultI < 0 || defaultI >= body.length) subtarget.set(start, Instruction.jmp(endI - start)); + else subtarget.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start)); + + // for (int i = start; i < end; i++) { + // var instr = target.get(i); + // if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) { + // target.set(i, Instruction.jmp(end - i)); + // } + // } for (var el : caseToStatement.entrySet()) { var i = statementToIndex.get(el.getValue()); - if (i == null) i = end; - target.set(el.getKey(), Instruction.jmpIf(i - el.getKey())); + if (i == null) i = endI; + subtarget.set(el.getKey(), Instruction.jmpIf(i - el.getKey())); } } - public SwitchNode(Location loc, Node value, int defaultI, SwitchCase[] cases, Node[] body) { + public SwitchNode(Location loc, String label, Node value, int defaultI, SwitchCase[] cases, Node[] body) { super(loc); + this.label = label; this.value = value; this.defaultI = defaultI; this.cases = cases; @@ -90,14 +104,14 @@ public class SwitchNode extends Node { if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed(); n += 4; - var valRes = JavaScript.parseExpression(src, i + n, 0); - if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'case'"); - n += valRes.n; + var val = JavaScript.parseExpression(src, i + n, 0); + if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a value after 'case'"); + n += val.n; if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'case' value"); n++; - return ParseRes.res(valRes.result, n); + return ParseRes.res(val.result, n); } private static ParseRes parseDefaultCase(Source src, int i) { var n = Parsing.skipEmpty(src, i); @@ -116,15 +130,19 @@ public class SwitchNode extends Node { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); + var label = JavaScript.parseLabel(src, i + n); + n += label.n; + n += Parsing.skipEmpty(src, i + n); + if (!Parsing.isIdentifier(src, i + n, "switch")) return ParseRes.failed(); n += 6; n += Parsing.skipEmpty(src, i + n); if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'"); n++; - var valRes = JavaScript.parseExpression(src, i + n, 0); - if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a switch value"); - n += valRes.n; + var val = JavaScript.parseExpression(src, i + n, 0); + if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a switch value"); + n += val.n; n += Parsing.skipEmpty(src, i + n); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after switch value"); @@ -177,7 +195,7 @@ public class SwitchNode extends Node { } return ParseRes.res(new SwitchNode( - loc, valRes.result, defaultI, + loc, label.result, val.result, defaultI, cases.toArray(SwitchCase[]::new), statements.toArray(Node[]::new) ), n); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java index 3552adc..2c6cb37 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java @@ -12,8 +12,7 @@ import me.topchetoeu.jscript.compilation.Node; public class ThrowNode extends Node { public final Node value; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { value.compile(target, true); target.add(Instruction.throwInstr()).setLocation(loc()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java b/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java index e9b34a2..52d6723 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -8,60 +8,98 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundNode; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; +import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; public class TryNode extends Node { public final Node tryBody; public final Node catchBody; public final Node finallyBody; - public final String name; + public final String captureName; + public final String label; - @Override public void declare(CompileResult target) { - tryBody.declare(target); - if (catchBody != null) catchBody.declare(target); - if (finallyBody != null) finallyBody.declare(target); + @Override public void resolve(CompileResult target) { + tryBody.resolve(target); + catchBody.resolve(target); + finallyBody.resolve(target); } @Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) { int replace = target.temp(); + var endSuppl = new DeferredIntSupplier(); int start = replace + 1, catchStart = -1, finallyStart = -1; - tryBody.compile(target, false); - target.add(Instruction.tryEnd()); + LabelContext.getBreak(target.env).push(loc(), label, endSuppl); + + { + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + + tryBody.compile(subtarget, false); + subtarget.add(Instruction.tryEnd()); + + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + } if (catchBody != null) { catchStart = target.size() - start; - target.scope.define(name, true); - catchBody.compile(target, false); - target.scope.undefine(); - target.add(Instruction.tryEnd()); + + var subtarget = target.subtarget(); + var decN = captureName != null ? 1 : 0; + + if (captureName != null) subtarget.scope.defineStrict(captureName, false, catchBody.loc()); + + var _subtarget = subtarget; + + subtarget.add(() -> Instruction.stackAlloc(_subtarget.scope.allocCount() - decN)); + + catchBody.compile(subtarget, false); + + subtarget.add(Instruction.tryEnd()); + + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount() - decN)); } if (finallyBody != null) { finallyStart = target.size() - start; - finallyBody.compile(target, false); - target.add(Instruction.tryEnd()); + + var subtarget = target.subtarget(); + finallyBody.compile(subtarget, false); + subtarget.add(Instruction.tryEnd()); } + LabelContext.getBreak(target.env).pop(label); + + endSuppl.set(target.size()); + target.set(replace, Instruction.tryStart(catchStart, finallyStart, target.size() - start)); target.setLocationAndDebug(replace, loc(), BreakpointType.STEP_OVER); if (pollute) target.add(Instruction.pushUndefined()); } - public TryNode(Location loc, Node tryBody, Node catchBody, Node finallyBody, String name) { + public TryNode(Location loc, String label, Node tryBody, Node catchBody, Node finallyBody, String captureName) { super(loc); this.tryBody = tryBody; this.catchBody = catchBody; this.finallyBody = finallyBody; - this.name = name; + this.captureName = captureName; + this.label = label; } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); + var labelRes = JavaScript.parseLabel(src, i + n); + n += labelRes.n; + n += Parsing.skipEmpty(src, i + n); + if (!Parsing.isIdentifier(src, i + n, "try")) return ParseRes.failed(); n += 3; @@ -70,7 +108,7 @@ public class TryNode extends Node { n += tryBody.n; n += Parsing.skipEmpty(src, i + n); - String name = null; + String capture = null; Node catchBody = null, finallyBody = null; if (Parsing.isIdentifier(src, i + n, "catch")) { @@ -80,7 +118,7 @@ public class TryNode extends Node { n++; var nameRes = Parsing.parseIdentifier(src, i + n); if (!nameRes.isSuccess()) return nameRes.chainError(src.loc(i + n), "xpected a catch variable name"); - name = nameRes.result; + capture = nameRes.result; n += nameRes.n; n += Parsing.skipEmpty(src, i + n); @@ -108,6 +146,6 @@ public class TryNode extends Node { if (finallyBody == null && catchBody == null) ParseRes.error(src.loc(i + n), "Expected catch or finally"); - return ParseRes.res(new TryNode(loc, tryBody.result, catchBody, finallyBody, name), n); + return ParseRes.res(new TryNode(loc, labelRes.result, tryBody.result, catchBody, finallyBody, capture), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index 484818b..fda05e1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -2,53 +2,51 @@ package me.topchetoeu.jscript.compilation.control; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; public class WhileNode extends Node { public final Node condition, body; public final String label; - @Override - public void declare(CompileResult target) { - body.declare(target); + @Override public void resolve(CompileResult target) { + body.resolve(target); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { int start = target.size(); condition.compile(target, true); int mid = target.temp(); + + var end = new DeferredIntSupplier(); + + + LabelContext.pushLoop(target.env, loc(), label, end, start); + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + body.compile(target, false, BreakpointType.STEP_OVER); - int end = target.size(); + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + LabelContext.popLoop(target.env, label); - replaceBreaks(target, label, mid + 1, end, start, end + 1); + var endI = target.size(); + end.set(endI + 1); - target.add(Instruction.jmp(start - end)); - target.set(mid, Instruction.jmpIfNot(end - mid + 1)); + // replaceBreaks(target, label, mid + 1, end, start, end + 1); + + target.add(Instruction.jmp(start - end.getAsInt())); + target.set(mid, Instruction.jmpIfNot(end.getAsInt() - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } - public static ParseRes parseLabel(Source src, int i) { - int n = Parsing.skipEmpty(src, i); - - var nameRes = Parsing.parseIdentifier(src, i + n); - if (!nameRes.isSuccess()) return nameRes.chainError(); - n += nameRes.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, ":")) return ParseRes.failed(); - n++; - - return ParseRes.res(nameRes.result, n); - } - public WhileNode(Location loc, String label, Node condition, Node body) { super(loc); this.label = label; @@ -56,24 +54,24 @@ public class WhileNode extends Node { this.body = body; } - public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) { - for (int i = start; i < end; i++) { - var instr = target.get(i); - if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) { - target.set(i, Instruction.jmp(continuePoint - i)); - } - if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) { - target.set(i, Instruction.jmp(breakPoint - i)); - } - } - } + // public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) { + // for (int i = start; i < end; i++) { + // var instr = target.get(i); + // if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) { + // target.set(i, Instruction.jmp(continuePoint - i)); + // } + // if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) { + // target.set(i, Instruction.jmp(breakPoint - i)); + // } + // } + // } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - var labelRes = WhileNode.parseLabel(src, i + n); - n += labelRes.n; + var label = JavaScript.parseLabel(src, i + n); + n += label.n; n += Parsing.skipEmpty(src, i + n); if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed(); @@ -83,18 +81,18 @@ public class WhileNode extends Node { if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'."); n++; - var condRes = JavaScript.parseExpression(src, i + n, 0); - if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a while condition."); - n += condRes.n; + var cond = JavaScript.parseExpression(src, i + n, 0); + if (!cond.isSuccess()) return cond.chainError(src.loc(i + n), "Expected a while condition."); + n += cond.n; n += Parsing.skipEmpty(src, i + n); if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after while condition."); n++; - var res = JavaScript.parseStatement(src, i + n); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a while body."); - n += res.n; + var body = JavaScript.parseStatement(src, i + n); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a while body."); + n += body.n; - return ParseRes.res(new WhileNode(loc, labelRes.result, condRes.result, res.result), n); + return ParseRes.res(new WhileNode(loc, label.result, cond.result, body.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 72f7468..691ca0e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -5,9 +5,12 @@ import java.util.HashMap; import me.topchetoeu.jscript.common.parsing.Location; public class FunctionScope extends Scope { - private final VariableList captures = new VariableList(); - private final VariableList locals = new VariableList(captures); + private final VariableList captures = new VariableList().setIndexMap(v -> ~v); + private final VariableList specials = new VariableList(); + private final VariableList locals = new VariableList(specials); private HashMap childToParent = new HashMap<>(); + public final String selfName; + public final VariableDescriptor selfVar; private void removeCapture(String name) { var res = captures.remove(name); @@ -28,10 +31,14 @@ public class FunctionScope extends Scope { } @Override public VariableDescriptor get(String name, boolean capture) { + if (specials.has(name)) return specials.get(name); if (locals.has(name)) return locals.get(name); if (captures.has(name)) return captures.get(name); + if (selfName != null && selfName.equals(name)) return selfVar; var parentVar = parent.get(name, true); + if (parentVar == null) return null; + var childVar = captures.add(parentVar); childToParent.put(childVar, parentVar); @@ -40,6 +47,7 @@ public class FunctionScope extends Scope { } @Override public boolean has(String name) { + if (specials.has(name)) return true; if (locals.has(name)) return true; if (captures.has(name)) return true; if (parent != null) return parent.has(name); @@ -47,9 +55,24 @@ public class FunctionScope extends Scope { return false; } - public int localsCount() { - return locals.size(); + @Override public boolean end() { + if (!super.end()) return false; + + captures.freeze(); + locals.freeze(); + return true; } + + @Override public int localsCount() { + return locals.size() + specials.size(); + } + @Override public int capturesCount() { + return captures.size(); + } + @Override public int allocCount() { + return 0; + } + public int offset() { return captures.size() + locals.size(); } @@ -66,6 +89,22 @@ public class FunctionScope extends Scope { return res; } - public FunctionScope() { super(); } - public FunctionScope(Scope parent) { super(parent); } + public FunctionScope(String selfName, String[] args) { + super(); + this.selfName = selfName; + + if (selfName != null) this.selfVar = VariableDescriptor.of(selfName, true, -1); + else this.selfVar = null; + + for (var arg : args) specials.add(arg, false); + } + public FunctionScope(String selfName, String[] args, Scope parent) { + super(parent); + this.selfName = selfName; + + if (selfName != null) this.selfVar = VariableDescriptor.of(selfName, true, -1); + else this.selfVar = null; + + for (var arg : args) specials.add(arg, false); + } } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java index 1f7e8a3..6151a8e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java @@ -19,5 +19,15 @@ public final class GlobalScope extends Scope { return false; } + @Override public int localsCount() { + return 0; + } + @Override public int capturesCount() { + return 0; + } + @Override public int allocCount() { + return 0; + } + public GlobalScope() { super(); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java index 9244fb6..c348168 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java @@ -2,7 +2,7 @@ package me.topchetoeu.jscript.compilation.scope; import me.topchetoeu.jscript.common.parsing.Location; -public class LocalScope extends Scope { +public final class LocalScope extends Scope { private final VariableList locals = new VariableList(); @Override public int offset() { @@ -43,6 +43,18 @@ public class LocalScope extends Scope { return true; } + @Override public int localsCount() { + if (parent == null) return 0; + else return parent.localsCount(); + } + @Override public int capturesCount() { + if (parent == null) return 0; + else return parent.capturesCount(); + } + @Override public int allocCount() { + return locals.size(); + } + public Iterable all() { return () -> locals.iterator(); } diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java index 51cf155..c41c2bd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -41,13 +41,17 @@ public abstract class Scope { */ public abstract int offset(); + public abstract int localsCount(); + public abstract int capturesCount(); + public abstract int allocCount(); + public boolean end() { if (!active) return false; this.active = false; if (this.parent != null) { assert this.parent.child == this; - this.parent.child = this; + this.parent.child = null; } return true; diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java index 2fca3a8..dc583af 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -4,22 +4,49 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.function.IntSupplier; +import java.util.function.IntUnaryOperator; -public class VariableList implements Iterable { +public final class VariableList implements Iterable { private class ListVar extends VariableDescriptor { private ListVar next; private ListVar prev; + private boolean frozen; + private int index; @Override public int index() { - throw new RuntimeException("The index of a variable may not be retrieved until the scope has been finalized"); - // var res = 0; - // if (offset != null) res = offset.getAsInt(); + if (frozen) { + if (offset == null) { + return indexConverter == null ? index : indexConverter.applyAsInt(index); + } + else { + return indexConverter == null ? + index + offset.getAsInt() : + indexConverter.applyAsInt(index + offset.getAsInt()); + } + } - // for (var it = prev; it != null; it = it.prev) { - // res++; - // } + var res = 0; + if (offset != null) res = offset.getAsInt(); - // return res; + for (var it = prev; it != null; it = it.prev) { + res++; + } + + return indexConverter == null ? res : indexConverter.applyAsInt(res); + } + + public ListVar freeze() { + if (frozen) return this; + this.frozen = true; + + if (prev == null) return this; + assert prev.frozen; + + this.index = prev.index + 1; + this.next = null; + this.prev = null; + + return this; } public ListVar(String name, boolean readonly, ListVar next, ListVar prev) { @@ -38,6 +65,7 @@ public class VariableList implements Iterable { private ArrayList frozenList = null; private final IntSupplier offset; + private IntUnaryOperator indexConverter = null; public boolean frozen() { if (frozenMap != null) { @@ -60,26 +88,52 @@ public class VariableList implements Iterable { public VariableDescriptor add(VariableDescriptor val) { return add(val.name, val.readonly); - } - public VariableDescriptor add(String name, boolean readonly) { - if (frozen()) throw new RuntimeException("The scope has been frozen"); - if (map.containsKey(name)) return map.get(name); + } + public VariableDescriptor add(String name, boolean readonly) { + if (frozen()) throw new RuntimeException("The scope has been frozen"); + if (map.containsKey(name)) return map.get(name); - var res = new ListVar(name, readonly, null, last); - last.next = res; - last = res; - map.put(name, res); + var res = new ListVar(name, readonly, null, last); - return res; - } + if (last != null) { + assert first != null; + + last.next = res; + res.prev = last; + + last = res; + } + else { + first = last = res; + } + + map.put(name, res); + + return res; + } public VariableDescriptor remove(String name) { if (frozen()) throw new RuntimeException("The scope has been frozen"); var el = map.get(name); if (el == null) return null; - el.prev.next = el.next; - el.next.prev = el.prev; + if (el.prev != null) { + assert el != first; + el.prev.next = el.next; + } + else { + assert el == first; + first = first.next; + } + + if (el.next != null) { + assert el != last; + el.next.prev = el.prev; + } + else { + assert el == last; + last = last.prev; + } el.next = null; el.prev = null; @@ -88,7 +142,8 @@ public class VariableList implements Iterable { } public VariableDescriptor get(String name) { - return map.get(name); + if (frozen()) return frozenMap.get(name); + else return map.get(name); } public VariableDescriptor get(int i) { if (frozen()) { @@ -126,12 +181,17 @@ public class VariableList implements Iterable { frozenMap = new HashMap<>(); frozenList = new ArrayList<>(); - var i = 0; - if (offset != null) i = offset.getAsInt(); + for (var it = first; it != null; ) { + frozenMap.put(it.name, it); + frozenList.add(it); - for (var it = first; it != null; it = it.next) { - frozenMap.put(it.name, VariableDescriptor.of(it.name, it.readonly, i++)); + var tmp = it; + it = it.next; + tmp.freeze(); } + + map = null; + first = last = null; } @Override public Iterator iterator() { @@ -161,6 +221,11 @@ public class VariableList implements Iterable { return res; } + public VariableList setIndexMap(IntUnaryOperator map) { + indexConverter = map; + return this; + } + public VariableList(IntSupplier offset) { this.offset = offset; } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java new file mode 100644 index 0000000..104340f --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java @@ -0,0 +1,17 @@ +package me.topchetoeu.jscript.compilation.values; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Node; + + +public class ArgumentsNode extends Node { + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.loadArgs()); + } + + public ArgumentsNode(Location loc) { + super(loc); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java index a2f2fe2..148a2a4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java @@ -11,17 +11,10 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; + public class ArrayNode extends Node { public final Node[] statements; - @Override public boolean pure() { - for (var stm : statements) { - if (!stm.pure()) return false; - } - - return true; - } - @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadArr(statements.length)); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java b/src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java deleted file mode 100644 index 2a73052..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/FunctionNode.java +++ /dev/null @@ -1,150 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.Instruction.Type; -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.common.parsing.ParseRes; -import me.topchetoeu.jscript.common.parsing.Parsing; -import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundNode; -import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; - -public class FunctionNode extends Node { - public final CompoundNode body; - public final String varName; - public final String[] args; - public final boolean statement; - public final Location end; - - @Override public boolean pure() { return varName == null && statement; } - - @Override - public void declare(CompileResult target) { - if (varName != null && statement) target.scope.define(varName); - } - - public static void checkBreakAndCont(CompileResult target, int start) { - for (int i = start; i < target.size(); i++) { - if (target.get(i).type == Type.NOP) { - if (target.get(i).is(0, "break") ) { - throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop."); - } - if (target.get(i).is(0, "cont")) { - throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop."); - } - } - } - } - - private CompileResult compileBody(CompileResult target, String name, boolean pollute, BreakpointType bp) { - for (var i = 0; i < args.length; i++) { - for (var j = 0; j < i; j++) { - if (args[i].equals(args[j])) { - throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'."); - } - } - } - - var subtarget = new CompileResult(target.scope.child()); - - subtarget.scope.define("this"); - var argsVar = subtarget.scope.define("arguments"); - - if (args.length > 0) { - for (var i = 0; i < args.length; i++) { - subtarget.add(Instruction.loadVar(argsVar)); - subtarget.add(Instruction.pushValue(i)); - subtarget.add(Instruction.loadMember()); - subtarget.add(Instruction.storeVar(subtarget.scope.define(args[i]))); - } - } - - if (!statement && this.varName != null) { - subtarget.add(Instruction.storeSelfFunc((int)subtarget.scope.define(this.varName))).setLocationAndDebug(loc(), bp); - } - - body.declare(subtarget); - body.compile(subtarget, false); - subtarget.length = args.length; - subtarget.add(Instruction.ret()).setLocation(end); - checkBreakAndCont(subtarget, 0); - - if (pollute) target.add(Instruction.loadFunc(target.children.size(), name, subtarget.scope.getCaptures())); - return target.addChild(subtarget); - } - - public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - if (this.varName != null) name = this.varName; - - var hasVar = this.varName != null && statement; - - compileBody(target, name, pollute || hasVar, bp); - - if (hasVar) { - var key = target.scope.getKey(this.varName); - - if (key instanceof String) target.add(Instruction.makeVar((String)key)); - target.add(Instruction.storeVar(target.scope.getKey(this.varName), false)); - } - } - public void compile(CompileResult target, boolean pollute, String name) { - compile(target, pollute, name, BreakpointType.NONE); - } - @Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) { - compile(target, pollute, (String)null, bp); - } - @Override public void compile(CompileResult target, boolean pollute) { - compile(target, pollute, (String)null, BreakpointType.NONE); - } - - public FunctionNode(Location loc, Location end, String varName, String[] args, boolean statement, CompoundNode body) { - super(loc); - - this.end = end; - this.varName = varName; - this.statement = statement; - - this.args = args; - this.body = body; - } - - public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) { - if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name); - else stm.compile(target, pollute); - } - public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name, BreakpointType bp) { - if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name, bp); - else stm.compile(target, pollute, bp); - } - - public static ParseRes parseFunction(Source src, int i, boolean statement) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!Parsing.isIdentifier(src, i + n, "function")) return ParseRes.failed(); - n += 8; - - var nameRes = Parsing.parseIdentifier(src, i + n); - if (!nameRes.isSuccess() && statement) return ParseRes.error(src.loc(i + n), "A statement function requires a name"); - n += nameRes.n; - n += Parsing.skipEmpty(src, i + n); - - var args = JavaScript.parseParamList(src, i + n); - if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); - n += args.n; - - var res = CompoundNode.parse(src, i + n); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a compound statement for function."); - n += res.n; - - return ParseRes.res(new FunctionNode( - loc, src.loc(i + n - 1), - nameRes.result, args.result.toArray(String[]::new), - statement, res.result - ), n); - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java b/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java index d80658b..11823a3 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java @@ -5,11 +5,9 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Node; -public class GlobalThisNode extends Node { - @Override public boolean pure() { return true; } - @Override - public void compile(CompileResult target, boolean pollute) { +public class GlobalThisNode extends Node { + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.loadGlob()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index 257ed81..48bab87 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -11,16 +11,19 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundNode; +import me.topchetoeu.jscript.compilation.FunctionNode; +import me.topchetoeu.jscript.compilation.FunctionValueNode; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; + public class ObjectNode extends Node { public static class ObjProp { public final String name; public final String access; - public final FunctionNode func; - - public ObjProp(String name, String access, FunctionNode func) { + public final FunctionValueNode func; + + public ObjProp(String name, String access, FunctionValueNode func) { this.name = name; this.access = access; this.func = func; @@ -31,14 +34,6 @@ public class ObjectNode extends Node { public final Map getters; public final Map setters; - @Override public boolean pure() { - for (var el : map.values()) { - if (!el.pure()) return false; - } - - return true; - } - @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadObj()); @@ -114,7 +109,7 @@ public class ObjectNode extends Node { return ParseRes.res(new ObjProp( name.result, access.result, - new FunctionNode(loc, end, access + " " + name.result.toString(), params.result.toArray(String[]::new), false, body.result) + new FunctionValueNode(loc, end, params.result.toArray(String[]::new), body.result, access + " " + name.result.toString()) ), n); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java b/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java index 11d00b5..7ac7c51 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java @@ -11,11 +11,7 @@ import me.topchetoeu.jscript.compilation.Node; public class RegexNode extends Node { public final String pattern, flags; - // Not really pure, since a function is called, but can be ignored. - @Override public boolean pure() { return true; } - - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadRegex(pattern, flags)); if (!pollute) target.add(Instruction.discard()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ThisNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ThisNode.java new file mode 100644 index 0000000..ad74007 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/values/ThisNode.java @@ -0,0 +1,17 @@ +package me.topchetoeu.jscript.compilation.values; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Node; + + +public class ThisNode extends Node { + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.loadThis()); + } + + public ThisNode(Location loc) { + super(loc); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 8f4d614..bea5355 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -1,5 +1,7 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.function.Supplier; + import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.parsing.Location; @@ -11,22 +13,61 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.values.operations.VariableAssignNode; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class VariableNode extends Node implements AssignableNode { public final String name; - @Override public boolean pure() { return false; } + // @Override public EvalResult evaluate(CompileResult target) { + // var i = target.scope.getKey(name); - @Override - public Node toAssign(Node val, Operation operation) { + // if (i instanceof String) return EvalResult.NONE; + // else return EvalResult.UNKNOWN; + // } + + @Override public Node toAssign(Node val, Operation operation) { return new VariableAssignNode(loc(), name, val, operation); } - @Override - public void compile(CompileResult target, boolean pollute) { - var i = target.scope.getKey(name); - target.add(Instruction.loadVar(i)); - if (!pollute) target.add(Instruction.discard()); + @Override public void compile(CompileResult target, boolean pollute) { + var i = target.scope.get(name, true); + + if (i == null) { + target.add((Supplier)() -> { + if (target.scope.has(name)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name)); + return Instruction.globGet(name); + }); + + if (!pollute) target.add(Instruction.discard()); + } + else if (pollute) { + target.add(Instruction.loadVar(i.index())); + } + } + + public static Supplier toGet(CompileResult target, Location loc, String name, Supplier onGlobal) { + var i = target.scope.get(name, true); + + if (i == null) return () -> { + if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); + else return onGlobal.get(); + }; + else return () -> Instruction.loadVar(i.index()); + } + public static Supplier toGet(CompileResult target, Location loc, String name) { + return toGet(target, loc, name, () -> Instruction.globGet(name)); + } + + + public static Supplier toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { + var i = target.scope.get(name, true); + + if (i == null) return () -> { + if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); + else return Instruction.globSet(name, keep, define); + }; + else return () -> Instruction.storeVar(i.index(), keep); + } public VariableNode(Location loc, String name) { diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java index a15b16a..8a5462b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java @@ -8,8 +8,6 @@ import me.topchetoeu.jscript.compilation.Node; public class BoolNode extends Node { public final boolean value; - @Override public boolean pure() { return true; } - @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushValue(value)); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java index e8c4d54..cae8bd6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java @@ -6,10 +6,8 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Node; public class NullNode extends Node { - @Override public boolean pure() { return true; } - @Override public void compile(CompileResult target, boolean pollute) { - target.add(Instruction.pushNull()); + if (pollute) target.add(Instruction.pushNull()); } public NullNode(Location loc) { super(loc); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java index 246bf68..5afd40e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java @@ -11,8 +11,6 @@ import me.topchetoeu.jscript.compilation.Node; public class NumberNode extends Node { public final double value; - @Override public boolean pure() { return true; } - @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushValue(value)); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java index b288b12..e50d82b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java @@ -11,8 +11,6 @@ import me.topchetoeu.jscript.compilation.Node; public class StringNode extends Node { public final String value; - @Override public boolean pure() { return true; } - @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushValue(value)); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index b4635fb..bdd3b74 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -13,8 +13,10 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.BoolNode; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; @@ -51,12 +53,18 @@ public class CallNode extends Node { else if (func instanceof VariableNode) { res = ((VariableNode)func).name; } - else if (func instanceof VariableIndexNode) { - var i = ((VariableIndexNode)func).index; - - if (i == 0) res = "this"; - else if (i == 1) res = "arguments"; + else if (func instanceof ThisNode) { + res = "this"; } + else if (func instanceof ArgumentsNode) { + res = "arguments"; + } + // else if (func instanceof VariableIndexNode) { + // var i = ((VariableIndexNode)func).index; + + // if (i == 0) res = "this"; + // else if (i == 1) res = "arguments"; + // } else if (func instanceof ArrayNode) { var els = new ArrayList(); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java index 063f503..a68ebb6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java @@ -9,10 +9,18 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; + public class DiscardNode extends Node { public final Node value; - @Override public boolean pure() { return value.pure(); } + // @Override public EvalResult evaluate(CompileResult target) { + // if (value == null) return EvalResult.FALSY; + // var res = value.evaluate(target); + + // if (res.isPure) return EvalResult.FALSY; + // else if (res.never) return EvalResult.NEVER; + // else return EvalResult.FALSY_IMPURE; + // } @Override public void compile(CompileResult target, boolean pollute) { if (value != null) value.compile(target, false); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java index 2b5c68e..e9e2e3d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java @@ -13,8 +13,7 @@ public class IndexAssignNode extends Node { public final Node value; public final Operation operation; - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { if (operation != null) { object.compile(target, true); index.compile(target, true); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java index f8c1387..85a6da4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java @@ -17,8 +17,7 @@ public class IndexNode extends Node implements AssignableNode { public final Node object; public final Node index; - @Override - public Node toAssign(Node val, Operation operation) { + @Override public Node toAssign(Node val, Operation operation) { return new IndexAssignNode(loc(), object, index, val, operation); } public void compile(CompileResult target, boolean dupObj, boolean pollute) { @@ -29,8 +28,7 @@ public class IndexNode extends Node implements AssignableNode { target.add(Instruction.loadMember()).setLocationAndDebug(loc(), BreakpointType.STEP_IN); if (!pollute) target.add(Instruction.discard()); } - @Override - public void compile(CompileResult target, boolean pollute) { + @Override public void compile(CompileResult target, boolean pollute) { compile(target, false, pollute); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java index 71294b3..a63506b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java @@ -12,10 +12,15 @@ import me.topchetoeu.jscript.compilation.Node; public class LazyAndNode extends Node { public final Node first, second; - @Override public boolean pure() { return first.pure() && second.pure(); } + // @Override public EvalResult evaluate(CompileResult target) { + // var firstRes = first.evaluate(target); + // if (firstRes.falsy) return firstRes; + // if (!firstRes.isPure) return firstRes; - @Override - public void compile(CompileResult target, boolean pollute) { + // return second.evaluate(target); + // } + + @Override public void compile(CompileResult target, boolean pollute) { first.compile(target, true); if (pollute) target.add(Instruction.dup()); int start = target.temp(); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java index 225961e..ac374ce 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java @@ -9,13 +9,19 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; + public class LazyOrNode extends Node { public final Node first, second; - @Override public boolean pure() { return first.pure() && second.pure(); } + // @Override public EvalResult evaluate(CompileResult target) { + // var firstRes = first.evaluate(target); + // if (firstRes.truthy) return firstRes; + // if (!firstRes.isPure) return firstRes; - @Override - public void compile(CompileResult target, boolean pollute) { + // return second.evaluate(target); + // } + + @Override public void compile(CompileResult target, boolean pollute) { first.compile(target, true); if (pollute) target.add(Instruction.dup()); int start = target.temp(); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java index d59951b..57039c6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java @@ -93,14 +93,6 @@ public class OperationNode extends Node { public final Node[] args; public final Operation operation; - @Override public boolean pure() { - for (var el : args) { - if (!el.pure()) return false; - } - - return true; - } - @Override public void compile(CompileResult target, boolean pollute) { for (var arg : args) { arg.compile(target, true); diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java index 1eabc75..15f8387 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java @@ -8,26 +8,32 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; + import me.topchetoeu.jscript.compilation.values.VariableNode; public class TypeofNode extends Node { public final Node value; - // Not really pure, since a variable from the global scope could be accessed, - // which could lead to code execution, that would get omitted - @Override public boolean pure() { return true; } + // @Override public EvalResult evaluate(CompileResult target) { + // if (value instanceof VariableNode) { + // var i = target.scope.getKey(((VariableNode)value).name); + // if (i instanceof String) return EvalResult.NONE; + // } - @Override - public void compile(CompileResult target, boolean pollute) { - if (value instanceof VariableNode) { - var i = target.scope.getKey(((VariableNode)value).name); - if (i instanceof String) { - target.add(Instruction.typeof((String)i)); - return; - } + // return EvalResult.UNKNOWN; + // } + + @Override public void compile(CompileResult target, boolean pollute) { + if (value instanceof VariableNode varNode) { + target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, () -> Instruction.typeof(varNode.name))); + if (!pollute) target.add(Instruction.discard()); + + return; } + value.compile(target, pollute); target.add(Instruction.typeof()); + if (!pollute) target.add(Instruction.discard()); } public TypeofNode(Location loc, Node value) { diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java index f51f92d..940a27c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java @@ -4,28 +4,25 @@ import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.FunctionNode; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.values.FunctionNode; +import me.topchetoeu.jscript.compilation.values.VariableNode; public class VariableAssignNode extends Node { public final String name; public final Node value; public final Operation operation; - @Override public boolean pure() { return false; } - - @Override - public void compile(CompileResult target, boolean pollute) { - var i = target.scope.getKey(name); + @Override public void compile(CompileResult target, boolean pollute) { if (operation != null) { - target.add(Instruction.loadVar(i)); + target.add(VariableNode.toGet(target, loc(), name)); FunctionNode.compileWithName(value, target, true, name); target.add(Instruction.operation(operation)); - target.add(Instruction.storeVar(i, pollute)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)); } else { FunctionNode.compileWithName(value, target, true, name); - target.add(Instruction.storeVar(i, pollute)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java deleted file mode 100644 index a5dbb48..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableIndexNode.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.compilation.values.operations; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Node; - -public class VariableIndexNode extends Node { - public final int index; - - @Override public boolean pure() { return true; } - - @Override - public void compile(CompileResult target, boolean pollute) { - if (pollute) target.add(Instruction.loadVar(index)); - } - - public VariableIndexNode(Location loc, int i) { - super(loc); - this.index = i; - } -} diff --git a/src/java/me/topchetoeu/jscript/common/Compiler.java b/src/java/me/topchetoeu/jscript/runtime/Compiler.java similarity index 84% rename from src/java/me/topchetoeu/jscript/common/Compiler.java rename to src/java/me/topchetoeu/jscript/runtime/Compiler.java index 1d6e7d2..de430b2 100644 --- a/src/java/me/topchetoeu/jscript/common/Compiler.java +++ b/src/java/me/topchetoeu/jscript/runtime/Compiler.java @@ -1,5 +1,6 @@ -package me.topchetoeu.jscript.common; +package me.topchetoeu.jscript.runtime; +import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.parsing.Filename; @@ -7,7 +8,7 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; +import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { @@ -30,7 +31,7 @@ public interface Compiler { }); } - private static void registerFunc(Environment env, FunctionBody body, CompileResult res) { + static void registerFunc(Environment env, FunctionBody body, CompileResult res) { var map = res.map(); DebugContext.get(env).onFunctionLoad(body, map); @@ -41,6 +42,6 @@ public interface Compiler { } public static CodeFunction compileFunc(Environment env, Filename filename, String raw) { - return new CodeFunction(env, filename.toString(), get(env).compile(env, filename, raw), new ValueVariable[0]); + return new CodeFunction(env, filename.toString(), get(env).compile(env, filename, raw), new Value[0][]); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Engine.java b/src/java/me/topchetoeu/jscript/runtime/Engine.java index 1b83179..e3ada89 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Engine.java +++ b/src/java/me/topchetoeu/jscript/runtime/Engine.java @@ -18,8 +18,7 @@ public class Engine implements EventLoop { this.micro = micro; } - @Override - public int compareTo(Task other) { + @Override public int compareTo(Task other) { return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index 0227682..2c548ba 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -3,7 +3,6 @@ package me.topchetoeu.jscript.runtime; import java.util.concurrent.Future; import java.util.function.Supplier; -import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.parsing.Filename; diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index cc56651..1ccf5bb 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,7 +1,9 @@ package me.topchetoeu.jscript.runtime; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Stack; @@ -11,8 +13,6 @@ import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.scope.LocalScope; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; @@ -97,14 +97,24 @@ public class Frame { } } - public final LocalScope scope; - public final Object thisArg; - public final Object[] args; + /** + * A list of one-element arrays of values. This is so that we can pass captures to other functions + */ + public final Value[][] captures; + public final List locals = new ArrayList<>(); + public final Value self; + public final Value argsVal; + public final Value[] args; public final boolean isNew; public final Stack tryStack = new Stack<>(); public final CodeFunction function; public final Environment env; + public Value[] getVar(int i) { + if (i < 0) return captures[~i]; + else return locals.get(i); + } + public Value[] stack = new Value[32]; public int stackPtr = 0; public int codePtr = 0; @@ -178,6 +188,7 @@ public class Frame { } } catch (EngineException e) { error = e; } + // catch (RuntimeException e) { error = EngineException.ofError("InternalError", e.getMessage()); } } while (!tryStack.empty()) { @@ -201,12 +212,12 @@ public class Frame { if (newCtx != tryCtx) { switch (newCtx.state) { case CATCH: - if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value)); + if (tryCtx.state != TryState.CATCH) locals.add(new Value[] { error.value }); codePtr = tryCtx.catchStart; stackPtr = tryCtx.restoreStackPtr; break; case FINALLY: - if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1); + if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1); codePtr = tryCtx.finallyStart; stackPtr = tryCtx.restoreStackPtr; default: @@ -222,7 +233,7 @@ public class Frame { } else { popTryFlag = false; - if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1); + if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1); if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) { codePtr = tryCtx.finallyStart; @@ -318,41 +329,45 @@ public class Frame { } /** - * Gets an object proxy of the local scope + * Gets an object proxy of the local locals */ public ObjectValue getLocalScope() { - var names = new String[scope.locals.length]; - var map = DebugContext.get(env).getMapOrEmpty(function); + throw new RuntimeException("Not supported"); - for (int i = 0; i < scope.locals.length; i++) { - var name = "local_" + (i - 2); + // var names = new String[locals.locals.length]; + // var map = DebugContext.get(env).getMapOrEmpty(function); - if (i == 0) name = "this"; - else if (i == 1) name = "arguments"; - else if (i < map.localNames.length) name = map.localNames[i]; + // for (int i = 0; i < locals.locals.length; i++) { + // var name = "local_" + (i - 2); - names[i] = name; - } + // if (i == 0) name = "this"; + // else if (i == 1) name = "arguments"; + // else if (i < map.localNames.length) name = map.localNames[i]; - return new ScopeValue(scope.locals, names); + // names[i] = name; + // } + + // return new ScopeValue(locals, names); } /** - * Gets an object proxy of the capture scope + * Gets an object proxy of the capture locals */ public ObjectValue getCaptureScope() { - var names = new String[scope.captures.length]; + // throw new RuntimeException("Not supported"); + + var names = new String[captures.length]; var map = DebugContext.get(env).getMapOrEmpty(function); - for (int i = 0; i < scope.captures.length; i++) { + for (int i = 0; i < captures.length; i++) { var name = "capture_" + (i - 2); if (i < map.captureNames.length) name = map.captureNames[i]; names[i] = name; } - return new ScopeValue(scope.captures, names); + return new ScopeValue(captures, names); } /** - * Gets an array proxy of the local scope + * Gets an array proxy of the local locals */ public ObjectValue getValStackScope() { return new ObjectValue() { @@ -393,13 +408,25 @@ public class Frame { public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) { this.env = env; - this.args = args; - this.isNew = isNew; - this.scope = new LocalScope(func.body.localsN, func.captures); - this.scope.get(0).set(thisArg); - this.scope.get(1).value = new ArgumentsValue(this, args); - - this.thisArg = thisArg; this.function = func; + this.isNew = isNew; + + this.self = thisArg; + this.args = args; + this.argsVal = new ArgumentsValue(this, args); + this.captures = func.captures; + + for (var i = 0; i < func.body.argsN; i++) { + this.locals.add(new Value[] { args[i] }); + } + + for (var i = 0; i < func.body.localsN; i++) { + this.locals.add(new Value[] { Value.UNDEFINED }); + } + + // this.locals = new LocalScope(func.body.localsN, func.captures); + // this.locals.get(0).set(thisArg); + // this.locals.get(1).value = new ArgumentsValue(this, args); + } } diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 4b0db70..9a7829e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -7,8 +7,6 @@ import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.Value; @@ -60,12 +58,6 @@ public class InstructionRunner { return null; } - private static Value execMakeVar(Environment env, Instruction instr, Frame frame) { - var name = (String)instr.get(0); - GlobalScope.get(env).define(env, false, name); - frame.codePtr++; - return null; - } private static Value execDefProp(Environment env, Instruction instr, Frame frame) { var setterVal = frame.pop(); var getterVal = frame.pop(); @@ -146,12 +138,11 @@ public class InstructionRunner { return null; } private static Value execLoadVar(Environment env, Instruction instr, Frame frame) { - var i = instr.get(0); - - if (i instanceof String) frame.push(GlobalScope.get(env).get(env, (String)i)); - else frame.push(frame.scope.get((int)i).get(env)); + int i = instr.get(0); + frame.push(frame.getVar(i)[0]); frame.codePtr++; + return null; } private static Value execLoadObj(Environment env, Instruction instr, Frame frame) { @@ -162,7 +153,7 @@ public class InstructionRunner { return null; } private static Value execLoadGlob(Environment env, Instruction instr, Frame frame) { - frame.push(GlobalScope.get(env).object); + frame.push(Value.global(env)); frame.codePtr++; return null; } @@ -176,10 +167,10 @@ public class InstructionRunner { private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { int id = instr.get(0); String name = instr.get(1); - var captures = new ValueVariable[instr.params.length - 2]; + var captures = new Value[instr.params.length - 2][]; for (var i = 2; i < instr.params.length; i++) { - captures[i - 2] = frame.scope.get(instr.get(i)); + captures[i - 2] = frame.getVar(instr.get(i)); } var func = new CodeFunction(env, name, frame.function.body.children[id], captures); @@ -231,20 +222,14 @@ public class InstructionRunner { } private static Value execStoreVar(Environment env, Instruction instr, Frame frame) { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); - var i = instr.get(0); - - if (i instanceof String) GlobalScope.get(env).set(env, (String)i, val); - else frame.scope.get((int)i).set(env, val); + int i = instr.get(0); + frame.getVar(i)[0] = val; frame.codePtr++; + return null; } - private static Value execStoreSelfFunc(Environment env, Instruction instr, Frame frame) { - frame.scope.locals[(int)instr.get(0)].set(env, frame.function); - frame.codePtr++; - return null; - } - + private static Value execJmp(Environment env, Instruction instr, Frame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; @@ -271,12 +256,7 @@ public class InstructionRunner { String name = instr.get(0); Value obj; - if (name != null) { - if (GlobalScope.get(env).has(env, name)) { - obj = GlobalScope.get(env).get(env, name); - } - else obj = null; - } + if (name != null) obj = Value.global(env).getMember(env, name); else obj = frame.pop(); frame.push(obj.type()); @@ -309,6 +289,73 @@ public class InstructionRunner { return null; } + private static Value exexGlobDef(Environment env, Instruction instr, Frame frame) { + var name = (String)instr.get(0); + + if (!Value.global(env).hasMember(env, name, false)) { + if (!Value.global(env).defineOwnMember(env, name, Value.UNDEFINED)) throw EngineException.ofError("Couldn't define variable " + name); + } + + frame.codePtr++; + return null; + } + private static Value exexGlobGet(Environment env, Instruction instr, Frame frame) { + var name = (String)instr.get(0); + var res = Value.global(env).getMemberOrNull(env, name); + + if (res == null) throw EngineException.ofSyntax(name + " is not defined"); + else frame.push(res); + + frame.codePtr++; + return null; + } + private static Value exexGlobSet(Environment env, Instruction instr, Frame frame) { + var name = (String)instr.get(0); + var keep = (boolean)instr.get(1); + var define = (boolean)instr.get(2); + + var val = keep ? frame.peek() : frame.pop(); + var res = false; + + if (define) res = Value.global(env).setMember(env, name, val); + else res = Value.global(env).setMemberIfExists(env, name, val); + + if (!res) throw EngineException.ofError("Couldn't set variable " + name); + + frame.codePtr++; + return null; + } + + private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) { + frame.push(frame.argsVal); + frame.codePtr++; + return null; + } + private static Value execLoadThis(Environment env, Instruction instr, Frame frame) { + frame.push(frame.self); + frame.codePtr++; + return null; + } + + private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) { + int n = instr.get(0); + + for (var i = 0; i < n; i++) frame.locals.add(new Value[] { Value.UNDEFINED }); + + frame.codePtr++; + return null; + } + private static Value execStackFree(Environment env, Instruction instr, Frame frame) { + int n = instr.get(0); + + for (var i = 0; i < n; i++) { + frame.locals.remove(frame.locals.size() - 1); + } + + frame.codePtr++; + return null; + } + public static Value exec(Environment env, Instruction instr, Frame frame) { switch (instr.type) { case NOP: return execNop(env, instr, frame); @@ -335,12 +382,12 @@ public class InstructionRunner { case LOAD_MEMBER: return execLoadMember(env, instr, frame); case LOAD_REGEX: return execLoadRegEx(env, instr, frame); case LOAD_GLOB: return execLoadGlob(env, instr, frame); + case LOAD_ARGS: return execLoadArgs(env, instr, frame); + case LOAD_THIS: return execLoadThis(env, instr, frame); case DISCARD: return execDiscard(env, instr, frame); case STORE_MEMBER: return execStoreMember(env, instr, frame); case STORE_VAR: return execStoreVar(env, instr, frame); - case STORE_SELF_FUNC: return execStoreSelfFunc(env, instr, frame); - case MAKE_VAR: return execMakeVar(env, instr, frame); case KEYS: return execKeys(env, instr, frame); case DEF_PROP: return execDefProp(env, instr, frame); @@ -353,6 +400,13 @@ public class InstructionRunner { case OPERATION: return execOperation(env, instr, frame); + case GLOB_DEF: return exexGlobDef(env, instr, frame); + case GLOB_GET: return exexGlobGet(env, instr, frame); + case GLOB_SET: return exexGlobSet(env, instr, frame); + + case STACK_ALLOC: return execStackAlloc(env, instr, frame); + case STACK_FREE: return execStackFree(env, instr, frame); + default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 18a4278..21ddfaa 100644 --- a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -6,7 +6,6 @@ import java.nio.file.Path; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Reading; import me.topchetoeu.jscript.common.environment.Environment; @@ -16,7 +15,6 @@ import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.Value; @@ -324,18 +322,17 @@ public class SimpleRepl { // environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); // environment.add(Compiler.KEY, new JSCompiler(environment)); environment.add(EventLoop.KEY, engine); - environment.add(GlobalScope.KEY, new GlobalScope()); environment.add(DebugContext.KEY, new DebugContext()); // environment.add(EventLoop.KEY, engine); environment.add(Compiler.KEY, Compiler.DEFAULT); - var glob = GlobalScope.get(environment); + var glob = Value.global(environment); - glob.define(null, false, new NativeFunction("exit", args -> { + glob.defineOwnMember(null, "exit", new NativeFunction("exit", args -> { Thread.currentThread().interrupt(); throw new InterruptException(); })); - glob.define(null, false, new NativeFunction("log", args -> { + glob.defineOwnMember(null, "print", new NativeFunction("print", args -> { for (var el : args.args) { if (el instanceof StringValue) System.out.print(((StringValue)el).value); else System.out.print(el.toReadable(args.env)); @@ -356,7 +353,7 @@ public class SimpleRepl { EventLoop.get(environment).pushMsg( false, environment, Filename.parse("jscript://init.js"), Reading.resourceToString("lib/index.js"), - Value.UNDEFINED, GlobalScope.get(environment).object, primordials(environment) + Value.UNDEFINED, Value.global(environment), primordials(environment) ).get(); } diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 1c0706d..f9a21f4 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -96,6 +96,8 @@ public class EngineException extends RuntimeException { var res = new ObjectValue(); res.setPrototype(proto); + if (msg == null) msg = ""; + if (name != null) res.defineOwnMember(Environment.empty(), "name", FieldMember.of(new StringValue(name))); res.defineOwnMember(Environment.empty(), "message", FieldMember.of(new StringValue(msg))); return res; diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java index 2db27e1..023df78 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java @@ -7,7 +7,7 @@ public class SyntaxException extends RuntimeException { public final String msg; public SyntaxException(Location loc, String msg) { - super(String.format("Syntax error (at %s): %s", loc, msg)); + super(String.format("Syntax error %s: %s", loc, msg)); this.loc = loc; this.msg = msg; } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java deleted file mode 100644 index 4092749..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.topchetoeu.jscript.runtime.scope; - -import java.util.HashSet; -import java.util.Set; - -import me.topchetoeu.jscript.common.environment.Environment; -import me.topchetoeu.jscript.common.environment.Key; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.Value; -import me.topchetoeu.jscript.runtime.values.Member.FieldMember; -import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; -import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; -import me.topchetoeu.jscript.runtime.values.primitives.StringValue; - -public class GlobalScope { - public static final Key KEY = Key.of(); - - public final ObjectValue object; - - public boolean has(Environment ext, String name) { - return object.hasMember(ext, new StringValue(name), false); - } - - public GlobalScope child() { - var res = new GlobalScope(); - res.object.setPrototype(null, this.object); - return res; - } - - public void define(Environment ext, String name, Variable variable) { - object.defineOwnMember(ext, name, variable.toField(true, true)); - } - public void define(Environment ext, boolean readonly, String name, Value val) { - object.defineOwnMember(ext, name, FieldMember.of(val, !readonly)); - } - public void define(Environment ext, boolean readonly, String ...names) { - for (var name : names) define(ext, name, new ValueVariable(readonly, Value.UNDEFINED)); - } - public void define(Environment ext, boolean readonly, FunctionValue val) { - define(ext, readonly, val.name, val); - } - - public Value get(Environment env, String name) { - if (!object.hasMember(env, name, false)) throw EngineException.ofSyntax(name + " is not defined"); - else return object.getMember(env, name); - } - public void set(Environment ext, String name, Value val) { - if (!object.hasMember(ext, name, false)) throw EngineException.ofSyntax(name + " is not defined"); - if (!object.setMember(ext, name, val)) throw EngineException.ofSyntax("Assignment to constant variable"); - } - - public Set keys() { - var res = new HashSet(); - - for (var key : keys()) { - if (key instanceof String) res.add((String)key); - } - - return res; - } - - public GlobalScope() { - this.object = new ObjectValue(); - this.object.setPrototype(null, null); - } - public GlobalScope(ObjectValue val) { - this.object = val; - } - - public static GlobalScope get(Environment ext) { - if (ext.has(KEY)) return ext.get(KEY); - else return new GlobalScope(); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/LocalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/LocalScope.java deleted file mode 100644 index 6adce80..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/scope/LocalScope.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.jscript.runtime.scope; - -import java.util.ArrayList; - -public class LocalScope { - public final ValueVariable[] captures; - public final ValueVariable[] locals; - public final ArrayList catchVars = new ArrayList<>(); - - public ValueVariable get(int i) { - if (i >= locals.length) return catchVars.get(i - locals.length); - if (i >= 0) return locals[i]; - else return captures[~i]; - } - - - public int size() { - return captures.length + locals.length; - } - - public LocalScope(int n, ValueVariable[] captures) { - locals = new ValueVariable[n]; - this.captures = captures; - - for (int i = 0; i < n; i++) { - locals[i] = new ValueVariable(false, null); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java deleted file mode 100644 index 22e2329..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.topchetoeu.jscript.runtime.scope; - -import me.topchetoeu.jscript.common.environment.Environment; -import me.topchetoeu.jscript.runtime.values.Value; - -public class ValueVariable implements Variable { - public boolean readonly; - public Value value; - - @Override public boolean readonly() { return readonly; } - @Override public final Value get(Environment env) { return get(); } - @Override public final boolean set(Environment env, Value val) { return set(val); } - - public Value get() { return value; } - public boolean set(Value val) { - if (readonly) return false; - this.value = val; - return true; - } - - public ValueVariable(boolean readonly, Value val) { - this.readonly = readonly; - this.value = val; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java deleted file mode 100644 index 43393a1..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.topchetoeu.jscript.runtime.scope; - -import me.topchetoeu.jscript.common.environment.Environment; -import me.topchetoeu.jscript.runtime.values.Value; -import me.topchetoeu.jscript.runtime.values.Member.FieldMember; - -public interface Variable { - Value get(Environment env); - default boolean readonly() { return true; } - default boolean set(Environment env, Value val) { return false; } - - default FieldMember toField(boolean configurable, boolean enumerable) { - var self = this; - - return new FieldMember(!readonly(), configurable, enumerable) { - @Override public Value get(Environment env, Value _self) { - return self.get(env); - } - @Override public boolean set(Environment env, Value val, Value _self) { - return self.set(env, val); - } - }; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index 9282791..ed90d04 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -64,6 +64,7 @@ public abstract class Value { public static final Key SYNTAX_ERR_PROTO = Key.of(); public static final Key TYPE_ERR_PROTO = Key.of(); public static final Key RANGE_ERR_PROTO = Key.of(); + public static final Key GLOBAL = Key.of(); public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); @@ -278,13 +279,31 @@ public abstract class Value { return deleteOwnMember(env, new KeyCache(key)); } - public final Value getMember(Environment env, KeyCache key) { + public final Value getMemberOrNull(Environment env, KeyCache key) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { var member = obj.getOwnMember(env, key); if (member != null) return member.get(env, obj); } - return Value.UNDEFINED; + return null; + } + public final Value getMemberOrNull(Environment env, Value key) { + return getMemberOrNull(env, new KeyCache(key)); + } + public final Value getMemberOrNull(Environment env, String key) { + return getMemberOrNull(env, new KeyCache(key)); + } + public final Value getMemberOrNull(Environment env, int key) { + return getMemberOrNull(env, new KeyCache(key)); + } + public final Value getMemberOrNull(Environment env, double key) { + return getMemberOrNull(env, new KeyCache(key)); + } + + public final Value getMember(Environment env, KeyCache key) { + var res = getMemberOrNull(env, key); + if (res != null) return res; + else return Value.UNDEFINED; } public final Value getMember(Environment env, Value key) { return getMember(env, new KeyCache(key)); @@ -330,6 +349,33 @@ public abstract class Value { return setMember(env, new KeyCache(key), val); } + public final boolean setMemberIfExists(Environment env, KeyCache key, Value val) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + var member = obj.getOwnMember(env, key); + if (member != null) { + if (member.set(env, val, obj)) { + if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env)); + return true; + } + else return false; + } + } + + return false; + } + public final boolean setMemberIfExists(Environment env, Value key, Value val) { + return setMemberIfExists(env, new KeyCache(key), val); + } + public final boolean setMemberIfExists(Environment env, String key, Value val) { + return setMemberIfExists(env, new KeyCache(key), val); + } + public final boolean setMemberIfExists(Environment env, int key, Value val) { + return setMemberIfExists(env, new KeyCache(key), val); + } + public final boolean setMemberIfExists(Environment env, double key, Value val) { + return setMemberIfExists(env, new KeyCache(key), val); + } + public final boolean hasMember(Environment env, KeyCache key, boolean own) { for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { if (obj.getOwnMember(env, key) != null) return true; @@ -478,13 +524,11 @@ public abstract class Value { } } - @Override - public boolean hasNext() { + @Override public boolean hasNext() { loadNext(); return supplier != null; } - @Override - public Object next() { + @Override public Object next() { loadNext(); var res = value; value = null; @@ -632,4 +676,8 @@ public abstract class Value { return prefix + " internal error " + str.toString(); } } + + public static final ObjectValue global(Environment env) { + return env.initFrom(GLOBAL, () -> new ObjectValue()); + } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index b462e5f..d3e9e5c 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -3,12 +3,11 @@ package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.Value; public class CodeFunction extends FunctionValue { public final FunctionBody body; - public final ValueVariable[] captures; + public final Value[][] captures; public Environment env; @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { @@ -26,7 +25,7 @@ public class CodeFunction extends FunctionValue { } } - public CodeFunction(Environment env, String name, FunctionBody body, ValueVariable[] captures) { + public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { super(name, body.argsN); this.captures = captures; this.env = env; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java index 23299ce..43a326b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java @@ -201,12 +201,10 @@ public class ArrayValue extends ObjectValue implements Iterable { return new Iterator<>() { private int i = 0; - @Override - public boolean hasNext() { + @Override public boolean hasNext() { return i < size(); } - @Override - public Value next() { + @Override public Value next() { if (!hasNext()) return null; return get(i++); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java index 0d4ae0e..c84a295 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java @@ -1,7 +1,6 @@ package me.topchetoeu.jscript.runtime.values.objects; import me.topchetoeu.jscript.common.environment.Environment; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; @@ -15,17 +14,18 @@ public class ScopeValue extends ObjectValue { } @Override public Value get(Environment env, Value self) { - return variables[i].get(env); + return variables[i][0]; } @Override public boolean set(Environment env, Value val, Value self) { - return variables[i].set(env, val); + variables[i][0] = val; + return true; } } - public final ValueVariable[] variables; + public final Value[][] variables; - public ScopeValue(ValueVariable[] variables, String[] names) { + public ScopeValue(Value[][] variables, String[] names) { this.variables = variables; for (var i = 0; i < names.length && i < variables.length; i++) { defineOwnMember(Environment.empty(), i, new VariableField(i)); -- 2.45.2 From 2b6d4a87cab72de41700a1508d2746e178d4730a Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:07:15 +0300 Subject: [PATCH 13/48] fix: for in and for of not reading open paren --- .../compilation/control/ForInNode.java | 4 ++ .../compilation/control/ForOfNode.java | 39 ++++++++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index 1ae1dfa..12a52e6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -85,6 +85,10 @@ public class ForInNode extends Node { n += 3; n += Parsing.skipEmpty(src, i + n); + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected an opening paren"); + n++; + n += Parsing.skipEmpty(src, i + n); + var isDecl = false; if (Parsing.isIdentifier(src, i + n, "var")) { diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index d963c5e..514a255 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -7,8 +7,11 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForOfNode extends Node { public final String varName; @@ -19,18 +22,13 @@ public class ForOfNode extends Node { @Override public void resolve(CompileResult target) { body.resolve(target); - if (isDeclaration) target.scope.resolve(varName); + if (isDeclaration) target.scope.define(varName, false, varLocation); } @Override public void compile(CompileResult target, boolean pollute) { - var key = target.scope.getKey(varName); - - if (key instanceof String) target.add(Instruction.globDef((String)key)); - iterable.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.dup()); - target.add(Instruction.loadVar("Symbol")); - target.add(Instruction.pushValue("iterator")); + target.add(Instruction.loadIntrinsics("it_key")); target.add(Instruction.loadMember()).setLocation(iterable.loc()); target.add(Instruction.loadMember()).setLocation(iterable.loc()); target.add(Instruction.call(0)).setLocation(iterable.loc()); @@ -48,18 +46,29 @@ public class ForOfNode extends Node { target.add(Instruction.pushValue("value")); target.add(Instruction.loadMember()).setLocation(varLocation); - target.add(Instruction.storeVar(key)).setLocationAndDebug(iterable.loc(), BreakpointType.STEP_OVER); + target.add(VariableNode.toSet(target, varLocation, varName, false, isDeclaration)); + + var end = new DeferredIntSupplier(); + + LabelContext.pushLoop(target.env, loc(), label, end, start); + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); body.compile(target, false, BreakpointType.STEP_OVER); - int end = target.size(); + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + LabelContext.popLoop(target.env, label); - WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); + int endI = target.size(); + end.set(endI); - target.add(Instruction.jmp(start - end)); + // WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); + + target.add(Instruction.jmp(start - endI)); target.add(Instruction.discard()); target.add(Instruction.discard()); - target.set(mid, Instruction.jmpIf(end - mid + 1)); + target.set(mid, Instruction.jmpIf(endI - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } @@ -85,6 +94,10 @@ public class ForOfNode extends Node { n += 3; n += Parsing.skipEmpty(src, i + n); + if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected an opening paren"); + n++; + n += Parsing.skipEmpty(src, i + n); + var isDecl = false; if (Parsing.isIdentifier(src, i + n, "var")) { @@ -98,7 +111,7 @@ public class ForOfNode extends Node { n += name.n; n += Parsing.skipEmpty(src, i + n); - if (!Parsing.isIdentifier(src, i + n, "fo")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); + if (!Parsing.isIdentifier(src, i + n, "of")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); n += 2; var obj = JavaScript.parseExpression(src, i + n, 0); -- 2.45.2 From 163dfe7b6e491ff71df3097bf42668daee01c43d Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:10:07 +0300 Subject: [PATCH 14/48] feat: implement access to intrinsics --- src/java/me/topchetoeu/jscript/common/Instruction.java | 6 +++++- .../me/topchetoeu/jscript/runtime/InstructionRunner.java | 6 ++++++ src/java/me/topchetoeu/jscript/runtime/values/Value.java | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/java/me/topchetoeu/jscript/common/Instruction.java b/src/java/me/topchetoeu/jscript/common/Instruction.java index 478fddc..2e7f5b2 100644 --- a/src/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/java/me/topchetoeu/jscript/common/Instruction.java @@ -36,7 +36,8 @@ public class Instruction { LOAD_ARR(0x31), LOAD_OBJ(0x32), LOAD_GLOB(0x33), - LOAD_REGEX(0x34), + LOAD_INTRINSICS(0x34), + LOAD_REGEX(0x35), LOAD_VAR(0x40), LOAD_MEMBER(0x41), @@ -334,6 +335,9 @@ public class Instruction { public static Instruction loadGlob() { return new Instruction(Type.LOAD_GLOB); } + public static Instruction loadIntrinsics(String key) { + return new Instruction(Type.LOAD_INTRINSICS, key); + } public static Instruction loadMember() { return new Instruction(Type.LOAD_MEMBER); } diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 9a7829e..a0bd64e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -157,6 +157,11 @@ public class InstructionRunner { frame.codePtr++; return null; } + private static Value execLoadIntrinsics(Environment env, Instruction instr, Frame frame) { + frame.push(Value.intrinsics(env).get((String)instr.get(0))); + frame.codePtr++; + return null; + } private static Value execLoadArr(Environment env, Instruction instr, Frame frame) { var res = new ArrayValue(); res.setSize(instr.get(0)); @@ -382,6 +387,7 @@ public class InstructionRunner { case LOAD_MEMBER: return execLoadMember(env, instr, frame); case LOAD_REGEX: return execLoadRegEx(env, instr, frame); case LOAD_GLOB: return execLoadGlob(env, instr, frame); + case LOAD_INTRINSICS: return execLoadIntrinsics(env, instr, frame); case LOAD_ARGS: return execLoadArgs(env, instr, frame); case LOAD_THIS: return execLoadThis(env, instr, frame); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index ed90d04..7f322f0 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -65,6 +66,7 @@ public abstract class Value { public static final Key TYPE_ERR_PROTO = Key.of(); public static final Key RANGE_ERR_PROTO = Key.of(); public static final Key GLOBAL = Key.of(); + public static final Key> INTRINSICS = Key.of(); public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); public static final VoidValue NULL = new VoidValue("null", new StringValue("object")); @@ -680,4 +682,7 @@ public abstract class Value { public static final ObjectValue global(Environment env) { return env.initFrom(GLOBAL, () -> new ObjectValue()); } + public static final Map intrinsics(Environment env) { + return env.initFrom(INTRINSICS, () -> new HashMap<>()); + } } -- 2.45.2 From 1b87c2f5a6338fbe5fb121790f68ef21efc938f4 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:10:16 +0300 Subject: [PATCH 15/48] fix: add for-of to statement list --- src/java/me/topchetoeu/jscript/compilation/JavaScript.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index 75aaa83..ffa5208 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -17,6 +17,7 @@ import me.topchetoeu.jscript.compilation.control.DeleteNode; import me.topchetoeu.jscript.compilation.control.DoWhileNode; import me.topchetoeu.jscript.compilation.control.ForInNode; import me.topchetoeu.jscript.compilation.control.ForNode; +import me.topchetoeu.jscript.compilation.control.ForOfNode; import me.topchetoeu.jscript.compilation.control.IfNode; import me.topchetoeu.jscript.compilation.control.ReturnNode; import me.topchetoeu.jscript.compilation.control.SwitchNode; @@ -187,7 +188,7 @@ public class JavaScript { SwitchNode::parse, ForNode::parse, ForInNode::parse, - // ForOfNode::parse, + ForOfNode::parse, DoWhileNode::parse, TryNode::parse, CompoundNode::parse, -- 2.45.2 From 82d6f52a26c3bfb55d098fbe8c9abc5ea7f6fd42 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:13:57 +0300 Subject: [PATCH 16/48] refactor: make some classes final for performance --- src/java/me/topchetoeu/jscript/runtime/Engine.java | 5 +---- src/java/me/topchetoeu/jscript/runtime/Frame.java | 2 +- src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java | 2 +- .../jscript/runtime/values/functions/CodeFunction.java | 2 +- .../jscript/runtime/values/functions/NativeFunction.java | 2 +- .../jscript/runtime/values/objects/ScopeValue.java | 2 +- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/runtime/Engine.java b/src/java/me/topchetoeu/jscript/runtime/Engine.java index e3ada89..e3cb05e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Engine.java +++ b/src/java/me/topchetoeu/jscript/runtime/Engine.java @@ -7,7 +7,7 @@ import java.util.function.Supplier; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -public class Engine implements EventLoop { +public final class Engine implements EventLoop { private static class Task implements Comparable> { public final Supplier runnable; public final CompletableFuture notifier = new CompletableFuture(); @@ -74,7 +74,4 @@ public class Engine implements EventLoop { public boolean isRunning() { return this.thread != null; } - - public Engine() { - } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 1ccf5bb..4e9fb8c 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -21,7 +21,7 @@ import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; -public class Frame { +public final class Frame { public static final Key KEY = Key.of(); public static enum TryState { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java b/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java index c1d4840..f5660ed 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java @@ -5,7 +5,7 @@ import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; -public class KeyCache { +public final class KeyCache { public final Value value; private Integer intCache; private Double doubleCache; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index d3e9e5c..ca23b3d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -5,7 +5,7 @@ import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.values.Value; -public class CodeFunction extends FunctionValue { +public final class CodeFunction extends FunctionValue { public final FunctionBody body; public final Value[][] captures; public Environment env; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index 4615f8d..0ad250a 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; -public class NativeFunction extends FunctionValue { +public final class NativeFunction extends FunctionValue { public static interface NativeFunctionRunner { Value run(Arguments args); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java index c84a295..a968637 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java @@ -4,7 +4,7 @@ import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; -public class ScopeValue extends ObjectValue { +public final class ScopeValue extends ObjectValue { private class VariableField extends FieldMember { private int i; -- 2.45.2 From 6932bea67766ac5c110d728677d32588906d3553 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:24:23 +0300 Subject: [PATCH 17/48] refactor: remove unused class --- .../topchetoeu/jscript/compilation/Path.java | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/java/me/topchetoeu/jscript/compilation/Path.java diff --git a/src/java/me/topchetoeu/jscript/compilation/Path.java b/src/java/me/topchetoeu/jscript/compilation/Path.java deleted file mode 100644 index 9d5f897..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/Path.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -import java.util.function.Predicate; - -public class Path { - public final Path parent; - public final Node node; - - public Path getParent(Predicate> predicate) { - for (Path it = this; it != null; it = it.parent) { - if (predicate.test(it)) return it; - } - - return null; - } - - public Path getParent(Class type, Predicate> predicate) { - for (Path it = this; it != null; it = it.parent) { - if (type.isInstance(it.node) && predicate.test(it)) return it; - } - - return null; - } - - public Path(Path parent, Node node) { - this.parent = parent; - this.node = node; - } -} -- 2.45.2 From 52f7c15ac8e80456f167f5a797468b775117d5a6 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:43:49 +0300 Subject: [PATCH 18/48] refactor: change how function scope keeps track of arguments --- .../compilation/scope/FunctionScope.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 691ca0e..5741a63 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -9,8 +9,6 @@ public class FunctionScope extends Scope { private final VariableList specials = new VariableList(); private final VariableList locals = new VariableList(specials); private HashMap childToParent = new HashMap<>(); - public final String selfName; - public final VariableDescriptor selfVar; private void removeCapture(String name) { var res = captures.remove(name); @@ -29,12 +27,19 @@ public class FunctionScope extends Scope { else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes"); else return parent.defineStrict(name, readonly, loc); } + public VariableDescriptor defineArg(String name, Location loc) { + return specials.add(name, false); + } + public boolean hasArg(String name) { + return specials.has(name); + } - @Override public VariableDescriptor get(String name, boolean capture) { - if (specials.has(name)) return specials.get(name); - if (locals.has(name)) return locals.get(name); - if (captures.has(name)) return captures.get(name); - if (selfName != null && selfName.equals(name)) return selfVar; + public VariableDescriptor get(String name, boolean capture, boolean skipSelf) { + if (!skipSelf) { + if (specials.has(name)) return specials.get(name); + if (locals.has(name)) return locals.get(name); + if (captures.has(name)) return captures.get(name); + } var parentVar = parent.get(name, true); if (parentVar == null) return null; @@ -46,6 +51,10 @@ public class FunctionScope extends Scope { return childVar; } + @Override public VariableDescriptor get(String name, boolean capture) { + return get(name, capture, false); + } + @Override public boolean has(String name) { if (specials.has(name)) return true; if (locals.has(name)) return true; @@ -89,22 +98,10 @@ public class FunctionScope extends Scope { return res; } - public FunctionScope(String selfName, String[] args) { + public FunctionScope() { super(); - this.selfName = selfName; - - if (selfName != null) this.selfVar = VariableDescriptor.of(selfName, true, -1); - else this.selfVar = null; - - for (var arg : args) specials.add(arg, false); } - public FunctionScope(String selfName, String[] args, Scope parent) { + public FunctionScope(Scope parent) { super(parent); - this.selfName = selfName; - - if (selfName != null) this.selfVar = VariableDescriptor.of(selfName, true, -1); - else this.selfVar = null; - - for (var arg : args) specials.add(arg, false); } } -- 2.45.2 From 87e077d70d691695cc6d15794ce6454f7cc53683 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 17:44:20 +0300 Subject: [PATCH 19/48] oops --- .../jscript/compilation/FunctionNode.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 2b05291..daca4a5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -47,10 +47,18 @@ public abstract class FunctionNode extends Node { } } - var funcScope = new FunctionScope(storeSelf ? name : null, args, target.scope); - var subtarget = new CompileResult(target.env, new LocalScope(funcScope)); + var env = target.env.child() + .remove(LabelContext.BREAK_CTX) + .remove(LabelContext.CONTINUE_CTX); - // compileStoreSelf(subtarget, pollute, bp); + var funcScope = new FunctionScope(target.scope); + var subtarget = new CompileResult(env, new LocalScope(funcScope)); + + for (var arg : args) { + // TODO: Implement default values + // TODO: Implement argument location + funcScope.defineArg(arg, loc()); + } body.resolve(subtarget); body.compile(subtarget, false); -- 2.45.2 From 7ab78b9cea243ff18ae8adcd85b648c7d949df43 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:19:56 +0300 Subject: [PATCH 20/48] fix: control flow nodes were making scopes instead of compound nodes --- .../jscript/compilation/CompoundNode.java | 14 ++++-- .../jscript/compilation/FunctionNode.java | 1 - .../jscript/compilation/JavaScript.java | 2 - .../compilation/control/ForInNode.java | 6 --- .../jscript/compilation/control/ForNode.java | 11 +---- .../compilation/control/ForOfNode.java | 6 --- .../jscript/compilation/control/IfNode.java | 28 ++--------- .../jscript/compilation/control/TryNode.java | 49 ++++++------------- .../compilation/control/WhileNode.java | 6 --- 9 files changed, 31 insertions(+), 92 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java index 4db2815..a45f58b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -22,9 +22,12 @@ public class CompoundNode extends Node { @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { List statements = new ArrayList(); + var subtarget = target.subtarget(); + subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + for (var stm : this.statements) { - if (stm instanceof FunctionStatementNode) { - stm.compile(target, false); + if (stm instanceof FunctionStatementNode func) { + func.compile(subtarget, false); } else statements.add(stm); } @@ -34,10 +37,13 @@ public class CompoundNode extends Node { for (var i = 0; i < statements.size(); i++) { var stm = statements.get(i); - if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER); - else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER); + if (i != statements.size() - 1) stm.compile(subtarget, false, BreakpointType.STEP_OVER); + else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER); } + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + if (!polluted && pollute) { target.add(Instruction.pushUndefined()); } diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java index daca4a5..ed54d94 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -66,7 +66,6 @@ public abstract class FunctionNode extends Node { subtarget.length = args.length; subtarget.scope.end(); funcScope.end(); - subtarget.add(Instruction.ret()).setLocation(end); if (pollute) compileLoadFunc(target, funcScope.getCaptureIndices(), name); diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index ffa5208..96f2bbb 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -288,8 +288,6 @@ public class JavaScript { target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); } - target.add(Instruction.ret()).setLocation(stm.loc()); - return target; } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index 12a52e6..e2c2735 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -44,13 +44,7 @@ public class ForInNode extends Node { var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); - var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); - body.compile(target, false, BreakpointType.STEP_OVER); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); LabelContext.popLoop(target.env, label); int endI = target.size(); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java index bf361f4..8a7c3d3 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -30,16 +30,9 @@ public class ForNode extends Node { int mid = target.temp(); var end = new DeferredIntSupplier(); + LabelContext.pushLoop(target.env, loc(), label, end, start); - - var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); - - body.compile(subtarget, false, BreakpointType.STEP_OVER); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); - + body.compile(target, false, BreakpointType.STEP_OVER); LabelContext.popLoop(target.env, label); // int beforeAssign = target.size(); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index 514a255..87ffb45 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -51,13 +51,7 @@ public class ForOfNode extends Node { var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); - var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); - body.compile(target, false, BreakpointType.STEP_OVER); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); LabelContext.popLoop(target.env, label); int endI = target.size(); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java index e4045a4..d6a07f5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -29,15 +29,7 @@ public class IfNode extends Node { var end = new DeferredIntSupplier(); LabelContext.getBreak(target.env).push(loc(), label, end); - - var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); - - body.compile(subtarget, false, BreakpointType.STEP_OVER); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); - + body.compile(target, false, BreakpointType.STEP_OVER); LabelContext.getBreak(target.env).pop(label); int endI = target.size(); @@ -50,25 +42,11 @@ public class IfNode extends Node { var end = new DeferredIntSupplier(); LabelContext.getBreak(target.env).push(loc(), label, end); - - var bodyTarget = target.subtarget(); - bodyTarget.add(() -> Instruction.stackAlloc(bodyTarget.scope.allocCount())); - - body.compile(bodyTarget, false, BreakpointType.STEP_OVER); - - bodyTarget.scope.end(); - bodyTarget.add(Instruction.stackFree(bodyTarget.scope.allocCount())); + body.compile(target, false, BreakpointType.STEP_OVER); int mid = target.temp(); - var elseTarget = target.subtarget(); - elseTarget.add(() -> Instruction.stackAlloc(elseTarget.scope.allocCount())); - - body.compile(elseTarget, false, BreakpointType.STEP_OVER); - - elseTarget.scope.end(); - elseTarget.add(Instruction.stackFree(elseTarget.scope.allocCount())); - + body.compile(target, false, BreakpointType.STEP_OVER); LabelContext.getBreak(target.env).pop(label); int endI = target.size(); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java b/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java index 52d6723..04e4650 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -14,9 +14,9 @@ import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; public class TryNode extends Node { - public final Node tryBody; - public final Node catchBody; - public final Node finallyBody; + public final CompoundNode tryBody; + public final CompoundNode catchBody; + public final CompoundNode finallyBody; public final String captureName; public final String label; @@ -34,43 +34,26 @@ public class TryNode extends Node { LabelContext.getBreak(target.env).push(loc(), label, endSuppl); - { - var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); - - tryBody.compile(subtarget, false); - subtarget.add(Instruction.tryEnd()); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); - } + tryBody.compile(target, false); + target.add(Instruction.tryEnd()); if (catchBody != null) { catchStart = target.size() - start; - var subtarget = target.subtarget(); - var decN = captureName != null ? 1 : 0; + if (captureName != null) { + var subtarget = target.subtarget(); + subtarget.scope.defineStrict(captureName, false, catchBody.loc()); + catchBody.compile(subtarget, false); + subtarget.scope.end(); + } + else catchBody.compile(target, false); - if (captureName != null) subtarget.scope.defineStrict(captureName, false, catchBody.loc()); - - var _subtarget = subtarget; - - subtarget.add(() -> Instruction.stackAlloc(_subtarget.scope.allocCount() - decN)); - - catchBody.compile(subtarget, false); - - subtarget.add(Instruction.tryEnd()); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount() - decN)); + target.add(Instruction.tryEnd()); } if (finallyBody != null) { finallyStart = target.size() - start; - - var subtarget = target.subtarget(); - finallyBody.compile(subtarget, false); - subtarget.add(Instruction.tryEnd()); + finallyBody.compile(target, false); } LabelContext.getBreak(target.env).pop(label); @@ -83,7 +66,7 @@ public class TryNode extends Node { if (pollute) target.add(Instruction.pushUndefined()); } - public TryNode(Location loc, String label, Node tryBody, Node catchBody, Node finallyBody, String captureName) { + public TryNode(Location loc, String label, CompoundNode tryBody, CompoundNode catchBody, CompoundNode finallyBody, String captureName) { super(loc); this.tryBody = tryBody; this.catchBody = catchBody; @@ -109,7 +92,7 @@ public class TryNode extends Node { n += Parsing.skipEmpty(src, i + n); String capture = null; - Node catchBody = null, finallyBody = null; + CompoundNode catchBody = null, finallyBody = null; if (Parsing.isIdentifier(src, i + n, "catch")) { n += 5; diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index fda05e1..55787df 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -28,13 +28,7 @@ public class WhileNode extends Node { LabelContext.pushLoop(target.env, loc(), label, end, start); - var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); - body.compile(target, false, BreakpointType.STEP_OVER); - - subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); LabelContext.popLoop(target.env, label); var endI = target.size(); -- 2.45.2 From c39c06b792b3be49e338c8d14a6a354bd07317b8 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:20:36 +0300 Subject: [PATCH 21/48] refactor: move away intrinsic logic to final methods for performance --- build.gradle | 9 +- settings.gradle | 8 + .../me/topchetoeu/jscript/runtime/Frame.java | 56 ++-- .../jscript/runtime/InstructionRunner.java | 100 +++++- .../jscript/runtime/debug/DebugContext.java | 4 + .../jscript/runtime/values/Member.java | 2 +- .../jscript/runtime/values/Value.java | 292 +++++++++--------- .../values/functions/CodeFunction.java | 10 +- .../runtime/values/objects/ObjectValue.java | 2 - .../runtime/values/primitives/BoolValue.java | 5 +- .../values/primitives/NumberValue.java | 10 +- .../values/primitives/StringValue.java | 14 +- .../values/primitives/SymbolValue.java | 3 - .../runtime/values/primitives/VoidValue.java | 11 - 14 files changed, 309 insertions(+), 217 deletions(-) diff --git a/build.gradle b/build.gradle index 34358c7..96537a3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,21 @@ + plugins { id "application" + // these idiots don't optimize in the compile-time, but in the runtime + // who let these knuckleheads make a language + + // TODO: figure out how to integrate proguard + // id "com.github.xaverkapeller.proguard-annotations" } repositories { mavenCentral() + gradlePluginPortal() } dependencies { - annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' // Genuinely fuck Java + annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' } diff --git a/settings.gradle b/settings.gradle index fc7ee6c..2129498 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,13 @@ +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' } + rootProject.name = properties.project_name diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 4e9fb8c..c979933 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,7 +1,6 @@ package me.topchetoeu.jscript.runtime; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -109,6 +108,7 @@ public final class Frame { public final Stack tryStack = new Stack<>(); public final CodeFunction function; public final Environment env; + private final DebugContext dbg; public Value[] getVar(int i) { if (i < 0) return captures[~i]; @@ -132,24 +132,15 @@ public final class Frame { return peek(0); } public Value peek(int offset) { - if (stackPtr <= offset) return null; - else return stack[stackPtr - 1 - offset]; + return stack[stackPtr - 1 - offset]; } public Value pop() { - if (stackPtr == 0) return Value.UNDEFINED; return stack[--stackPtr]; } public Value[] take(int n) { - int srcI = stackPtr - n; - if (srcI < 0) srcI = 0; - - int dstI = n + srcI - stackPtr; - int copyN = stackPtr - srcI; - Value[] res = new Value[n]; - Arrays.fill(res, Value.UNDEFINED); - System.arraycopy(stack, srcI, res, dstI, copyN); - stackPtr -= copyN; + System.arraycopy(stack, stackPtr - n, res, 0, n); + stackPtr -= n; return res; } @@ -162,33 +153,45 @@ public final class Frame { stack[stackPtr++] = val; } + public void replace(Value val) { + stack[stackPtr - 1] = val; + } // for the love of christ don't touch this - private Value next(Value value, Value returnValue, EngineException error) { + /** + * This is provided only for optimization-sike. All parameters must be null except at most one, otherwise undefined behavior + */ + public final Value next(Value value, Value returnValue, EngineException error) { if (value != null) push(value); Instruction instr = null; - if (codePtr >= 0 && codePtr < function.body.instructions.length) instr = function.body.instructions[codePtr]; + if (codePtr != function.body.instructions.length) instr = function.body.instructions[codePtr]; if (returnValue == null && error == null) { try { if (Thread.interrupted()) throw new InterruptException(); - if (instr == null) returnValue = null; + if (instr == null) { + if (stackPtr > 0) returnValue = stack[stackPtr - 1]; + else returnValue = Value.UNDEFINED; + } else { - DebugContext.get(env).onInstruction(env, this, instr, null, null, false); + dbg.onInstruction(env, this, instr); try { this.jumpFlag = this.popTryFlag = false; returnValue = InstructionRunner.exec(env, instr, this); } catch (EngineException e) { - error = e.add(env, function.name, DebugContext.get(env).getMapOrEmpty(function).toLocation(codePtr, true)); + error = e.add(env, function.name, dbg.getMapOrEmpty(function).toLocation(codePtr, true)); } } } catch (EngineException e) { error = e; } - // catch (RuntimeException e) { error = EngineException.ofError("InternalError", e.getMessage()); } + catch (RuntimeException e) { + System.out.println(dbg.getMapOrEmpty(function).toLocation(codePtr, true)); + throw e; + } } while (!tryStack.empty()) { @@ -265,17 +268,17 @@ public final class Frame { if (error != null) { var caught = false; - for (var frame : DebugContext.get(env).getStackFrames()) { + for (var frame : dbg.getStackFrames()) { for (var tryCtx : frame.tryStack) { if (tryCtx.state == TryState.TRY) caught = true; } } - DebugContext.get(env).onInstruction(env, this, instr, null, error, caught); + dbg.onInstruction(env, this, instr, null, error, caught); throw error; } if (returnValue != null) { - DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false); + dbg.onInstruction(env, this, instr, returnValue, null, false); return returnValue; } @@ -285,7 +288,7 @@ public final class Frame { /** * Executes the next instruction in the frame */ - public Value next() { + public final Value next() { return next(null, null, null); } /** @@ -294,7 +297,7 @@ public final class Frame { * * @param value The value to induce */ - public Value next(Value value) { + public final Value next(Value value) { return next(value, null, null); } /** @@ -305,7 +308,7 @@ public final class Frame { * * @param error The error to induce */ - public Value induceError(EngineException error) { + public final Value induceError(EngineException error) { return next(null, null, error); } /** @@ -317,7 +320,7 @@ public final class Frame { * * @param value The retunr value to induce */ - public Value induceReturn(Value value) { + public final Value induceReturn(Value value) { return next(null, value, null); } @@ -408,6 +411,7 @@ public final class Frame { public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) { this.env = env; + this.dbg = DebugContext.get(env); this.function = func; this.isNew = isNew; diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index a0bd64e..48ecd51 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -285,11 +285,105 @@ public class InstructionRunner { private static Value execOperation(Environment env, Instruction instr, Frame frame) { Operation op = instr.get(0); - var args = new Value[op.operands]; + Value res; + var stack = frame.stack; - for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); + frame.stackPtr -= 1; + var ptr = frame.stackPtr; - frame.push(Value.operation(env, op, args)); + // for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); + + switch (op) { + case ADD: + res = Value.add(env, stack[ptr - 1], stack[ptr]); + break; + case SUBTRACT: + res = Value.subtract(env, stack[ptr - 1], stack[ptr]); + break; + case DIVIDE: + res = Value.divide(env, stack[ptr - 1], stack[ptr]); + break; + case MULTIPLY: + res = Value.multiply(env, stack[ptr - 1], stack[ptr]); + break; + case MODULO: + res = Value.modulo(env, stack[ptr - 1], stack[ptr]); + break; + + case AND: + res = Value.and(env, stack[ptr - 1], stack[ptr]); + break; + case OR: + res = Value.or(env, stack[ptr - 1], stack[ptr]); + break; + case XOR: + res = Value.xor(env, stack[ptr - 1], stack[ptr]); + break; + + case EQUALS: + res = BoolValue.of(stack[ptr - 1].equals(stack[ptr])); + break; + case NOT_EQUALS: + res = BoolValue.of(!stack[ptr - 1].equals(stack[ptr])); + break; + case LOOSE_EQUALS: + res = BoolValue.of(Value.looseEqual(env, stack[ptr - 1], stack[ptr])); + break; + case LOOSE_NOT_EQUALS: + res = BoolValue.of(!Value.looseEqual(env, stack[ptr - 1], stack[ptr])); + break; + + case GREATER: + res = BoolValue.of(Value.greater(env, stack[ptr - 1], stack[ptr])); + break; + case GREATER_EQUALS: + res = BoolValue.of(Value.greaterOrEqual(env, stack[ptr - 1], stack[ptr])); + break; + case LESS: + res = BoolValue.of(Value.less(env, stack[ptr - 1], stack[ptr])); + break; + case LESS_EQUALS: + res = BoolValue.of(Value.lessOrEqual(env, stack[ptr - 1], stack[ptr])); + break; + + case INVERSE: + res = Value.bitwiseNot(env, stack[ptr++]); + frame.stackPtr++; + break; + case NOT: + res = BoolValue.of(!stack[ptr++].toBoolean()); + frame.stackPtr++; + break; + case POS: + res = stack[ptr++].toNumber(env); + frame.stackPtr++; + break; + case NEG: + res = Value.negative(env, stack[ptr++]); + frame.stackPtr++; + break; + + case SHIFT_LEFT: + res = Value.shiftLeft(env, stack[ptr], stack[ptr]); + break; + case SHIFT_RIGHT: + res = Value.shiftRight(env, stack[ptr], stack[ptr]); + break; + case USHIFT_RIGHT: + res = Value.unsignedShiftRight(env, stack[ptr], stack[ptr]); + break; + + case IN: + res = BoolValue.of(stack[ptr - 1].hasMember(env, stack[ptr], false)); + break; + case INSTANCEOF: + res = BoolValue.of(stack[ptr - 1].isInstanceOf(env, stack[ptr].getMember(env, new StringValue("prototype")))); + break; + + default: return null; + } + + stack[ptr - 1] = res; frame.codePtr++; return null; } diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java index df26c27..d8f709b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java @@ -86,6 +86,10 @@ public class DebugContext { if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught); else return false; } + public boolean onInstruction(Environment env, Frame frame, Instruction instruction) { + if (debugger != null) return debugger.onInstruction(env, frame, instruction, null, null, false); + else return false; + } public void onSource(Filename filename, String source) { if (debugger != null) debugger.onSourceLoad(filename, source); if (sources != null) sources.put(filename, source); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/java/me/topchetoeu/jscript/runtime/values/Member.java index 8f99012..92828a0 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Member.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Member.java @@ -87,7 +87,7 @@ public interface Member { if (field.configurable != configurable) return false; if (field.enumerable != enumerable) return false; - if (!writable) return field.get(env, self).strictEquals(env, get(env, self)); + if (!writable) return field.get(env, self).equals(get(env, self)); set(env, field.get(env, self), self); writable = field.writable; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index 7f322f0..358fbf9 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -10,7 +10,6 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; -import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.json.JSON; @@ -112,65 +111,6 @@ public abstract class Value { public final int toInt(Environment env) { return (int)toNumber(env).value; } public final long toLong(Environment env) { return (long)toNumber(env).value; } - public Value add(Environment env, Value other) { - if (this instanceof StringValue || other instanceof StringValue) { - return new StringValue(this.toString(env).value + other.toString(env).value); - } - else return new NumberValue(this.toNumber(env).value + other.toNumber(env).value); - } - public NumberValue subtract(Environment env, Value other) { - return new NumberValue(toNumber(env).value - other.toNumber(env).value); - } - public NumberValue multiply(Environment env, Value other) { - return new NumberValue(toNumber(env).value - other.toNumber(env).value); - } - public NumberValue divide(Environment env, Value other) { - return new NumberValue(toNumber(env).value / other.toNumber(env).value); - } - public NumberValue modulo(Environment env, Value other) { - return new NumberValue(toNumber(env).value % other.toNumber(env).value); - } - public NumberValue negative(Environment env) { - return new NumberValue(-toNumber(env).value); - } - - public NumberValue and(Environment env, Value other) { - return new NumberValue(this.toInt(env) & other.toInt(env)); - } - public NumberValue or(Environment env, Value other) { - return new NumberValue(this.toInt(env) | other.toInt(env)); - } - public NumberValue xor(Environment env, Value other) { - return new NumberValue(this.toInt(env) ^ other.toInt(env)); - } - public NumberValue bitwiseNot(Environment env) { - return new NumberValue(~this.toInt(env)); - } - - public NumberValue shiftLeft(Environment env, Value other) { - return new NumberValue(this.toInt(env) << other.toInt(env)); - } - public NumberValue shiftRight(Environment env, Value other) { - return new NumberValue(this.toInt(env) >> other.toInt(env)); - } - public NumberValue unsignedShiftRight(Environment env, Value other) { - long a = this.toInt(env); - long b = other.toInt(env); - - if (a < 0) a += 0x100000000l; - if (b < 0) b += 0x100000000l; - - return new NumberValue(a >>> b); - } - - public CompareResult compare(Environment env, Value other) { - var a = this.toPrimitive(env); - var b = other.toPrimitive(env); - - if (a instanceof StringValue && b instanceof StringValue) return a.compare(env, b); - else return a.toNumber(env).compare(env, b.toNumber(env)); - } - public final boolean isInstanceOf(Environment env, Value proto) { for (var val = getPrototype(env); val != null; val = getPrototype(env)) { if (val.equals(proto)) return true; @@ -179,44 +119,6 @@ public abstract class Value { return false; } - public static Value operation(Environment env, Operation op, Value ...args) { - switch (op) { - case ADD: return args[0].add(env, args[1]); - case SUBTRACT: return args[0].subtract(env, args[1]); - case DIVIDE: return args[0].divide(env, args[1]); - case MULTIPLY: return args[0].multiply(env, args[1]); - case MODULO: return args[0].modulo(env, args[1]); - - case AND: return args[0].and(env, args[1]); - case OR: return args[0].or(env, args[1]); - case XOR: return args[0].xor(env, args[1]); - - case EQUALS: return BoolValue.of(args[0].strictEquals(env, args[1])); - case NOT_EQUALS: return BoolValue.of(!args[0].strictEquals(env, args[1])); - case LOOSE_EQUALS: return BoolValue.of(args[0].looseEqual(env, args[1])); - case LOOSE_NOT_EQUALS: return BoolValue.of(!args[0].looseEqual(env, args[1])); - - case GREATER: return BoolValue.of(args[0].compare(env, args[1]).greater()); - case GREATER_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).greaterOrEqual()); - case LESS: return BoolValue.of(args[0].compare(env, args[1]).less()); - case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual()); - - case INVERSE: return args[0].bitwiseNot(env); - case NOT: return BoolValue.of(!args[0].toBoolean()); - case POS: return args[0].toNumber(env); - case NEG: return args[0].negative(env); - - case SHIFT_LEFT: return args[0].shiftLeft(env, args[1]); - case SHIFT_RIGHT: return args[0].shiftRight(env, args[1]); - case USHIFT_RIGHT: return args[0].unsignedShiftRight(env, args[1]); - - case IN: return BoolValue.of(args[0].hasMember(env, args[1], false)); - case INSTANCEOF: return BoolValue.of(args[0].isInstanceOf(env, args[1].getMember(env, new StringValue("prototype")))); - - default: return null; - } - } - public abstract Member getOwnMember(Environment env, KeyCache key); public abstract Map getOwnMembers(Environment env); public abstract Map getOwnSymbolMembers(Environment env); @@ -475,33 +377,6 @@ public abstract class Value { else return null; } - public abstract boolean strictEquals(Environment env, Value other); - - public final boolean looseEqual(Environment env, Value other) { - var a = this; - var b = other; - - // In loose equality, null is equivalent to undefined - if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue; - - // If both are objects, just compare their references - if (!a.isPrimitive() && !b.isPrimitive()) return a.strictEquals(env, b); - - // Convert values to primitives - a = a.toPrimitive(env); - b = b.toPrimitive(env); - - // Compare symbols by reference - if (a instanceof SymbolValue || b instanceof SymbolValue) return a.strictEquals(env, b); - // Compare booleans as numbers - if (a instanceof BoolValue || b instanceof BoolValue) return a.toNumber(env).strictEquals(env, b.toNumber(env)); - // Comparse numbers as numbers - if (a instanceof NumberValue || b instanceof NumberValue) return a.toNumber(env).strictEquals(env, b.toNumber(env)); - - // Default to strings - return a.toString(env).strictEquals(env, b.toString(env)); - } - public Iterable toIterable(Environment env) { return () -> { if (!(this instanceof FunctionValue)) return Collections.emptyIterator(); @@ -541,19 +416,6 @@ public abstract class Value { }; } - public static FunctionValue fromIterator(Environment ext, Iterable iterable) { - var it = iterable.iterator(); - - return new NativeFunction("", args -> { - var obj = new ObjectValue(); - - if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE)); - else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next())); - - return obj; - }); - } - public void callWith(Environment env, Iterable it) { for (var el : it) { this.call(env, Value.UNDEFINED, el); @@ -656,6 +518,153 @@ public abstract class Value { return toReadable(ext, new HashSet<>(), 0); } + public static final ObjectValue global(Environment env) { + return env.initFrom(GLOBAL, () -> new ObjectValue()); + } + public static final Map intrinsics(Environment env) { + return env.initFrom(INTRINSICS, () -> new HashMap<>()); + } + + public static FunctionValue fromIterator(Environment ext, Iterable iterable) { + var it = iterable.iterator(); + + return new NativeFunction("", args -> { + var obj = new ObjectValue(); + + if (!it.hasNext()) obj.defineOwnMember(args.env, "done", FieldMember.of(BoolValue.TRUE)); + else obj.defineOwnMember(args.env, "value", FieldMember.of(it.next())); + + return obj; + }); + } + + public static final boolean lessOrEqual(Environment env, Value a, Value b) { + a = a.toPrimitive(env); + b = b.toPrimitive(env); + + if (a instanceof StringValue aStr && b instanceof StringValue bStr) { + return aStr.value.compareTo(bStr.value) <= 0; + } + else { + return a.toNumber(env).value <= b.toNumber(env).value; + } + } + public static final boolean greaterOrEqual(Environment env, Value a, Value b) { + a = a.toPrimitive(env); + b = b.toPrimitive(env); + + if (a instanceof StringValue aStr && b instanceof StringValue bStr) { + return aStr.value.compareTo(bStr.value) >= 0; + } + else { + return a.toNumber(env).value >= b.toNumber(env).value; + } + } + public static final boolean less(Environment env, Value a, Value b) { + a = a.toPrimitive(env); + b = b.toPrimitive(env); + + if (a instanceof StringValue aStr && b instanceof StringValue bStr) { + return aStr.value.compareTo(bStr.value) >= 0; + } + else { + return a.toNumber(env).value < b.toNumber(env).value; + } + } + public static final boolean greater(Environment env, Value a, Value b) { + a = a.toPrimitive(env); + b = b.toPrimitive(env); + + if (a instanceof StringValue aStr && b instanceof StringValue bStr) { + return aStr.value.compareTo(bStr.value) >= 0; + } + else { + return a.toNumber(env).value > b.toNumber(env).value; + } + } + + public static final Value add(Environment env, Value a, Value b) { + a = a.toPrimitive(env); + b = b.toPrimitive(env); + + if (a instanceof StringValue || b instanceof StringValue) { + return new StringValue(a.toString(env).value + b.toString(env).value); + } + else { + return new NumberValue(a.toNumber(env).value + b.toNumber(env).value); + } + } + + public static final NumberValue subtract(Environment env, Value a, Value b) { + return new NumberValue(a.toNumber(env).value - b.toNumber(env).value); + } + public static final NumberValue multiply(Environment env, Value a, Value b) { + return new NumberValue(a.toNumber(env).value - b.toNumber(env).value); + } + public static final NumberValue divide(Environment env, Value a, Value b) { + return new NumberValue(a.toNumber(env).value / b.toNumber(env).value); + } + public static final NumberValue modulo(Environment env, Value a, Value b) { + return new NumberValue(a.toNumber(env).value % b.toNumber(env).value); + } + public static final NumberValue negative(Environment env, Value a) { + return new NumberValue(-a.toNumber(env).value); + } + + public static final NumberValue and(Environment env, Value a, Value b) { + return new NumberValue(a.toInt(env) & b.toInt(env)); + } + public static final NumberValue or(Environment env, Value a, Value b) { + return new NumberValue(a.toInt(env) | b.toInt(env)); + } + public static final NumberValue xor(Environment env, Value a, Value b) { + return new NumberValue(a.toInt(env) ^ b.toInt(env)); + } + public static final NumberValue bitwiseNot(Environment env, Value a) { + return new NumberValue(~a.toInt(env)); + } + + public static final NumberValue shiftLeft(Environment env, Value a, Value b) { + return new NumberValue(a.toInt(env) << b.toInt(env)); + } + public static final NumberValue shiftRight(Environment env, Value a, Value b) { + return new NumberValue(a.toInt(env) >> b.toInt(env)); + } + public static final NumberValue unsignedShiftRight(Environment env, Value a, Value b) { + long _a = a.toInt(env); + long _b = b.toInt(env); + + if (_a < 0) _a += 0x100000000l; + if (_b < 0) _b += 0x100000000l; + + return new NumberValue(_a >>> _b); + } + + public static final boolean looseEqual(Environment env, Value a, Value b) { + // In loose equality, null is equivalent to undefined + if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue; + + // If both are objects, just compare their references + if (!a.isPrimitive() && !b.isPrimitive()) return a.equals(b); + + // Convert values to primitives + a = a.toPrimitive(env); + b = b.toPrimitive(env); + + // Compare symbols by reference + if (a instanceof SymbolValue || b instanceof SymbolValue) return a.equals(b); + // Compare booleans as numbers + if (a instanceof BoolValue || b instanceof BoolValue) return a.toNumber(env).equals(b.toNumber(env)); + // Comparse numbers as numbers + if (a instanceof NumberValue || b instanceof NumberValue) return a.toNumber(env).equals(b.toNumber(env)); + + // Default to strings + return a.toString(env).equals(b.toString(env)); + } + + // public static Value operation(Environment env, Operation op, Value ...args) { + // } + public static final String errorToReadable(RuntimeException err, String prefix) { prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix; if (err instanceof EngineException) { @@ -678,11 +687,4 @@ public abstract class Value { return prefix + " internal error " + str.toString(); } } - - public static final ObjectValue global(Environment env) { - return env.initFrom(GLOBAL, () -> new ObjectValue()); - } - public static final Map intrinsics(Environment env) { - return env.initFrom(INTRINSICS, () -> new HashMap<>()); - } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index ca23b3d..335dcdf 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -10,13 +10,12 @@ public final class CodeFunction extends FunctionValue { public final Value[][] captures; public Environment env; - @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { - var frame = new Frame(env, isNew, thisArg, args, this); + private Value onCall(Frame frame) { frame.onPush(); try { while (true) { - var res = frame.next(); + var res = frame.next(null, null, null); if (res != null) return res; } } @@ -25,6 +24,11 @@ public final class CodeFunction extends FunctionValue { } } + @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { + var frame = new Frame(env, isNew, thisArg, args, this); + return onCall(frame); + } + public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { super(name, body.argsN); this.captures = captures; diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java index f1c81b8..da5cba4 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -70,8 +70,6 @@ public class ObjectValue extends Value { @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } @Override public StringValue type() { return typeString; } - @Override public boolean strictEquals(Environment ext, Value other) { return this == other; } - public final void preventExtensions() { extensible = false; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java index a869252..4b567db 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -1,7 +1,6 @@ package me.topchetoeu.jscript.runtime.values.primitives; import me.topchetoeu.jscript.common.environment.Environment; -import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class BoolValue extends PrimitiveValue { @@ -23,8 +22,8 @@ public final class BoolValue extends PrimitiveValue { return env.get(BOOL_PROTO); } - @Override public boolean strictEquals(Environment ext, Value other) { - if (other instanceof BoolValue) return value == ((BoolValue)other).value; + @Override public boolean equals(Object other) { + if (other instanceof BoolValue bool) return value == bool.value; else return false; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java index 09c2029..2fe9265 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java @@ -5,7 +5,6 @@ import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class NumberValue extends PrimitiveValue { @@ -25,13 +24,8 @@ public final class NumberValue extends PrimitiveValue { return env.get(NUMBER_PROTO); } - @Override public CompareResult compare(Environment env, Value other) { - if (other instanceof NumberValue) return CompareResult.from(Double.compare(value, ((NumberValue)other).value)); - else return super.compare(env, other); - } - @Override public boolean strictEquals(Environment ext, Value other) { - other = other.toPrimitive(ext); - if (other instanceof NumberValue) return value == ((NumberValue)other).value; + @Override public boolean equals(Object other) { + if (other instanceof NumberValue val) return value == val.value; else return false; } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index ec87847..8df3f52 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -1,13 +1,11 @@ package me.topchetoeu.jscript.runtime.values.primitives; import java.util.Map; -import java.util.Objects; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.runtime.values.Member; -import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class StringValue extends PrimitiveValue { @@ -27,17 +25,11 @@ public final class StringValue extends PrimitiveValue { } @Override public StringValue toString(Environment ext) { return this; } - @Override public Value add(Environment ext, Value other) { - return new StringValue(value + other.toString(ext).value); + @Override public boolean equals(Object other) { + if (other instanceof StringValue val) return value.length() == val.value.length() && value.equals(val.value); + else return false; } - @Override public CompareResult compare(Environment env, Value other) { - if (other instanceof StringValue) return CompareResult.from(value.compareTo(((StringValue)other).value)); - else return super.compare(env, other); - } - @Override public boolean strictEquals(Environment ext, Value other) { - return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value); - } @Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); } @Override public Map getOwnMembers(Environment env) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java index f6cabf8..e5baeb4 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -27,9 +27,6 @@ public final class SymbolValue extends PrimitiveValue { throw EngineException.ofType("Cannot convert a Symbol value to a number"); } - @Override public boolean strictEquals(Environment ext, Value other) { - return other == this; - } @Override public ObjectValue getPrototype(Environment env) { return env.get(SYMBOL_PROTO); } @Override public String toString() { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java index 5c37052..9e0539d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -6,7 +6,6 @@ import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; -import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class VoidValue extends PrimitiveValue { @@ -20,16 +19,6 @@ public final class VoidValue extends PrimitiveValue { @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } @Override public StringValue toString(Environment ext) { return nameString; } - @Override public Value add(Environment ext, Value other) { - if (!other.isPrimitive()) other = other.toPrimitive(ext); - - if (other instanceof StringValue) return nameString.add(ext, other); - else return NumberValue.NAN; - } - - @Override public boolean strictEquals(Environment ext, Value other) { - return this == other; - } @Override public ObjectValue getPrototype(Environment env) { return null; } @Override public Member getOwnMember(Environment env, KeyCache key) { -- 2.45.2 From 4cbc108686d15281133502252ef21211e34a911a Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:09:52 +0300 Subject: [PATCH 22/48] feat: implement optional arguments --- .../jscript/common/FunctionBody.java | 5 +- .../jscript/compilation/CompileResult.java | 28 ++++++---- .../jscript/compilation/CompoundNode.java | 10 ++-- .../jscript/compilation/FunctionNode.java | 50 +++++++++-------- .../compilation/FunctionStatementNode.java | 4 +- .../compilation/FunctionValueNode.java | 4 +- .../jscript/compilation/JavaScript.java | 53 ++++++++++++------- .../jscript/compilation/LabelContext.java | 10 ++-- .../jscript/compilation/Parameter.java | 15 ++++++ .../jscript/compilation/Parameters.java | 28 ++++++++++ .../compilation/VariableDeclareNode.java | 2 +- .../compilation/control/BreakNode.java | 2 +- .../compilation/control/ContinueNode.java | 2 +- .../compilation/control/SwitchNode.java | 2 +- .../compilation/scope/FunctionScope.java | 2 +- .../compilation/values/ObjectNode.java | 4 +- .../compilation/values/VariableNode.java | 17 +++--- .../me/topchetoeu/jscript/runtime/Frame.java | 9 +++- .../values/functions/CodeFunction.java | 2 +- 19 files changed, 166 insertions(+), 83 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/compilation/Parameter.java create mode 100644 src/java/me/topchetoeu/jscript/compilation/Parameters.java diff --git a/src/java/me/topchetoeu/jscript/common/FunctionBody.java b/src/java/me/topchetoeu/jscript/common/FunctionBody.java index 9d3cab1..3b91614 100644 --- a/src/java/me/topchetoeu/jscript/common/FunctionBody.java +++ b/src/java/me/topchetoeu/jscript/common/FunctionBody.java @@ -3,10 +3,11 @@ package me.topchetoeu.jscript.common; public class FunctionBody { public final FunctionBody[] children; public final Instruction[] instructions; - public final int localsN, capturesN, argsN; + public final int localsN, capturesN, argsN, length; - public FunctionBody(int localsN, int capturesN, int argsN, Instruction[] instructions, FunctionBody[] children) { + public FunctionBody(int localsN, int capturesN, int length, int argsN, Instruction[] instructions, FunctionBody[] children) { this.children = children; + this.length = length; this.argsN = argsN; this.localsN = localsN; this.capturesN = capturesN; diff --git a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java index 59ce8fd..0a78253 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -3,7 +3,7 @@ package me.topchetoeu.jscript.compilation; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; -import java.util.function.Supplier; +import java.util.function.IntFunction; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.Instruction; @@ -16,11 +16,11 @@ import me.topchetoeu.jscript.compilation.scope.LocalScope; import me.topchetoeu.jscript.compilation.scope.Scope; public final class CompileResult { - public final List> instructions; + public final List> instructions; public final List children; public final FunctionMapBuilder map; public final Environment env; - public int length = 0; + public int length, assignN; public final Scope scope; public int temp() { @@ -29,18 +29,18 @@ public final class CompileResult { } public CompileResult add(Instruction instr) { - instructions.add(() -> instr); + instructions.add(i -> instr); return this; } - public CompileResult add(Supplier instr) { + public CompileResult add(IntFunction instr) { instructions.add(instr); return this; } public CompileResult set(int i, Instruction instr) { - instructions.set(i, () -> instr); + instructions.set(i, _i -> instr); return this; } - public CompileResult set(int i, Supplierinstr) { + public CompileResult set(int i, IntFunctioninstr) { instructions.set(i, instr); return this; } @@ -76,7 +76,10 @@ public final class CompileResult { public Instruction[] instructions() { var res = new Instruction[instructions.size()]; var i = 0; - for (var suppl : instructions) res[i++] = suppl.get(); + for (var suppl : instructions) { + res[i] = suppl.apply(i); + i++; + } return res; } @@ -90,10 +93,15 @@ public final class CompileResult { var instrRes = new Instruction[instructions.size()]; var i = 0; - for (var suppl : instructions) instrRes[i++] = suppl.get(); + + for (var suppl : instructions) { + instrRes[i] = suppl.apply(i); + i++; + } return new FunctionBody( - scope.localsCount() + scope.allocCount(), scope.capturesCount(), length, + scope.localsCount() + scope.allocCount(), scope.capturesCount(), + length, assignN, instrRes, builtChildren ); } diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java index a45f58b..0a03de1 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -19,11 +19,11 @@ public class CompoundNode extends Node { for (var stm : statements) stm.resolve(target); } - @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { + public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) { List statements = new ArrayList(); var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount())); for (var stm : this.statements) { if (stm instanceof FunctionStatementNode func) { @@ -42,13 +42,17 @@ public class CompoundNode extends Node { } subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + if (alloc) subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); if (!polluted && pollute) { target.add(Instruction.pushUndefined()); } } + @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { + compile(target, pollute, true, type); + } + public CompoundNode setEnd(Location loc) { this.end = loc; return this; diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java index ed54d94..66c88e4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; @@ -12,7 +13,7 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public abstract class FunctionNode extends Node { public final CompoundNode body; - public final String[] args; + public final Parameters params; public final Location end; public abstract String name(); @@ -39,14 +40,6 @@ public abstract class FunctionNode extends Node { } private CompileResult compileBody(CompileResult target, String name, boolean storeSelf, boolean pollute, BreakpointType bp) { - for (var i = 0; i < args.length; i++) { - for (var j = 0; j < i; j++) { - if (args[i].equals(args[j])) { - throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'."); - } - } - } - var env = target.env.child() .remove(LabelContext.BREAK_CTX) .remove(LabelContext.CONTINUE_CTX); @@ -54,16 +47,31 @@ public abstract class FunctionNode extends Node { var funcScope = new FunctionScope(target.scope); var subtarget = new CompileResult(env, new LocalScope(funcScope)); - for (var arg : args) { + for (var param : params.params) { // TODO: Implement default values // TODO: Implement argument location - funcScope.defineArg(arg, loc()); + if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed"); + var i = funcScope.defineParam(param.name, param.loc); + + if (param.node != null) { + var end = new DeferredIntSupplier(); + + subtarget.add(_i -> Instruction.loadVar(i.index())); + subtarget.add(Instruction.pushUndefined()); + subtarget.add(Instruction.operation(Operation.EQUALS)); + subtarget.add(_i -> Instruction.jmpIfNot(end.getAsInt() - _i)); + param.node.compile(subtarget, pollute); + subtarget.add(_i -> Instruction.storeVar(i.index())); + + end.set(subtarget.size()); + } } body.resolve(subtarget); - body.compile(subtarget, false); + body.compile(subtarget, false, false, BreakpointType.NONE); - subtarget.length = args.length; + subtarget.length = params.length; + subtarget.assignN = params.params.size(); subtarget.scope.end(); funcScope.end(); @@ -88,11 +96,11 @@ public abstract class FunctionNode extends Node { compile(target, pollute, (String)null, BreakpointType.NONE); } - public FunctionNode(Location loc, Location end, String[] args, CompoundNode body) { + public FunctionNode(Location loc, Location end, Parameters params, CompoundNode body) { super(loc); this.end = end; - this.args = args; + this.params = params; this.body = body; } @@ -117,21 +125,21 @@ public abstract class FunctionNode extends Node { n += name.n; n += Parsing.skipEmpty(src, i + n); - var args = JavaScript.parseParamList(src, i + n); - if (!args.isSuccess()) return args.chainError(src.loc(i + n), "Expected a parameter list"); - n += args.n; + var params = JavaScript.parseParameters(src, i + n); + if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected a parameter list"); + n += params.n; var body = CompoundNode.parse(src, i + n); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for function."); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for function"); n += body.n; if (statement) return ParseRes.res(new FunctionStatementNode( loc, src.loc(i + n - 1), - args.result.toArray(String[]::new), body.result, name.result + params.result, body.result, name.result ), n); else return ParseRes.res(new FunctionValueNode( loc, src.loc(i + n - 1), - args.result.toArray(String[]::new), body.result, name.result + params.result, body.result, name.result ), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 1b404f3..183fcd9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -18,8 +18,8 @@ public class FunctionStatementNode extends FunctionNode { target.add(VariableNode.toSet(target, end, this.name, pollute, true)); } - public FunctionStatementNode(Location loc, Location end, String[] args, CompoundNode body, String name) { - super(loc, end, args, body); + public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { + super(loc, end, params, body); this.name = name; } } diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index c89d48d..89f96dd 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -12,8 +12,8 @@ public class FunctionValueNode extends FunctionNode { compile(target, pollute, true, name, bp); } - public FunctionValueNode(Location loc, Location end, String[] args, CompoundNode body, String name) { - super(loc, end, args, body); + public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { + super(loc, end, params, body); this.name = name; } } diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index 96f2bbb..7b58aae 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -1,10 +1,10 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; -import java.util.List; import java.util.Set; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.ParseRes; @@ -212,40 +212,53 @@ public class JavaScript { return ParseRes.failed(); } - public static ParseRes> parseParamList(Source src, int i) { + public static ParseRes parseParameters(Source src, int i) { var n = Parsing.skipEmpty(src, i); var openParen = Parsing.parseOperator(src, i + n, "("); - if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list."); + if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list"); n += openParen.n; - var args = new ArrayList(); + var params = new ArrayList(); var closeParen = Parsing.parseOperator(src, i + n, ")"); n += closeParen.n; if (!closeParen.isSuccess()) { while (true) { - var argRes = Parsing.parseIdentifier(src, i + n); - if (argRes.isSuccess()) { - args.add(argRes.result); - n += argRes.n; - n += Parsing.skipEmpty(src, i); + n += Parsing.skipEmpty(src, i + n); - if (src.is(i + n, ",")) { - n++; - n += Parsing.skipEmpty(src, i + n); - } - if (src.is(i + n, ")")) { - n++; - break; - } + var paramLoc = src.loc(i); + + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an argument or a closing brace"); + n += name.n; + n += Parsing.skipEmpty(src, i + n); + + if (src.is(i + n, "=")) { + n++; + + var val = parseExpression(src, i + n, 2); + if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a default value"); + n += val.n; + n += Parsing.skipEmpty(src, i + n); + + params.add(new Parameter(paramLoc, name.result, val.result)); + } + else params.add(new Parameter(paramLoc, name.result, null)); + + if (src.is(i + n, ",")) { + n++; + n += Parsing.skipEmpty(src, i + n); + } + if (src.is(i + n, ")")) { + n++; + break; } - else return ParseRes.error(src.loc(i + n), "Expected an argument, or a closing brace."); } } - return ParseRes.res(args, n); + return ParseRes.res(new Parameters(params), n); } public static Node[] parse(Environment env, Filename filename, String raw) { @@ -279,7 +292,7 @@ public class JavaScript { try { stm.resolve(target); - stm.compile(target, true); + stm.compile(target, true, false, BreakpointType.NONE); // FunctionNode.checkBreakAndCont(target, 0); } catch (SyntaxException e) { diff --git a/src/java/me/topchetoeu/jscript/compilation/LabelContext.java b/src/java/me/topchetoeu/jscript/compilation/LabelContext.java index 5cfb1a3..0dea994 100644 --- a/src/java/me/topchetoeu/jscript/compilation/LabelContext.java +++ b/src/java/me/topchetoeu/jscript/compilation/LabelContext.java @@ -2,8 +2,8 @@ package me.topchetoeu.jscript.compilation; import java.util.HashMap; import java.util.LinkedList; +import java.util.function.IntFunction; import java.util.function.IntSupplier; -import java.util.function.Supplier; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.environment.Environment; @@ -25,15 +25,15 @@ public class LabelContext { return map.get(name); } - public Supplier getJump(int offset) { + public IntFunction getJump() { var res = get(); if (res == null) return null; - else return () -> Instruction.jmp(res.getAsInt() - offset); + else return i -> Instruction.jmp(res.getAsInt() - i); } - public Supplier getJump(int offset, String name) { + public IntFunction getJump(String name) { var res = get(name); if (res == null) return null; - else return () -> Instruction.jmp(res.getAsInt() - offset); + else return i -> Instruction.jmp(res.getAsInt() - i); } public void push(IntSupplier jumpTarget) { diff --git a/src/java/me/topchetoeu/jscript/compilation/Parameter.java b/src/java/me/topchetoeu/jscript/compilation/Parameter.java new file mode 100644 index 0000000..9404d5d --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/Parameter.java @@ -0,0 +1,15 @@ +package me.topchetoeu.jscript.compilation; + +import me.topchetoeu.jscript.common.parsing.Location; + +public final class Parameter { + public final Location loc; + public final String name; + public final Node node; + + public Parameter(Location loc, String name, Node node) { + this.name = name; + this.node = node; + this.loc = loc; + } +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/compilation/Parameters.java b/src/java/me/topchetoeu/jscript/compilation/Parameters.java new file mode 100644 index 0000000..c104a38 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/Parameters.java @@ -0,0 +1,28 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public final class Parameters { + public final int length; + public final List params; + public final Set names; + + public Parameters(List params) { + this.names = new HashSet<>(); + var len = params.size(); + + for (var i = params.size() - 1; i >= 0; i--) { + if (params.get(i).node == null) break; + len--; + } + + for (var param : params) { + this.names.add(param.name); + } + + this.params = params; + this.length = len; + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 84c7014..69b91f8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -40,7 +40,7 @@ public class VariableDeclareNode extends Node { target.add(VariableNode.toSet(target, entry.location, entry.name, false, true)); } else { - target.add(() -> { + target.add(_i -> { var i = target.scope.get(entry.name, true); if (i == null) return Instruction.globDef(entry.name); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java b/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java index e052b30..b74e2a4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java @@ -15,7 +15,7 @@ public class BreakNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { - var res = LabelContext.getBreak(target.env).getJump(target.size()); + var res = LabelContext.getBreak(target.env).getJump(); if (res == null) { if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); else throw new SyntaxException(loc(), "Illegal break statement"); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java index be09e72..afc94a5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java @@ -15,7 +15,7 @@ public class ContinueNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { - var res = LabelContext.getCont(target.env).getJump(target.size()); + var res = LabelContext.getCont(target.env).getJump(); if (res == null) { if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); else throw new SyntaxException(loc(), "Illegal continue statement"); diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index 311a1a9..f41a997 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -44,7 +44,7 @@ public class SwitchNode extends Node { value.compile(target, true, BreakpointType.STEP_OVER); var subtarget = target.subtarget(); - subtarget.add(() -> Instruction.stackAlloc(subtarget.scope.allocCount())); + subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.allocCount())); // TODO: create a jump map for (var ccase : cases) { diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 5741a63..3e70aa5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -27,7 +27,7 @@ public class FunctionScope extends Scope { else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes"); else return parent.defineStrict(name, readonly, loc); } - public VariableDescriptor defineArg(String name, Location loc) { + public VariableDescriptor defineParam(String name, Location loc) { return specials.add(name, false); } public boolean hasArg(String name) { diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index 48bab87..9382b81 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -97,7 +97,7 @@ public class ObjectNode extends Node { if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); n += name.n; - var params = JavaScript.parseParamList(src, i + n); + var params = JavaScript.parseParameters(src, i + n); if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); n += params.n; @@ -109,7 +109,7 @@ public class ObjectNode extends Node { return ParseRes.res(new ObjProp( name.result, access.result, - new FunctionValueNode(loc, end, params.result.toArray(String[]::new), body.result, access + " " + name.result.toString()) + new FunctionValueNode(loc, end, params.result, body.result, access + " " + name.result.toString()) ), n); } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index bea5355..1ced342 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.compilation.values; +import java.util.function.IntFunction; import java.util.function.Supplier; import me.topchetoeu.jscript.common.Instruction; @@ -33,7 +34,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); if (i == null) { - target.add((Supplier)() -> { + target.add(_i -> { if (target.scope.has(name)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name)); return Instruction.globGet(name); }); @@ -45,28 +46,28 @@ public class VariableNode extends Node implements AssignableNode { } } - public static Supplier toGet(CompileResult target, Location loc, String name, Supplier onGlobal) { + public static IntFunction toGet(CompileResult target, Location loc, String name, Supplier onGlobal) { var i = target.scope.get(name, true); - if (i == null) return () -> { + if (i == null) return _i -> { if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); else return onGlobal.get(); }; - else return () -> Instruction.loadVar(i.index()); + else return _i -> Instruction.loadVar(i.index()); } - public static Supplier toGet(CompileResult target, Location loc, String name) { + public static IntFunction toGet(CompileResult target, Location loc, String name) { return toGet(target, loc, name, () -> Instruction.globGet(name)); } - public static Supplier toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { + public static IntFunction toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { var i = target.scope.get(name, true); - if (i == null) return () -> { + if (i == null) return _i -> { if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); else return Instruction.globSet(name, keep, define); }; - else return () -> Instruction.storeVar(i.index(), keep); + else return _i -> Instruction.storeVar(i.index(), keep); } diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index c979933..f91ba76 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -420,11 +420,16 @@ public final class Frame { this.argsVal = new ArgumentsValue(this, args); this.captures = func.captures; - for (var i = 0; i < func.body.argsN; i++) { + var i = 0; + + for (; i < func.body.argsN && i < args.length; i++) { this.locals.add(new Value[] { args[i] }); } + for (; i < args.length; i++) { + this.locals.add(new Value[] { Value.UNDEFINED }); + } - for (var i = 0; i < func.body.localsN; i++) { + for (i = 0; i < func.body.localsN; i++) { this.locals.add(new Value[] { Value.UNDEFINED }); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index 335dcdf..6eb5aa6 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -30,7 +30,7 @@ public final class CodeFunction extends FunctionValue { } public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { - super(name, body.argsN); + super(name, body.length); this.captures = captures; this.env = env; this.body = body; -- 2.45.2 From 506726fd763fb1ca2e6acf2a087a938226bee9fc Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:36:25 +0300 Subject: [PATCH 23/48] feat: implement ES6 variables and rest args --- .../jscript/common/FunctionBody.java | 5 +- .../jscript/common/Instruction.java | 72 +++++++++++++++--- .../jscript/compilation/CompoundNode.java | 8 +- .../compilation/FunctionArrowNode.java | 73 ++++++++++++++++++ .../jscript/compilation/FunctionNode.java | 75 +++++++++++++------ .../compilation/FunctionStatementNode.java | 2 +- .../compilation/FunctionValueNode.java | 2 +- .../jscript/compilation/JavaScript.java | 70 +++++++++++++---- .../jscript/compilation/Parameters.java | 19 ++--- .../compilation/VariableDeclareNode.java | 40 ++++++---- .../compilation/control/ForInNode.java | 28 +++---- .../jscript/compilation/control/ForNode.java | 31 ++++---- .../compilation/control/ForOfNode.java | 35 ++++----- .../compilation/values/VariableNode.java | 4 +- 14 files changed, 331 insertions(+), 133 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java diff --git a/src/java/me/topchetoeu/jscript/common/FunctionBody.java b/src/java/me/topchetoeu/jscript/common/FunctionBody.java index 3b91614..f1e2033 100644 --- a/src/java/me/topchetoeu/jscript/common/FunctionBody.java +++ b/src/java/me/topchetoeu/jscript/common/FunctionBody.java @@ -3,12 +3,11 @@ package me.topchetoeu.jscript.common; public class FunctionBody { public final FunctionBody[] children; public final Instruction[] instructions; - public final int localsN, capturesN, argsN, length; + public final int localsN, capturesN, length; - public FunctionBody(int localsN, int capturesN, int length, int argsN, Instruction[] instructions, FunctionBody[] children) { + public FunctionBody(int localsN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) { this.children = children; this.length = length; - this.argsN = argsN; this.localsN = localsN; this.capturesN = capturesN; this.instructions = instructions; diff --git a/src/java/me/topchetoeu/jscript/common/Instruction.java b/src/java/me/topchetoeu/jscript/common/Instruction.java index 2e7f5b2..d8354be 100644 --- a/src/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/java/me/topchetoeu/jscript/common/Instruction.java @@ -1,16 +1,15 @@ package me.topchetoeu.jscript.common; -// import java.io.DataInputStream; -// import java.io.DataOutputStream; -// import java.io.IOException; import java.util.HashMap; +import java.util.function.IntFunction; +import java.util.function.IntSupplier; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class Instruction { public static enum Type { - NOP(0x00), - RETURN(0x01), + RETURN(0x00), + NOP(0x01), THROW(0x02), THROW_SYNTAX(0x03), DELETE(0x04), @@ -41,10 +40,18 @@ public class Instruction { LOAD_VAR(0x40), LOAD_MEMBER(0x41), - LOAD_ARGS(0x42), - LOAD_THIS(0x43), + LOAD_MEMBER_INT(0x42), + LOAD_MEMBER_STR(0x43), + + LOAD_ARGS(0x44), + LOAD_REST_ARGS(0x45), + LOAD_CALLEE(0x46), + LOAD_THIS(0x47), + STORE_VAR(0x48), STORE_MEMBER(0x49), + STORE_MEMBER_INT(0x4A), + STORE_MEMBER_STR(0x4B), DEF_PROP(0x50), KEYS(0x51), @@ -286,6 +293,7 @@ public class Instruction { public static Instruction callNew(int argn) { return new Instruction(Type.CALL_NEW, argn, ""); } + public static Instruction jmp(int offset) { return new Instruction(Type.JMP, offset); } @@ -296,6 +304,17 @@ public class Instruction { return new Instruction(Type.JMP_IFN, offset); } + public static IntFunction jmp(IntSupplier pos) { + return i -> new Instruction(Type.JMP, pos.getAsInt() - i); + } + public static IntFunction jmpIf(IntSupplier pos) { + return i -> new Instruction(Type.JMP_IF, pos.getAsInt() - i); + } + public static IntFunction jmpIfNot(IntSupplier pos) { + return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i); + } + + public static Instruction pushUndefined() { return new Instruction(Type.PUSH_UNDEFINED); } @@ -313,7 +332,7 @@ public class Instruction { } public static Instruction globDef(String name) { - return new Instruction(Type.GLOB_GET, name); + return new Instruction(Type.GLOB_DEF, name); } public static Instruction globGet(String name) { @@ -332,6 +351,12 @@ public class Instruction { public static Instruction loadArgs() { return new Instruction(Type.LOAD_ARGS); } + public static Instruction loadRestArgs(int offset) { + return new Instruction(Type.LOAD_REST_ARGS, offset); + } + public static Instruction loadCallee() { + return new Instruction(Type.LOAD_CALLEE); + } public static Instruction loadGlob() { return new Instruction(Type.LOAD_GLOB); } @@ -341,17 +366,26 @@ public class Instruction { public static Instruction loadMember() { return new Instruction(Type.LOAD_MEMBER); } + public static Instruction loadMember(int member) { + return new Instruction(Type.LOAD_MEMBER_INT, member); + } + public static Instruction loadMember(String member) { + return new Instruction(Type.LOAD_MEMBER_STR, member); + } public static Instruction loadRegex(String pattern, String flags) { return new Instruction(Type.LOAD_REGEX, pattern, flags); } - public static Instruction loadFunc(int id, String name, int[] captures) { + public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, String name, int[] captures) { if (name == null) name = ""; - var args = new Object[2 + captures.length]; + var args = new Object[5 + captures.length]; args[0] = id; args[1] = name; - for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i]; + args[2] = callable; + args[3] = constructible; + args[4] = captureThis; + for (var i = 0; i < captures.length; i++) args[i + 5] = captures[i]; return new Instruction(Type.LOAD_FUNC, args); } public static Instruction loadObj() { @@ -373,12 +407,28 @@ public class Instruction { public static Instruction storeVar(int i, boolean keep) { return new Instruction(Type.STORE_VAR, i, keep); } + public static Instruction storeMember() { return new Instruction(Type.STORE_MEMBER, false); } public static Instruction storeMember(boolean keep) { return new Instruction(Type.STORE_MEMBER, keep); } + + public static Instruction storeMember(String key) { + return new Instruction(Type.STORE_MEMBER_STR, key, false); + } + public static Instruction storeMember(String key, boolean keep) { + return new Instruction(Type.STORE_MEMBER_STR, key, keep); + } + + public static Instruction storeMember(int key) { + return new Instruction(Type.STORE_MEMBER_INT, key, false); + } + public static Instruction storeMember(int key, boolean keep) { + return new Instruction(Type.STORE_MEMBER_STR, key, keep); + } + public static Instruction discard() { return new Instruction(Type.DISCARD); } diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java index 0a03de1..19466c4 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -22,7 +22,7 @@ public class CompoundNode extends Node { public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) { List statements = new ArrayList(); - var subtarget = target.subtarget(); + var subtarget = alloc ? target.subtarget() : target; if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount())); for (var stm : this.statements) { @@ -41,8 +41,10 @@ public class CompoundNode extends Node { else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER); } - subtarget.scope.end(); - if (alloc) subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + if (alloc) { + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + } if (!polluted && pollute) { target.add(Instruction.pushUndefined()); diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java new file mode 100644 index 0000000..e6f72fa --- /dev/null +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -0,0 +1,73 @@ +package me.topchetoeu.jscript.compilation; + +import java.util.List; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.control.ReturnNode; + +public class FunctionArrowNode extends FunctionNode { + @Override public String name() { return null; } + + @Override protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { + target.add(Instruction.loadFunc(id, true, false, true, null, captures)); + } + + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { + compile(target, pollute, false, name, null, bp); + } + + public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) { + super(loc, end, params, expToBody(body)); + } + + private static final CompoundNode expToBody(Node node) { + if (node instanceof CompoundNode res) return res; + else return new CompoundNode(node.loc(), new ReturnNode(node.loc(), node)); + } + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + Parameters params; + + if (src.is(i + n, "(")) { + var paramsRes = JavaScript.parseParameters(src, i + n); + if (!paramsRes.isSuccess()) return paramsRes.chainError(); + n += paramsRes.n; + n += Parsing.skipEmpty(src, i + n); + + params = paramsRes.result; + } + else { + var singleParam = Parsing.parseIdentifier(src, i + n); + if (!singleParam.isSuccess()) return ParseRes.failed(); + + var paramLoc = src.loc(i + n); + n += singleParam.n; + n += Parsing.skipEmpty(src, i + n); + + params = new Parameters(List.of(new Parameter(paramLoc, singleParam.result, null))); + } + + if (!src.is(i + n, "=>")) return ParseRes.failed(); + n += 2; + + ParseRes body = ParseRes.first(src, i + n, + (s, j) -> JavaScript.parseExpression(s, j, 2), + CompoundNode::parse + ); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected an expression or a compount statement after '=>'"); + n += body.n; + + return ParseRes.res(new FunctionArrowNode( + loc, src.loc(i + n - 1), + params, body.result + ), n); + } +} diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 66c88e4..d725326 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -35,11 +35,11 @@ public abstract class FunctionNode extends Node { // } // } - protected void compileLoadFunc(CompileResult target, int[] captures, String name) { - target.add(Instruction.loadFunc(target.children.size(), name, captures)); + protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { + target.add(Instruction.loadFunc(id, true, true, false, name, captures)); } - private CompileResult compileBody(CompileResult target, String name, boolean storeSelf, boolean pollute, BreakpointType bp) { + private CompileResult compileBody(CompileResult target, boolean hasArgs, String name, String selfName, boolean pollute, BreakpointType bp) { var env = target.env.child() .remove(LabelContext.BREAK_CTX) .remove(LabelContext.CONTINUE_CTX); @@ -47,43 +47,74 @@ public abstract class FunctionNode extends Node { var funcScope = new FunctionScope(target.scope); var subtarget = new CompileResult(env, new LocalScope(funcScope)); - for (var param : params.params) { - // TODO: Implement default values - // TODO: Implement argument location - if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed"); - var i = funcScope.defineParam(param.name, param.loc); + subtarget.length = params.params.size(); - if (param.node != null) { - var end = new DeferredIntSupplier(); + if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs()); - subtarget.add(_i -> Instruction.loadVar(i.index())); - subtarget.add(Instruction.pushUndefined()); - subtarget.add(Instruction.operation(Operation.EQUALS)); - subtarget.add(_i -> Instruction.jmpIfNot(end.getAsInt() - _i)); - param.node.compile(subtarget, pollute); - subtarget.add(_i -> Instruction.storeVar(i.index())); + if (hasArgs) { + var argsVar = funcScope.defineParam("arguments", true, loc()); + subtarget.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); + } - end.set(subtarget.size()); + if (params.params.size() > 0) { + if (params.params.size() > 1) subtarget.add(Instruction.dup(params.params.size() - 1)); + var i = 0; + + for (var param : params.params) { + if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed"); + if (!JavaScript.checkVarName(param.name)) { + throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name)); + } + var varI = funcScope.defineParam(param.name, false, param.loc); + + subtarget.add(Instruction.loadMember(i++)); + + if (param.node != null) { + var end = new DeferredIntSupplier(); + + subtarget.add(Instruction.dup()); + subtarget.add(Instruction.pushUndefined()); + subtarget.add(Instruction.operation(Operation.EQUALS)); + subtarget.add(Instruction.jmpIfNot(end)); + subtarget.add(Instruction.discard()); + param.node.compile(subtarget, pollute); + + end.set(subtarget.size()); + } + + subtarget.add(Instruction.storeVar(varI.index())); } } + if (params.restName != null) { + if (funcScope.hasArg(params.restName)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed"); + var restVar = funcScope.defineParam(params.restName, true, params.restLocation); + subtarget.add(Instruction.loadRestArgs(params.params.size())); + subtarget.add(_i -> Instruction.storeVar(restVar.index())); + } + + if (selfName != null && !funcScope.hasArg(name)) { + var i = funcScope.defineParam(selfName, true, end); + + subtarget.add(Instruction.loadCallee()); + subtarget.add(_i -> Instruction.storeVar(i.index(), false)); + } + body.resolve(subtarget); body.compile(subtarget, false, false, BreakpointType.NONE); - subtarget.length = params.length; - subtarget.assignN = params.params.size(); subtarget.scope.end(); funcScope.end(); - if (pollute) compileLoadFunc(target, funcScope.getCaptureIndices(), name); + if (pollute) compileLoadFunc(target, target.children.size(), funcScope.getCaptureIndices(), name); return target.addChild(subtarget); } - public void compile(CompileResult target, boolean pollute, boolean storeSelf, String name, BreakpointType bp) { + public void compile(CompileResult target, boolean pollute, boolean hasArgs, String name, String selfName, BreakpointType bp) { if (this.name() != null) name = this.name(); - compileBody(target, name, storeSelf, pollute, bp); + compileBody(target, hasArgs, name, selfName, pollute, bp); } public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); public void compile(CompileResult target, boolean pollute, String name) { diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 183fcd9..9d0398a 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -14,7 +14,7 @@ public class FunctionStatementNode extends FunctionNode { } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - compile(target, true, false, this.name, bp); + compile(target, true, true, name, this.name, bp); target.add(VariableNode.toSet(target, end, this.name, pollute, true)); } diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index 89f96dd..cfe0bf8 100644 --- a/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -9,7 +9,7 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - compile(target, pollute, true, name, bp); + compile(target, pollute, true, name, null, bp); } public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java index 7b58aae..c5574d6 100644 --- a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -26,9 +26,7 @@ import me.topchetoeu.jscript.compilation.control.TryNode; import me.topchetoeu.jscript.compilation.control.WhileNode; import me.topchetoeu.jscript.compilation.scope.GlobalScope; import me.topchetoeu.jscript.compilation.scope.LocalScope; -import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; -import me.topchetoeu.jscript.compilation.values.GlobalThisNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; import me.topchetoeu.jscript.compilation.values.ThisNode; @@ -45,7 +43,20 @@ import me.topchetoeu.jscript.compilation.values.operations.OperationNode; import me.topchetoeu.jscript.compilation.values.operations.TypeofNode; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -public class JavaScript { +public final class JavaScript { + public static enum DeclarationType { + VAR(false, false), + CONST(true, true), + LET(true, false); + + public final boolean strict, readonly; + + private DeclarationType(boolean strict, boolean readonly) { + this.strict = strict; + this.readonly = readonly; + } + } + static final Set reserved = Set.of( "true", "false", "void", "null", "this", "if", "else", "try", "catch", "finally", "for", "do", "while", "switch", "case", "default", "new", @@ -84,6 +95,7 @@ public class JavaScript { ChangeNode::parsePrefixIncrease, OperationNode::parsePrefix, ArrayNode::parse, + FunctionArrowNode::parse, JavaScript::parseParens, CallNode::parseNew, TypeofNode::parse, @@ -103,11 +115,11 @@ public class JavaScript { if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), n); if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); - if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n); + // if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); - if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); - if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); + // if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); + // if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); return ParseRes.failed(); } @@ -228,10 +240,25 @@ public class JavaScript { while (true) { n += Parsing.skipEmpty(src, i + n); + if (src.is(i + n, "...")) { + n += 3; + var restLoc = src.loc(i); + + var restName = Parsing.parseIdentifier(src, i + n); + if (!restName.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a rest parameter"); + n += restName.n; + n += Parsing.skipEmpty(src, i + n); + + if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected an end of parameters list after rest parameter"); + n++; + + return ParseRes.res(new Parameters(params, restName.result, restLoc), n); + } + var paramLoc = src.loc(i); var name = Parsing.parseIdentifier(src, i + n); - if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an argument or a closing brace"); + if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace"); n += name.n; n += Parsing.skipEmpty(src, i + n); @@ -239,7 +266,7 @@ public class JavaScript { n++; var val = parseExpression(src, i + n, 2); - if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a default value"); + if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter default value"); n += val.n; n += Parsing.skipEmpty(src, i + n); @@ -251,6 +278,7 @@ public class JavaScript { n++; n += Parsing.skipEmpty(src, i + n); } + if (src.is(i + n, ")")) { n++; break; @@ -261,6 +289,17 @@ public class JavaScript { return ParseRes.res(new Parameters(params), n); } + public static ParseRes parseDeclarationType(Source src, int i) { + var res = Parsing.parseIdentifier(src, i); + if (!res.isSuccess()) return res.chainError(); + + if (res.result.equals("var")) return ParseRes.res(DeclarationType.VAR, res.n); + if (res.result.equals("let")) return ParseRes.res(DeclarationType.LET, res.n); + if (res.result.equals("const")) return ParseRes.res(DeclarationType.CONST, res.n); + + return ParseRes.failed(); + } + public static Node[] parse(Environment env, Filename filename, String raw) { var src = new Source(env, filename, raw); var list = new ArrayList(); @@ -289,17 +328,20 @@ public class JavaScript { public static CompileResult compile(Environment env, Node ...statements) { var target = new CompileResult(env, new LocalScope(new GlobalScope())); var stm = new CompoundNode(null, statements); + var argsI = target.scope.defineStrict("arguments", true, null); + target.add(Instruction.loadArgs()); + target.add(_i -> Instruction.storeVar(argsI.index())); - try { + // try { stm.resolve(target); stm.compile(target, true, false, BreakpointType.NONE); // FunctionNode.checkBreakAndCont(target, 0); - } - catch (SyntaxException e) { - target = new CompileResult(env, new LocalScope(new GlobalScope())); + // } + // catch (SyntaxException e) { + // target = new CompileResult(env, new LocalScope(new GlobalScope())); - target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); - } + // target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); + // } return target; } diff --git a/src/java/me/topchetoeu/jscript/compilation/Parameters.java b/src/java/me/topchetoeu/jscript/compilation/Parameters.java index c104a38..307170f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/Parameters.java +++ b/src/java/me/topchetoeu/jscript/compilation/Parameters.java @@ -1,16 +1,16 @@ package me.topchetoeu.jscript.compilation; -import java.util.HashSet; import java.util.List; -import java.util.Set; + +import me.topchetoeu.jscript.common.parsing.Location; public final class Parameters { public final int length; public final List params; - public final Set names; + public final String restName; + public final Location restLocation; - public Parameters(List params) { - this.names = new HashSet<>(); + public Parameters(List params, String restName, Location restLocation) { var len = params.size(); for (var i = params.size() - 1; i >= 0; i--) { @@ -18,11 +18,12 @@ public final class Parameters { len--; } - for (var param : params) { - this.names.add(param.name); - } - this.params = params; this.length = len; + this.restName = restName; + this.restLocation = restLocation; + } + public Parameters(List params) { + this(params, null, null); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 69b91f8..24ec36b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -9,6 +9,7 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.values.VariableNode; public class VariableDeclareNode extends Node { @@ -25,51 +26,56 @@ public class VariableDeclareNode extends Node { } public final List values; + public final DeclarationType declType; @Override public void resolve(CompileResult target) { - for (var entry : values) { - target.scope.define(entry.name, false, entry.location); + if (!declType.strict) { + for (var entry : values) { + target.scope.define(entry.name, false, entry.location); + } } } + // let a = 10, b = "test"; var c = () => a + b @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { if (entry.name == null) continue; + if (declType.strict) target.scope.defineStrict(entry.name, declType.readonly, entry.location); if (entry.value != null) { FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER); target.add(VariableNode.toSet(target, entry.location, entry.name, false, true)); } - else { - target.add(_i -> { - var i = target.scope.get(entry.name, true); + else target.add(_i -> { + var i = target.scope.get(entry.name, true); - if (i == null) return Instruction.globDef(entry.name); - else return Instruction.nop(); - }); - } + if (i == null) return Instruction.globDef(entry.name); + else return Instruction.nop(); + }); } if (pollute) target.add(Instruction.pushUndefined()); } - public VariableDeclareNode(Location loc, List values) { + public VariableDeclareNode(Location loc, DeclarationType declType, List values) { super(loc); this.values = values; + this.declType = declType; } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - if (!Parsing.isIdentifier(src, i + n, "var")) return ParseRes.failed(); - n += 3; + var declType = JavaScript.parseDeclarationType(src, i + n); + if (!declType.isSuccess()) return declType.chainError(); + n += declType.n; var res = new ArrayList(); var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new VariableDeclareNode(loc, res), n); + return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n); } while (true) { @@ -83,6 +89,7 @@ public class VariableDeclareNode extends Node { } Node val = null; + var endN = n; n += Parsing.skipEmpty(src, i + n); if (src.is(i + n, "=")) { @@ -92,6 +99,7 @@ public class VariableDeclareNode extends Node { if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='"); n += valRes.n; + endN = n; n += Parsing.skipEmpty(src, i + n); val = valRes.result; } @@ -103,11 +111,11 @@ public class VariableDeclareNode extends Node { continue; } - end = JavaScript.parseStatementEnd(src, i + n); + end = JavaScript.parseStatementEnd(src, i + endN); if (end.isSuccess()) { - n += end.n; - return ParseRes.res(new VariableDeclareNode(loc, res), n); + n += end.n + endN - n; + return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n); } else return end.chainError(src.loc(i + n), "Expected a comma or end of statement"); } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index e2c2735..4a8e4d5 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -12,21 +12,24 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForInNode extends Node { public final String varName; - public final boolean isDeclaration; + public final DeclarationType declType; public final Node object, body; public final String label; public final Location varLocation; @Override public void resolve(CompileResult target) { body.resolve(target); - if (isDeclaration) target.scope.define(varName, false, loc()); + if (declType != null && !declType.strict) target.scope.define(varName, false, loc()); } @Override public void compile(CompileResult target, boolean pollute) { + if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation); + object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(true)); @@ -36,9 +39,8 @@ public class ForInNode extends Node { target.add(Instruction.operation(Operation.EQUALS)); int mid = target.temp(); - target.add(Instruction.pushValue("value")).setLocation(varLocation); - target.add(Instruction.loadMember()).setLocation(varLocation); - target.add(VariableNode.toSet(target, loc(), varName, pollute, isDeclaration)); + target.add(Instruction.loadMember("value")).setLocation(varLocation); + target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict)); target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); var end = new DeferredIntSupplier(); @@ -49,19 +51,17 @@ public class ForInNode extends Node { int endI = target.size(); - // WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); - target.add(Instruction.jmp(start - endI)); target.add(Instruction.discard()); target.set(mid, Instruction.jmpIf(endI - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } - public ForInNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) { + public ForInNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) { super(loc); this.varLocation = varLocation; this.label = label; - this.isDeclaration = isDecl; + this.declType = declType; this.varName = varName; this.object = object; this.body = body; @@ -83,12 +83,8 @@ public class ForInNode extends Node { n++; n += Parsing.skipEmpty(src, i + n); - var isDecl = false; - - if (Parsing.isIdentifier(src, i + n, "var")) { - isDecl = true; - n += 3; - } + var declType = JavaScript.parseDeclarationType(src, i + n); + n += declType.n; var name = Parsing.parseIdentifier(src, i + n); if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop"); @@ -111,6 +107,6 @@ public class ForInNode extends Node { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; - return ParseRes.res(new ForInNode(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForInNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java index 8a7c3d3..e0d125c 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -23,28 +23,31 @@ public class ForNode extends Node { body.resolve(target); } @Override public void compile(CompileResult target, boolean pollute) { - declaration.compile(target, false, BreakpointType.STEP_OVER); + var subtarget = target.subtarget(); + subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount())); - int start = target.size(); - condition.compile(target, true, BreakpointType.STEP_OVER); - int mid = target.temp(); + declaration.compile(subtarget, false, BreakpointType.STEP_OVER); + + int start = subtarget.size(); + condition.compile(subtarget, true, BreakpointType.STEP_OVER); + int mid = subtarget.temp(); var end = new DeferredIntSupplier(); - LabelContext.pushLoop(target.env, loc(), label, end, start); - body.compile(target, false, BreakpointType.STEP_OVER); - LabelContext.popLoop(target.env, label); + LabelContext.pushLoop(subtarget.env, loc(), label, end, start); + body.compile(subtarget, false, BreakpointType.STEP_OVER); + LabelContext.popLoop(subtarget.env, label); - // int beforeAssign = target.size(); - assignment.compile(target, false, BreakpointType.STEP_OVER); - int endI = target.size(); + assignment.compile(subtarget, false, BreakpointType.STEP_OVER); + int endI = subtarget.size(); end.set(endI); - // WhileNode.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1); + subtarget.add(Instruction.jmp(start - endI)); + subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1)); + if (pollute) subtarget.add(Instruction.pushUndefined()); - target.add(Instruction.jmp(start - endI)); - target.set(mid, Instruction.jmpIfNot(endI - mid + 1)); - if (pollute) target.add(Instruction.pushUndefined()); + subtarget.scope.end(); + subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); } public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) { diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index 87ffb45..6cc1aa9 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -11,42 +11,41 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForOfNode extends Node { public final String varName; - public final boolean isDeclaration; + public final DeclarationType declType; public final Node iterable, body; public final String label; public final Location varLocation; @Override public void resolve(CompileResult target) { body.resolve(target); - if (isDeclaration) target.scope.define(varName, false, varLocation); + if (declType != null && !declType.strict) target.scope.define(varName, false, varLocation); } @Override public void compile(CompileResult target, boolean pollute) { + if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation); + iterable.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.dup()); target.add(Instruction.loadIntrinsics("it_key")); target.add(Instruction.loadMember()).setLocation(iterable.loc()); - target.add(Instruction.loadMember()).setLocation(iterable.loc()); target.add(Instruction.call(0)).setLocation(iterable.loc()); int start = target.size(); target.add(Instruction.dup()); target.add(Instruction.dup()); - target.add(Instruction.pushValue("next")); - target.add(Instruction.loadMember()).setLocation(iterable.loc()); + target.add(Instruction.loadMember("next")).setLocation(iterable.loc()); target.add(Instruction.call(0)).setLocation(iterable.loc()); target.add(Instruction.dup()); - target.add(Instruction.pushValue("done")); - target.add(Instruction.loadMember()).setLocation(iterable.loc()); + target.add(Instruction.loadMember("done")).setLocation(iterable.loc()); int mid = target.temp(); - target.add(Instruction.pushValue("value")); - target.add(Instruction.loadMember()).setLocation(varLocation); - target.add(VariableNode.toSet(target, varLocation, varName, false, isDeclaration)); + target.add(Instruction.loadMember("value")).setLocation(varLocation); + target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict)); var end = new DeferredIntSupplier(); @@ -57,8 +56,6 @@ public class ForOfNode extends Node { int endI = target.size(); end.set(endI); - // WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1); - target.add(Instruction.jmp(start - endI)); target.add(Instruction.discard()); target.add(Instruction.discard()); @@ -66,11 +63,11 @@ public class ForOfNode extends Node { if (pollute) target.add(Instruction.pushUndefined()); } - public ForOfNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) { + public ForOfNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) { super(loc); this.varLocation = varLocation; this.label = label; - this.isDeclaration = isDecl; + this.declType = declType; this.varName = varName; this.iterable = object; this.body = body; @@ -92,12 +89,8 @@ public class ForOfNode extends Node { n++; n += Parsing.skipEmpty(src, i + n); - var isDecl = false; - - if (Parsing.isIdentifier(src, i + n, "var")) { - isDecl = true; - n += 3; - } + var declType = JavaScript.parseDeclarationType(src, i + n); + n += declType.n; var name = Parsing.parseIdentifier(src, i + n); if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop"); @@ -120,6 +113,6 @@ public class ForOfNode extends Node { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); n += bodyRes.n; - return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, isDecl, name.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 1ced342..bca18a2 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -42,7 +42,7 @@ public class VariableNode extends Node implements AssignableNode { if (!pollute) target.add(Instruction.discard()); } else if (pollute) { - target.add(Instruction.loadVar(i.index())); + target.add(_i -> Instruction.loadVar(i.index())); } } @@ -67,8 +67,8 @@ public class VariableNode extends Node implements AssignableNode { if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); else return Instruction.globSet(name, keep, define); }; + else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable")); else return _i -> Instruction.storeVar(i.index(), keep); - } public VariableNode(Location loc, String name) { -- 2.45.2 From e3f1bc0949b8fb62e0e9057437ead188c90d9d26 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:36:48 +0300 Subject: [PATCH 24/48] fix: incorrect compilation of if-else --- src/java/me/topchetoeu/jscript/compilation/control/IfNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java index d6a07f5..ef6546b 100644 --- a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -46,7 +46,7 @@ public class IfNode extends Node { int mid = target.temp(); - body.compile(target, false, BreakpointType.STEP_OVER); + elseBody.compile(target, false, BreakpointType.STEP_OVER); LabelContext.getBreak(target.env).pop(label); int endI = target.size(); -- 2.45.2 From 3f25868f198c34d33ce2909b6a954aa37cf43bc5 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:38:16 +0300 Subject: [PATCH 25/48] fix: some scope bug fixes --- .../compilation/scope/FunctionScope.java | 21 +++++++------------ .../jscript/compilation/scope/LocalScope.java | 5 ++++- .../compilation/scope/VariableList.java | 4 +--- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 3e70aa5..d20a277 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -27,19 +27,17 @@ public class FunctionScope extends Scope { else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes"); else return parent.defineStrict(name, readonly, loc); } - public VariableDescriptor defineParam(String name, Location loc) { - return specials.add(name, false); + public VariableDescriptor defineParam(String name, boolean readonly, Location loc) { + return specials.add(name, readonly); } public boolean hasArg(String name) { return specials.has(name); } - public VariableDescriptor get(String name, boolean capture, boolean skipSelf) { - if (!skipSelf) { - if (specials.has(name)) return specials.get(name); - if (locals.has(name)) return locals.get(name); - if (captures.has(name)) return captures.get(name); - } + @Override public VariableDescriptor get(String name, boolean capture) { + if (specials.has(name)) return specials.get(name); + if (locals.has(name)) return locals.get(name); + if (captures.has(name)) return captures.get(name); var parentVar = parent.get(name, true); if (parentVar == null) return null; @@ -51,10 +49,6 @@ public class FunctionScope extends Scope { return childVar; } - @Override public VariableDescriptor get(String name, boolean capture) { - return get(name, capture, false); - } - @Override public boolean has(String name) { if (specials.has(name)) return true; if (locals.has(name)) return true; @@ -83,7 +77,7 @@ public class FunctionScope extends Scope { } public int offset() { - return captures.size() + locals.size(); + return specials.size() + locals.size(); } public int[] getCaptureIndices() { @@ -93,6 +87,7 @@ public class FunctionScope extends Scope { for (var el : captures) { assert childToParent.containsKey(el); res[i] = childToParent.get(el).index(); + i++; } return res; diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java index c348168..71599f7 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java @@ -3,7 +3,10 @@ package me.topchetoeu.jscript.compilation.scope; import me.topchetoeu.jscript.common.parsing.Location; public final class LocalScope extends Scope { - private final VariableList locals = new VariableList(); + private final VariableList locals = new VariableList(() -> { + if (parent != null) return parent.offset(); + else return 0; + }); @Override public int offset() { if (parent != null) return parent.offset() + locals.size(); diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java index dc583af..036c313 100644 --- a/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -38,13 +38,11 @@ public final class VariableList implements Iterable { public ListVar freeze() { if (frozen) return this; this.frozen = true; - + this.next = null; if (prev == null) return this; - assert prev.frozen; this.index = prev.index + 1; this.next = null; - this.prev = null; return this; } -- 2.45.2 From 78d233a6bdd4b104dc1b8372dfd9c356054c31a9 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:39:11 +0300 Subject: [PATCH 26/48] refactor: remove ArgumentsNode --- .../compilation/values/ArgumentsNode.java | 17 ----------------- .../compilation/values/operations/CallNode.java | 10 ---------- 2 files changed, 27 deletions(-) delete mode 100644 src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java deleted file mode 100644 index 104340f..0000000 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Node; - - -public class ArgumentsNode extends Node { - @Override public void compile(CompileResult target, boolean pollute) { - if (pollute) target.add(Instruction.loadArgs()); - } - - public ArgumentsNode(Location loc) { - super(loc); - } -} diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index bdd3b74..91dbc78 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -13,7 +13,6 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.ThisNode; @@ -56,15 +55,6 @@ public class CallNode extends Node { else if (func instanceof ThisNode) { res = "this"; } - else if (func instanceof ArgumentsNode) { - res = "arguments"; - } - // else if (func instanceof VariableIndexNode) { - // var i = ((VariableIndexNode)func).index; - - // if (i == 0) res = "this"; - // else if (i == 1) res = "arguments"; - // } else if (func instanceof ArrayNode) { var els = new ArrayList(); -- 2.45.2 From 2a5f6aa9aa7fecee8c547cb7030afe72c5d211eb Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:41:17 +0300 Subject: [PATCH 27/48] fix: use _STR and _INT variants of member instructions --- .../values/operations/IndexAssignNode.java | 48 +++++++++++++++---- .../values/operations/IndexNode.java | 16 ++++++- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java index e9e2e3d..8765690 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java @@ -6,6 +6,8 @@ import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.constants.NumberNode; +import me.topchetoeu.jscript.compilation.values.constants.StringNode; public class IndexAssignNode extends Node { public final Node object; @@ -16,21 +18,49 @@ public class IndexAssignNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { if (operation != null) { object.compile(target, true); - index.compile(target, true); - target.add(Instruction.dup(2)); - target.add(Instruction.loadMember()); - value.compile(target, true); - target.add(Instruction.operation(operation)); + if (index instanceof NumberNode num && (int)num.value == num.value) { + target.add(Instruction.loadMember((int)num.value)); + value.compile(target, true); + target.add(Instruction.operation(operation)); + target.add(Instruction.storeMember((int)num.value, pollute)); + } + else if (index instanceof StringNode str) { + target.add(Instruction.loadMember(str.value)); + value.compile(target, true); + target.add(Instruction.operation(operation)); + target.add(Instruction.storeMember(str.value, pollute)); + } + else { + index.compile(target, true); + target.add(Instruction.dup(2)); - target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN); + target.add(Instruction.loadMember()); + value.compile(target, true); + target.add(Instruction.operation(operation)); + + target.add(Instruction.storeMember(pollute)); + } + target.setLocationAndDebug(loc(), BreakpointType.STEP_IN); } else { object.compile(target, true); - index.compile(target, true); - value.compile(target, true); - target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);; + if (index instanceof NumberNode num && (int)num.value == num.value) { + value.compile(target, true); + target.add(Instruction.storeMember((int)num.value, pollute)); + } + else if (index instanceof StringNode str) { + value.compile(target, true); + target.add(Instruction.storeMember(str.value, pollute)); + } + else { + index.compile(target, true); + value.compile(target, true); + target.add(Instruction.storeMember(pollute)); + } + + target.setLocationAndDebug(loc(), BreakpointType.STEP_IN);; } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java index 85a6da4..d2791ac 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java @@ -11,6 +11,7 @@ import me.topchetoeu.jscript.compilation.AssignableNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; public class IndexNode extends Node implements AssignableNode { @@ -24,8 +25,19 @@ public class IndexNode extends Node implements AssignableNode { object.compile(target, true); if (dupObj) target.add(Instruction.dup()); - index.compile(target, true); - target.add(Instruction.loadMember()).setLocationAndDebug(loc(), BreakpointType.STEP_IN); + if (index instanceof NumberNode num && (int)num.value == num.value) { + target.add(Instruction.loadMember((int)num.value)); + } + else if (index instanceof StringNode str) { + target.add(Instruction.loadMember(str.value)); + } + else { + index.compile(target, true); + target.add(Instruction.loadMember()); + } + + target.setLocationAndDebug(loc(), BreakpointType.STEP_IN); + if (!pollute) target.add(Instruction.discard()); } @Override public void compile(CompileResult target, boolean pollute) { -- 2.45.2 From f929015f55471a9913246b58dddf29dfe410a5d0 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:41:52 +0300 Subject: [PATCH 28/48] ammend to prev commit --- .../me/topchetoeu/jscript/compilation/values/ArrayNode.java | 6 +++--- .../topchetoeu/jscript/compilation/values/ObjectNode.java | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java index 148a2a4..4cbf300 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java @@ -18,13 +18,13 @@ public class ArrayNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadArr(statements.length)); + if (statements.length > 0) target.add(Instruction.dup(statements.length)); + for (var i = 0; i < statements.length; i++) { var el = statements[i]; if (el != null) { - target.add(Instruction.dup()); - target.add(Instruction.pushValue(i)); el.compile(target, true); - target.add(Instruction.storeMember()); + target.add(Instruction.storeMember(i)); } } diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index 9382b81..5a5de0e 100644 --- a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -39,10 +39,9 @@ public class ObjectNode extends Node { for (var el : map.entrySet()) { target.add(Instruction.dup()); - target.add(Instruction.pushValue(el.getKey())); var val = el.getValue(); FunctionNode.compileWithName(val, target, true, el.getKey().toString()); - target.add(Instruction.storeMember()); + target.add(Instruction.storeMember(el.getKey())); } var keys = new ArrayList(); -- 2.45.2 From 546d6634664d2347bf7c778b37e635b0bb07960e Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:42:21 +0300 Subject: [PATCH 29/48] feat: add this arg capture --- .../me/topchetoeu/jscript/compilation/CompileResult.java | 5 ++--- .../jscript/runtime/values/functions/CodeFunction.java | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java index 0a78253..1c3d619 100644 --- a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -20,7 +20,7 @@ public final class CompileResult { public final List children; public final FunctionMapBuilder map; public final Environment env; - public int length, assignN; + public int length; public final Scope scope; public int temp() { @@ -101,8 +101,7 @@ public final class CompileResult { return new FunctionBody( scope.localsCount() + scope.allocCount(), scope.capturesCount(), - length, assignN, - instrRes, builtChildren + length, instrRes, builtChildren ); } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index 6eb5aa6..d26db0d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -8,6 +8,7 @@ import me.topchetoeu.jscript.runtime.values.Value; public final class CodeFunction extends FunctionValue { public final FunctionBody body; public final Value[][] captures; + public Value self; public Environment env; private Value onCall(Frame frame) { @@ -25,8 +26,8 @@ public final class CodeFunction extends FunctionValue { } @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { - var frame = new Frame(env, isNew, thisArg, args, this); - return onCall(frame); + if (self != null) return onCall(new Frame(env, isNew, self, args, this)); + else return onCall(new Frame(env, isNew, thisArg, args, this)); } public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { -- 2.45.2 From 7c8efaf066245251d5ea6726d53a3ce241fe7ca9 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:43:40 +0300 Subject: [PATCH 30/48] fix: incorrect printing of object-like values --- .../jscript/runtime/values/Value.java | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index 358fbf9..bf17a7f 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -19,7 +19,6 @@ import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; -import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; @@ -427,32 +426,26 @@ public abstract class Value { } } - private final boolean isEmptyFunc(Environment env, ObjectValue val) { - if (!(val instanceof FunctionValue)) return false; - if (val.members.size() + val.symbolMembers.size() > 1) return false; - - var proto = ((FunctionValue)val).prototype; - if (!(proto instanceof ObjectValue)) return false; - var protoObj = (ObjectValue)proto; - - if (protoObj.getMember(env, new StringValue("constructor")) != val) return false; - if (protoObj.getOwnMembers(env).size() + protoObj.getOwnSymbolMembers(env).size() != 1) return false; - - return true; - } private final String toReadable(Environment env, HashSet passed, int tab) { if (passed.contains(this)) return "[circular]"; - if (this instanceof ObjectValue) { + if (this instanceof ObjectValue obj) { var res = new StringBuilder(); var dbg = DebugContext.get(env); var printed = true; + var keys = this.getMembers(env, true, false); - if (this instanceof FunctionValue) { + if (this instanceof FunctionValue func) { res.append(this.toString()); - var loc = this instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)this).start() : null; + var loc = dbg.getMapOrEmpty(func).start(); if (loc != null) res.append(" @ " + loc); + + if ( + func.prototype instanceof ObjectValue objProto && + objProto.getMember(env, "constructor") == func && + objProto.getOwnMembers(env).size() + objProto.getOwnSymbolMembers(env).size() == 1 + ) { keys.remove("constructor"); } } else if (this instanceof ArrayValue) { res.append("["); @@ -461,7 +454,11 @@ public abstract class Value { for (int i = 0; i < arr.size(); i++) { if (i != 0) res.append(", "); else res.append(" "); - if (arr.has(i)) res.append(arr.get(i).toReadable(env, passed, tab)); + + if (arr.hasMember(env, i, true)) { + res.append(arr.getMember(env, i).toReadable(env, passed, tab)); + keys.remove(i + ""); + } else res.append(""); } @@ -469,15 +466,14 @@ public abstract class Value { } else printed = false; - if (tab > 3) return "{...}"; passed.add(this); - var obj = (ObjectValue)this; - if (obj.getOwnSymbolMembers(env).size() + obj.getOwnMembers(env).size() == 0 || isEmptyFunc(env, obj)) { + if (keys.size() + obj.getOwnSymbolMembers(env).size() == 0) { if (!printed) res.append("{}\n"); } - else { + else if (!printed) { + if (tab > 3) return "{...}"; res.append("{\n"); for (var entry : obj.getOwnSymbolMembers(env).entrySet()) { -- 2.45.2 From 93c246ad97833e5f59c13a6f9b4fed2cd8f186e7 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:44:24 +0300 Subject: [PATCH 31/48] refactor: remove arg loading from frame --- src/java/me/topchetoeu/jscript/runtime/Frame.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index f91ba76..acfb0af 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -422,20 +422,8 @@ public final class Frame { var i = 0; - for (; i < func.body.argsN && i < args.length; i++) { - this.locals.add(new Value[] { args[i] }); - } - for (; i < args.length; i++) { - this.locals.add(new Value[] { Value.UNDEFINED }); - } - for (i = 0; i < func.body.localsN; i++) { this.locals.add(new Value[] { Value.UNDEFINED }); } - - // this.locals = new LocalScope(func.body.localsN, func.captures); - // this.locals.get(0).set(thisArg); - // this.locals.get(1).value = new ArgumentsValue(this, args); - } } -- 2.45.2 From 5359c54694c064cbe1a4dacb710451c39499b93f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:44:44 +0300 Subject: [PATCH 32/48] fix: rethrow SyntaxException from compilation as EngineException --- .../me/topchetoeu/jscript/runtime/Compiler.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/runtime/Compiler.java b/src/java/me/topchetoeu/jscript/runtime/Compiler.java index de430b2..70edd70 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Compiler.java +++ b/src/java/me/topchetoeu/jscript/runtime/Compiler.java @@ -8,17 +8,22 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { public static final Compiler DEFAULT = (env, filename, raw) -> { - var res = JavaScript.compile(env, filename, raw); - var body = res.body(); - DebugContext.get(env).onSource(filename, raw); - registerFunc(env, body, res); - - return body; + try { + var res = JavaScript.compile(env, filename, raw); + var body = res.body(); + DebugContext.get(env).onSource(filename, raw); + registerFunc(env, body, res); + return body; + } + catch (SyntaxException e) { + throw EngineException.ofSyntax(e.loc + ": " + e.msg); + } }; public Key KEY = Key.of(); -- 2.45.2 From bd503ed943f80fc326856b291047bc9ec2fbbaf7 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:44:58 +0300 Subject: [PATCH 33/48] feat: impl new instructions --- .../jscript/runtime/InstructionRunner.java | 81 +++++++++++++++++-- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 48ecd51..48fcd01 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -118,7 +118,7 @@ public class InstructionRunner { int count = instr.get(0); for (var i = 0; i < count; i++) { - frame.push(frame.peek(count - 1)); + frame.push(frame.peek()); } frame.codePtr++; @@ -172,14 +172,20 @@ public class InstructionRunner { private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { int id = instr.get(0); String name = instr.get(1); - var captures = new Value[instr.params.length - 2][]; + boolean callable = instr.get(2); + boolean constructible = instr.get(3); + boolean captureThis = instr.get(4); + + var captures = new Value[instr.params.length - 5][]; - for (var i = 2; i < instr.params.length; i++) { - captures[i - 2] = frame.getVar(instr.get(i)); + for (var i = 5; i < instr.params.length; i++) { + captures[i - 5] = frame.getVar(instr.get(i)); } var func = new CodeFunction(env, name, frame.function.body.children[id], captures); - + if (!callable) func.enableCall = false; + if (!constructible) func.enableNew = false; + if (captureThis) func.self = frame.self; frame.push(func); frame.codePtr++; @@ -187,10 +193,32 @@ public class InstructionRunner { } private static Value execLoadMember(Environment env, Instruction instr, Frame frame) { var key = frame.pop(); - var obj = frame.pop(); try { - frame.push(obj.getMember(env, key)); + var top = frame.stackPtr - 1; + frame.stack[top] = frame.stack[top].getMember(env, key); + } + catch (IllegalArgumentException e) { + throw EngineException.ofType(e.getMessage()); + } + frame.codePtr++; + return null; + } + private static Value execLoadMemberInt(Environment env, Instruction instr, Frame frame) { + try { + var top = frame.stackPtr - 1; + frame.stack[top] = frame.stack[top].getMember(env, (int)instr.get(0)); + } + catch (IllegalArgumentException e) { + throw EngineException.ofType(e.getMessage()); + } + frame.codePtr++; + return null; + } + private static Value execLoadMemberStr(Environment env, Instruction instr, Frame frame) { + try { + var top = frame.stackPtr - 1; + frame.stack[top] = frame.stack[top].getMember(env, (String)instr.get(0)); } catch (IllegalArgumentException e) { throw EngineException.ofType(e.getMessage()); @@ -225,6 +253,24 @@ public class InstructionRunner { frame.codePtr++; return null; } + private static Value execStoreMemberStr(Environment env, Instruction instr, Frame frame) { + var val = frame.pop(); + var obj = frame.pop(); + + if (!obj.setMember(env, (String)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'."); + if ((boolean)instr.get(1)) frame.push(val); + frame.codePtr++; + return null; + } + private static Value execStoreMemberInt(Environment env, Instruction instr, Frame frame) { + var val = frame.pop(); + var obj = frame.pop(); + + if (!obj.setMember(env, (int)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'."); + if ((boolean)instr.get(1)) frame.push(val); + frame.codePtr++; + return null; + } private static Value execStoreVar(Environment env, Instruction instr, Frame frame) { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); int i = instr.get(0); @@ -430,6 +476,21 @@ public class InstructionRunner { frame.codePtr++; return null; } + private static Value execLoadRestArgs(Environment env, Instruction instr, Frame frame) { + int offset = instr.get(0); + var res = new ArrayValue(); + + if (offset < frame.args.length) res.copyFrom(frame.args, instr.get(0), 0, frame.args.length - offset); + + frame.push(res); + frame.codePtr++; + return null; + } + private static Value execLoadCallee(Environment env, Instruction instr, Frame frame) { + frame.push(frame.function); + frame.codePtr++; + return null; + } private static Value execLoadThis(Environment env, Instruction instr, Frame frame) { frame.push(frame.self); frame.codePtr++; @@ -479,14 +540,20 @@ public class InstructionRunner { case LOAD_ARR: return execLoadArr(env, instr, frame); case LOAD_FUNC: return execLoadFunc(env, instr, frame); case LOAD_MEMBER: return execLoadMember(env, instr, frame); + case LOAD_MEMBER_INT: return execLoadMemberInt(env, instr, frame); + case LOAD_MEMBER_STR: return execLoadMemberStr(env, instr, frame); case LOAD_REGEX: return execLoadRegEx(env, instr, frame); case LOAD_GLOB: return execLoadGlob(env, instr, frame); case LOAD_INTRINSICS: return execLoadIntrinsics(env, instr, frame); case LOAD_ARGS: return execLoadArgs(env, instr, frame); + case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame); + case LOAD_CALLEE: return execLoadCallee(env, instr, frame); case LOAD_THIS: return execLoadThis(env, instr, frame); case DISCARD: return execDiscard(env, instr, frame); case STORE_MEMBER: return execStoreMember(env, instr, frame); + case STORE_MEMBER_STR: return execStoreMemberStr(env, instr, frame); + case STORE_MEMBER_INT: return execStoreMemberInt(env, instr, frame); case STORE_VAR: return execStoreVar(env, instr, frame); case KEYS: return execKeys(env, instr, frame); -- 2.45.2 From 6f8efe74c4beb4e0c3c33dfe532a9692e561e71c Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:45:14 +0300 Subject: [PATCH 34/48] fix: print was returning java null, not JS undefined --- src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 21ddfaa..8fc9e68 100644 --- a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -337,8 +337,9 @@ public class SimpleRepl { if (el instanceof StringValue) System.out.print(((StringValue)el).value); else System.out.print(el.toReadable(args.env)); } + System.out.println(); - return null; + return Value.UNDEFINED; })); } private static void initEngine() { -- 2.45.2 From ce9b419757bf078c856bb92e736362170424e8ae Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:45:34 +0300 Subject: [PATCH 35/48] reafactor: make globals initializer use ES6 features --- src/assets/lib/index.js | 576 ++++++++++++++++++++-------------------- 1 file changed, 284 insertions(+), 292 deletions(-) diff --git a/src/assets/lib/index.js b/src/assets/lib/index.js index 96d0e60..1c34848 100644 --- a/src/assets/lib/index.js +++ b/src/assets/lib/index.js @@ -1,321 +1,313 @@ -#! my special comment lol +const target = arguments[0]; +const primordials = arguments[1]; -(function(target, primordials) { - var makeSymbol = primordials.symbol.makeSymbol; - var getSymbol = primordials.symbol.getSymbol; - var getSymbolKey = primordials.symbol.getSymbolKey; - var getSymbolDescription = primordials.symbol.getSymbolDescription; +const makeSymbol = primordials.symbol.makeSymbol; +const getSymbol = primordials.symbol.getSymbol; +const getSymbolKey = primordials.symbol.getSymbolKey; +const getSymbolDescription = primordials.symbol.getSymbolDescription; - var parseInt = primordials.number.parseInt; - var parseFloat = primordials.number.parseFloat; - var isNaN = primordials.number.isNaN; - var NaN = primordials.number.NaN; - var Infinity = primordials.number.Infinity; +const parseInt = primordials.number.parseInt; +const parseFloat = primordials.number.parseFloat; +const isNaN = primordials.number.isNaN; +const NaN = primordials.number.NaN; +const Infinity = primordials.number.Infinity; - var fromCharCode = primordials.string.fromCharCode; - var fromCodePoint = primordials.string.fromCodePoint; - var stringBuild = primordials.string.stringBuild; +const fromCharCode = primordials.string.fromCharCode; +const fromCodePoint = primordials.string.fromCodePoint; +const stringBuild = primordials.string.stringBuild; - var defineProperty = primordials.object.defineProperty; - var defineField = primordials.object.defineField; - var getOwnMember = primordials.object.getMember; - var getOwnSymbolMember = primordials.object.getOwnSymbolMember; - var getOwnMembers = primordials.object.getOwnMembers; - var getOwnSymbolMembers = primordials.object.getOwnSymbolMembers; - var getPrototype = primordials.object.getPrototype; - var setPrototype = primordials.object.setPrototype; +const defineProperty = primordials.object.defineProperty; +const defineField = primordials.object.defineField; +const getOwnMember = primordials.object.getMember; +const getOwnSymbolMember = primordials.object.getOwnSymbolMember; +const getOwnMembers = primordials.object.getOwnMembers; +const getOwnSymbolMembers = primordials.object.getOwnSymbolMembers; +const getPrototype = primordials.object.getPrototype; +const setPrototype = primordials.object.setPrototype; - var invokeType = primordials.function.invokeType; - var setConstructable = primordials.function.setConstructable; - var setCallable = primordials.function.setCallable; - var invoke = primordials.function.invoke; +const invokeType = primordials.function.invokeType; +const setConstructable = primordials.function.setConstructable; +const setCallable = primordials.function.setCallable; +const invoke = primordials.function.invoke; - var setGlobalPrototype = primordials.setGlobalPrototype; - var compile = primordials.compile; +const setGlobalPrototype = primordials.setGlobalPrototype; +const compile = primordials.compile; - var json = primordials.json; +const json = primordials.json; - var valueKey = makeSymbol("Primitive.value"); +const valueKey = makeSymbol("Primitive.value"); +const undefined = ({}).definitelyDefined; - function unwrapThis(self, type, constr, name, arg, defaultVal) { - if (arg == null) arg = "this"; - if (typeof self === type) return self; - if (self instanceof constr && valueKey in self) self = self[valueKey]; - if (typeof self === type) return self; - if (arguments.length > 5) return defaultVal; +target.undefined = undefined; - throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); +const unwrapThis = (self, type, constr, name, arg, defaultVal) => { + if (arg == null) arg = "this"; + if (typeof self === type) return self; + if (self instanceof constr && valueKey in self) self = self[valueKey]; + if (typeof self === type) return self; + if (arguments.length > 5) return defaultVal; + + throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); +} + +const wrapIndex = (i, len) => {}; + +const Symbol = (name = "") => makeSymbol(name); + +defineField(Symbol, "for", true, false, true, function(name) { + return getSymbol(name + ""); +}); +defineField(Symbol, "keyFor", true, false, true, function(symbol) { + return getSymbolKey(unwrapThis(symbol, "symbol", Symbol, "Symbol.keyFor")); +}); + +defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); +defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); +defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); +defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); +defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); +defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); +defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); +defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); +defineField(Symbol, "prototype", false, false, false, {}); + +defineProperty(Symbol.prototype, "description", false, true, function () { + return getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); +}, undefined); +defineField(Symbol.prototype, "toString", true, false, true, function() { + return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; +}); +defineField(Symbol.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); +}); + +target.Symbol = Symbol; + +const Number = function(value) { + if (invokeType(arguments) === "call") { + if (arguments.length === 0) return 0; + else return +value; } - function wrapIndex(i, len) { + this[valueKey] = target.Number(value); +}; + +defineField(Number, "isFinite", true, false, true, function(value) { + value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined); + + if (value === undefined || isNaN(value)) return false; + if (value === Infinity || value === -Infinity) return false; + + return true; +}); +defineField(Number, "isInteger", true, false, true, function(value) { + value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined); + if (value === undefined) return false; + return parseInt(value) === value; +}); +defineField(Number, "isNaN", true, false, true, function(value) { + return isNaN(value); +}); +defineField(Number, "isSafeInteger", true, false, true, function(value) { + value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined); + if (value === undefined || parseInt(value) !== value) return false; + return value >= -9007199254740991 && value <= 9007199254740991; +}); +defineField(Number, "parseFloat", true, false, true, function(value) { + value = 0 + value; + return parseFloat(value); +}); +defineField(Number, "parseInt", true, false, true, function(value, radix) { + value = 0 + value; + radix = +radix; + if (isNaN(radix)) radix = 10; + + return parseInt(value, radix); +}); + +defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); +defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); +defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); +defineField(Number, "POSITIVE_INFINITY", false, false, false, +Infinity); +defineField(Number, "NEGATIVE_INFINITY", false, false, false, -Infinity); +defineField(Number, "NaN", false, false, false, NaN); +defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); +defineField(Number, "MIN_VALUE", false, false, false, 5e-324); +defineField(Number, "prototype", false, false, false, {}); + +defineField(Number.prototype, "toString", true, false, true); +defineField(Number.prototype, "toString", true, false, true, function() { + return "" + unwrapThis(this, "number", Number, "Number.prototype.toString"); +}); +defineField(Number.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "number", Number, "Number.prototype.toString"); +}); + +target.Number = Number; + +const String = function(value) { + if (invokeType(arguments) === "call") { + if (arguments.length === 0) return ""; + else return value + ""; } - var Symbol = function(name) { - if (arguments.length === 0) return makeSymbol(""); - else return makeSymbol(name + ""); - }; - setConstructable(Symbol, false); + this[valueKey] = String(value); +}; - defineField(Symbol, "for", true, false, true, function(name) { - return getSymbol(name + ""); - }); - defineField(Symbol, "keyFor", true, false, true, function(symbol) { - return getSymbolKey(unwrapThis(symbol, "symbol", Symbol, "Symbol.keyFor")); - }); +defineField(String, "fromCharCode", true, false, true, function() { + const res = []; + res[arguments.length] = 0; - defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); - defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); - defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); - defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); - defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); - defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); - defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); - defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); - defineField(Symbol, "prototype", false, false, false, {}); + for (let i = 0; i < arguments.length; i++) { + res[i] = fromCharCode(+arguments[i]); + } - defineProperty(Symbol.prototype, "description", false, true, function () { - return getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); - }, undefined); - defineField(Symbol.prototype, "toString", true, false, true, function() { - return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; - }); - defineField(Symbol.prototype, "valueOf", true, false, true, function() { - return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); - }); + return stringBuild(res); +}); +defineField(String, "fromCodePoint", true, false, true, function() { + const res = []; + res[arguments.length] = 0; - target.Symbol = Symbol; + for (let i = 0; i < arguments.length; i++) { + res[i] = fromCodePoint(+arguments[i]); + } - var Number = function(value) { - if (invokeType(arguments) === "call") { - if (arguments.length === 0) return 0; - else return +value; + return stringBuild(res); +}); + +defineField(String, "prototype", false, false, false, {}); + +defineField(String.prototype, "at", true, false, true, function(index) { + throw "Not implemented :/"; + return unwrapThis(this, "string", String, "String.prototype.at")[index]; +}); +defineField(String.prototype, "toString", true, false, true, function() { + return unwrapThis(this, "string", String, "String.prototype.toString"); +}); +defineField(String.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "string", String, "String.prototype.valueOf"); +}); + +target.String = String; + +const Boolean = function(value) { + if (invokeType(arguments) === "call") { + if (arguments.length === 0) return false; + else return !!value; + } + + this[valueKey] = Boolean(value); +}; + +defineField(Boolean, "prototype", false, false, false, {}); + +defineField(Boolean.prototype, "toString", true, false, true, function() { + return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString"); +}); +defineField(Boolean.prototype, "valueOf", true, false, true, function() { + return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf"); +}); + +target.Boolean = Boolean; + +const Object = function(value) { + if (typeof value === 'object' && value !== null) return value; + + if (typeof value === 'string') return new String(value); + if (typeof value === 'number') return new Number(value); + if (typeof value === 'boolean') return new Boolean(value); + if (typeof value === 'symbol') { + const res = {}; + setPrototype(res, Symbol.prototype); + res[valueKey] = value; + return res; + } + + const target = this; + if (target == null || typeof target !== 'object') target = {}; + + this[valueKey] = Object(value); +}; + +defineField(Object, "prototype", false, false, false, setPrototype({}, null)); + +defineField(Object.prototype, "toString", true, false, true, function() { + if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; + else if (typeof this === "number" || this instanceof Number) return "[object Number]"; + else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; + else if (typeof this === "string" || this instanceof String) return "[object String]"; + else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; + else if (typeof this === "function") return "[object Function]"; + else return "[object Object]"; +}); +defineField(Object.prototype, "valueOf", true, false, true, function() { + return this; +}); + +target.Boolean = Boolean; + +const Function = function() { + const parts = ["return function annonymous("]; + + for (let i = 0; i < arguments.length - 1; i++) { + if (i > 0) parts[parts.length] = ","; + parts[parts.length] = arguments[i]; + } + parts[parts.length] = "){\n"; + parts[parts.length] = String(arguments[arguments.length - 1]); + parts[parts.length] = "\n}"; + + print(parts); + + const res = compile(stringBuild(parts))(); + return res; +}; + +defineField(Function, "compile", true, false, true, (src = "", options = {}) => { + if (options.globals == null) options.globals = []; + if (options.wrap == null) options.wrap = true; + + const parts = []; + + if (options.wrap) parts[parts.length] = "return (function() {\n"; + if (options.globals.length > 0) { + parts[parts.length] = "var "; + + for (let i = 0; i < options.globals.length; i++) { + if (i > 0) parts[parts.length] = ","; + parts[parts.length] = options.globals[i]; } - this[valueKey] = target.Number(value); - }; + parts[parts.length] = ";((g=arguments[0])=>{"; - defineField(Number, "isFinite", true, false, true, function(value) { - value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined); - - if (value === undefined || isNaN(value)) return false; - if (value === Infinity || value === -Infinity) return false; - - return true; - }); - defineField(Number, "isInteger", true, false, true, function(value) { - value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined); - if (value === undefined) return false; - return parseInt(value) === value; - }); - defineField(Number, "isNaN", true, false, true, function(value) { - return isNaN(value); - }); - defineField(Number, "isSafeInteger", true, false, true, function(value) { - value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined); - if (value === undefined || parseInt(value) !== value) return false; - return value >= -9007199254740991 && value <= 9007199254740991; - }); - defineField(Number, "parseFloat", true, false, true, function(value) { - value = 0 + value; - return parseFloat(value); - }); - defineField(Number, "parseInt", true, false, true, function(value, radix) { - value = 0 + value; - radix = +radix; - if (isNaN(radix)) radix = 10; - - return parseInt(value, radix); - }); - - defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); - defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); - defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); - defineField(Number, "POSITIVE_INFINITY", false, false, false, +Infinity); - defineField(Number, "NEGATIVE_INFINITY", false, false, false, -Infinity); - defineField(Number, "NaN", false, false, false, NaN); - defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); - defineField(Number, "MIN_VALUE", false, false, false, 5e-324); - defineField(Number, "prototype", false, false, false, {}); - - defineField(Number.prototype, "toString", true, false, true); - defineField(Number.prototype, "toString", true, false, true, function() { - return "" + unwrapThis(this, "number", Number, "Number.prototype.toString"); - }); - defineField(Number.prototype, "valueOf", true, false, true, function() { - return unwrapThis(this, "number", Number, "Number.prototype.toString"); - }); - - target.Number = Number; - - var String = function(value) { - if (invokeType(arguments) === "call") { - if (arguments.length === 0) return ""; - else return value + ""; + for (let i = 0; i < options.globals.length; i++) { + const name = options.globals[i]; + parts[parts.length] = name + "=g[" + json.stringify(name) + "];"; } - this[valueKey] = target.String(value); - }; + parts[parts.length] = "})()\n"; + } - defineField(String, "fromCharCode", true, false, true, function() { - var res = []; - res[arguments.length] = 0; + parts[parts.length] = src; + if (options.wrap) parts[parts.length] = "\n})(arguments[0])"; - for (var i = 0; i < arguments.length; i++) { - res[res.length] = fromCharCode(+arguments[i]); - } + const res = compile(stringBuild(parts)); + return res; +}); +defineField(Function, "prototype", false, false, false, setPrototype({}, null)); - return stringBuild(res); - }); - defineField(String, "fromCodePoint", true, false, true, function(value) { - var res = []; - res[arguments.length] = 0; +defineField(Function.prototype, "toString", true, false, true, function() { + if (this.name !== "") return "function " + this.name + "(...) { ... }"; + else return "function (...) { ... }"; +}); +defineField(Function.prototype, "valueOf", true, false, true, function() { + return this; +}); - for (var i = 0; i < arguments.length; i++) { - res[res.length] = fromCodePoint(+arguments[i]); - } +target.Function = Function; - return stringBuild(res); - }); - - defineField(String, "prototype", false, false, false, {}); - - defineField(String.prototype, "at", true, false, true, function(index) { - return "" + unwrapThis(this, "string", String, "String.prototype.at"); - }); - defineField(String.prototype, "toString", true, false, true, function() { - return unwrapThis(this, "string", String, "String.prototype.toString"); - }); - defineField(String.prototype, "valueOf", true, false, true, function() { - return unwrapThis(this, "string", String, "String.prototype.valueOf"); - }); - - target.String = String; - - var Boolean = function(value) { - if (invokeType(arguments) === "call") { - if (arguments.length === 0) return false; - else return !!value; - } - - this[valueKey] = target.Boolean(value); - }; - - defineField(Boolean, "prototype", false, false, false, {}); - - defineField(Boolean.prototype, "toString", true, false, true, function() { - return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString"); - }); - defineField(Boolean.prototype, "valueOf", true, false, true, function() { - return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf"); - }); - - target.Boolean = Boolean; - - var Object = function(value) { - if (typeof value === 'object' && value !== null) return value; - - if (typeof value === 'string') return new String(value); - if (typeof value === 'number') return new Number(value); - if (typeof value === 'boolean') return new Boolean(value); - if (typeof value === 'symbol') { - var res = {}; - setPrototype(res, Symbol.prototype); - res[valueKey] = value; - return res; - } - - var target = this; - if (target === undefined || target === null || typeof target !== 'object') target = {}; - - this[valueKey] = target.Object(value); - }; - - defineField(Object, "prototype", false, false, false, setPrototype({}, null)); - - defineField(Object.prototype, "toString", true, false, true, function() { - if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; - else if (typeof this === "number" || this instanceof Number) return "[object Number]"; - else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; - else if (typeof this === "string" || this instanceof String) return "[object String]"; - else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; - else if (typeof this === "function") return "[object Function]"; - else return "[object Object]"; - }); - defineField(Object.prototype, "valueOf", true, false, true, function() { - return this; - }); - - target.Boolean = Boolean; - - var Function = function() { - if (invokeType(arguments) === "new") return Function(value); - - var res = ["return function ("]; - - for (var i = 0; i < arguments.length - 1; i++) { - if (i > 0) res[res.length] = ","; - res[res.length] = arguments[i]; - } - res[res.length] = "){"; - res[res.length] = String(arguments[arguments.length - 1]); - res[res.length] = "}"; - - log(res); - - return compile(stringBuild(res))(); - }; - - defineField(Function, "compile", true, false, true, function(src, options) { - if (options == null) options = {}; - if (src == null) src = ""; - - if (options.globals == null) options.globals = []; - if (options.wrap == null) options.wrap = true; - - var res = []; - - if (options.wrap) res[res.length] = "return (function() {\n"; - if (options.globals.length > 0) { - res[res.length] = "var "; - - for (var i = 0; i < options.globals.length; i++) { - if (i > 0) res[res.length] = ","; - res[res.length] = options.globals[i]; - } - - res[res.length] = ";(function(g){"; - - for (var i = 0; i < options.globals.length; i++) { - var name = options.globals[i]; - res[res.length] = name; - res[res.length] = "=g["; - res[res.length] = json.stringify(name); - res[res.length] = "];"; - } - - res[res.length] = "})(arguments[0] || {});\n"; - } - - res[res.length] = src; - if (options.wrap) res[res.length] = "\n})(arguments[0])"; - - return compile(stringBuild(res)); - }); - defineField(Function, "prototype", false, false, false, setPrototype({}, null)); - - defineField(Function.prototype, "toString", true, false, true, function() { - if (this.name !== "") return "function " + this.name + "(...) { ... }"; - else return "function (...) { ... }"; - }); - defineField(Function.prototype, "valueOf", true, false, true, function() { - return this; - }); - - target.Function = Function; - - setGlobalPrototype("string", String.prototype); - setGlobalPrototype("number", Number.prototype); - setGlobalPrototype("boolean", Boolean.prototype); - setGlobalPrototype("symbol", Symbol.prototype); - setGlobalPrototype("object", Object.prototype); -})(arguments[0], arguments[1]); \ No newline at end of file +setGlobalPrototype("string", String.prototype); +setGlobalPrototype("number", Number.prototype); +setGlobalPrototype("boolean", Boolean.prototype); +setGlobalPrototype("symbol", Symbol.prototype); +setGlobalPrototype("object", Object.prototype); \ No newline at end of file -- 2.45.2 From 1589ef51b051b3099695da96503799b262f7e2ab Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:32:54 +0300 Subject: [PATCH 36/48] refactor: move src and resources to standard places --- src/{ => main}/assets/debugger/favicon.png | Bin src/{ => main}/assets/debugger/index.html | 0 src/{ => main}/assets/debugger/protocol.json | 0 src/{ => main}/assets/lib/index.js | 0 src/{ => main}/assets/metadata.json | 0 .../java/me/topchetoeu/jscript/common/Buffer.java | 0 .../me/topchetoeu/jscript/common/FunctionBody.java | 0 .../me/topchetoeu/jscript/common/Instruction.java | 0 .../java/me/topchetoeu/jscript/common/Metadata.java | 0 .../me/topchetoeu/jscript/common/Operation.java | 0 .../java/me/topchetoeu/jscript/common/Reading.java | 0 .../jscript/common/environment/Environment.java | 0 .../topchetoeu/jscript/common/environment/Key.java | 0 .../jscript/common/environment/MultiKey.java | 0 .../me/topchetoeu/jscript/common/json/JSON.java | 0 .../topchetoeu/jscript/common/json/JSONElement.java | 0 .../me/topchetoeu/jscript/common/json/JSONList.java | 0 .../me/topchetoeu/jscript/common/json/JSONMap.java | 0 .../jscript/common/mapping/ConvertType.java | 0 .../jscript/common/mapping/FunctionMap.java | 0 .../topchetoeu/jscript/common/parsing/Filename.java | 0 .../topchetoeu/jscript/common/parsing/Location.java | 0 .../topchetoeu/jscript/common/parsing/ParseRes.java | 0 .../topchetoeu/jscript/common/parsing/Parser.java | 0 .../topchetoeu/jscript/common/parsing/Parsing.java | 0 .../topchetoeu/jscript/common/parsing/Source.java | 0 .../jscript/common/parsing/SourceLocation.java | 0 .../jscript/compilation/AssignableNode.java | 0 .../jscript/compilation/CompileResult.java | 0 .../jscript/compilation/CompoundNode.java | 0 .../jscript/compilation/DeferredIntSupplier.java | 0 .../jscript/compilation/FunctionArrowNode.java | 0 .../jscript/compilation/FunctionNode.java | 0 .../jscript/compilation/FunctionStatementNode.java | 0 .../jscript/compilation/FunctionValueNode.java | 0 .../topchetoeu/jscript/compilation/JavaScript.java | 0 .../jscript/compilation/LabelContext.java | 0 .../me/topchetoeu/jscript/compilation/Node.java | 0 .../jscript/compilation/NodeChildren.java | 0 .../topchetoeu/jscript/compilation/Parameter.java | 0 .../topchetoeu/jscript/compilation/Parameters.java | 0 .../jscript/compilation/VariableDeclareNode.java | 0 .../jscript/compilation/control/BreakNode.java | 0 .../jscript/compilation/control/ContinueNode.java | 0 .../jscript/compilation/control/DebugNode.java | 0 .../jscript/compilation/control/DeleteNode.java | 0 .../jscript/compilation/control/DoWhileNode.java | 0 .../jscript/compilation/control/ForInNode.java | 0 .../jscript/compilation/control/ForNode.java | 0 .../jscript/compilation/control/ForOfNode.java | 0 .../jscript/compilation/control/IfNode.java | 0 .../jscript/compilation/control/ReturnNode.java | 0 .../jscript/compilation/control/SwitchNode.java | 0 .../jscript/compilation/control/ThrowNode.java | 0 .../jscript/compilation/control/TryNode.java | 0 .../jscript/compilation/control/WhileNode.java | 0 .../jscript/compilation/scope/FunctionScope.java | 0 .../jscript/compilation/scope/GlobalScope.java | 0 .../jscript/compilation/scope/LocalScope.java | 0 .../topchetoeu/jscript/compilation/scope/Scope.java | 0 .../compilation/scope/VariableDescriptor.java | 0 .../jscript/compilation/scope/VariableList.java | 0 .../jscript/compilation/values/ArrayNode.java | 0 .../jscript/compilation/values/GlobalThisNode.java | 0 .../jscript/compilation/values/ObjectNode.java | 0 .../jscript/compilation/values/RegexNode.java | 0 .../jscript/compilation/values/ThisNode.java | 0 .../jscript/compilation/values/VariableNode.java | 0 .../compilation/values/constants/BoolNode.java | 0 .../compilation/values/constants/NullNode.java | 0 .../compilation/values/constants/NumberNode.java | 0 .../compilation/values/constants/StringNode.java | 0 .../compilation/values/operations/CallNode.java | 0 .../compilation/values/operations/ChangeNode.java | 0 .../compilation/values/operations/DiscardNode.java | 0 .../values/operations/IndexAssignNode.java | 0 .../compilation/values/operations/IndexNode.java | 0 .../compilation/values/operations/LazyAndNode.java | 0 .../compilation/values/operations/LazyOrNode.java | 0 .../values/operations/OperationNode.java | 0 .../compilation/values/operations/TypeofNode.java | 0 .../values/operations/VariableAssignNode.java | 0 .../topchetoeu/jscript/runtime/ArgumentsValue.java | 0 .../me/topchetoeu/jscript/runtime/Compiler.java | 0 .../java/me/topchetoeu/jscript/runtime/Engine.java | 0 .../me/topchetoeu/jscript/runtime/EventLoop.java | 0 .../java/me/topchetoeu/jscript/runtime/Frame.java | 0 .../jscript/runtime/InstructionRunner.java | 0 .../topchetoeu/jscript/runtime/JSONConverter.java | 0 .../me/topchetoeu/jscript/runtime/SimpleRepl.java | 0 .../jscript/runtime/debug/DebugContext.java | 0 .../jscript/runtime/debug/DebugHandler.java | 0 .../jscript/runtime/exceptions/EngineException.java | 0 .../runtime/exceptions/InterruptException.java | 0 .../jscript/runtime/exceptions/SyntaxException.java | 0 .../topchetoeu/jscript/runtime/values/KeyCache.java | 0 .../topchetoeu/jscript/runtime/values/Member.java | 0 .../me/topchetoeu/jscript/runtime/values/Value.java | 0 .../jscript/runtime/values/functions/Arguments.java | 0 .../runtime/values/functions/CodeFunction.java | 0 .../runtime/values/functions/FunctionValue.java | 0 .../runtime/values/functions/NativeFunction.java | 0 .../jscript/runtime/values/objects/ArrayValue.java | 0 .../jscript/runtime/values/objects/ObjectValue.java | 0 .../jscript/runtime/values/objects/ScopeValue.java | 0 .../runtime/values/primitives/BoolValue.java | 0 .../runtime/values/primitives/NumberValue.java | 0 .../runtime/values/primitives/PrimitiveValue.java | 0 .../runtime/values/primitives/StringValue.java | 0 .../runtime/values/primitives/SymbolValue.java | 0 .../runtime/values/primitives/VoidValue.java | 0 111 files changed, 0 insertions(+), 0 deletions(-) rename src/{ => main}/assets/debugger/favicon.png (100%) rename src/{ => main}/assets/debugger/index.html (100%) rename src/{ => main}/assets/debugger/protocol.json (100%) rename src/{ => main}/assets/lib/index.js (100%) rename src/{ => main}/assets/metadata.json (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/Buffer.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/FunctionBody.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/Instruction.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/Metadata.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/Operation.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/Reading.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/environment/Environment.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/environment/Key.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/environment/MultiKey.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/json/JSON.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/json/JSONElement.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/json/JSONList.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/json/JSONMap.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/mapping/ConvertType.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/Filename.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/Location.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/ParseRes.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/Parser.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/Parsing.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/Source.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/AssignableNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/CompileResult.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/CompoundNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/FunctionNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/JavaScript.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/LabelContext.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/Node.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/NodeChildren.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/Parameter.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/Parameters.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/BreakNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/DebugNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/ForInNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/ForNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/IfNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/TryNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/control/WhileNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/scope/Scope.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/scope/VariableList.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/RegexNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/ThisNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/VariableNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/Compiler.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/Engine.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/EventLoop.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/Frame.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/InstructionRunner.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/JSONConverter.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/SimpleRepl.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/exceptions/InterruptException.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/KeyCache.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/Member.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/Value.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java (100%) rename src/{ => main}/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java (100%) diff --git a/src/assets/debugger/favicon.png b/src/main/assets/debugger/favicon.png similarity index 100% rename from src/assets/debugger/favicon.png rename to src/main/assets/debugger/favicon.png diff --git a/src/assets/debugger/index.html b/src/main/assets/debugger/index.html similarity index 100% rename from src/assets/debugger/index.html rename to src/main/assets/debugger/index.html diff --git a/src/assets/debugger/protocol.json b/src/main/assets/debugger/protocol.json similarity index 100% rename from src/assets/debugger/protocol.json rename to src/main/assets/debugger/protocol.json diff --git a/src/assets/lib/index.js b/src/main/assets/lib/index.js similarity index 100% rename from src/assets/lib/index.js rename to src/main/assets/lib/index.js diff --git a/src/assets/metadata.json b/src/main/assets/metadata.json similarity index 100% rename from src/assets/metadata.json rename to src/main/assets/metadata.json diff --git a/src/java/me/topchetoeu/jscript/common/Buffer.java b/src/main/java/me/topchetoeu/jscript/common/Buffer.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/Buffer.java rename to src/main/java/me/topchetoeu/jscript/common/Buffer.java diff --git a/src/java/me/topchetoeu/jscript/common/FunctionBody.java b/src/main/java/me/topchetoeu/jscript/common/FunctionBody.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/FunctionBody.java rename to src/main/java/me/topchetoeu/jscript/common/FunctionBody.java diff --git a/src/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/Instruction.java rename to src/main/java/me/topchetoeu/jscript/common/Instruction.java diff --git a/src/java/me/topchetoeu/jscript/common/Metadata.java b/src/main/java/me/topchetoeu/jscript/common/Metadata.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/Metadata.java rename to src/main/java/me/topchetoeu/jscript/common/Metadata.java diff --git a/src/java/me/topchetoeu/jscript/common/Operation.java b/src/main/java/me/topchetoeu/jscript/common/Operation.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/Operation.java rename to src/main/java/me/topchetoeu/jscript/common/Operation.java diff --git a/src/java/me/topchetoeu/jscript/common/Reading.java b/src/main/java/me/topchetoeu/jscript/common/Reading.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/Reading.java rename to src/main/java/me/topchetoeu/jscript/common/Reading.java diff --git a/src/java/me/topchetoeu/jscript/common/environment/Environment.java b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/environment/Environment.java rename to src/main/java/me/topchetoeu/jscript/common/environment/Environment.java diff --git a/src/java/me/topchetoeu/jscript/common/environment/Key.java b/src/main/java/me/topchetoeu/jscript/common/environment/Key.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/environment/Key.java rename to src/main/java/me/topchetoeu/jscript/common/environment/Key.java diff --git a/src/java/me/topchetoeu/jscript/common/environment/MultiKey.java b/src/main/java/me/topchetoeu/jscript/common/environment/MultiKey.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/environment/MultiKey.java rename to src/main/java/me/topchetoeu/jscript/common/environment/MultiKey.java diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/main/java/me/topchetoeu/jscript/common/json/JSON.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/json/JSON.java rename to src/main/java/me/topchetoeu/jscript/common/json/JSON.java diff --git a/src/java/me/topchetoeu/jscript/common/json/JSONElement.java b/src/main/java/me/topchetoeu/jscript/common/json/JSONElement.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/json/JSONElement.java rename to src/main/java/me/topchetoeu/jscript/common/json/JSONElement.java diff --git a/src/java/me/topchetoeu/jscript/common/json/JSONList.java b/src/main/java/me/topchetoeu/jscript/common/json/JSONList.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/json/JSONList.java rename to src/main/java/me/topchetoeu/jscript/common/json/JSONList.java diff --git a/src/java/me/topchetoeu/jscript/common/json/JSONMap.java b/src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/json/JSONMap.java rename to src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java diff --git a/src/java/me/topchetoeu/jscript/common/mapping/ConvertType.java b/src/main/java/me/topchetoeu/jscript/common/mapping/ConvertType.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/mapping/ConvertType.java rename to src/main/java/me/topchetoeu/jscript/common/mapping/ConvertType.java diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/main/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java rename to src/main/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Filename.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Filename.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/Filename.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/Filename.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Location.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Location.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/Location.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/Location.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/ParseRes.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parser.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Parser.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/Parser.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/Parser.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/Parsing.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/Source.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Source.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/Source.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/Source.java diff --git a/src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java b/src/main/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java similarity index 100% rename from src/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java rename to src/main/java/me/topchetoeu/jscript/common/parsing/SourceLocation.java diff --git a/src/java/me/topchetoeu/jscript/compilation/AssignableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/AssignableNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/AssignableNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/AssignableNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/CompileResult.java rename to src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java diff --git a/src/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/CompoundNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java b/src/main/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java rename to src/main/java/me/topchetoeu/jscript/compilation/DeferredIntSupplier.java diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/FunctionNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/JavaScript.java rename to src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java diff --git a/src/java/me/topchetoeu/jscript/compilation/LabelContext.java b/src/main/java/me/topchetoeu/jscript/compilation/LabelContext.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/LabelContext.java rename to src/main/java/me/topchetoeu/jscript/compilation/LabelContext.java diff --git a/src/java/me/topchetoeu/jscript/compilation/Node.java b/src/main/java/me/topchetoeu/jscript/compilation/Node.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/Node.java rename to src/main/java/me/topchetoeu/jscript/compilation/Node.java diff --git a/src/java/me/topchetoeu/jscript/compilation/NodeChildren.java b/src/main/java/me/topchetoeu/jscript/compilation/NodeChildren.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/NodeChildren.java rename to src/main/java/me/topchetoeu/jscript/compilation/NodeChildren.java diff --git a/src/java/me/topchetoeu/jscript/compilation/Parameter.java b/src/main/java/me/topchetoeu/jscript/compilation/Parameter.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/Parameter.java rename to src/main/java/me/topchetoeu/jscript/compilation/Parameter.java diff --git a/src/java/me/topchetoeu/jscript/compilation/Parameters.java b/src/main/java/me/topchetoeu/jscript/compilation/Parameters.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/Parameters.java rename to src/main/java/me/topchetoeu/jscript/compilation/Parameters.java diff --git a/src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/BreakNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DebugNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DebugNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/DebugNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/DebugNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/ForInNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/ForNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/IfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/IfNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/TryNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/TryNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/control/WhileNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java rename to src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java rename to src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java rename to src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/scope/Scope.java rename to src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java rename to src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java diff --git a/src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/scope/VariableList.java rename to src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/RegexNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/ThisNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ThisNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/ThisNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/ThisNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/VariableNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexAssignNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java diff --git a/src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java similarity index 100% rename from src/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java rename to src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java diff --git a/src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java b/src/main/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/ArgumentsValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/Compiler.java b/src/main/java/me/topchetoeu/jscript/runtime/Compiler.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/Compiler.java rename to src/main/java/me/topchetoeu/jscript/runtime/Compiler.java diff --git a/src/java/me/topchetoeu/jscript/runtime/Engine.java b/src/main/java/me/topchetoeu/jscript/runtime/Engine.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/Engine.java rename to src/main/java/me/topchetoeu/jscript/runtime/Engine.java diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/main/java/me/topchetoeu/jscript/runtime/EventLoop.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/EventLoop.java rename to src/main/java/me/topchetoeu/jscript/runtime/EventLoop.java diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/Frame.java rename to src/main/java/me/topchetoeu/jscript/runtime/Frame.java diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java rename to src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java diff --git a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/JSONConverter.java rename to src/main/java/me/topchetoeu/jscript/runtime/JSONConverter.java diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java rename to src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/main/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java rename to src/main/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java b/src/main/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java rename to src/main/java/me/topchetoeu/jscript/runtime/debug/DebugHandler.java diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/main/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java rename to src/main/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/InterruptException.java b/src/main/java/me/topchetoeu/jscript/runtime/exceptions/InterruptException.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/exceptions/InterruptException.java rename to src/main/java/me/topchetoeu/jscript/runtime/exceptions/InterruptException.java diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java b/src/main/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java rename to src/main/java/me/topchetoeu/jscript/runtime/exceptions/SyntaxException.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java b/src/main/java/me/topchetoeu/jscript/runtime/values/KeyCache.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/KeyCache.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/KeyCache.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Member.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/Member.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/Member.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/Value.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/Value.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java similarity index 100% rename from src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java rename to src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java -- 2.45.2 From 807b3918faa4caa923e37b736a6d672a7ce3e782 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:55:59 +0300 Subject: [PATCH 37/48] motherfucker --- build.gradle | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/build.gradle b/build.gradle index a7ede98..88bad52 100644 --- a/build.gradle +++ b/build.gradle @@ -1,46 +1,14 @@ -<<<<<<< HEAD - -plugins { - id "application" - // these idiots don't optimize in the compile-time, but in the runtime - // who let these knuckleheads make a language -======= import java.text.SimpleDateFormat plugins { id 'application' id 'net.nemerosa.versioning' version '2.15.0' id 'org.ajoberstar.grgit' version '5.0.0-rc.3' // required by gradle ->>>>>>> master // TODO: figure out how to integrate proguard // id "com.github.xaverkapeller.proguard-annotations" } -<<<<<<< HEAD -repositories { - mavenCentral() - gradlePluginPortal() -} - -dependencies { - // Genuinely fuck Java - annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' - compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2' -} - -java { - toolchain.languageVersion = JavaLanguageVersion.of(17) -} - -configure([tasks.compileJava]) { - sourceCompatibility = 17 // for the IDE support - options.release = 11 - - javaCompiler = javaToolchains.compilerFor { - languageVersion = JavaLanguageVersion.of(17) - } -======= base.archivesName = project.project_name version = project.project_version group = project.project_group @@ -68,7 +36,6 @@ java { configure([tasks.compileJava]) { options.release = 11 ->>>>>>> master } jar { -- 2.45.2 From 5b4adf52862dbb03d515c279f4e61d79b6d3470b Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:28:13 +0300 Subject: [PATCH 38/48] a clusterfuck of fixes with let and const --- .../jscript/compilation/CompileResult.java | 39 +++- .../jscript/compilation/CompoundNode.java | 2 +- .../compilation/FunctionArrowNode.java | 7 +- .../jscript/compilation/FunctionNode.java | 136 ++++++------ .../compilation/FunctionStatementNode.java | 7 +- .../compilation/FunctionValueNode.java | 8 +- .../jscript/compilation/JavaScript.java | 44 ++-- .../compilation/VariableDeclareNode.java | 5 +- .../compilation/control/ForInNode.java | 5 +- .../jscript/compilation/control/ForNode.java | 2 +- .../compilation/control/ForOfNode.java | 5 +- .../compilation/control/SwitchNode.java | 2 +- .../jscript/compilation/control/TryNode.java | 3 +- .../compilation/scope/FunctionScope.java | 105 +++++++--- .../compilation/scope/GlobalScope.java | 33 --- .../jscript/compilation/scope/LocalScope.java | 69 ------- .../jscript/compilation/scope/Scope.java | 151 +++++++++++--- .../jscript/compilation/scope/Variable.java | 41 ++++ .../compilation/scope/VariableDescriptor.java | 19 -- .../compilation/scope/VariableList.java | 194 ++++++++---------- .../compilation/values/VariableNode.java | 6 +- src/main/resources/lib/index.js | 2 +- 22 files changed, 486 insertions(+), 399 deletions(-) delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index 1c3d619..b68adc4 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; +import java.util.function.Consumer; import java.util.function.IntFunction; import me.topchetoeu.jscript.common.FunctionBody; @@ -12,15 +13,28 @@ import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder; import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.scope.LocalScope; import me.topchetoeu.jscript.compilation.scope.Scope; public final class CompileResult { + public static final class ChildData { + public final int id; + public final CompileResult result; + + public ChildData(int id, CompileResult result) { + this.result = result; + this.id = id; + } + } + public final List> instructions; + // public final List> childrenTasks; public final List children; public final FunctionMapBuilder map; public final Environment env; public int length; + public Runnable buildTask = () -> { + throw new IllegalStateException("Compile result is not ready to be built"); + }; public final Scope scope; public int temp() { @@ -68,11 +82,16 @@ public final class CompileResult { setLocationAndDebug(instructions.size() - 1, loc, type); } - public CompileResult addChild(CompileResult child) { - this.children.add(child); - return child; + public int addChild(CompileResult res) { + this.children.add(res); + return this.children.size() - 1; } + // public int addChild(Supplier supplier) { + // this.childrenTasks.add(() -> supplier.get()); + // return childrenTasks.size() - 1; + // } + public Instruction[] instructions() { var res = new Instruction[instructions.size()]; var i = 0; @@ -106,15 +125,17 @@ public final class CompileResult { } public CompileResult subtarget() { - return new CompileResult(new LocalScope(scope), this); + return new CompileResult(new Scope(scope), this); } - public CompileResult(Environment env, Scope scope) { + public CompileResult(Environment env, Scope scope, int length, Consumer task) { this.scope = scope; - instructions = new ArrayList<>(); - children = new LinkedList<>(); - map = FunctionMap.builder(); + this.instructions = new ArrayList<>(); + this.children = new LinkedList<>(); + this.map = FunctionMap.builder(); this.env = env; + this.length = length; + this.buildTask = () -> task.accept(this); } private CompileResult(Scope scope, CompileResult parent) { this.scope = scope; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index 19466c4..f89ebd6 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -43,7 +43,7 @@ public class CompoundNode extends Node { if (alloc) { subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount())); } if (!polluted && pollute) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index e6f72fa..851724b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -13,12 +13,9 @@ import me.topchetoeu.jscript.compilation.control.ReturnNode; public class FunctionArrowNode extends FunctionNode { @Override public String name() { return null; } - @Override protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { - target.add(Instruction.loadFunc(id, true, false, true, null, captures)); - } - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - compile(target, pollute, false, name, null, bp); + var id = target.addChild(compileBody(target, false, name, null)); + target.add(_i -> Instruction.loadFunc(id, true, false, true, null, captures(id, target))); } public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index d725326..06f6a3f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -3,12 +3,13 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.scope.FunctionScope; -import me.topchetoeu.jscript.compilation.scope.LocalScope; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public abstract class FunctionNode extends Node { @@ -35,87 +36,90 @@ public abstract class FunctionNode extends Node { // } // } - protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { - target.add(Instruction.loadFunc(id, true, true, false, name, captures)); + // protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { + // target.add(Instruction.loadFunc(id, true, true, false, name, captures)); + // } + + protected final int[] captures(int id, CompileResult target) { + return ((FunctionScope)target.children.get(id).scope).getCaptureIndices(); } - private CompileResult compileBody(CompileResult target, boolean hasArgs, String name, String selfName, boolean pollute, BreakpointType bp) { - var env = target.env.child() + public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, boolean hasArgs, String _name, String selfName) { + var name = this.name() != null ? this.name() : _name; + + env = env.child() .remove(LabelContext.BREAK_CTX) .remove(LabelContext.CONTINUE_CTX); - var funcScope = new FunctionScope(target.scope); - var subtarget = new CompileResult(env, new LocalScope(funcScope)); + return new CompileResult(env, scope, params.params.size(), target -> { + if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs()); - subtarget.length = params.params.size(); - - if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs()); - - if (hasArgs) { - var argsVar = funcScope.defineParam("arguments", true, loc()); - subtarget.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); - } - - if (params.params.size() > 0) { - if (params.params.size() > 1) subtarget.add(Instruction.dup(params.params.size() - 1)); - var i = 0; - - for (var param : params.params) { - if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed"); - if (!JavaScript.checkVarName(param.name)) { - throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name)); - } - var varI = funcScope.defineParam(param.name, false, param.loc); - - subtarget.add(Instruction.loadMember(i++)); - - if (param.node != null) { - var end = new DeferredIntSupplier(); - - subtarget.add(Instruction.dup()); - subtarget.add(Instruction.pushUndefined()); - subtarget.add(Instruction.operation(Operation.EQUALS)); - subtarget.add(Instruction.jmpIfNot(end)); - subtarget.add(Instruction.discard()); - param.node.compile(subtarget, pollute); - - end.set(subtarget.size()); - } - - subtarget.add(Instruction.storeVar(varI.index())); + if (hasArgs) { + var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); + target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); } - } - if (params.restName != null) { - if (funcScope.hasArg(params.restName)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed"); - var restVar = funcScope.defineParam(params.restName, true, params.restLocation); - subtarget.add(Instruction.loadRestArgs(params.params.size())); - subtarget.add(_i -> Instruction.storeVar(restVar.index())); - } + if (params.params.size() > 0) { + if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1)); + var i = 0; - if (selfName != null && !funcScope.hasArg(name)) { - var i = funcScope.defineParam(selfName, true, end); + for (var param : params.params) { + if (scope.has(param.name, false)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed"); + if (!JavaScript.checkVarName(param.name)) { + throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name)); + } + var varI = scope.define(new Variable(param.name, false), param.loc); - subtarget.add(Instruction.loadCallee()); - subtarget.add(_i -> Instruction.storeVar(i.index(), false)); - } + target.add(Instruction.loadMember(i++)); - body.resolve(subtarget); - body.compile(subtarget, false, false, BreakpointType.NONE); + if (param.node != null) { + var end = new DeferredIntSupplier(); - subtarget.scope.end(); - funcScope.end(); + target.add(Instruction.dup()); + target.add(Instruction.pushUndefined()); + target.add(Instruction.operation(Operation.EQUALS)); + target.add(Instruction.jmpIfNot(end)); + target.add(Instruction.discard()); + param.node.compile(target, true); - if (pollute) compileLoadFunc(target, target.children.size(), funcScope.getCaptureIndices(), name); + end.set(target.size()); + } - return target.addChild(subtarget); + target.add(_i -> Instruction.storeVar(varI.index())); + } + } + + if (params.restName != null) { + if (scope.has(params.restName, false)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed"); + var restVar = scope.defineParam(new Variable(params.restName, false), params.restLocation); + target.add(Instruction.loadRestArgs(params.params.size())); + target.add(_i -> Instruction.storeVar(restVar.index())); + } + + if (selfName != null && !scope.has(name, false)) { + var i = scope.defineParam(new Variable(selfName, true), end); + + target.add(Instruction.loadCallee()); + target.add(_i -> Instruction.storeVar(i.index(), false)); + } + + body.resolve(target); + body.compile(target, lastReturn, false, BreakpointType.NONE); + + scope.end(); + + for (var child : target.children) child.buildTask.run(); + + scope.finish(); + }); + + // if (pollute) compileLoadFunc(target, target.children.size(), subscope.getCaptureIndices(), name); + // return target.addChild(subtarget); + } + public final CompileResult compileBody(CompileResult parent, boolean hasArgs, String name, String selfName) { + return compileBody(parent.env, new FunctionScope(parent.scope), false, hasArgs, name, selfName); } - public void compile(CompileResult target, boolean pollute, boolean hasArgs, String name, String selfName, BreakpointType bp) { - if (this.name() != null) name = this.name(); - - compileBody(target, hasArgs, name, selfName, pollute, bp); - } public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); public void compile(CompileResult target, boolean pollute, String name) { compile(target, pollute, name, BreakpointType.NONE); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 9d0398a..0c4be86 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -1,7 +1,9 @@ package me.topchetoeu.jscript.compilation; +import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class FunctionStatementNode extends FunctionNode { @@ -10,11 +12,12 @@ public class FunctionStatementNode extends FunctionNode { @Override public String name() { return name; } @Override public void resolve(CompileResult target) { - target.scope.define(name, false, end); + target.scope.define(new Variable(name, false), end); } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - compile(target, true, true, name, this.name, bp); + var id = target.addChild(compileBody(target, false, name, null)); + target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); target.add(VariableNode.toSet(target, end, this.name, pollute, true)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index cfe0bf8..85f97a0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.compilation; +import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; @@ -8,8 +9,13 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } + // @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { + // compileBody(target, pollute, true, name, null, bp); + // } + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - compile(target, pollute, true, name, null, bp); + var id = target.addChild(compileBody(target, false, name, null)); + target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); } public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index c5574d6..bd9b880 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -1,10 +1,9 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; +import java.util.List; import java.util.Set; -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.ParseRes; @@ -24,8 +23,7 @@ import me.topchetoeu.jscript.compilation.control.SwitchNode; import me.topchetoeu.jscript.compilation.control.ThrowNode; import me.topchetoeu.jscript.compilation.control.TryNode; import me.topchetoeu.jscript.compilation.control.WhileNode; -import me.topchetoeu.jscript.compilation.scope.GlobalScope; -import me.topchetoeu.jscript.compilation.scope.LocalScope; +import me.topchetoeu.jscript.compilation.scope.FunctionScope; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; @@ -326,24 +324,32 @@ public final class JavaScript { } public static CompileResult compile(Environment env, Node ...statements) { - var target = new CompileResult(env, new LocalScope(new GlobalScope())); - var stm = new CompoundNode(null, statements); - var argsI = target.scope.defineStrict("arguments", true, null); - target.add(Instruction.loadArgs()); - target.add(_i -> Instruction.storeVar(argsI.index())); + var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, statements), null); + var res = func.compileBody(env, new FunctionScope(true), true, true, null, null); + res.buildTask.run(); + return res; - // try { - stm.resolve(target); - stm.compile(target, true, false, BreakpointType.NONE); - // FunctionNode.checkBreakAndCont(target, 0); - // } - // catch (SyntaxException e) { - // target = new CompileResult(env, new LocalScope(new GlobalScope())); + // var target = new CompileResult(env, new FunctionScope(true)); + // var stm = ; + // var argsI = target.scope.defineStrict(new Variable("arguments", true), null); + // target.add(Instruction.loadArgs()); + // target.add(_i -> Instruction.storeVar(argsI.index())); - // target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); - // } + // // try { + // stm.resolve(target); + // stm.compile(target, true, false, BreakpointType.NONE); + // // FunctionNode.checkBreakAndCont(target, 0); + // // } + // // catch (SyntaxException e) { + // // target = new CompileResult(env, new LocalScope(new GlobalScope())); - return target; + // // target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); + // // } + + // target.scope.end(); + // target.scope.finish(); + + // return target; } public static CompileResult compile(Environment env, Filename filename, String raw) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 24ec36b..c35dffd 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -10,6 +10,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class VariableDeclareNode extends Node { @@ -31,7 +32,7 @@ public class VariableDeclareNode extends Node { @Override public void resolve(CompileResult target) { if (!declType.strict) { for (var entry : values) { - target.scope.define(entry.name, false, entry.location); + target.scope.define(new Variable(entry.name, false), entry.location); } } } @@ -39,7 +40,7 @@ public class VariableDeclareNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { if (entry.name == null) continue; - if (declType.strict) target.scope.defineStrict(entry.name, declType.readonly, entry.location); + if (declType.strict) target.scope.defineStrict(new Variable(entry.name, declType.readonly), entry.location); if (entry.value != null) { FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index 4a8e4d5..7c65d31 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -13,6 +13,7 @@ import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForInNode extends Node { @@ -24,11 +25,11 @@ public class ForInNode extends Node { @Override public void resolve(CompileResult target) { body.resolve(target); - if (declType != null && !declType.strict) target.scope.define(varName, false, loc()); + if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), loc()); } @Override public void compile(CompileResult target, boolean pollute) { - if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation); + if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation); object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(true)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java index e0d125c..f09148b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -47,7 +47,7 @@ public class ForNode extends Node { if (pollute) subtarget.add(Instruction.pushUndefined()); subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount())); } public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index 6cc1aa9..9f55541 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -12,6 +12,7 @@ import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForOfNode extends Node { @@ -23,11 +24,11 @@ public class ForOfNode extends Node { @Override public void resolve(CompileResult target) { body.resolve(target); - if (declType != null && !declType.strict) target.scope.define(varName, false, varLocation); + if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), varLocation); } @Override public void compile(CompileResult target, boolean pollute) { - if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation); + if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation); iterable.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.dup()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index f41a997..a48f432 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -65,7 +65,7 @@ public class SwitchNode extends Node { LabelContext.getBreak(target.env).pop(label); subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount())); int endI = subtarget.size(); end.set(endI); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java index 04e4650..b972a1a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -12,6 +12,7 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.scope.Variable; public class TryNode extends Node { public final CompoundNode tryBody; @@ -42,7 +43,7 @@ public class TryNode extends Node { if (captureName != null) { var subtarget = target.subtarget(); - subtarget.scope.defineStrict(captureName, false, catchBody.loc()); + subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc()); catchBody.compile(subtarget, false); subtarget.scope.end(); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index d20a277..ee3444d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -1,79 +1,113 @@ package me.topchetoeu.jscript.compilation.scope; import java.util.HashMap; +import java.util.HashSet; import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class FunctionScope extends Scope { private final VariableList captures = new VariableList().setIndexMap(v -> ~v); private final VariableList specials = new VariableList(); private final VariableList locals = new VariableList(specials); - private HashMap childToParent = new HashMap<>(); + private final HashMap childToParent = new HashMap<>(); + private final HashSet blacklistNames = new HashSet<>(); + + private final Scope captureParent; + + public final boolean passtrough; private void removeCapture(String name) { var res = captures.remove(name); - if (res != null) childToParent.remove(res); + if (res != null) { + childToParent.remove(res); + res.setIndexSupplier(() -> { throw new SyntaxException(null, res.name + " has been shadowed"); }); + } } - @Override public VariableDescriptor define(String name, boolean readonly, Location loc) { - var old = locals.get(name); - if (old != null) return old; + @Override public Variable define(Variable var, Location loc) { + checkNotEnded(); + if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name); + // if (specials.has(var.name)) throw alreadyDefinedErr(loc, var.name); + // if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name); - removeCapture(name); - return locals.add(name, readonly); + if (passtrough) { + blacklistNames.add(var.name); + return null; + } + + removeCapture(var.name); + return locals.add(var); } - @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { - if (locals.has(name)) throw alreadyDefinedErr(loc, name); - else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes"); - else return parent.defineStrict(name, readonly, loc); + @Override public Variable defineStrict(Variable var, Location loc) { + checkNotEnded(); + if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name); + if (specials.has(var.name)) throw alreadyDefinedErr(loc, var.name); + if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name); + + var res = super.defineStrict(var, loc); + removeCapture(var.name); + return res; } - public VariableDescriptor defineParam(String name, boolean readonly, Location loc) { - return specials.add(name, readonly); - } - public boolean hasArg(String name) { - return specials.has(name); + public Variable defineParam(Variable var, Location loc) { + return specials.add(var); } - @Override public VariableDescriptor get(String name, boolean capture) { + @Override public boolean flattenVariable(Variable variable, boolean capturable) { + // if (!ended()) throw new IllegalStateException("Tried to flatten a variable before the scope has ended"); + this.locals.overlay(variable); + return true; + } + + @Override public Variable get(String name, boolean capture) { + var superRes = super.get(name, capture); + if (superRes != null) return superRes; + if (specials.has(name)) return specials.get(name); if (locals.has(name)) return locals.get(name); if (captures.has(name)) return captures.get(name); + if (captureParent == null) return null; - var parentVar = parent.get(name, true); + var parentVar = captureParent.get(name, true); if (parentVar == null) return null; - var childVar = captures.add(parentVar); + var childVar = captures.add(parentVar.clone()); childToParent.put(childVar, parentVar); return childVar; } - @Override public boolean has(String name) { + @Override public boolean has(String name, boolean capture) { if (specials.has(name)) return true; if (locals.has(name)) return true; - if (captures.has(name)) return true; - if (parent != null) return parent.has(name); + + if (capture) { + if (captures.has(name)) return true; + if (captureParent != null) return captureParent.has(name, true); + } return false; } - @Override public boolean end() { - if (!super.end()) return false; + @Override public boolean finish() { + if (!super.finish()) return false; captures.freeze(); locals.freeze(); + specials.freeze(); + return true; } - @Override public int localsCount() { - return locals.size() + specials.size(); + @Override public int allocCount() { + return 0; } @Override public int capturesCount() { return captures.size(); } - @Override public int allocCount() { - return 0; + @Override public int localsCount() { + return locals.size() + specials.size() + super.allocCount(); } public int offset() { @@ -84,7 +118,7 @@ public class FunctionScope extends Scope { var res = new int[captures.size()]; var i = 0; - for (var el : captures) { + for (var el : captures.all()) { assert childToParent.containsKey(el); res[i] = childToParent.get(el).index(); i++; @@ -93,10 +127,15 @@ public class FunctionScope extends Scope { return res; } - public FunctionScope() { - super(); - } public FunctionScope(Scope parent) { - super(parent); + super(); + if (parent.finished()) throw new RuntimeException("Parent is finished"); + this.captureParent = parent; + this.passtrough = false; + } + public FunctionScope(boolean passtrough) { + super(); + this.captureParent = null; + this.passtrough = passtrough; } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java deleted file mode 100644 index 6151a8e..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.topchetoeu.jscript.compilation.scope; - -import me.topchetoeu.jscript.common.parsing.Location; - -public final class GlobalScope extends Scope { - @Override public VariableDescriptor define(String name, boolean readonly, Location loc) { - return null; - } - @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { - return null; - } - @Override public VariableDescriptor get(String name, boolean capture) { - return null; - } - @Override public int offset() { - return 0; - } - @Override public boolean has(String name) { - return false; - } - - @Override public int localsCount() { - return 0; - } - @Override public int capturesCount() { - return 0; - } - @Override public int allocCount() { - return 0; - } - - public GlobalScope() { super(); } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java deleted file mode 100644 index 71599f7..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.compilation.scope; - -import me.topchetoeu.jscript.common.parsing.Location; - -public final class LocalScope extends Scope { - private final VariableList locals = new VariableList(() -> { - if (parent != null) return parent.offset(); - else return 0; - }); - - @Override public int offset() { - if (parent != null) return parent.offset() + locals.size(); - else return locals.size(); - } - - @Override public VariableDescriptor define(String name, boolean readonly, Location loc) { - if (locals.has(name)) throw alreadyDefinedErr(loc, name); - - return parent.define(name, readonly, loc); - } - @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { - if (locals.has(name)) throw alreadyDefinedErr(loc, name); - return locals.add(name, readonly); - } - - @Override public VariableDescriptor get(String name, boolean capture) { - var res = locals.get(name); - - if (res != null) return res; - if (parent != null) return parent.get(name, capture); - - return null; - } - - @Override public boolean has(String name) { - if (locals.has(name)) return true; - if (parent != null) return parent.has(name); - - return false; - } - - @Override public boolean end() { - if (!super.end()) return false; - - this.locals.freeze(); - return true; - } - - @Override public int localsCount() { - if (parent == null) return 0; - else return parent.localsCount(); - } - @Override public int capturesCount() { - if (parent == null) return 0; - else return parent.capturesCount(); - } - @Override public int allocCount() { - return locals.size(); - } - - public Iterable all() { - return () -> locals.iterator(); - } - - - public LocalScope(Scope parent) { - super(parent); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java index c41c2bd..7846efd 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -1,24 +1,60 @@ package me.topchetoeu.jscript.compilation.scope; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -public abstract class Scope { - public final Scope parent; - private boolean active = true; +public class Scope { + protected final VariableList variables = new VariableList(this::parentOffset); + + private boolean ended = false; + private boolean finished = false; private Scope child; + private List prevChildren = new LinkedList<>(); + + public final Scope parent; + public final HashSet captured = new HashSet<>(); + + /** + * Wether or not the scope is going to be entered multiple times. + * If set to true, captured variables will be kept as allocations, otherwise will be converted to locals + */ + public boolean singleEntry = true; + + private final int parentOffset() { + if (parent != null) return parent.offset(); + else return 0; + } protected final SyntaxException alreadyDefinedErr(Location loc, String name) { return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name)); } + /** + * Throws if the scope is ended + */ + protected final void checkNotEnded() { + if (ended) throw new IllegalStateException("Cannot define in an ended scope"); + } + /** * Defines an ES5-style variable + * * @returns The index supplier of the variable if it is a local, or null if it is a global * @throws SyntaxException If an ES2015-style variable with the same name exists anywhere from the current function to the current scope * @throws RuntimeException If the scope is finalized or has an active child */ - public abstract VariableDescriptor define(String name, boolean readonly, Location loc); + public Variable define(Variable var, Location loc) { + checkNotEnded(); + if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name); + if (parent != null) return parent.define(var, loc); + + return null; + } + /** * Defines an ES2015-style variable * @param readonly True if const, false if let @@ -26,29 +62,76 @@ public abstract class Scope { * @throws SyntaxException If any variable with the same name exists in the current scope * @throws RuntimeException If the scope is finalized or has an active child */ - public abstract VariableDescriptor defineStrict(String name, boolean readonly, Location loc); + public Variable defineStrict(Variable var, Location loc) { + checkNotEnded(); + if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name); + + variables.add(var); + return var.setIndexSupplier(() -> variables.indexOfKey(var.name)); + } /** * Gets the index supplier of the given variable name, or null if it is a global * - * @param capture Used to signal to the scope that the variable is going to be captured. - * Not passing this could lead to a local variable being optimized out as an ES5-style variable, - * which could break the semantics of a capture + * @param capture If true, the variable is being captured by a function */ - public abstract VariableDescriptor get(String name, boolean capture); - public abstract boolean has(String name); + public Variable get(String name, boolean capture) { + var res = variables.get(name); + if (res != null) return res; + if (parent != null) return parent.get(name, capture); + + return null; + } + /** + * Checks if the given variable name is accessible + * + * @param capture If true, will check beyond this function's scope + */ + public boolean has(String name, boolean capture) { + if (variables.has(name)) return true; + if (parent != null) return parent.has(name, capture); + + return false; + } /** * Gets the index offset from this scope to its children */ - public abstract int offset(); + public int offset() { + if (parent != null) return parent.offset() + variables.size(); + else return variables.size(); + } - public abstract int localsCount(); - public abstract int capturesCount(); - public abstract int allocCount(); + /** + * Adds this variable to the current function's locals record. Capturable indicates whether or not the variable + * should still be capturable, or be put in an array (still not implemented) + * + * @return Whether or not the request was actually fuliflled + */ + public boolean flattenVariable(Variable variable, boolean capturable) { + if (parent == null) return false; + return parent.flattenVariable(variable, capturable); + } - public boolean end() { - if (!active) return false; + public int localsCount() { return 0; } + public int capturesCount() { return 0; } + public int allocCount() { return variables.size(); } + + /** + * Ends this scope. This will make it possible for another child to take its place + */ + public boolean end() { + if (ended) return false; + + this.ended = true; + + for (var v : variables.all()) { + if (captured.contains(v)) { + if (singleEntry) this.flattenVariable(v, true); + } + else { + this.flattenVariable(v, false); + } + } - this.active = false; if (this.parent != null) { assert this.parent.child == this; this.parent.child = null; @@ -57,17 +140,39 @@ public abstract class Scope { return true; } - public final boolean active() { return active; } + /** + * Finalizes this scope. The scope will become immutable after this call + * @return + */ + public boolean finish() { + if (finished) return false; + if (parent != null && !parent.finished) throw new IllegalStateException("Tried to finish a child before the parent was finished"); + + this.variables.freeze(); + this.finished = true; + + for (var child : prevChildren) child.finish(); + + return true; + } + + public final boolean ended() { return ended; } + public final boolean finished() { return finished; } public final Scope child() { return child; } public Scope() { - this.parent = null; + this(null); } public Scope(Scope parent) { - if (!parent.active) throw new RuntimeException("Parent is not active"); - if (parent.child != null) throw new RuntimeException("Parent has an active child"); + if (parent != null) { + if (parent.ended) throw new RuntimeException("Parent is not active"); + if (parent.finished) throw new RuntimeException("Parent is finished"); + if (parent.child != null) throw new RuntimeException("Parent has an active child"); - this.parent = parent; - this.parent.child = this; + this.parent = parent; + this.parent.child = this; + this.parent.prevChildren.add(this); + } + else this.parent = null; } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java new file mode 100644 index 0000000..af74088 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java @@ -0,0 +1,41 @@ +package me.topchetoeu.jscript.compilation.scope; + +import java.util.function.IntSupplier; + +public final class Variable { + private IntSupplier indexSupplier; + private boolean frozen; + + public final boolean readonly; + public final String name; + + public final int index() { + if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized"); + return indexSupplier.getAsInt(); + } + + public final void freeze() { + this.frozen = true; + } + + public final Variable setIndexSupplier(IntSupplier index) { + this.indexSupplier = index; + return this; + } + public final IntSupplier indexSupplier() { + return indexSupplier; + } + + public final Variable clone() { + return new Variable(name, readonly).setIndexSupplier(indexSupplier); + } + + public Variable(String name, boolean readonly) { + this.name = name; + this.readonly = readonly; + } + + public static Variable of(String name, boolean readonly, int i) { + return new Variable(name, readonly).setIndexSupplier(() -> i); + } +} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java deleted file mode 100644 index 29b3bb5..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.compilation.scope; - -public abstract class VariableDescriptor { - public final boolean readonly; - public final String name; - - public abstract int index(); - - public VariableDescriptor(String name, boolean readonly) { - this.name = name; - this.readonly = readonly; - } - - public static VariableDescriptor of(String name, boolean readonly, int i) { - return new VariableDescriptor(name, readonly) { - @Override public int index() { return i; } - }; - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java index 036c313..c67859d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -5,15 +5,17 @@ import java.util.HashMap; import java.util.Iterator; import java.util.function.IntSupplier; import java.util.function.IntUnaryOperator; +import java.util.stream.StreamSupport; -public final class VariableList implements Iterable { - private class ListVar extends VariableDescriptor { - private ListVar next; - private ListVar prev; - private boolean frozen; - private int index; +public final class VariableList { + private final class Node implements IntSupplier { + public Variable var; + public Node next; + public Node prev; + public boolean frozen; + public int index; - @Override public int index() { + @Override public int getAsInt() { if (frozen) { if (offset == null) { return indexConverter == null ? index : indexConverter.applyAsInt(index); @@ -35,41 +37,38 @@ public final class VariableList implements Iterable { return indexConverter == null ? res : indexConverter.applyAsInt(res); } - public ListVar freeze() { - if (frozen) return this; + public void freeze() { + if (frozen) return; this.frozen = true; this.next = null; - if (prev == null) return this; + this.var.freeze(); + if (prev == null) return; this.index = prev.index + 1; this.next = null; - return this; + return; } - public ListVar(String name, boolean readonly, ListVar next, ListVar prev) { - super(name, readonly); - + public Node(Variable var, Node next, Node prev) { + this.var = var; this.next = next; this.prev = prev; } } - private ListVar first, last; + private Node first, last; - private HashMap map = new HashMap<>(); - - private HashMap frozenMap = null; - private ArrayList frozenList = null; + private final HashMap map = new HashMap<>(); + private ArrayList frozenList = null; private final IntSupplier offset; private IntUnaryOperator indexConverter = null; public boolean frozen() { - if (frozenMap != null) { + if (frozenList != null) { assert frozenList != null; - assert frozenMap != null; - assert map == null; + assert map != null; assert first == null; assert last == null; @@ -77,95 +76,87 @@ public final class VariableList implements Iterable { } else { assert frozenList == null; - assert frozenMap == null; assert map != null; return false; } } - public VariableDescriptor add(VariableDescriptor val) { - return add(val.name, val.readonly); - } - public VariableDescriptor add(String name, boolean readonly) { - if (frozen()) throw new RuntimeException("The scope has been frozen"); - if (map.containsKey(name)) return map.get(name); - - var res = new ListVar(name, readonly, null, last); - - if (last != null) { - assert first != null; - - last.next = res; - res.prev = last; - - last = res; - } - else { - first = last = res; - } - - map.put(name, res); - - return res; - } - public VariableDescriptor remove(String name) { + private Variable add(Variable val, boolean overlay) { if (frozen()) throw new RuntimeException("The scope has been frozen"); + if (!overlay && map.containsKey(val.name)) { + var node = this.map.get(val.name); + val.setIndexSupplier(node); + return node.var; + } - var el = map.get(name); - if (el == null) return null; + var node = new Node(val, null, last); - if (el.prev != null) { - assert el != first; - el.prev.next = el.next; + if (last != null) { + assert first != null; + + last.next = node; + node.prev = last; + + last = node; } else { - assert el == first; + first = last = node; + } + + map.put(val.name, node); + val.setIndexSupplier(node); + + return val; + } + + public Variable add(Variable val) { + return this.add(val, false); + } + public Variable overlay(Variable val) { + return this.add(val, true); + } + public Variable remove(String key) { + if (frozen()) throw new RuntimeException("The scope has been frozen"); + + var node = map.get(key); + if (node == null) return null; + + if (node.prev != null) { + assert node != first; + node.prev.next = node.next; + } + else { + assert node == first; first = first.next; } - if (el.next != null) { - assert el != last; - el.next.prev = el.prev; + if (node.next != null) { + assert node != last; + node.next.prev = node.prev; } else { - assert el == last; + assert node == last; last = last.prev; } - el.next = null; - el.prev = null; + node.next = null; + node.prev = null; - return el; + return node.var; } - public VariableDescriptor get(String name) { - if (frozen()) return frozenMap.get(name); - else return map.get(name); + public Variable get(String name) { + var res = map.get(name); + if (res != null) return res.var; + else return null; } - public VariableDescriptor get(int i) { - if (frozen()) { - if (i < 0 || i >= frozenList.size()) return null; - return frozenList.get(i); - } - else { - if (i < 0 || i >= map.size()) return null; - - if (i < map.size() / 2) { - var it = first; - for (var j = 0; j < i; it = it.next, j++); - return it; - } - else { - var it = last; - for (var j = map.size() - 1; j >= i; it = it.prev, j--); - return it; - } - } + public int indexOfKey(String name) { + return map.get(name).getAsInt(); } public boolean has(String name) { - return this.get(name) != null; + return this.map.containsKey(name); } public int size() { @@ -176,47 +167,38 @@ public final class VariableList implements Iterable { public void freeze() { if (frozen()) return; - frozenMap = new HashMap<>(); frozenList = new ArrayList<>(); - for (var it = first; it != null; ) { - frozenMap.put(it.name, it); - frozenList.add(it); + for (var node = first; node != null; ) { + frozenList.add(node); - var tmp = it; - it = it.next; + var tmp = node; + node = node.next; tmp.freeze(); } - map = null; first = last = null; } - @Override public Iterator iterator() { - if (frozen()) return frozenList.iterator(); - else return new Iterator() { - private ListVar curr = first; + public Iterable all() { + if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator(); + else return () -> new Iterator() { + private Node curr = first; @Override public boolean hasNext() { return curr != null; } - @Override public VariableDescriptor next() { + @Override public Variable next() { if (curr == null) return null; var res = curr; curr = curr.next; - return res; + return res.var; } }; } - - public VariableDescriptor[] toArray() { - var res = new VariableDescriptor[size()]; - var i = 0; - - for (var el : this) res[i++] = el; - - return res; + public Iterable keys() { + return () -> StreamSupport.stream(all().spliterator(), false).map(v -> v.name).iterator(); } public VariableList setIndexMap(IntUnaryOperator map) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index bca18a2..1ea10ad 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -35,7 +35,7 @@ public class VariableNode extends Node implements AssignableNode { if (i == null) { target.add(_i -> { - if (target.scope.has(name)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name)); + if (target.scope.has(name, false)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name)); return Instruction.globGet(name); }); @@ -50,7 +50,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); if (i == null) return _i -> { - if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); + if (target.scope.has(name, false)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); else return onGlobal.get(); }; else return _i -> Instruction.loadVar(i.index()); @@ -64,7 +64,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); if (i == null) return _i -> { - if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); + if (target.scope.has(name, false)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); else return Instruction.globSet(name, keep, define); }; else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable")); diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index 1c34848..c807527 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -45,7 +45,7 @@ const unwrapThis = (self, type, constr, name, arg, defaultVal) => { if (typeof self === type) return self; if (self instanceof constr && valueKey in self) self = self[valueKey]; if (typeof self === type) return self; - if (arguments.length > 5) return defaultVal; + if (defaultVal !== undefined) return defaultVal; throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); } -- 2.45.2 From eac4a3af23e63885b33010a04343065b900843fb Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:31:03 +0300 Subject: [PATCH 39/48] fix: throw access before decl for const and let in runtime --- src/main/java/me/topchetoeu/jscript/common/Instruction.java | 4 ++++ .../topchetoeu/jscript/compilation/values/VariableNode.java | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index d8354be..1bb48d5 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.function.IntFunction; import java.util.function.IntSupplier; +import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class Instruction { @@ -261,6 +262,9 @@ public class Instruction { public static Instruction throwSyntax(String err) { return new Instruction(Type.THROW_SYNTAX, err); } + public static Instruction throwSyntax(Location loc, String err) { + return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage()); + } public static Instruction delete() { return new Instruction(Type.DELETE); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 1ea10ad..4e2e276 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -35,7 +35,7 @@ public class VariableNode extends Node implements AssignableNode { if (i == null) { target.add(_i -> { - if (target.scope.has(name, false)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name)); + if (target.scope.has(name, false)) return Instruction.throwSyntax(loc(), String.format("Cannot access '%s' before initialization", name)); return Instruction.globGet(name); }); @@ -50,7 +50,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); if (i == null) return _i -> { - if (target.scope.has(name, false)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); + if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); else return onGlobal.get(); }; else return _i -> Instruction.loadVar(i.index()); @@ -64,7 +64,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); if (i == null) return _i -> { - if (target.scope.has(name, false)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name)); + if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); else return Instruction.globSet(name, keep, define); }; else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable")); -- 2.45.2 From 55caf1e20690887458dc24d5ce5f7b728a123043 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:03:04 +0300 Subject: [PATCH 40/48] refactor: remove unneeded old comments --- .../common/environment/Environment.java | 12 ---------- .../jscript/common/parsing/ParseRes.java | 1 - .../jscript/compilation/CompileResult.java | 9 ------- .../jscript/compilation/FunctionNode.java | 24 ------------------- .../compilation/FunctionValueNode.java | 4 ---- .../jscript/compilation/JavaScript.java | 24 ------------------- .../compilation/VariableDeclareNode.java | 1 - .../compilation/control/BreakNode.java | 1 - .../compilation/control/ContinueNode.java | 1 - .../compilation/control/DoWhileNode.java | 1 - .../compilation/control/SwitchNode.java | 6 ----- .../compilation/control/WhileNode.java | 14 ----------- .../compilation/scope/FunctionScope.java | 2 -- .../compilation/values/VariableNode.java | 9 +------ .../values/operations/DiscardNode.java | 9 ------- .../values/operations/LazyAndNode.java | 8 ------- .../values/operations/LazyOrNode.java | 8 ------- .../values/operations/TypeofNode.java | 9 ------- 18 files changed, 1 insertion(+), 142 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java index 6195fa2..7efce20 100644 --- a/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java +++ b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java @@ -123,18 +123,6 @@ public class Environment { public Environment addAll(Map, ?> map) { return addAll(map, true); } - // public Environment addAll(Environment env) { - // this.map.putAll(env.map); - // this.hidden.removeAll(env.map.keySet()); - - // for (var el : env.multi.entrySet()) { - // for (var val : el.getValue()) { - // add(el.getKey(), val); - // } - // } - - // return this; - // } @SuppressWarnings("unchecked") public Environment remove(Key key) { diff --git a/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java index 7c1f553..17873e4 100644 --- a/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java +++ b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java @@ -59,7 +59,6 @@ public class ParseRes { @SafeVarargs @SuppressWarnings("all") - // to hell with all of java's bullshit generics that do jack shit nothing public static ParseRes first(Source src, int i, Parser ...parsers) { int n = Parsing.skipEmpty(src, i); ParseRes error = ParseRes.failed(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index b68adc4..4234653 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -27,7 +27,6 @@ public final class CompileResult { } public final List> instructions; - // public final List> childrenTasks; public final List children; public final FunctionMapBuilder map; public final Environment env; @@ -58,9 +57,6 @@ public final class CompileResult { instructions.set(i, instr); return this; } - // public Instruction get(int i) { - // return instructions.get(i); - // } public int size() { return instructions.size(); } public void setDebug(Location loc, BreakpointType type) { @@ -87,11 +83,6 @@ public final class CompileResult { return this.children.size() - 1; } - // public int addChild(Supplier supplier) { - // this.childrenTasks.add(() -> supplier.get()); - // return childrenTasks.size() - 1; - // } - public Instruction[] instructions() { var res = new Instruction[instructions.size()]; var i = 0; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 06f6a3f..6433c32 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -19,27 +19,6 @@ public abstract class FunctionNode extends Node { public abstract String name(); - // @Override public void declare(CompileResult target) { - // if (varName != null && statement) target.scope.define(varName); - // } - - // public static void checkBreakAndCont(CompileResult target, int start) { - // for (int i = start; i < target.size(); i++) { - // if (target.get(i).type == Type.NOP) { - // if (target.get(i).is(0, "break") ) { - // throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop."); - // } - // if (target.get(i).is(0, "cont")) { - // throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop."); - // } - // } - // } - // } - - // protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { - // target.add(Instruction.loadFunc(id, true, true, false, name, captures)); - // } - protected final int[] captures(int id, CompileResult target) { return ((FunctionScope)target.children.get(id).scope).getCaptureIndices(); } @@ -112,9 +91,6 @@ public abstract class FunctionNode extends Node { scope.finish(); }); - - // if (pollute) compileLoadFunc(target, target.children.size(), subscope.getCaptureIndices(), name); - // return target.addChild(subtarget); } public final CompileResult compileBody(CompileResult parent, boolean hasArgs, String name, String selfName) { return compileBody(parent.env, new FunctionScope(parent.scope), false, hasArgs, name, selfName); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index 85f97a0..f0bfd5f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -9,10 +9,6 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } - // @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - // compileBody(target, pollute, true, name, null, bp); - // } - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, false, name, null)); target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index bd9b880..676fdb2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -113,11 +113,9 @@ public final class JavaScript { if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), n); if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); - // if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); // if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); - // if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); return ParseRes.failed(); } @@ -328,28 +326,6 @@ public final class JavaScript { var res = func.compileBody(env, new FunctionScope(true), true, true, null, null); res.buildTask.run(); return res; - - // var target = new CompileResult(env, new FunctionScope(true)); - // var stm = ; - // var argsI = target.scope.defineStrict(new Variable("arguments", true), null); - // target.add(Instruction.loadArgs()); - // target.add(_i -> Instruction.storeVar(argsI.index())); - - // // try { - // stm.resolve(target); - // stm.compile(target, true, false, BreakpointType.NONE); - // // FunctionNode.checkBreakAndCont(target, 0); - // // } - // // catch (SyntaxException e) { - // // target = new CompileResult(env, new LocalScope(new GlobalScope())); - - // // target.add(Instruction.throwSyntax(e)).setLocation(stm.loc()); - // // } - - // target.scope.end(); - // target.scope.finish(); - - // return target; } public static CompileResult compile(Environment env, Filename filename, String raw) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index c35dffd..5fc137f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -36,7 +36,6 @@ public class VariableDeclareNode extends Node { } } } - // let a = 10, b = "test"; var c = () => a + b @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { if (entry.name == null) continue; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java index b74e2a4..b09b8ca 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java @@ -22,7 +22,6 @@ public class BreakNode extends Node { } target.add(res); - // target.add(Instruction.nop("break", label)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java index afc94a5..da7eebb 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java @@ -22,7 +22,6 @@ public class ContinueNode extends Node { } target.add(res); - // () -> Instruction.nop("cont", label)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java index c07a004..1f3de82 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java @@ -34,7 +34,6 @@ public class DoWhileNode extends Node { int endI = target.size(); end.set(endI + 1); - // WhileNode.replaceBreaks(target, label, start, mid - 1, mid, end + 1); target.add(Instruction.jmpIf(start - endI)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index a48f432..e3b1496 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -75,12 +75,6 @@ public class SwitchNode extends Node { if (defaultI < 0 || defaultI >= body.length) subtarget.set(start, Instruction.jmp(endI - start)); else subtarget.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start)); - // for (int i = start; i < end; i++) { - // var instr = target.get(i); - // if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) { - // target.set(i, Instruction.jmp(end - i)); - // } - // } for (var el : caseToStatement.entrySet()) { var i = statementToIndex.get(el.getValue()); if (i == null) i = endI; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index 55787df..94330cf 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -34,8 +34,6 @@ public class WhileNode extends Node { var endI = target.size(); end.set(endI + 1); - // replaceBreaks(target, label, mid + 1, end, start, end + 1); - target.add(Instruction.jmp(start - end.getAsInt())); target.set(mid, Instruction.jmpIfNot(end.getAsInt() - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); @@ -48,18 +46,6 @@ public class WhileNode extends Node { this.body = body; } - // public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) { - // for (int i = start; i < end; i++) { - // var instr = target.get(i); - // if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) { - // target.set(i, Instruction.jmp(continuePoint - i)); - // } - // if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) { - // target.set(i, Instruction.jmp(breakPoint - i)); - // } - // } - // } - public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index ee3444d..550817c 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -28,8 +28,6 @@ public class FunctionScope extends Scope { @Override public Variable define(Variable var, Location loc) { checkNotEnded(); if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name); - // if (specials.has(var.name)) throw alreadyDefinedErr(loc, var.name); - // if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name); if (passtrough) { blacklistNames.add(var.name); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 4e2e276..02c4d36 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -19,13 +19,6 @@ import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class VariableNode extends Node implements AssignableNode { public final String name; - // @Override public EvalResult evaluate(CompileResult target) { - // var i = target.scope.getKey(name); - - // if (i instanceof String) return EvalResult.NONE; - // else return EvalResult.UNKNOWN; - // } - @Override public Node toAssign(Node val, Operation operation) { return new VariableAssignNode(loc(), name, val, operation); } @@ -57,7 +50,7 @@ public class VariableNode extends Node implements AssignableNode { } public static IntFunction toGet(CompileResult target, Location loc, String name) { return toGet(target, loc, name, () -> Instruction.globGet(name)); - } + } public static IntFunction toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java index a68ebb6..eaf7b89 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java @@ -13,15 +13,6 @@ import me.topchetoeu.jscript.compilation.Node; public class DiscardNode extends Node { public final Node value; - // @Override public EvalResult evaluate(CompileResult target) { - // if (value == null) return EvalResult.FALSY; - // var res = value.evaluate(target); - - // if (res.isPure) return EvalResult.FALSY; - // else if (res.never) return EvalResult.NEVER; - // else return EvalResult.FALSY_IMPURE; - // } - @Override public void compile(CompileResult target, boolean pollute) { if (value != null) value.compile(target, false); if (pollute) target.add(Instruction.pushUndefined()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java index a63506b..318751e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java @@ -12,14 +12,6 @@ import me.topchetoeu.jscript.compilation.Node; public class LazyAndNode extends Node { public final Node first, second; - // @Override public EvalResult evaluate(CompileResult target) { - // var firstRes = first.evaluate(target); - // if (firstRes.falsy) return firstRes; - // if (!firstRes.isPure) return firstRes; - - // return second.evaluate(target); - // } - @Override public void compile(CompileResult target, boolean pollute) { first.compile(target, true); if (pollute) target.add(Instruction.dup()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java index ac374ce..9b1e5a9 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java @@ -13,14 +13,6 @@ import me.topchetoeu.jscript.compilation.Node; public class LazyOrNode extends Node { public final Node first, second; - // @Override public EvalResult evaluate(CompileResult target) { - // var firstRes = first.evaluate(target); - // if (firstRes.truthy) return firstRes; - // if (!firstRes.isPure) return firstRes; - - // return second.evaluate(target); - // } - @Override public void compile(CompileResult target, boolean pollute) { first.compile(target, true); if (pollute) target.add(Instruction.dup()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java index 15f8387..4d3b07d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java @@ -14,15 +14,6 @@ import me.topchetoeu.jscript.compilation.values.VariableNode; public class TypeofNode extends Node { public final Node value; - // @Override public EvalResult evaluate(CompileResult target) { - // if (value instanceof VariableNode) { - // var i = target.scope.getKey(((VariableNode)value).name); - // if (i instanceof String) return EvalResult.NONE; - // } - - // return EvalResult.UNKNOWN; - // } - @Override public void compile(CompileResult target, boolean pollute) { if (value instanceof VariableNode varNode) { target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, () -> Instruction.typeof(varNode.name))); -- 2.45.2 From b6a90b108ba345bb08937595aed5dd4e1d18a771 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:03:36 +0300 Subject: [PATCH 41/48] fix: wrong check for var decl collisions --- .../me/topchetoeu/jscript/compilation/scope/FunctionScope.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 550817c..85bb3eb 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -27,7 +27,7 @@ public class FunctionScope extends Scope { @Override public Variable define(Variable var, Location loc) { checkNotEnded(); - if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name); + if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name); if (passtrough) { blacklistNames.add(var.name); -- 2.45.2 From e509edc4595983d326999525f988494e1632abf7 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:03:49 +0300 Subject: [PATCH 42/48] fix: wrong arguments when compilling function bodies --- .../topchetoeu/jscript/compilation/FunctionStatementNode.java | 2 +- .../me/topchetoeu/jscript/compilation/FunctionValueNode.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 0c4be86..b90307a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -16,7 +16,7 @@ public class FunctionStatementNode extends FunctionNode { } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, false, name, null)); + var id = target.addChild(compileBody(target, true, name, null)); target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); target.add(VariableNode.toSet(target, end, this.name, pollute, true)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index f0bfd5f..2b60022 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -10,7 +10,7 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, false, name, null)); + var id = target.addChild(compileBody(target, true, name, null)); target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); } -- 2.45.2 From d7353e19ed5bfe58f9fc08a2b553e7875af1b7a8 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:18:53 +0300 Subject: [PATCH 43/48] fix: treat "arguments" as a keyword (as per strict soecifications) --- .../jscript/common/Instruction.java | 4 ++-- .../compilation/FunctionArrowNode.java | 24 +++++++++++-------- .../jscript/compilation/FunctionNode.java | 10 ++++---- .../jscript/compilation/JavaScript.java | 5 ++-- .../compilation/values/ArgumentsNode.java | 17 +++++++++++++ .../values/operations/CallNode.java | 4 ++++ .../me/topchetoeu/jscript/runtime/Frame.java | 3 ++- .../jscript/runtime/InstructionRunner.java | 8 +++++-- .../values/functions/CodeFunction.java | 8 +++++-- 9 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index 1bb48d5..838049c 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -352,8 +352,8 @@ public class Instruction { public static Instruction loadThis() { return new Instruction(Type.LOAD_THIS); } - public static Instruction loadArgs() { - return new Instruction(Type.LOAD_ARGS); + public static Instruction loadArgs(boolean real) { + return new Instruction(Type.LOAD_ARGS, real); } public static Instruction loadRestArgs(int offset) { return new Instruction(Type.LOAD_REST_ARGS, offset); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index 851724b..7aaeb93 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -54,17 +54,21 @@ public class FunctionArrowNode extends FunctionNode { if (!src.is(i + n, "=>")) return ParseRes.failed(); n += 2; + n += Parsing.skipEmpty(src, i + n); - ParseRes body = ParseRes.first(src, i + n, - (s, j) -> JavaScript.parseExpression(s, j, 2), - CompoundNode::parse - ); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected an expression or a compount statement after '=>'"); - n += body.n; + if (src.is(i + n, "{")) { + var body = CompoundNode.parse(src, i + n); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'"); + n += body.n; - return ParseRes.res(new FunctionArrowNode( - loc, src.loc(i + n - 1), - params, body.result - ), n); + return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n); + } + else { + var body = JavaScript.parseExpression(src, i + n, 2); + if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'"); + n += body.n; + + return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n); + } } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 6433c32..876f21a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -31,12 +31,12 @@ public abstract class FunctionNode extends Node { .remove(LabelContext.CONTINUE_CTX); return new CompileResult(env, scope, params.params.size(), target -> { - if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs()); + if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs(true)); - if (hasArgs) { - var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); - target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); - } + // if (hasArgs) { + // var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); + // target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); + // } if (params.params.size() > 0) { if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index 676fdb2..c8bf4ef 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -24,6 +24,7 @@ import me.topchetoeu.jscript.compilation.control.ThrowNode; import me.topchetoeu.jscript.compilation.control.TryNode; import me.topchetoeu.jscript.compilation.control.WhileNode; import me.topchetoeu.jscript.compilation.scope.FunctionScope; +import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; @@ -60,7 +61,7 @@ public final class JavaScript { "finally", "for", "do", "while", "switch", "case", "default", "new", "function", "var", "return", "throw", "typeof", "delete", "break", "continue", "debugger", "implements", "interface", "package", "private", - "protected", "public", "static" + "protected", "public", "static", "arguments" ); public static ParseRes parseParens(Source src, int i) { @@ -115,7 +116,7 @@ public final class JavaScript { if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); - // if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); + if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); return ParseRes.failed(); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java new file mode 100644 index 0000000..38ab529 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java @@ -0,0 +1,17 @@ +package me.topchetoeu.jscript.compilation.values; + +import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Node; + + +public class ArgumentsNode extends Node { + @Override public void compile(CompileResult target, boolean pollute) { + if (pollute) target.add(Instruction.loadArgs(false)); + } + + public ArgumentsNode(Location loc) { + super(loc); + } +} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index 91dbc78..74f11c0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -13,6 +13,7 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.ThisNode; @@ -55,6 +56,9 @@ public class CallNode extends Node { else if (func instanceof ThisNode) { res = "this"; } + else if (func instanceof ArgumentsNode) { + res = "arguments"; + } else if (func instanceof ArrayNode) { var els = new ArrayList(); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index acfb0af..31f9434 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -101,8 +101,9 @@ public final class Frame { */ public final Value[][] captures; public final List locals = new ArrayList<>(); - public final Value self; public final Value argsVal; + public Value self; + public Value fakeArgs; public final Value[] args; public final boolean isNew; public final Stack tryStack = new Stack<>(); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 48fcd01..585a389 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -185,7 +185,10 @@ public class InstructionRunner { var func = new CodeFunction(env, name, frame.function.body.children[id], captures); if (!callable) func.enableCall = false; if (!constructible) func.enableNew = false; - if (captureThis) func.self = frame.self; + if (captureThis) { + func.self = frame.self; + func.argsVal = frame.argsVal; + } frame.push(func); frame.codePtr++; @@ -472,7 +475,8 @@ public class InstructionRunner { } private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) { - frame.push(frame.argsVal); + if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal); + else frame.push(frame.fakeArgs); frame.codePtr++; return null; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index d26db0d..7d900fa 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -9,6 +9,7 @@ public final class CodeFunction extends FunctionValue { public final FunctionBody body; public final Value[][] captures; public Value self; + public Value argsVal; public Environment env; private Value onCall(Frame frame) { @@ -26,8 +27,11 @@ public final class CodeFunction extends FunctionValue { } @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { - if (self != null) return onCall(new Frame(env, isNew, self, args, this)); - else return onCall(new Frame(env, isNew, thisArg, args, this)); + var frame = new Frame(env, isNew, thisArg, args, this); + if (argsVal != null) frame.fakeArgs = argsVal; + if (self != null) frame.self = self; + + return onCall(frame); } public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { -- 2.45.2 From 8f13ff3e0b3925098cbeb0dfa044d19331aa9e47 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:29:20 +0300 Subject: [PATCH 44/48] fix: scope gets polluted by arguments with named function expressions --- .../jscript/compilation/FunctionArrowNode.java | 2 +- .../jscript/compilation/FunctionNode.java | 13 +++++++------ .../jscript/compilation/FunctionStatementNode.java | 2 +- .../jscript/compilation/FunctionValueNode.java | 2 +- .../topchetoeu/jscript/compilation/JavaScript.java | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index 7aaeb93..f816f3f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -14,7 +14,7 @@ public class FunctionArrowNode extends FunctionNode { @Override public String name() { return null; } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, false, name, null)); + var id = target.addChild(compileBody(target, name, null)); target.add(_i -> Instruction.loadFunc(id, true, false, true, null, captures(id, target))); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 876f21a..ea13605 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -23,7 +23,7 @@ public abstract class FunctionNode extends Node { return ((FunctionScope)target.children.get(id).scope).getCaptureIndices(); } - public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, boolean hasArgs, String _name, String selfName) { + public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) { var name = this.name() != null ? this.name() : _name; env = env.child() @@ -31,7 +31,7 @@ public abstract class FunctionNode extends Node { .remove(LabelContext.CONTINUE_CTX); return new CompileResult(env, scope, params.params.size(), target -> { - if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs(true)); + // if (params.params.size() > 0) target.add(Instruction.loadArgs(true)); // if (hasArgs) { // var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); @@ -39,6 +39,7 @@ public abstract class FunctionNode extends Node { // } if (params.params.size() > 0) { + target.add(Instruction.loadArgs(true)); if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1)); var i = 0; @@ -70,13 +71,13 @@ public abstract class FunctionNode extends Node { if (params.restName != null) { if (scope.has(params.restName, false)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed"); - var restVar = scope.defineParam(new Variable(params.restName, false), params.restLocation); + var restVar = scope.define(new Variable(params.restName, false), params.restLocation); target.add(Instruction.loadRestArgs(params.params.size())); target.add(_i -> Instruction.storeVar(restVar.index())); } if (selfName != null && !scope.has(name, false)) { - var i = scope.defineParam(new Variable(selfName, true), end); + var i = scope.defineSpecial(new Variable(selfName, true), end); target.add(Instruction.loadCallee()); target.add(_i -> Instruction.storeVar(i.index(), false)); @@ -92,8 +93,8 @@ public abstract class FunctionNode extends Node { scope.finish(); }); } - public final CompileResult compileBody(CompileResult parent, boolean hasArgs, String name, String selfName) { - return compileBody(parent.env, new FunctionScope(parent.scope), false, hasArgs, name, selfName); + public final CompileResult compileBody(CompileResult parent, String name, String selfName) { + return compileBody(parent.env, new FunctionScope(parent.scope), false, name, selfName); } public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index b90307a..550730a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -16,7 +16,7 @@ public class FunctionStatementNode extends FunctionNode { } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, true, name, null)); + var id = target.addChild(compileBody(target, name, null)); target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); target.add(VariableNode.toSet(target, end, this.name, pollute, true)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index 2b60022..db22d72 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -10,7 +10,7 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, true, name, null)); + var id = target.addChild(compileBody(target, name, null)); target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index c8bf4ef..94dfcd2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -324,7 +324,7 @@ public final class JavaScript { public static CompileResult compile(Environment env, Node ...statements) { var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, statements), null); - var res = func.compileBody(env, new FunctionScope(true), true, true, null, null); + var res = func.compileBody(env, new FunctionScope(true), true, null, null); res.buildTask.run(); return res; } -- 2.45.2 From 9ec99def3f09b54b5bbc041fff5d6e69deee4767 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:29:42 +0300 Subject: [PATCH 45/48] fix: variable declarations shouldn't collide with defined name of named function exp --- .../me/topchetoeu/jscript/compilation/scope/FunctionScope.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 85bb3eb..757c4d8 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -40,14 +40,13 @@ public class FunctionScope extends Scope { @Override public Variable defineStrict(Variable var, Location loc) { checkNotEnded(); if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name); - if (specials.has(var.name)) throw alreadyDefinedErr(loc, var.name); if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name); var res = super.defineStrict(var, loc); removeCapture(var.name); return res; } - public Variable defineParam(Variable var, Location loc) { + public Variable defineSpecial(Variable var, Location loc) { return specials.add(var); } -- 2.45.2 From 4bfc062aaf1fc94e48525578dbf886caf8561988 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:13:34 +0300 Subject: [PATCH 46/48] fix: correctly flatten locals in control flow statements --- .../jscript/compilation/CompoundNode.java | 34 +++++++++---- .../compilation/FunctionArrowNode.java | 2 +- .../jscript/compilation/FunctionNode.java | 2 +- .../jscript/compilation/JavaScript.java | 2 +- .../compilation/VariableDeclareNode.java | 2 +- .../compilation/control/ForInNode.java | 3 +- .../jscript/compilation/control/ForNode.java | 7 +-- .../compilation/control/ForOfNode.java | 3 +- .../compilation/control/WhileNode.java | 3 +- .../compilation/scope/FunctionScope.java | 7 +-- .../jscript/compilation/scope/Scope.java | 49 +++++++++++++------ .../compilation/scope/VariableList.java | 14 +++++- .../compilation/values/VariableNode.java | 6 +-- 13 files changed, 92 insertions(+), 42 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index f89ebd6..29888f6 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -13,17 +13,21 @@ import me.topchetoeu.jscript.common.parsing.Source; public class CompoundNode extends Node { public final Node[] statements; + public final boolean hasScope; public Location end; @Override public void resolve(CompileResult target) { for (var stm : statements) stm.resolve(target); } - public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) { + public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) { List statements = new ArrayList(); - var subtarget = alloc ? target.subtarget() : target; - if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount())); + var subtarget = hasScope ? target.subtarget() : target; + if (hasScope) { + subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount())); + subtarget.scope.singleEntry = singleEntry; + } for (var stm : this.statements) { if (stm instanceof FunctionStatementNode func) { @@ -41,7 +45,7 @@ public class CompoundNode extends Node { else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER); } - if (alloc) { + if (hasScope) { subtarget.scope.end(); subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount())); } @@ -60,11 +64,21 @@ public class CompoundNode extends Node { return this; } - public CompoundNode(Location loc, Node ...statements) { + public CompoundNode(Location loc, boolean hasScope, Node ...statements) { super(loc); + this.hasScope = hasScope; this.statements = statements; } + public static void compileMultiEntry(Node node, CompileResult target, boolean pollute, BreakpointType type) { + if (node instanceof CompoundNode comp) { + comp.compile(target, pollute, false, type); + } + else { + node.compile(target, pollute, type); + } + } + public static ParseRes parseComma(Source src, int i, Node prev, int precedence) { if (precedence > 1) return ParseRes.failed(); @@ -78,14 +92,14 @@ public class CompoundNode extends Node { if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma"); n += curr.n; - if (prev instanceof CompoundNode) { + if (prev instanceof CompoundNode comp) { var children = new ArrayList(); - children.addAll(List.of(((CompoundNode)prev).statements)); + children.addAll(List.of(comp.statements)); children.add(curr.result); - return ParseRes.res(new CompoundNode(loc, children.toArray(Node[]::new)), n); + return ParseRes.res(new CompoundNode(loc, comp.hasScope, children.toArray(Node[]::new)), n); } - else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n); + else return ParseRes.res(new CompoundNode(loc, false, prev, curr.result), n); } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); @@ -115,6 +129,6 @@ public class CompoundNode extends Node { statements.add(res.result); } - return ParseRes.res(new CompoundNode(loc, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n); + return ParseRes.res(new CompoundNode(loc, true, statements.toArray(Node[]::new)).setEnd(src.loc(i + n - 1)), n); } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index f816f3f..036533c 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -24,7 +24,7 @@ public class FunctionArrowNode extends FunctionNode { private static final CompoundNode expToBody(Node node) { if (node instanceof CompoundNode res) return res; - else return new CompoundNode(node.loc(), new ReturnNode(node.loc(), node)); + else return new CompoundNode(node.loc(), false, new ReturnNode(node.loc(), node)); } public static ParseRes parse(Source src, int i) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index ea13605..154b360 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -84,7 +84,7 @@ public abstract class FunctionNode extends Node { } body.resolve(target); - body.compile(target, lastReturn, false, BreakpointType.NONE); + body.compile(target, lastReturn, BreakpointType.NONE); scope.end(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index 94dfcd2..5e06b59 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -323,7 +323,7 @@ public final class JavaScript { } public static CompileResult compile(Environment env, Node ...statements) { - var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, statements), null); + var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, true, statements), null); var res = func.compileBody(env, new FunctionScope(true), true, null, null); res.buildTask.run(); return res; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 5fc137f..7584d48 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -46,7 +46,7 @@ public class VariableDeclareNode extends Node { target.add(VariableNode.toSet(target, entry.location, entry.name, false, true)); } else target.add(_i -> { - var i = target.scope.get(entry.name, true); + var i = target.scope.get(entry.name, false); if (i == null) return Instruction.globDef(entry.name); else return Instruction.nop(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index 7c65d31..59688a5 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -8,6 +8,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; @@ -47,7 +48,7 @@ public class ForInNode extends Node { var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); - body.compile(target, false, BreakpointType.STEP_OVER); + CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); LabelContext.popLoop(target.env, label); int endI = target.size(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java index f09148b..8e9651a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; @@ -29,16 +30,16 @@ public class ForNode extends Node { declaration.compile(subtarget, false, BreakpointType.STEP_OVER); int start = subtarget.size(); - condition.compile(subtarget, true, BreakpointType.STEP_OVER); + CompoundNode.compileMultiEntry(condition, subtarget, true, BreakpointType.STEP_OVER); int mid = subtarget.temp(); var end = new DeferredIntSupplier(); LabelContext.pushLoop(subtarget.env, loc(), label, end, start); - body.compile(subtarget, false, BreakpointType.STEP_OVER); + CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); LabelContext.popLoop(subtarget.env, label); - assignment.compile(subtarget, false, BreakpointType.STEP_OVER); + CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER); int endI = subtarget.size(); end.set(endI); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index 9f55541..b675e7b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; @@ -51,7 +52,7 @@ public class ForOfNode extends Node { var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); - body.compile(target, false, BreakpointType.STEP_OVER); + CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); LabelContext.popLoop(target.env, label); int endI = target.size(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index 94330cf..d6d9447 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; @@ -28,7 +29,7 @@ public class WhileNode extends Node { LabelContext.pushLoop(target.env, loc(), label, end, start); - body.compile(target, false, BreakpointType.STEP_OVER); + CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); LabelContext.popLoop(target.env, label); var endI = target.size(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 757c4d8..4f8f143 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -60,9 +60,10 @@ public class FunctionScope extends Scope { var superRes = super.get(name, capture); if (superRes != null) return superRes; - if (specials.has(name)) return specials.get(name); - if (locals.has(name)) return locals.get(name); - if (captures.has(name)) return captures.get(name); + if (specials.has(name)) return addCaptured(specials.get(name), capture); + if (locals.has(name)) return addCaptured(locals.get(name), capture); + if (captures.has(name)) return addCaptured(captures.get(name), capture); + if (captureParent == null) return null; var parentVar = captureParent.get(name, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java index 7846efd..3129e36 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -18,6 +18,11 @@ public class Scope { public final Scope parent; public final HashSet captured = new HashSet<>(); + protected final Variable addCaptured(Variable var, boolean captured) { + if (captured) this.captured.add(var); + return var; + } + /** * Wether or not the scope is going to be entered multiple times. * If set to true, captured variables will be kept as allocations, otherwise will be converted to locals @@ -76,7 +81,7 @@ public class Scope { */ public Variable get(String name, boolean capture) { var res = variables.get(name); - if (res != null) return res; + if (res != null) return addCaptured(res, capture); if (parent != null) return parent.get(name, capture); return null; @@ -107,8 +112,14 @@ public class Scope { * @return Whether or not the request was actually fuliflled */ public boolean flattenVariable(Variable variable, boolean capturable) { - if (parent == null) return false; - return parent.flattenVariable(variable, capturable); + if (singleEntry || !capturable) { + if (parent == null) return false; + return parent.flattenVariable(variable, capturable); + } + else { + variables.overlay(variable); + return true; + } } public int localsCount() { return 0; } @@ -123,15 +134,6 @@ public class Scope { this.ended = true; - for (var v : variables.all()) { - if (captured.contains(v)) { - if (singleEntry) this.flattenVariable(v, true); - } - else { - this.flattenVariable(v, false); - } - } - if (this.parent != null) { assert this.parent.child == this; this.parent.child = null; @@ -146,13 +148,30 @@ public class Scope { */ public boolean finish() { if (finished) return false; - if (parent != null && !parent.finished) throw new IllegalStateException("Tried to finish a child before the parent was finished"); + if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished"); + + for (var child : prevChildren) child.finish(); + + var captured = new HashSet(); + var normal = new HashSet(); + + for (var v : variables.all()) { + if (this.captured.contains(v)) { + if (singleEntry) captured.add(v); + } + else normal.add(v); + } + + for (var v : captured) variables.remove(v); + for (var v : normal) variables.remove(v); + + for (var v : captured) flattenVariable(v, true); + for (var v : normal) flattenVariable(v, false); + this.variables.freeze(); this.finished = true; - for (var child : prevChildren) child.finish(); - return true; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java index c67859d..9dfed4b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -61,6 +61,7 @@ public final class VariableList { private final HashMap map = new HashMap<>(); private ArrayList frozenList = null; + private HashMap varMap = new HashMap<>(); private final IntSupplier offset; private IntUnaryOperator indexConverter = null; @@ -105,6 +106,7 @@ public final class VariableList { } map.put(val.name, node); + varMap.put(val, node); val.setIndexSupplier(node); return val; @@ -117,9 +119,15 @@ public final class VariableList { return this.add(val, true); } public Variable remove(String key) { + var res = map.get(key); + if (res != null) return remove(res.var); + else return null; + } + public Variable remove(Variable var) { + if (var == null) return null; if (frozen()) throw new RuntimeException("The scope has been frozen"); - var node = map.get(key); + var node = varMap.get(var); if (node == null) return null; if (node.prev != null) { @@ -143,6 +151,9 @@ public final class VariableList { node.next = null; node.prev = null; + map.remove(node.var.name); + varMap.remove(node.var); + return node.var; } @@ -178,6 +189,7 @@ public final class VariableList { } first = last = null; + varMap = null; } public Iterable all() { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 02c4d36..0bf8ea6 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -24,7 +24,7 @@ public class VariableNode extends Node implements AssignableNode { } @Override public void compile(CompileResult target, boolean pollute) { - var i = target.scope.get(name, true); + var i = target.scope.get(name, false); if (i == null) { target.add(_i -> { @@ -40,7 +40,7 @@ public class VariableNode extends Node implements AssignableNode { } public static IntFunction toGet(CompileResult target, Location loc, String name, Supplier onGlobal) { - var i = target.scope.get(name, true); + var i = target.scope.get(name, false); if (i == null) return _i -> { if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); @@ -54,7 +54,7 @@ public class VariableNode extends Node implements AssignableNode { public static IntFunction toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { - var i = target.scope.get(name, true); + var i = target.scope.get(name, false); if (i == null) return _i -> { if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); -- 2.45.2 From 07411f62c8d203c3bb17a88e370abd7f96752e66 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:14:59 +0300 Subject: [PATCH 47/48] feat: implement capturable locals realloc --- .../me/topchetoeu/jscript/common/Instruction.java | 14 +++++++++----- .../jscript/runtime/InstructionRunner.java | 11 ++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index 838049c..d846072 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -64,7 +64,8 @@ public class Instruction { GLOB_DEF(0x62), STACK_ALLOC(0x70), - STACK_FREE(0x71); + STACK_REALLOC(0x71), + STACK_FREE(0x72); private static final HashMap types = new HashMap<>(); public final int numeric; @@ -456,11 +457,14 @@ public class Instruction { return new Instruction(Type.OPERATION, op); } - public static Instruction stackAlloc(int i) { - return new Instruction(Type.STACK_ALLOC, i); + public static Instruction stackAlloc(int n) { + return new Instruction(Type.STACK_ALLOC, n); } - public static Instruction stackFree(int i) { - return new Instruction(Type.STACK_FREE, i); + public static Instruction stackRealloc(int n) { + return new Instruction(Type.STACK_REALLOC, n); + } + public static Instruction stackFree(int n) { + return new Instruction(Type.STACK_FREE, n); } @Override public String toString() { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 585a389..67badc2 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -505,7 +505,15 @@ public class InstructionRunner { int n = instr.get(0); for (var i = 0; i < n; i++) frame.locals.add(new Value[] { Value.UNDEFINED }); - + + frame.codePtr++; + return null; + } + private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) { + int n = instr.get(0); + + for (var i = frame.locals.size() - n; i < frame.locals.size(); i++) frame.locals.set(i, new Value[] { frame.locals.get(i)[0] }); + frame.codePtr++; return null; } @@ -576,6 +584,7 @@ public class InstructionRunner { case GLOB_SET: return exexGlobSet(env, instr, frame); case STACK_ALLOC: return execStackAlloc(env, instr, frame); + case STACK_REALLOC: return execStackRealloc(env, instr, frame); case STACK_FREE: return execStackFree(env, instr, frame); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); -- 2.45.2 From 0004839f6f1cd1782be1e15d1e4ade31c2c212fd Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:15:26 +0300 Subject: [PATCH 48/48] fix: realloc for declarations after each iteration --- .../me/topchetoeu/jscript/compilation/control/ForNode.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java index 8e9651a..e2181e2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -25,6 +25,7 @@ public class ForNode extends Node { } @Override public void compile(CompileResult target, boolean pollute) { var subtarget = target.subtarget(); + subtarget.scope.singleEntry = false; subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount())); declaration.compile(subtarget, false, BreakpointType.STEP_OVER); @@ -39,8 +40,11 @@ public class ForNode extends Node { CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); LabelContext.popLoop(subtarget.env, label); + subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.allocCount())); + CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER); int endI = subtarget.size(); + end.set(endI); subtarget.add(Instruction.jmp(start - endI)); -- 2.45.2