diff --git a/build.js b/build.js index 208d4a1..7586811 100644 --- a/build.js +++ b/build.js @@ -76,9 +76,11 @@ async function downloadTypescript(outFile) { console.log('Minifying typescript...'); - const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString()); + // const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString()); + const minified = { code: (await fs.readFile('tmp/typescript-es5.js')).toString() }; if (minified.error) throw minified.error; + // Patch unsupported regex syntax minified.code = minified.code.replaceAll('[-/\\\\^$*+?.()|[\\]{}]', '[-/\\\\^$*+?.()|\\[\\]{}]'); diff --git a/src/me/topchetoeu/jscript/lib/ArrayLib.java b/src/me/topchetoeu/jscript/lib/ArrayLib.java index b9b1065..370fe51 100644 --- a/src/me/topchetoeu/jscript/lib/ArrayLib.java +++ b/src/me/topchetoeu/jscript/lib/ArrayLib.java @@ -3,107 +3,20 @@ package me.topchetoeu.jscript.lib; import java.util.Iterator; import java.util.Stack; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeGetter; -import me.topchetoeu.jscript.interop.NativeSetter; - -@Native("Array") public class ArrayLib { - @NativeGetter(thisArg = true) public static int length(Context ctx, ArrayValue thisArg) { - return thisArg.size(); - } - @NativeSetter(thisArg = true) public static void length(Context ctx, ArrayValue thisArg, int len) { - thisArg.setSize(len); - } - - @Native(thisArg = true) public static ObjectValue values(Context ctx, ArrayValue thisArg) { - return Values.toJSIterator(ctx, thisArg); - } - @Native(thisArg = true) public static ObjectValue keys(Context ctx, ArrayValue thisArg) { - return Values.toJSIterator(ctx, () -> new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < thisArg.size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return i++; - } - }); - } - @Native(thisArg = true) public static ObjectValue entries(Context ctx, ArrayValue thisArg) { - return Values.toJSIterator(ctx, () -> new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < thisArg.size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return new ArrayValue(ctx, i, thisArg.get(i++)); - } - }); - } - - @Native(value = "@@Symbol.iterator", thisArg = true) - public static ObjectValue iterator(Context ctx, ArrayValue thisArg) { - return values(ctx, thisArg); - } - @Native(value = "@@Symbol.asyncIterator", thisArg = true) - public static ObjectValue asyncIterator(Context ctx, ArrayValue thisArg) { - return values(ctx, thisArg); - } - - @Native(thisArg = true) public static ArrayValue concat(Context ctx, ArrayValue thisArg, Object ...others) { - // TODO: Fully implement with non-array spreadable objects - var size = thisArg.size(); - - for (int i = 0; i < others.length; i++) { - if (others[i] instanceof ArrayValue) size += ((ArrayValue)others[i]).size(); - else i++; - } - - var res = new ArrayValue(size); - thisArg.copyTo(ctx, res, 0, 0, thisArg.size()); - - for (int i = 0, j = thisArg.size(); i < others.length; i++) { - if (others[i] instanceof ArrayValue) { - int n = ((ArrayValue)others[i]).size(); - ((ArrayValue)others[i]).copyTo(ctx, res, 0, j, n); - j += n; - } - else { - res.set(ctx, j++, others[i]); - } - } - - return res; - } - - @Native(thisArg = true) public static ArrayValue sort(Context ctx, ArrayValue arr, FunctionValue cmp) { - var defaultCmp = new NativeFunction("", (_ctx, thisArg, args) -> { - return Values.toString(ctx, args[0]).compareTo(Values.toString(ctx, args[1])); - }); - arr.sort((a, b) -> { - var res = Values.toNumber(ctx, (cmp == null ? defaultCmp : cmp).call(ctx, null, a, b)); - if (res < 0) return -1; - if (res > 0) return 1; - return 0; - }); - return arr; - } +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.ExposeType; +import me.topchetoeu.jscript.interop.WrapperName; +@WrapperName("Array") +public class ArrayLib { private static int normalizeI(int len, int i, boolean clamp) { if (i < 0) i += len; if (clamp) { @@ -113,91 +26,202 @@ import me.topchetoeu.jscript.interop.NativeSetter; return i; } - @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start, int end) { - start = normalizeI(arr.size(), start, true); - end = normalizeI(arr.size(), end, true); + @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)); + } - for (; start < end; start++) { - arr.set(ctx, start, val); + @Expose public static ObjectValue __values(Arguments args) { + return __iterator(args); + } + @Expose public static ObjectValue __keys(Arguments args) { + return Values.toJSIterator(args.ctx, () -> 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.ctx, () -> 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.ctx, 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)); + } + @Expose(value = "@@Symbol.asyncIterator") + public static ObjectValue __asyncIterator(Arguments args) { + return Values.toJSAsyncIterator(args.ctx, 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(args.ctx, res, 0, j, n); + j += n; + } + else { + res.set(args.ctx, 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.ctx, (cmp == null ? defaultCmp : cmp).call(args.ctx, 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.ctx, start, val); + return arr; } - @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val, int start) { - return fill(ctx, arr, val, start, arr.size()); - } - @Native(thisArg = true) public static ArrayValue fill(Context ctx, ArrayValue arr, Object val) { - return fill(ctx, arr, val, 0, arr.size()); - } + @Expose public static boolean __every(Arguments args) { + var arr = args.self(ArrayValue.class); + var func = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); - @Native(thisArg = true) public static boolean every(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) { for (var i = 0; i < arr.size(); i++) { - if (!Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) return false; + if (!Values.toBoolean(func.call(args.ctx, thisArg, arr.get(i), i, arr))) return false; } return true; } - @Native(thisArg = true) public static boolean some(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) { + @Expose public static boolean __some(Arguments args) { + var arr = args.self(ArrayValue.class); + var func = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); + for (var i = 0; i < arr.size(); i++) { - if (Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) return true; + if (Values.toBoolean(func.call(args.ctx, thisArg, arr.get(i), i, arr))) return true; } return false; } - - @Native(thisArg = true) public static ArrayValue filter(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) { + @Expose public static ArrayValue __filter(Arguments args) { + var arr = args.self(ArrayValue.class); + var func = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); var res = new ArrayValue(arr.size()); for (int i = 0, j = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(func.call(ctx, thisArg, arr.get(i), i, arr))) res.set(ctx, j++, arr.get(i)); + if (arr.has(i) && Values.toBoolean(func.call(args.ctx, thisArg, arr.get(i), i, arr))) res.set(args.ctx, j++, arr.get(i)); } return res; } - @Native(thisArg = true) public static ArrayValue map(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) { + @Expose public static ArrayValue __map(Arguments args) { + var arr = args.self(ArrayValue.class); + var func = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); var res = new ArrayValue(arr.size()); + res.setSize(arr.size()); + for (int i = 0, j = 0; i < arr.size(); i++) { - if (arr.has(i)) res.set(ctx, j++, func.call(ctx, thisArg, arr.get(i), i, arr)); + if (arr.has(i)) res.set(args.ctx, j++, func.call(args.ctx, thisArg, arr.get(i), i, arr)); } return res; } - @Native(thisArg = true) public static void forEach(Context ctx, ArrayValue arr, FunctionValue func, Object thisArg) { + @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(ctx, thisArg, arr.get(i), i, arr); + if (arr.has(i)) func.call(args.ctx, thisArg, arr.get(i), i, arr); } } - @Native(thisArg = true) public static Object reduce(Context ctx, ArrayValue arr, FunctionValue func, Object... args) { + @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; - var res = arr.get(0); - if (args.length > 0) res = args[0]; - else for (; !arr.has(i) && i < arr.size(); i++) res = arr.get(i); + if (args.n() < 2) for (; !arr.has(i) && i < arr.size(); i++) res = arr.get(i); for (; i < arr.size(); i++) { if (arr.has(i)) { - res = func.call(ctx, null, res, arr.get(i), i, arr); + res = func.call(args.ctx, null, res, arr.get(i), i, arr); } } return res; } - @Native(thisArg = true) public static Object reduceRight(Context ctx, ArrayValue arr, FunctionValue func, Object... args) { + @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(); - var res = arr.get(0); - if (args.length > 0) res = args[0]; - else while (!arr.has(i--) && i >= 0) res = arr.get(i); + if (args.n() < 1) while (!arr.has(i--) && i >= 0) res = arr.get(i); for (; i >= 0; i--) { if (arr.has(i)) { - res = func.call(ctx, null, res, arr.get(i), i, arr); + res = func.call(args.ctx, null, res, arr.get(i), i, arr); } } return res; } - @Native(thisArg = true) public static ArrayValue flat(Context ctx, ArrayValue arr, int depth) { + @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(); @@ -209,127 +233,165 @@ import me.topchetoeu.jscript.interop.NativeSetter; var el = stack.pop(); int d = depths.pop(); - if (d <= depth && el instanceof ArrayValue) { - for (int i = ((ArrayValue)el).size() - 1; i >= 0; i--) { - stack.push(((ArrayValue)el).get(i)); + 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(ctx, depth, arr); + else res.set(args.ctx, res.size(), el); } return res; } - @Native(thisArg = true) public static ArrayValue flatMap(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) { - return flat(ctx, map(ctx, arr, cmp, thisArg), 1); + @Expose public static ArrayValue __flatMap(Arguments args) { + return __flat(new Arguments(args.ctx, __map(args), 1)); } - @Native(thisArg = true) public static Object find(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) { + @Expose public static Object __find(Arguments args) { + var arr = args.self(ArrayValue.class); + var cmp = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); + for (int i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return arr.get(i); + if (arr.has(i) && Values.toBoolean(cmp.call(args.ctx, thisArg, arr.get(i), i, arr))) return arr.get(i); } return null; } - @Native(thisArg = true) public static Object findLast(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) { + @Expose public static Object __findLast(Arguments args) { + var arr = args.self(ArrayValue.class); + var cmp = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); + for (var i = arr.size() - 1; i >= 0; i--) { - if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return arr.get(i); + if (arr.has(i) && Values.toBoolean(cmp.call(args.ctx, thisArg, arr.get(i), i, arr))) return arr.get(i); } return null; } - @Native(thisArg = true) public static int findIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) { + @Expose public static int __findIndex(Arguments args) { + var arr = args.self(ArrayValue.class); + var cmp = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); + for (int i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return i; + if (arr.has(i) && Values.toBoolean(cmp.call(args.ctx, thisArg, arr.get(i), i, arr))) return i; } return -1; } - @Native(thisArg = true) public static int findLastIndex(Context ctx, ArrayValue arr, FunctionValue cmp, Object thisArg) { + @Expose public static int __findLastIndex(Arguments args) { + var arr = args.self(ArrayValue.class); + var cmp = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); + for (var i = arr.size() - 1; i >= 0; i--) { - if (arr.has(i) && Values.toBoolean(cmp.call(ctx, thisArg, arr.get(i), i, arr))) return i; + if (arr.has(i) && Values.toBoolean(cmp.call(args.ctx, thisArg, arr.get(i), i, arr))) return i; } return -1; } - @Native(thisArg = true) public static int indexOf(Context ctx, ArrayValue arr, Object val, int start) { - start = normalizeI(arr.size(), start, true); + @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(ctx, arr.get(i), val)) return i; + if (Values.strictEquals(args.ctx, arr.get(i), val)) return i; } return -1; } - @Native(thisArg = true) public static int lastIndexOf(Context ctx, ArrayValue arr, Object val, int start) { - start = normalizeI(arr.size(), start, true); + @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(ctx, arr.get(i), val)) return i; + if (Values.strictEquals(args.ctx, arr.get(i), val)) return i; } return -1; } - @Native(thisArg = true) public static boolean includes(Context ctx, ArrayValue arr, Object el, int start) { - return indexOf(ctx, arr, el, start) >= 0; + @Expose public static boolean __includes(Arguments args) { + return __indexOf(args) >= 0; } - @Native(thisArg = true) public static Object pop(Context ctx, ArrayValue arr) { + @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; } - @Native(thisArg = true) public static int push(Context ctx, ArrayValue arr, Object ...values) { - arr.copyFrom(ctx, values, 0, arr.size(), values.length); + @Expose public static int __push(Arguments args) { + var arr = args.self(ArrayValue.class); + var values = args.args; + + arr.copyFrom(args.ctx, values, 0, arr.size(), values.length); return arr.size(); } - @Native(thisArg = true) public static Object shift(Context ctx, ArrayValue arr) { + @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; } - @Native(thisArg = true) public static int unshift(Context ctx, ArrayValue arr, Object ...values) { + @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(ctx, values, 0, 0, values.length); + arr.copyFrom(args.ctx, values, 0, 0, values.length); return arr.size(); } - @Native(thisArg = true) public static ArrayValue slice(Context ctx, ArrayValue arr, int start, Object _end) { - start = normalizeI(arr.size(), start, true); - int end = normalizeI(arr.size(), (int)(_end == null ? arr.size() : Values.toNumber(ctx, _end)), true); + @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(ctx, res, start, 0, end - start); + arr.copyTo(args.ctx, res, start, 0, end - start); return res; } - @Native(thisArg = true) public static ArrayValue splice(Context ctx, ArrayValue arr, int start, Object _deleteCount, Object ...items) { - start = normalizeI(arr.size(), start, true); - int deleteCount = _deleteCount == null ? arr.size() - 1 : (int)Values.toNumber(ctx, _deleteCount); - deleteCount = normalizeI(arr.size(), deleteCount, true); + @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(ctx, res, start, 0, deleteCount); + arr.copyTo(args.ctx, res, start, 0, deleteCount); arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount); - arr.copyFrom(ctx, items, 0, start, items.length); + arr.copyFrom(args.ctx, items, 0, start, items.length); arr.setSize(size); return res; } - @Native(thisArg = true) public static String toString(Context ctx, ArrayValue arr) { - return join(ctx, arr, ","); + @Expose public static String __toString(Arguments args) { + return __join(new Arguments(args.ctx, args.self, ",")); } - @Native(thisArg = true) public static String join(Context ctx, ArrayValue arr, String sep) { + @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; @@ -342,30 +404,33 @@ import me.topchetoeu.jscript.interop.NativeSetter; var el = arr.get(i); if (el == null || el == Values.NULL) continue; - res.append(Values.toString(ctx, el)); + res.append(Values.toString(args.ctx, el)); } return res.toString(); } - @Native public static boolean isArray(Context ctx, Object val) { return val instanceof ArrayValue; } - @Native public static ArrayValue of(Context ctx, Object... args) { - var res = new ArrayValue(args.length); - res.copyFrom(ctx, args, 0, 0, args.length); - return res; + @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.ctx, args.slice(0).args); } - @NativeConstructor public static ArrayValue constructor(Context ctx, Object... args) { + @ExposeConstructor public static ArrayValue __constructor(Arguments args) { ArrayValue res; - if (args.length == 1 && args[0] instanceof Number) { - int len = ((Number)args[0]).intValue(); + if (args.n() == 1 && args.get(0) instanceof Number) { + var len = args.getInt(0); res = new ArrayValue(len); res.setSize(len); } else { - res = new ArrayValue(args.length); - res.copyFrom(ctx, args, 0, 0, args.length); + var val = args.slice(0).args; + res = new ArrayValue(val.length); + res.copyFrom(args.ctx, val, 0, 0, val.length); } return res; diff --git a/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java index 597c93d..b30fd5e 100644 --- a/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java +++ b/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java @@ -7,55 +7,60 @@ import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.lib.PromiseLib.Handle; -@Native("AsyncFunction") public class AsyncFunctionLib extends FunctionValue { +@WrapperName("AsyncFunction") +public class AsyncFunctionLib extends FunctionValue { public final FunctionValue factory; - public static class AsyncHelper { + private static class AsyncHelper { public PromiseLib promise = new PromiseLib(); public CodeFrame frame; private boolean awaiting = false; - private void next(Context ctx, Object inducedValue, Object inducedError) { + private void next(Context ctx, Object inducedValue, EngineException inducedError) { Object res = null; frame.onPush(); awaiting = false; while (!awaiting) { try { - res = frame.next(inducedValue, Runners.NO_RETURN, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); - inducedValue = inducedError = Runners.NO_RETURN; + res = frame.next(inducedValue, Runners.NO_RETURN, inducedError); + inducedValue = Runners.NO_RETURN; + inducedError = null; + if (res != Runners.NO_RETURN) { promise.fulfill(ctx, res); break; } } catch (EngineException e) { - promise.reject(ctx, e.value); + promise.reject(ctx, e); break; } } frame.onPop(); if (awaiting) { - PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); + PromiseLib.handle(ctx, frame.pop(), new Handle() { + @Override + public void onFulfil(Object val) { + next(ctx, val, null); + } + @Override + public void onReject(EngineException err) { + next(ctx, Runners.NO_RETURN, err); + } + }); } } - public Object fulfill(Context ctx, Object thisArg, Object ...args) { - next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN); - return null; - } - public Object reject(Context ctx, Object thisArg, Object ...args) { - next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null); - return null; - } - - public Object await(Context ctx, Object thisArg, Object[] args) { + public Object await(Arguments args) { this.awaiting = true; - return args.length > 0 ? args[0] : null; + return args.get(0); } } @@ -65,7 +70,7 @@ import me.topchetoeu.jscript.interop.Native; var func = factory.call(ctx, thisArg, new NativeFunction("await", handler::await)); if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); handler.frame = new CodeFrame(ctx, thisArg, args, (CodeFunction)func); - handler.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN); + handler.next(ctx, Runners.NO_RETURN, null); return handler.promise; } diff --git a/src/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java b/src/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java index 490905c..f4bdc46 100644 --- a/src/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java +++ b/src/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java @@ -6,9 +6,10 @@ import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("AsyncGeneratorFunction") public class AsyncGeneratorFunctionLib extends FunctionValue { +@WrapperName("AsyncGeneratorFunction") +public class AsyncGeneratorFunctionLib extends FunctionValue { public final FunctionValue factory; @Override diff --git a/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java index 53181ca..6e957ed 100644 --- a/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java +++ b/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java @@ -5,21 +5,23 @@ import java.util.Map; import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.frame.Runners; -import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.lib.PromiseLib.Handle; -@Native("AsyncGenerator") public class AsyncGeneratorLib { - @Native("@@Symbol.typeName") public final String name = "AsyncGenerator"; +@WrapperName("AsyncGenerator") +public class AsyncGeneratorLib { private int state = 0; private boolean done = false; private PromiseLib currPromise; public CodeFrame frame; - private void next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) { + private void next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) { if (done) { - if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError); + if (inducedError != null) throw inducedError; currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of( "done", true, "value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn @@ -33,8 +35,10 @@ import me.topchetoeu.jscript.interop.Native; frame.onPush(); while (state == 0) { try { - res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); - inducedValue = inducedReturn = inducedError = Runners.NO_RETURN; + res = frame.next(inducedValue, inducedReturn, inducedError); + inducedValue = inducedReturn = Runners.NO_RETURN; + inducedError = null; + if (res != Runners.NO_RETURN) { var obj = new ObjectValue(); obj.defineProperty(ctx, "done", true); @@ -44,14 +48,21 @@ import me.topchetoeu.jscript.interop.Native; } } catch (EngineException e) { - currPromise.reject(ctx, e.value); + currPromise.reject(ctx, e); break; } } frame.onPop(); if (state == 1) { - PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); + PromiseLib.handle(ctx, frame.pop(), new Handle() { + @Override public void onFulfil(Object val) { + next(ctx, val, Runners.NO_RETURN, null); + } + @Override public void onReject(EngineException err) { + next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, err); + } + }); } else if (state == 2) { var obj = new ObjectValue(); @@ -68,42 +79,29 @@ import me.topchetoeu.jscript.interop.Native; return "Generator [running]"; } - public Object fulfill(Context ctx, Object thisArg, Object ...args) { - next(ctx, args.length > 0 ? args[0] : null, Runners.NO_RETURN, Runners.NO_RETURN); - return null; - } - public Object reject(Context ctx, Object thisArg, Object ...args) { - next(ctx, Runners.NO_RETURN, args.length > 0 ? args[0] : null, Runners.NO_RETURN); - return null; - } - - @Native - public PromiseLib next(Context ctx, Object ...args) { - this.currPromise = new PromiseLib(); - if (args.length == 0) next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN); - else next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN); - return this.currPromise; - } - @Native("throw") - public PromiseLib _throw(Context ctx, Object error) { - this.currPromise = new PromiseLib(); - next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error); - return this.currPromise; - } - @Native("return") - public PromiseLib _return(Context ctx, Object value) { - this.currPromise = new PromiseLib(); - next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN); - return this.currPromise; - } - - - public Object await(Context ctx, Object thisArg, Object[] args) { + public Object await(Arguments args) { this.state = 1; - return args.length > 0 ? args[0] : null; + return args.get(0); } - public Object yield(Context ctx, Object thisArg, Object[] args) { + public Object yield(Arguments args) { this.state = 2; - return args.length > 0 ? args[0] : null; + return args.get(0); + } + + @Expose public PromiseLib __next(Arguments args) { + this.currPromise = new PromiseLib(); + if (args.has(0)) next(args.ctx, args.get(0), Runners.NO_RETURN, null); + else next(args.ctx, Runners.NO_RETURN, Runners.NO_RETURN, null); + return this.currPromise; + } + @Expose public PromiseLib __return(Arguments args) { + this.currPromise = new PromiseLib(); + next(args.ctx, Runners.NO_RETURN, args.get(0), null); + return this.currPromise; + } + @Expose public PromiseLib __throw(Arguments args) { + this.currPromise = new PromiseLib(); + next(args.ctx, Runners.NO_RETURN, Runners.NO_RETURN, new EngineException(args.get(0)).setCtx(args.ctx)); + return this.currPromise; } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/lib/BooleanLib.java b/src/me/topchetoeu/jscript/lib/BooleanLib.java index 2ac5e0d..0a77408 100644 --- a/src/me/topchetoeu/jscript/lib/BooleanLib.java +++ b/src/me/topchetoeu/jscript/lib/BooleanLib.java @@ -1,30 +1,31 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Boolean") public class BooleanLib { +@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; - @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) { - val = Values.toBoolean(val); - if (thisArg instanceof ObjectValue) return (boolean)val ? TRUE : FALSE; - else return val; - } - @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - return Values.toBoolean(thisArg) ? "true" : "false"; - } - @Native(thisArg = true) public static boolean valueOf(Context ctx, Object thisArg) { - return Values.toBoolean(thisArg); - } - 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) { + return args.self(Boolean.class); + } } diff --git a/src/me/topchetoeu/jscript/lib/DateLib.java b/src/me/topchetoeu/jscript/lib/DateLib.java index 339d961..599c19e 100644 --- a/src/me/topchetoeu/jscript/lib/DateLib.java +++ b/src/me/topchetoeu/jscript/lib/DateLib.java @@ -1,12 +1,17 @@ package me.topchetoeu.jscript.lib; import java.util.Calendar; +import java.util.Date; import java.util.TimeZone; -import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Date") public class DateLib { +@WrapperName("Date") +public class DateLib { private Calendar normal; private Calendar utc; @@ -22,244 +27,217 @@ import me.topchetoeu.jscript.interop.Native; normal = utc = null; } - @Native - public static double now() { - return new DateLib().getTime(); - } - - @Native - public double getYear() { + @Expose public double __getYear() { if (normal == null) return Double.NaN; return normal.get(Calendar.YEAR) - 1900; } - @Native - public double setYear(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double getFullYear() { + @Expose public double __getFullYear() { if (normal == null) return Double.NaN; return normal.get(Calendar.YEAR); } - @Native - public double getMonth() { + @Expose public double __getMonth() { if (normal == null) return Double.NaN; return normal.get(Calendar.MONTH); } - @Native - public double getDate() { + @Expose public double __getDate() { if (normal == null) return Double.NaN; return normal.get(Calendar.DAY_OF_MONTH); } - @Native - public double getDay() { + @Expose public double __getDay() { if (normal == null) return Double.NaN; return normal.get(Calendar.DAY_OF_WEEK); } - @Native - public double getHours() { + @Expose public double __getHours() { if (normal == null) return Double.NaN; return normal.get(Calendar.HOUR_OF_DAY); } - @Native - public double getMinutes() { + @Expose public double __getMinutes() { if (normal == null) return Double.NaN; return normal.get(Calendar.MINUTE); } - @Native - public double getSeconds() { + @Expose public double __getSeconds() { if (normal == null) return Double.NaN; return normal.get(Calendar.SECOND); } - @Native - public double getMilliseconds() { + @Expose public double __getMilliseconds() { if (normal == null) return Double.NaN; return normal.get(Calendar.MILLISECOND); } - @Native - public double getUTCFullYear() { + @Expose public double __getUTCFullYear() { if (utc == null) return Double.NaN; return utc.get(Calendar.YEAR); } - @Native - public double getUTCMonth() { + @Expose public double __getUTCMonth() { if (utc == null) return Double.NaN; return utc.get(Calendar.MONTH); } - @Native - public double getUTCDate() { + @Expose public double __getUTCDate() { if (utc == null) return Double.NaN; return utc.get(Calendar.DAY_OF_MONTH); } - @Native - public double getUTCDay() { + @Expose public double __getUTCDay() { if (utc == null) return Double.NaN; return utc.get(Calendar.DAY_OF_WEEK); } - @Native - public double getUTCHours() { + @Expose public double __getUTCHours() { if (utc == null) return Double.NaN; return utc.get(Calendar.HOUR_OF_DAY); } - @Native - public double getUTCMinutes() { + @Expose public double __getUTCMinutes() { if (utc == null) return Double.NaN; return utc.get(Calendar.MINUTE); } - @Native - public double getUTCSeconds() { + @Expose public double __getUTCSeconds() { if (utc == null) return Double.NaN; return utc.get(Calendar.SECOND); } - @Native - public double getUTCMilliseconds() { + @Expose public double __getUTCMilliseconds() { if (utc == null) return Double.NaN; return utc.get(Calendar.MILLISECOND); } - @Native - public double setFullYear(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setMonth(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setDate(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setDay(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setHours(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setMinutes(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setSeconds(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setMilliseconds(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCFullYear(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCMonth(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCDate(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCDay(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCHours(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCMinutes(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCSeconds(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double setUTCMilliseconds(Context ctx, double real) { + @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(); + return __getTime(); } - @Native - public double getTime() { + @Expose public double __getTime() { if (utc == null) return Double.NaN; return utc.getTimeInMillis(); } - @Native - public double getTimezoneOffset() { + @Expose public double __getTimezoneOffset() { if (normal == null) return Double.NaN; return normal.getTimeZone().getRawOffset() / 60000; } - @Native - public double valueOf() { + @Expose public double __valueOf() { if (normal == null) return Double.NaN; else return normal.getTimeInMillis(); } - @Native - public String toString() { + @Expose public String __toString() { return normal.getTime().toString(); } - @Native public DateLib(long timestamp) { normal = Calendar.getInstance(); utc = Calendar.getInstance(); @@ -268,8 +246,17 @@ import me.topchetoeu.jscript.interop.Native; utc.setTimeInMillis(timestamp); } - @Native public DateLib() { - this(new java.util.Date().getTime()); + 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/me/topchetoeu/jscript/lib/EncodingLib.java b/src/me/topchetoeu/jscript/lib/EncodingLib.java index 3f7a495..2bbbcb6 100644 --- a/src/me/topchetoeu/jscript/lib/EncodingLib.java +++ b/src/me/topchetoeu/jscript/lib/EncodingLib.java @@ -1,20 +1,89 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; +import java.util.ArrayList; + +import me.topchetoeu.jscript.Buffer; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.parsing.Parsing; -@Native("Encoding") +@WrapperName("Encoding") public class EncodingLib { - @Native public static ArrayValue encode(String value) { + 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 : value.getBytes()) res.set(null, res.size(), (int)el); + for (var el : args.getString(0).getBytes()) res.set(null, res.size(), (int)el); return res; } - @Native public static String decode(Context ctx, ArrayValue raw) { + @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(ctx, raw.get(i)); + for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, 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/me/topchetoeu/jscript/lib/EnvironmentLib.java b/src/me/topchetoeu/jscript/lib/EnvironmentLib.java index aa15d7d..d524529 100644 --- a/src/me/topchetoeu/jscript/lib/EnvironmentLib.java +++ b/src/me/topchetoeu/jscript/lib/EnvironmentLib.java @@ -3,28 +3,34 @@ package me.topchetoeu.jscript.lib; import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; -import me.topchetoeu.jscript.interop.NativeSetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeType; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Environment") +@WrapperName("Environment") public class EnvironmentLib { private Environment env; - @NativeGetter("@@env") public Environment env() { return env; } + @Expose(value = "@@env", type = ExposeType.GETTER) + public Environment __env() { return env; } - @NativeGetter public int id() { + @Expose(type = ExposeType.GETTER) + public int __id(Arguments args) { return env.hashCode(); } - @NativeGetter public ObjectValue global() { + @Expose(type = ExposeType.GETTER) + public ObjectValue __global(Arguments args) { return env.global.obj; } - @NativeGetter public FunctionValue compile() { + @Expose(type = ExposeType.GETTER) + public FunctionValue __compile() { return Environment.compileFunc(env); } - @NativeSetter public void compile(FunctionValue func) { - env.add(Environment.COMPILE_FUNC, func); + @Expose(type = ExposeType.SETTER) + public void __compile(Arguments args) { + env.add(Environment.COMPILE_FUNC, args.convert(0, FunctionValue.class)); } public EnvironmentLib(Environment env) { diff --git a/src/me/topchetoeu/jscript/lib/ErrorLib.java b/src/me/topchetoeu/jscript/lib/ErrorLib.java index e3cc945..3d9479a 100644 --- a/src/me/topchetoeu/jscript/lib/ErrorLib.java +++ b/src/me/topchetoeu/jscript/lib/ErrorLib.java @@ -1,19 +1,18 @@ package me.topchetoeu.jscript.lib; import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.Environment; -import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.interop.InitType; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeInit; +import me.topchetoeu.jscript.exceptions.ConvertException; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeField; -@Native("Error") public class ErrorLib { - private static String toString(Context ctx, boolean rethrown, Object cause, Object name, Object message, ArrayValue stack) { +@WrapperName("Error") +public class ErrorLib { + private static String toString(Context ctx, Object name, Object message) { if (name == null) name = ""; else name = Values.toString(ctx, name).trim(); if (message == null) message = ""; @@ -24,43 +23,31 @@ import me.topchetoeu.jscript.interop.NativeInit; if (!message.equals("") && !name.equals("")) res.append(": "); if (!message.equals("")) res.append(message); - if (cause instanceof ObjectValue) { - if (rethrown) res.append("\n (rethrown)"); - else res.append("\nCaused by ").append(toString(ctx, cause)); - } - return res.toString(); } - @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - if (thisArg instanceof ObjectValue) { - var stack = Values.getMember(ctx, thisArg, "stack"); - if (!(stack instanceof ArrayValue)) stack = null; - var cause = Values.getMember(ctx, thisArg, Symbol.get("Symbol.cause")); - return toString(ctx, - thisArg == cause, - cause, - Values.getMember(ctx, thisArg, "name"), - Values.getMember(ctx, thisArg, "message"), - (ArrayValue)stack - ); - } + @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") + ); else return "[Invalid error]"; } - @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) { + @Expose public static ObjectValue __constructor(Arguments args) { var target = new ObjectValue(); - if (thisArg instanceof ObjectValue) target = (ObjectValue)thisArg; + var message = args.getString(0, ""); + + try { + target = args.self(ObjectValue.class); + } + catch (ConvertException e) {} target.setPrototype(PlaceholderProto.ERROR); - target.defineProperty(ctx, "stack", ArrayValue.of(ctx, ctx.stackTrace())); - if (message == null) target.defineProperty(ctx, "message", ""); - else target.defineProperty(ctx, "message", Values.toString(ctx, message)); + target.defineProperty(args.ctx, "message", Values.toString(args.ctx, message)); return target; } - - @NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) { - target.defineProperty(null, "name", "Error"); - } } diff --git a/src/me/topchetoeu/jscript/lib/FileLib.java b/src/me/topchetoeu/jscript/lib/FileLib.java index 536e03c..08d52b2 100644 --- a/src/me/topchetoeu/jscript/lib/FileLib.java +++ b/src/me/topchetoeu/jscript/lib/FileLib.java @@ -1,27 +1,27 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.filesystem.File; import me.topchetoeu.jscript.filesystem.FilesystemException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("File") +@WrapperName("File") public class FileLib { public final File file; - @NativeGetter public PromiseLib pointer(Context ctx) { - return PromiseLib.await(ctx, () -> { + @Expose public PromiseLib __pointer(Arguments args) { + return PromiseLib.await(args.ctx, () -> { try { return file.seek(0, 1); } catch (FilesystemException e) { throw e.toEngineException(); } }); } - @NativeGetter public PromiseLib length(Context ctx) { - return PromiseLib.await(ctx, () -> { + @Expose public PromiseLib __length(Arguments args) { + return PromiseLib.await(args.ctx, () -> { try { long curr = file.seek(0, 1); long res = file.seek(0, 2); @@ -32,25 +32,27 @@ public class FileLib { }); } - @Native public PromiseLib read(Context ctx, int n) { - return PromiseLib.await(ctx, () -> { + @Expose public PromiseLib __read(Arguments args) { + return PromiseLib.await(args.ctx, () -> { + var n = args.getInt(0); try { var buff = new byte[n]; var res = new ArrayValue(); int resI = file.read(buff); - for (var i = resI - 1; i >= 0; i--) res.set(ctx, i, (int)buff[i]); + for (var i = resI - 1; i >= 0; i--) res.set(args.ctx, i, (int)buff[i]); return res; } catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public PromiseLib write(Context ctx, ArrayValue val) { - return PromiseLib.await(ctx, () -> { + @Expose public PromiseLib __write(Arguments args) { + return PromiseLib.await(args.ctx, () -> { + 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(ctx, val.get(i)); + for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, val.get(i)); file.write(res); return null; @@ -58,14 +60,17 @@ public class FileLib { catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public PromiseLib close(Context ctx) { - return PromiseLib.await(ctx, () -> { + @Expose public PromiseLib __close(Arguments args) { + return PromiseLib.await(args.ctx, () -> { file.close(); return null; }); } - @Native public PromiseLib seek(Context ctx, long ptr, int whence) { - return PromiseLib.await(ctx, () -> { + @Expose public PromiseLib __seek(Arguments args) { + return PromiseLib.await(args.ctx, () -> { + var ptr = args.getLong(0); + var whence = args.getInt(1); + try { return file.seek(ptr, whence); } diff --git a/src/me/topchetoeu/jscript/lib/FilesystemLib.java b/src/me/topchetoeu/jscript/lib/FilesystemLib.java index 2d3f62e..aac8370 100644 --- a/src/me/topchetoeu/jscript/lib/FilesystemLib.java +++ b/src/me/topchetoeu/jscript/lib/FilesystemLib.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.Iterator; import java.util.Stack; -import me.topchetoeu.jscript.Filename; import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; @@ -16,13 +15,16 @@ import me.topchetoeu.jscript.filesystem.Filesystem; import me.topchetoeu.jscript.filesystem.FilesystemException; import me.topchetoeu.jscript.filesystem.Mode; import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Filesystem") +@WrapperName("Filesystem") public class FilesystemLib { - @Native public static final int SEEK_SET = 0; - @Native public static final int SEEK_CUR = 1; - @Native public static final int SEEK_END = 2; + @ExposeField public static final int __SEEK_SET = 0; + @ExposeField public static final int __SEEK_CUR = 1; + @ExposeField public static final int __SEEK_END = 2; private static Filesystem fs(Context ctx) { var fs = Filesystem.get(ctx); @@ -30,30 +32,30 @@ public class FilesystemLib { throw EngineException.ofError("Current environment doesn't have a file system."); } - @Native public static String normalize(Context ctx, String... paths) { - return fs(ctx).normalize(paths); + @Expose public static String __normalize(Arguments args) { + return fs(args.ctx).normalize(args.convert(String.class)); } - @Native public static PromiseLib open(Context ctx, String _path, String mode) { - var path = fs(ctx).normalize(_path); - var _mode = Mode.parse(mode); + @Expose public static PromiseLib __open(Arguments args) { + return PromiseLib.await(args.ctx, () -> { + var fs = fs(args.ctx); + var path = fs.normalize(args.getString(0)); + var _mode = Mode.parse(args.getString(1)); - return PromiseLib.await(ctx, () -> { try { - if (fs(ctx).stat(path).type != EntryType.FILE) { + if (fs.stat(path).type != EntryType.FILE) { throw new FilesystemException(path, FSCode.NOT_FILE); } - var file = fs(ctx).open(path, _mode); + var file = fs.open(path, _mode); return new FileLib(file); } catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public static ObjectValue ls(Context ctx, String _path) throws IOException { - var path = fs(ctx).normalize(_path); + @Expose public static ObjectValue __ls(Arguments args) { - return Values.toJSAsyncIterator(ctx, new Iterator<>() { + return Values.toJSAsyncIterator(args.ctx, new Iterator<>() { private boolean failed, done; private File file; private String nextLine; @@ -62,11 +64,14 @@ public class FilesystemLib { if (done) return; if (!failed) { if (file == null) { - if (fs(ctx).stat(path).type != EntryType.FOLDER) { + var fs = fs(args.ctx); + var path = fs.normalize(args.getString(0)); + + if (fs.stat(path).type != EntryType.FOLDER) { throw new FilesystemException(path, FSCode.NOT_FOLDER); } - file = fs(ctx).open(path, Mode.READ); + file = fs.open(path, Mode.READ); } if (nextLine == null) { @@ -103,29 +108,33 @@ public class FilesystemLib { } }); } - @Native public static PromiseLib mkdir(Context ctx, String _path) throws IOException { - return PromiseLib.await(ctx, () -> { + @Expose public static PromiseLib __mkdir(Arguments args) throws IOException { + return PromiseLib.await(args.ctx, () -> { try { - fs(ctx).create(Filename.parse(_path).toString(), EntryType.FOLDER); + fs(args.ctx).create(args.getString(0), EntryType.FOLDER); return null; } catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public static PromiseLib mkfile(Context ctx, String path) throws IOException { - return PromiseLib.await(ctx, () -> { + @Expose public static PromiseLib __mkfile(Arguments args) throws IOException { + return PromiseLib.await(args.ctx, () -> { try { - fs(ctx).create(path, EntryType.FILE); + fs(args.ctx).create(args.getString(0), EntryType.FILE); return null; } catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public static PromiseLib rm(Context ctx, String path, boolean recursive) throws IOException { - return PromiseLib.await(ctx, () -> { + @Expose public static PromiseLib __rm(Arguments args) throws IOException { + return PromiseLib.await(args.ctx, () -> { try { - if (!recursive) fs(ctx).create(path, EntryType.NONE); + var fs = fs(args.ctx); + 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); @@ -134,13 +143,13 @@ public class FilesystemLib { var currPath = stack.pop(); FileStat stat; - try { stat = fs(ctx).stat(currPath); } + try { stat = fs.stat(currPath); } catch (FilesystemException e) { continue; } if (stat.type == EntryType.FOLDER) { - for (var el : fs(ctx).open(currPath, Mode.READ).readToString().split("\n")) stack.push(el); + for (var el : fs.open(currPath, Mode.READ).readToString().split("\n")) stack.push(el); } - else fs(ctx).create(currPath, EntryType.NONE); + else fs.create(currPath, EntryType.NONE); } } return null; @@ -148,22 +157,24 @@ public class FilesystemLib { catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public static PromiseLib stat(Context ctx, String path) throws IOException { - return PromiseLib.await(ctx, () -> { + @Expose public static PromiseLib __stat(Arguments args) throws IOException { + return PromiseLib.await(args.ctx, () -> { try { - var stat = fs(ctx).stat(path); + var fs = fs(args.ctx); + var path = fs.normalize(args.getString(0)); + var stat = fs.stat(path); var res = new ObjectValue(); - res.defineProperty(ctx, "type", stat.type.name); - res.defineProperty(ctx, "mode", stat.mode.name); + res.defineProperty(args.ctx, "type", stat.type.name); + res.defineProperty(args.ctx, "mode", stat.mode.name); return res; } catch (FilesystemException e) { throw e.toEngineException(); } }); } - @Native public static PromiseLib exists(Context ctx, String _path) throws IOException { - return PromiseLib.await(ctx, () -> { - try { fs(ctx).stat(_path); return true; } + @Expose public static PromiseLib __exists(Arguments args) throws IOException { + return PromiseLib.await(args.ctx, () -> { + try { fs(args.ctx).stat(args.getString(0)); return true; } catch (FilesystemException e) { return false; } }); } diff --git a/src/me/topchetoeu/jscript/lib/FunctionLib.java b/src/me/topchetoeu/jscript/lib/FunctionLib.java index 77f5068..f79e5d1 100644 --- a/src/me/topchetoeu/jscript/lib/FunctionLib.java +++ b/src/me/topchetoeu/jscript/lib/FunctionLib.java @@ -1,54 +1,56 @@ package me.topchetoeu.jscript.lib; import me.topchetoeu.jscript.Location; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Function") public class FunctionLib { - @Native(thisArg = true) public static Object location(Context ctx, FunctionValue func) { - if (func instanceof CodeFunction) return ((CodeFunction)func).loc().toString(); +@WrapperName("Function") +public class FunctionLib { + @Expose public static Object __location(Arguments args) { + if (args.self instanceof CodeFunction) return ((CodeFunction)args.self).loc().toString(); else return Location.INTERNAL.toString(); } - @Native(thisArg = true) public static Object apply(Context ctx, FunctionValue func, Object thisArg, ArrayValue args) { - return func.call(ctx, thisArg, args.toArray()); + @Expose public static Object __apply(Arguments args) { + return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.convert(1, ArrayValue.class).toArray()); } - @Native(thisArg = true) public static Object call(Context ctx, FunctionValue func, Object thisArg, Object... args) { - if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function."); - - return func.call(ctx, thisArg, args); + @Expose public static Object __call(Arguments args) { + return args.self(FunctionValue.class).call(args.ctx, args.get(0), args.slice(1).args); } - @Native(thisArg = true) public static FunctionValue bind(FunctionValue func, Object thisArg, Object... args) { - if (!(func instanceof FunctionValue)) throw EngineException.ofError("Expected this to be a function."); - - return new NativeFunction(func.name + " (bound)", (callCtx, _0, callArgs) -> { + @Expose public static FunctionValue __bind(Arguments args) { + var self = args.self(FunctionValue.class); + return new NativeFunction(self.name + " (bound)", callArgs -> { Object[] resArgs; - if (args.length == 0) resArgs = callArgs; + if (args.n() == 0) resArgs = callArgs.args; else { - resArgs = new Object[args.length + callArgs.length]; - System.arraycopy(args, 0, resArgs, 0, args.length); - System.arraycopy(callArgs, 0, resArgs, args.length, callArgs.length); + resArgs = new Object[args.n() + callArgs.n()]; + System.arraycopy(args.args, 0, resArgs, 0, args.n()); + System.arraycopy(callArgs.args, 0, resArgs, args.n(), callArgs.n()); } - return func.call(callCtx, thisArg, resArgs); + return self.call(callArgs.ctx, self, resArgs); }); } - @Native(thisArg = true) public static String toString(Context ctx, Object func) { - return func.toString(); + @Expose public static String __toString(Arguments args) { + return args.self.toString(); } - @Native public static FunctionValue async(FunctionValue func) { - return new AsyncFunctionLib(func); + @Expose(target = ExposeTarget.STATIC) + public static FunctionValue __async(Arguments args) { + return new AsyncFunctionLib(args.convert(0, FunctionValue.class)); } - @Native public static FunctionValue asyncGenerator(FunctionValue func) { - return new AsyncGeneratorFunctionLib(func); + @Expose(target = ExposeTarget.STATIC) + public static FunctionValue __asyncGenerator(Arguments args) { + return new AsyncGeneratorFunctionLib(args.convert(0, FunctionValue.class)); } - @Native public static FunctionValue generator(FunctionValue func) { - return new GeneratorFunctionLib(func); + @Expose(target = ExposeTarget.STATIC) + public static FunctionValue __generator(Arguments args) { + return new GeneratorFunctionLib(args.convert(0, FunctionValue.class)); } } diff --git a/src/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java b/src/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java index f08e1a5..e8a07f6 100644 --- a/src/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java +++ b/src/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java @@ -6,13 +6,13 @@ import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("GeneratorFunction") public class GeneratorFunctionLib extends FunctionValue { +@WrapperName("GeneratorFunction") +public class GeneratorFunctionLib extends FunctionValue { public final FunctionValue factory; - @Override - public Object call(Context ctx, Object thisArg, Object ...args) { + @Override public Object call(Context ctx, Object thisArg, Object ...args) { var handler = new GeneratorLib(); var func = factory.call(ctx, thisArg, new NativeFunction("yield", handler::yield)); if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); diff --git a/src/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/me/topchetoeu/jscript/lib/GeneratorLib.java index a21f828..cf4b5a9 100644 --- a/src/me/topchetoeu/jscript/lib/GeneratorLib.java +++ b/src/me/topchetoeu/jscript/lib/GeneratorLib.java @@ -5,18 +5,19 @@ import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.frame.Runners; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Generator") public class GeneratorLib { +@WrapperName("Generator") +public class GeneratorLib { private boolean yielding = true; private boolean done = false; public CodeFrame frame; - @Native("@@Symbol.typeName") public final String name = "Generator"; - - private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, Object inducedError) { + private ObjectValue next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) { if (done) { - if (inducedError != Runners.NO_RETURN) throw new EngineException(inducedError); + if (inducedError != Runners.NO_RETURN) throw inducedError; var res = new ObjectValue(); res.defineProperty(ctx, "done", true); res.defineProperty(ctx, "value", inducedReturn == Runners.NO_RETURN ? null : inducedReturn); @@ -29,8 +30,9 @@ import me.topchetoeu.jscript.interop.Native; frame.onPush(); while (!yielding) { try { - res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); - inducedReturn = inducedError = Runners.NO_RETURN; + res = frame.next(inducedValue, inducedReturn, inducedError); + inducedReturn = Runners.NO_RETURN; + inducedError = null; if (res != Runners.NO_RETURN) { done = true; break; @@ -52,29 +54,25 @@ import me.topchetoeu.jscript.interop.Native; return obj; } - @Native - public ObjectValue next(Context ctx, Object ...args) { - if (args.length == 0) return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, Runners.NO_RETURN); - else return next(ctx, args[0], Runners.NO_RETURN, Runners.NO_RETURN); + @Expose public ObjectValue __next(Arguments args) { + if (args.n() == 0) return next(args.ctx, Runners.NO_RETURN, Runners.NO_RETURN, null); + else return next(args.ctx, args.get(0), Runners.NO_RETURN, null); } - @Native("throw") - public ObjectValue _throw(Context ctx, Object error) { - return next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, error); + @Expose public ObjectValue __throw(Arguments args) { + return next(args.ctx, Runners.NO_RETURN, Runners.NO_RETURN, new EngineException(args.get(0)).setCtx(args.ctx)); } - @Native("return") - public ObjectValue _return(Context ctx, Object value) { - return next(ctx, Runners.NO_RETURN, value, Runners.NO_RETURN); + @Expose public ObjectValue __return(Arguments args) { + return next(args.ctx, Runners.NO_RETURN, args.get(0), null); } - @Override - public String toString() { + @Override public String toString() { if (done) return "Generator [closed]"; if (yielding) return "Generator [suspended]"; return "Generator [running]"; } - public Object yield(Context ctx, Object thisArg, Object[] args) { + public Object yield(Arguments args) { this.yielding = true; - return args.length > 0 ? args[0] : null; + return args.get(0); } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/lib/Internals.java b/src/me/topchetoeu/jscript/lib/Internals.java index 03116e8..88a220b 100644 --- a/src/me/topchetoeu/jscript/lib/Internals.java +++ b/src/me/topchetoeu/jscript/lib/Internals.java @@ -1,44 +1,50 @@ package me.topchetoeu.jscript.lib; import java.io.IOException; +import java.util.HashMap; -import me.topchetoeu.jscript.Buffer; import me.topchetoeu.jscript.Reading; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; import me.topchetoeu.jscript.modules.ModuleRepo; -import me.topchetoeu.jscript.parsing.Parsing; public class Internals { - @Native public static Object require(Context ctx, String name) { - var repo = ModuleRepo.get(ctx); + private static final Symbol THREADS = new Symbol("Internals.threads"); + private static final Symbol I = new Symbol("Internals.i"); + + @Expose(target = ExposeTarget.STATIC) + public static Object __require(Arguments args) { + var repo = ModuleRepo.get(args.ctx); if (repo != null) { - var res = repo.getModule(ctx, ModuleRepo.cwd(ctx), name); - res.load(ctx); + var res = repo.getModule(args.ctx, ModuleRepo.cwd(args.ctx), args.getString(0)); + res.load(args.ctx); return res.value(); } else throw EngineException.ofError("Modules are not supported."); } - @Native public static Object log(Context ctx, Object ...args) { - for (var arg : args) { - Values.printValue(ctx, arg); + @Expose(target = ExposeTarget.STATIC) + public static Object __log(Arguments args) { + for (var arg : args.args) { + Values.printValue(args.ctx, arg); System.out.print(" "); } System.out.println(); - if (args.length == 0) return null; - else return args[0]; + return args.get(0); } - @Native public static String readline(Context ctx) { + @Expose(target = ExposeTarget.STATIC) + public static String __readline() { try { return Reading.readline(); } @@ -48,23 +54,35 @@ public class Internals { } } - @Native public static Thread setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) { + @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; + var thread = new Thread(() -> { var ms = (long)delay; var ns = (int)((delay - ms) * 10000000); - try { - Thread.sleep(ms, ns); - } + try { Thread.sleep(ms, ns); } catch (InterruptedException e) { return; } - ctx.engine.pushMsg(false, ctx.environment, func, null, args); + args.ctx.engine.pushMsg(false, args.ctx.environment, func, null, arguments); }); + thread.start(); + var i = args.ctx.init(I, 1); + args.ctx.add(I, i + 1); + args.ctx.init(THREADS, new HashMap()).put(i, thread); return thread; } - @Native public static Thread setInterval(Context ctx, FunctionValue func, int delay, Object ...args) { + @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; + var thread = new Thread(() -> { var ms = (long)delay; var ns = (int)((delay - ms) * 10000000); @@ -75,96 +93,76 @@ public class Internals { } catch (InterruptedException e) { return; } - ctx.engine.pushMsg(false, ctx.environment, func, null, args); + args.ctx.engine.pushMsg(false, args.ctx.environment, func, null, arguments); } }); + thread.start(); + var i = args.ctx.init(I, 1); + args.ctx.add(I, i + 1); + args.ctx.init(THREADS, new HashMap()).put(i, thread); return thread; } - @Native public static void clearTimeout(Context ctx, Thread t) { - t.interrupt(); + @Expose(target = ExposeTarget.STATIC) + public static void __clearTimeout(Arguments args) { + var i = args.getInt(0); + HashMap map = args.ctx.get(THREADS); + if (map == null) return; + + var thread = map.get(i); + if (thread == null) return; + + thread.interrupt(); + map.remove(i); } - @Native public static void clearInterval(Context ctx, Thread t) { - t.interrupt(); + @Expose(target = ExposeTarget.STATIC) + public static void __clearInterval(Arguments args) { + __clearTimeout(args); } - @Native public static double parseInt(Context ctx, String val) { - return NumberLib.parseInt(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static double __parseInt(Arguments args) { + return NumberLib.__parseInt(args); } - @Native public static double parseFloat(Context ctx, String val) { - return NumberLib.parseFloat(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static double __parseFloat(Arguments args) { + return NumberLib.__parseFloat(args); } - @Native public static boolean isNaN(Context ctx, double val) { - return NumberLib.isNaN(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static boolean __isNaN(Arguments args) { + return NumberLib.__isNaN(args); } - @Native public static boolean isFinite(Context ctx, double val) { - return NumberLib.isFinite(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static boolean __isFinite(Arguments args) { + return NumberLib.__isFinite(args); } - @Native public static boolean isInfinite(Context ctx, double val) { - return NumberLib.isInfinite(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static boolean __isInfinite(Arguments args) { + return NumberLib.__isInfinite(args); } - @NativeGetter public static double NaN(Context ctx) { - return Double.NaN; + @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); } - @NativeGetter public static double Infinity(Context ctx) { - return Double.POSITIVE_INFINITY; + @Expose(target = ExposeTarget.STATIC) + public static String __decodeURIComponent(Arguments args) { + return EncodingLib.__decodeURIComponent(args); } - private static final String HEX = "0123456789ABCDEF"; - - private 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(); + @Expose(target = ExposeTarget.STATIC) + public static String __encodeURI(Arguments args) { + return EncodingLib.__encodeURI(args); } - private 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()); - } - - @Native public static String encodeURIComponent(String str) { - return encodeUriAny(str, ".-_!~*'()"); - } - @Native public static String decodeURIComponent(String str) { - return decodeUriAny(str, ""); - } - @Native public static String encodeURI(String str) { - return encodeUriAny(str, ";,/?:@&=+$#.-_!~*'()"); - } - @Native public static String decodeURI(String str) { - return decodeUriAny(str, ",/?:@&=+$#."); + @Expose(target = ExposeTarget.STATIC) + public static String __decodeURI(Arguments args) { + return EncodingLib.__decodeURI(args); } public static Environment apply(Environment env) { diff --git a/src/me/topchetoeu/jscript/lib/JSONLib.java b/src/me/topchetoeu/jscript/lib/JSONLib.java index e9aa8ad..3f025e1 100644 --- a/src/me/topchetoeu/jscript/lib/JSONLib.java +++ b/src/me/topchetoeu/jscript/lib/JSONLib.java @@ -1,21 +1,24 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.SyntaxException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; import me.topchetoeu.jscript.json.JSON; -@Native("JSON") public class JSONLib { - @Native - public static Object parse(Context ctx, String val) { +@WrapperName("JSON") +public class JSONLib { + @Expose(target = ExposeTarget.STATIC) + public static Object __parse(Arguments args) { try { - return JSON.toJs(JSON.parse(null, val)); + return JSON.toJs(JSON.parse(null, args.getString(0))); } catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); } } - @Native - public static String stringify(Context ctx, Object val) { - return me.topchetoeu.jscript.json.JSON.stringify(JSON.fromJs(ctx, val)); + @Expose(target = ExposeTarget.STATIC) + public static String __stringify(Arguments args) { + return me.topchetoeu.jscript.json.JSON.stringify(JSON.fromJs(args.ctx, args.get(0))); } } diff --git a/src/me/topchetoeu/jscript/lib/MapLib.java b/src/me/topchetoeu/jscript/lib/MapLib.java index b9d72ff..dd52eea 100644 --- a/src/me/topchetoeu/jscript/lib/MapLib.java +++ b/src/me/topchetoeu/jscript/lib/MapLib.java @@ -9,21 +9,26 @@ import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeType; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Map") public class MapLib { +@WrapperName("Map") +public class MapLib { private LinkedHashMap map = new LinkedHashMap<>(); - @Native("@@Symbol.typeName") public final String name = "Map"; - @Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) { - return this.entries(ctx); + @Expose("@@Symbol.iterator") + public ObjectValue __iterator(Arguments args) { + return this.__entries(args); } - @Native public void clear() { + @Expose public void __clear() { map.clear(); } - @Native public boolean delete(Object key) { + @Expose public boolean __delete(Arguments args) { + var key = args.get(0); if (map.containsKey(key)) { map.remove(key); return true; @@ -31,48 +36,56 @@ import me.topchetoeu.jscript.interop.NativeGetter; return false; } - @Native public ObjectValue entries(Context ctx) { - return ArrayValue.of(ctx, map + @Expose public ObjectValue __entries(Arguments args) { + return ArrayValue.of(args.ctx, map .entrySet() .stream() - .map(v -> new ArrayValue(ctx, v.getKey(), v.getValue())) + .map(v -> new ArrayValue(args.ctx, v.getKey(), v.getValue())) .collect(Collectors.toList()) ); } - @Native public ObjectValue keys(Context ctx) { - return ArrayValue.of(ctx, map.keySet()); + @Expose public ObjectValue __keys(Arguments args) { + return ArrayValue.of(args.ctx, map.keySet()); } - @Native public ObjectValue values(Context ctx) { - return ArrayValue.of(ctx, map.values()); + @Expose public ObjectValue __values(Arguments args) { + return ArrayValue.of(args.ctx, map.values()); } - @Native public Object get(Object key) { - return map.get(key); + @Expose public Object __get(Arguments args) { + return map.get(args.get(0)); } - @Native public MapLib set(Object key, Object val) { - map.put(key, val); + @Expose public MapLib __set(Arguments args) { + map.put(args.get(0), args.get(1)); return this; } - @Native public boolean has(Object key) { - return map.containsKey(key); + @Expose public boolean __has(Arguments args) { + return map.containsKey(args.get(0)); } - @NativeGetter public int size() { + @Expose(type = ExposeType.GETTER) + public int __size() { return map.size(); } - @Native public void forEach(Context ctx, FunctionValue func, Object thisArg) { + @Expose public void __forEach(Arguments args) { + var func = args.convert(0, FunctionValue.class); + var thisArg = args.get(1); + var keys = new ArrayList<>(map.keySet()); - for (var el : keys) func.call(ctx, thisArg, map.get(el), el,this); + for (var el : keys) func.call(args.ctx, thisArg, map.get(el), el,this); } - @Native public MapLib(Context ctx, Object iterable) { + public MapLib(Context ctx, Object iterable) { for (var el : Values.fromJSIterator(ctx, iterable)) { try { - set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1)); + map.put(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1)); } catch (IllegalArgumentException e) { } } } + + @ExposeConstructor public static MapLib __constructor(Arguments args) { + return new MapLib(args.ctx, args.get(0)); + } } diff --git a/src/me/topchetoeu/jscript/lib/MathLib.java b/src/me/topchetoeu/jscript/lib/MathLib.java index 381b5ae..d741127 100644 --- a/src/me/topchetoeu/jscript/lib/MathLib.java +++ b/src/me/topchetoeu/jscript/lib/MathLib.java @@ -1,21 +1,47 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Math") public class MathLib { - @Native public static final double E = Math.E; - @Native public static final double PI = Math.PI; - @Native public static final double SQRT2 = Math.sqrt(2); - @Native public static final double SQRT1_2 = Math.sqrt(.5); - @Native public static final double LN2 = Math.log(2); - @Native public static final double LN10 = Math.log(10); - @Native public static final double LOG2E = Math.log(Math.E) / LN2; - @Native public static final double LOG10E = Math.log10(Math.E); +@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); - @Native public static double asin(double x) { return Math.asin(x); } - @Native public static double acos(double x) { return Math.acos(x); } - @Native public static double atan(double x) { return Math.atan(x); } - @Native public static double atan2(double y, double x) { if (x == 0) { if (y == 0) return Double.NaN; return Math.signum(y) * Math.PI / 2; @@ -29,71 +55,157 @@ import me.topchetoeu.jscript.interop.Native; } - @Native public static double asinh(double x) { return Math.log(x + Math.sqrt(x * x + 1)); } - @Native public static double acosh(double x) { return Math.log(x + Math.sqrt(x * x - 1)); } - @Native public static double atanh(double x) { + @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)); } - @Native public static double sin(double x) { return Math.sin(x); } - @Native public static double cos(double x) { return Math.cos(x); } - @Native public static double tan(double x) { return Math.tan(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)); + } - @Native public static double sinh(double x) { return Math.sinh(x); } - @Native public static double cosh(double x) { return Math.cosh(x); } - @Native public static double tanh(double x) { return Math.tanh(x); } + @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)); + } - @Native public static double sqrt(double x) { return Math.sqrt(x); } - @Native public static double cbrt(double x) { return Math.cbrt(x); } + @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)); + } - @Native public static double hypot(double ...vals) { + @Expose(target = ExposeTarget.STATIC) + public static double __hypot(Arguments args) { var res = 0.; - for (var el : vals) { - var val = el; + for (var i = 0; i < args.n(); i++) { + var val = args.getDouble(i); res += val * val; } return Math.sqrt(res); } - @Native public static int imul(double a, double b) { return (int)a * (int)b; } + @Expose(target = ExposeTarget.STATIC) + public static int __imul(Arguments args) { return args.getInt(0) * args.getInt(1); } - @Native public static double exp(double x) { return Math.exp(x); } - @Native public static double expm1(double x) { return Math.expm1(x); } - @Native public static double pow(double x, double y) { return Math.pow(x, y); } + @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)); } - @Native public static double log(double x) { return Math.log(x); } - @Native public static double log10(double x) { return Math.log10(x); } - @Native public static double log1p(double x) { return Math.log1p(x); } - @Native public static double log2(double x) { return Math.log(x) / LN2; } + @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; + } - @Native public static double ceil(double x) { return Math.ceil(x); } - @Native public static double floor(double x) { return Math.floor(x); } - @Native public static double round(double x) { return Math.round(x); } - @Native public static float fround(double x) { return (float)x; } - @Native public static double trunc(double x) { return Math.floor(Math.abs(x)) * Math.signum(x); } - @Native public static double abs(double x) { return Math.abs(x); } + @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)); + } - @Native public static double max(double ...vals) { + @Expose(target = ExposeTarget.STATIC) + public static double __max(Arguments args) { var res = Double.NEGATIVE_INFINITY; - for (var el : vals) { + for (var i = 0; i < args.n(); i++) { + var el = args.getDouble(i); if (el > res) res = el; } return res; } - @Native public static double min(double ...vals) { + @Expose(target = ExposeTarget.STATIC) + public static double __min(Arguments args) { var res = Double.POSITIVE_INFINITY; - for (var el : vals) { + for (var i = 0; i < args.n(); i++) { + var el = args.getDouble(i); if (el < res) res = el; } return res; } - @Native public static double sign(double x) { return Math.signum(x); } + @Expose(target = ExposeTarget.STATIC) + public static double __sign(Arguments args) { + return Math.signum(args.getDouble(0)); + } - @Native public static double random() { return Math.random(); } - @Native public static int clz32(double x) { return Integer.numberOfLeadingZeros((int)x); } + @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/me/topchetoeu/jscript/lib/NumberLib.java b/src/me/topchetoeu/jscript/lib/NumberLib.java index 53998e5..5777fed 100644 --- a/src/me/topchetoeu/jscript/lib/NumberLib.java +++ b/src/me/topchetoeu/jscript/lib/NumberLib.java @@ -1,49 +1,66 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Number") public class NumberLib { - @Native public static final double EPSILON = java.lang.Math.ulp(1.0); - @Native public static final double MAX_SAFE_INTEGER = 9007199254740991.; - @Native public static final double MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER; +@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 - @Native public static final double MAX_VALUE = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.; - @Native public static final double MIN_VALUE = -MAX_VALUE; - @Native public static final double NaN = 0. / 0; - @Native public static final double NEGATIVE_INFINITY = -1. / 0; - @Native public static final double POSITIVE_INFINITY = 1. / 0; + @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; - @Native public static boolean isFinite(Context ctx, double val) { return Double.isFinite(val); } - @Native public static boolean isInfinite(Context ctx, double val) { return Double.isInfinite(val); } - @Native public static boolean isNaN(Context ctx, double val) { return Double.isNaN(val); } - @Native public static boolean isSafeInteger(Context ctx, double val) { - return val > MIN_SAFE_INTEGER && val < MAX_SAFE_INTEGER; + @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; } - @Native public static double parseFloat(Context ctx, String val) { - return Values.toNumber(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static double __parseFloat(Arguments args) { + return args.getDouble(0); } - @Native public static double parseInt(Context ctx, String val) { - return (long)Values.toNumber(ctx, val); + @Expose(target = ExposeTarget.STATIC) + public static double __parseInt(Arguments args) { + return args.getLong(0); } - @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) { - val = Values.toNumber(ctx, val); - if (thisArg instanceof ObjectValue) return new NumberLib((double)val); - else return val; + @ExposeConstructor public static Object __constructor(Arguments args) { + if (args.self instanceof ObjectValue) return new NumberLib(args.getDouble(0)); + else return args.getDouble(0); } - @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - return Values.toString(ctx, Values.toNumber(ctx, thisArg)); + @Expose public static String __toString(Arguments args) { + return Values.toString(args.ctx, args.getDouble(0)); } - @Native(thisArg = true) public static double valueOf(Context ctx, Object thisArg) { - if (thisArg instanceof NumberLib) return ((NumberLib)thisArg).value; - else return Values.toNumber(ctx, thisArg); + @Expose public static double __valueOf(Arguments args) { + if (args.self instanceof NumberLib) return args.self(NumberLib.class).value; + else return Values.toNumber(args.ctx, args.self); } public NumberLib(double val) { diff --git a/src/me/topchetoeu/jscript/lib/ObjectLib.java b/src/me/topchetoeu/jscript/lib/ObjectLib.java index 2114f20..ad14a7f 100644 --- a/src/me/topchetoeu/jscript/lib/ObjectLib.java +++ b/src/me/topchetoeu/jscript/lib/ObjectLib.java @@ -1,171 +1,219 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Object") public class ObjectLib { - @Native public static ObjectValue assign(Context ctx, ObjectValue dst, Object... src) { - for (var obj : src) { - for (var key : Values.getMembers(ctx, obj, true, true)) { - Values.setMember(ctx, dst, key, Values.getMember(ctx, obj, key)); +@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.ctx, obj, true, true)) { + Values.setMember(args.ctx, args.get(0), key, Values.getMember(args.ctx, obj, key)); } } - return dst; + return args.get(0); } - @Native public static ObjectValue create(Context ctx, ObjectValue proto, ObjectValue props) { + @Expose(target = ExposeTarget.STATIC) + public static ObjectValue __create(Arguments args) { var obj = new ObjectValue(); - obj.setPrototype(ctx, proto); - return defineProperties(ctx, obj, props); + obj.setPrototype(args.ctx, 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)); + } + + return obj; } - @Native public static ObjectValue defineProperty(Context ctx, ObjectValue obj, Object key, ObjectValue attrib) { - var hasVal = attrib.hasMember(ctx, "value", false); - var hasGet = attrib.hasMember(ctx, "get", false); - var hasSet = attrib.hasMember(ctx, "set", false); + @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 = attrib.hasMember(args.ctx, "value", false); + var hasGet = attrib.hasMember(args.ctx, "get", false); + var hasSet = attrib.hasMember(args.ctx, "set", false); if (hasVal) { if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property."); if (!obj.defineProperty( - ctx, key, - attrib.getMember(ctx, "value"), - Values.toBoolean(attrib.getMember(ctx, "writable")), - Values.toBoolean(attrib.getMember(ctx, "configurable")), - Values.toBoolean(attrib.getMember(ctx, "enumerable")) + args.ctx, key, + attrib.getMember(args.ctx, "value"), + Values.toBoolean(attrib.getMember(args.ctx, "writable")), + Values.toBoolean(attrib.getMember(args.ctx, "configurable")), + Values.toBoolean(attrib.getMember(args.ctx, "enumerable")) )) throw EngineException.ofType("Can't define property '" + key + "'."); } else { - var get = attrib.getMember(ctx, "get"); - var set = attrib.getMember(ctx, "set"); + var get = attrib.getMember(args.ctx, "get"); + var set = attrib.getMember(args.ctx, "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( - ctx, key, + args.ctx, key, (FunctionValue)get, (FunctionValue)set, - Values.toBoolean(attrib.getMember(ctx, "configurable")), - Values.toBoolean(attrib.getMember(ctx, "enumerable")) + Values.toBoolean(attrib.getMember(args.ctx, "configurable")), + Values.toBoolean(attrib.getMember(args.ctx, "enumerable")) )) throw EngineException.ofType("Can't define property '" + key + "'."); } return obj; } - @Native public static ObjectValue defineProperties(Context ctx, ObjectValue obj, ObjectValue attrib) { + @Expose(target = ExposeTarget.STATIC) + public static ObjectValue __defineProperties(Arguments args) { + var obj = args.convert(0, ObjectValue.class); + var attrib = args.convert(1, ObjectValue.class); + for (var key : Values.getMembers(null, obj, false, false)) { - obj.defineProperty(ctx, key, attrib.getMember(ctx, key)); + obj.defineProperty(args.ctx, key, attrib.getMember(args.ctx, key)); } return obj; } - @Native public static ArrayValue keys(Context ctx, Object obj, Object all) { + @Expose(target = ExposeTarget.STATIC) + public static ArrayValue __keys(Arguments args) { + var obj = args.convert(0, ObjectValue.class); + var all = args.getBoolean(1); var res = new ArrayValue(); - var _all = Values.toBoolean(all); - for (var key : Values.getMembers(ctx, obj, true, false)) { - if (_all || !(key instanceof Symbol)) res.set(ctx, res.size(), key); + for (var key : Values.getMembers(args.ctx, obj, true, false)) { + if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key); } return res; } - @Native public static ArrayValue entries(Context ctx, Object obj, Object all) { + @Expose(target = ExposeTarget.STATIC) + public static ArrayValue __entries(Arguments args) { var res = new ArrayValue(); - var _all = Values.toBoolean(all); + var obj = args.convert(0, ObjectValue.class); + var all = args.getBoolean(1); - for (var key : Values.getMembers(ctx, obj, true, false)) { - if (_all || !(key instanceof Symbol)) res.set(ctx, res.size(), new ArrayValue(ctx, key, Values.getMember(ctx, obj, key))); + 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))); } return res; } - @Native public static ArrayValue values(Context ctx, Object obj, Object all) { + @Expose(target = ExposeTarget.STATIC) + public static ArrayValue __values(Arguments args) { var res = new ArrayValue(); - var _all = Values.toBoolean(all); + var obj = args.convert(0, ObjectValue.class); + var all = args.getBoolean(1); - for (var key : Values.getMembers(ctx, obj, true, false)) { - if (_all || key instanceof String) res.set(ctx, res.size(), Values.getMember(ctx, obj, key)); + for (var key : Values.getMembers(args.ctx, obj, true, false)) { + if (all || key instanceof String) res.set(args.ctx, res.size(), Values.getMember(args.ctx, obj, key)); } return res; } - - @Native public static ObjectValue getOwnPropertyDescriptor(Context ctx, Object obj, Object key) { - return Values.getMemberDescriptor(ctx, obj, key); + + @Expose(target = ExposeTarget.STATIC) + public static ObjectValue __getOwnPropertyDescriptor(Arguments args) { + return Values.getMemberDescriptor(args.ctx, args.get(0), args.get(1)); } - @Native public static ObjectValue getOwnPropertyDescriptors(Context ctx, Object obj) { + @Expose(target = ExposeTarget.STATIC) + public static ObjectValue __getOwnPropertyDescriptors(Arguments args) { var res = new ObjectValue(); - for (var key : Values.getMembers(ctx, obj, true, true)) { - res.defineProperty(ctx, key, getOwnPropertyDescriptor(ctx, obj, key)); + var obj = args.get(0); + for (var key : Values.getMembers(args.ctx, obj, true, true)) { + res.defineProperty(args.ctx, key, __getOwnPropertyDescriptor(new Arguments(args.ctx, null, obj, key))); } return res; } - @Native public static ArrayValue getOwnPropertyNames(Context ctx, Object obj, Object all) { + @Expose(target = ExposeTarget.STATIC) + public static ArrayValue __getOwnPropertyNames(Arguments args) { var res = new ArrayValue(); - var _all = Values.toBoolean(all); + var obj = args.convert(0, ObjectValue.class); + var all = args.getBoolean(1); - for (var key : Values.getMembers(ctx, obj, true, true)) { - if (_all || !(key instanceof Symbol)) res.set(ctx, res.size(), key); + for (var key : Values.getMembers(args.ctx, obj, true, true)) { + if (all || !(key instanceof Symbol)) res.set(args.ctx, res.size(), key); } return res; } - @Native public static ArrayValue getOwnPropertySymbols(Context ctx, Object obj) { + @Expose(target = ExposeTarget.STATIC) + public static ArrayValue __getOwnPropertySymbols(Arguments args) { + var obj = args.convert(0, ObjectValue.class); var res = new ArrayValue(); - for (var key : Values.getMembers(ctx, obj, true, true)) { - if (key instanceof Symbol) res.set(ctx, res.size(), key); + for (var key : Values.getMembers(args.ctx, obj, true, true)) { + if (key instanceof Symbol) res.set(args.ctx, res.size(), key); } return res; } - @Native public static boolean hasOwn(Context ctx, Object obj, Object key) { - return Values.hasMember(ctx, obj, key, true); + @Expose(target = ExposeTarget.STATIC) + public static boolean __hasOwn(Arguments args) { + return Values.hasMember(args.ctx, args.get(0), args.get(1), true); } - @Native public static ObjectValue getPrototypeOf(Context ctx, Object obj) { - return Values.getPrototype(ctx, obj); + @Expose(target = ExposeTarget.STATIC) + public static ObjectValue __getPrototypeOf(Arguments args) { + return Values.getPrototype(args.ctx, args.get(0)); } - @Native public static Object setPrototypeOf(Context ctx, Object obj, Object proto) { - Values.setPrototype(ctx, obj, proto); - return obj; + @Expose(target = ExposeTarget.STATIC) + public static Object __setPrototypeOf(Arguments args) { + Values.setPrototype(args.ctx, args.get(0), args.get(1)); + return args.get(0); } - @Native public static ObjectValue fromEntries(Context ctx, Object iterable) { + @Expose(target = ExposeTarget.STATIC) + public static ObjectValue __fromEntries(Arguments args) { var res = new ObjectValue(); - for (var el : Values.fromJSIterator(ctx, iterable)) { + for (var el : Values.fromJSIterator(args.ctx, args.get(0))) { if (el instanceof ArrayValue) { - res.defineProperty(ctx, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1)); + res.defineProperty(args.ctx, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1)); } } return res; } - @Native public static Object preventExtensions(Context ctx, Object obj) { - if (obj instanceof ObjectValue) ((ObjectValue)obj).preventExtensions(); - return obj; + @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); } - @Native public static Object seal(Context ctx, Object obj) { - if (obj instanceof ObjectValue) ((ObjectValue)obj).seal(); - return obj; + @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); } - @Native public static Object freeze(Context ctx, Object obj) { - if (obj instanceof ObjectValue) ((ObjectValue)obj).freeze(); - return obj; + @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); } - @Native public static boolean isExtensible(Context ctx, Object obj) { + @Expose(target = ExposeTarget.STATIC) + public static boolean __isExtensible(Arguments args) { + var obj = args.get(0); return obj instanceof ObjectValue && ((ObjectValue)obj).extensible(); } - @Native public static boolean isSealed(Context ctx, Object obj) { + @Expose(target = ExposeTarget.STATIC) + public static boolean __isSealed(Arguments args) { + var obj = args.get(0); + if (obj instanceof ObjectValue && ((ObjectValue)obj).extensible()) { var _obj = (ObjectValue)obj; for (var key : _obj.keys(true)) { @@ -175,7 +223,10 @@ import me.topchetoeu.jscript.interop.NativeConstructor; return true; } - @Native public static boolean isFrozen(Context ctx, Object obj) { + @Expose(target = ExposeTarget.STATIC) + public static boolean __isFrozen(Arguments args) { + var obj = args.get(0); + if (obj instanceof ObjectValue && ((ObjectValue)obj).extensible()) { var _obj = (ObjectValue)obj; for (var key : _obj.keys(true)) { @@ -187,26 +238,31 @@ import me.topchetoeu.jscript.interop.NativeConstructor; return true; } - @Native(thisArg = true) public static Object valueOf(Context ctx, Object thisArg) { - return thisArg; + @Expose + public static Object __valueOf(Arguments args) { + return args.self; } - @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - var name = Values.getMember(ctx, thisArg, Symbol.get("Symbol.typeName")); + @Expose + public static String __toString(Arguments args) { + var name = Values.getMember(args.ctx, args.self, Symbol.get("Symbol.typeName")); if (name == null) name = "Unknown"; - else name = Values.toString(ctx, name); + else name = Values.toString(args.ctx, name); return "[object " + name + "]"; } - @Native(thisArg = true) public static boolean hasOwnProperty(Context ctx, Object thisArg, Object key) { - return ObjectLib.hasOwn(ctx, thisArg, Values.convert(ctx, key, String.class)); + @Expose + public static boolean __hasOwnProperty(Arguments args) { + return Values.hasMember(args.ctx, args.self, args.get(0), true); } - @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object arg) { + @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 BooleanLib.constructor(ctx, thisArg, arg); - else if (arg instanceof Number) return NumberLib.constructor(ctx, thisArg, arg); - else if (arg instanceof String) return StringLib.constructor(ctx, thisArg, arg); - // else if (arg instanceof Symbol) return SymbolPolyfill.constructor(ctx, thisArg, arg); + 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/me/topchetoeu/jscript/lib/PromiseLib.java b/src/me/topchetoeu/jscript/lib/PromiseLib.java index 3adac3a..e54bdd3 100644 --- a/src/me/topchetoeu/jscript/lib/PromiseLib.java +++ b/src/me/topchetoeu/jscript/lib/PromiseLib.java @@ -2,355 +2,314 @@ package me.topchetoeu.jscript.lib; import java.util.ArrayList; import java.util.List; -import java.util.Map; import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.EventLoop; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.NativeWrapper; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.ResultRunnable; -@Native("Promise") public class PromiseLib { - public static interface PromiseRunner { - Object run(); - } - private static class Handle { - public final Context ctx; - public final FunctionValue fulfilled; - public final FunctionValue rejected; +@WrapperName("Promise") +public class PromiseLib { + public static interface Handle { + void onFulfil(Object val); + void onReject(EngineException err); - public Handle(Context ctx, FunctionValue fulfilled, FunctionValue rejected) { - this.ctx = ctx; - this.fulfilled = fulfilled; - this.rejected = rejected; + default Handle defer(EventLoop loop) { + var self = this; + return new Handle() { + @Override public void onFulfil(Object val) { + loop.pushMsg(() -> self.onFulfil(val), true); + } + @Override public void onReject(EngineException val) { + loop.pushMsg(() -> self.onReject(val), true); + } + }; } } - @Native("resolve") - public static PromiseLib ofResolved(Context ctx, Object val) { - var res = new PromiseLib(); - res.fulfill(ctx, val); - return res; - } - @Native("reject") - public static PromiseLib ofRejected(Context ctx, Object val) { - var res = new PromiseLib(); - res.reject(ctx, val); - return res; - } - - @Native public static PromiseLib any(Context ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); - 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); - then(ctx, val, - new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), - new NativeFunction(null, (e, th, args) -> { - errors.set(ctx, index, args[0]); - n[0]--; - if (n[0] <= 0) res.reject(e, errors); - return null; - }) - ); - } - - return res; - } - @Native public static PromiseLib race(Context ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); - var res = new PromiseLib(); - - for (var i = 0; i < promises.size(); i++) { - var val = promises.get(i); - then(ctx, val, - new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }), - new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) - ); - } - - return res; - } - @Native public static PromiseLib all(Context ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - then(ctx, val, - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, args[0]); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }), - new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; }) - ); - } - - if (n[0] <= 0) res.fulfill(ctx, result); - - return res; - } - @Native public static PromiseLib allSettled(Context ctx, Object _promises) { - if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = Values.array(_promises); - if (promises.size() == 0) return ofResolved(ctx, new ArrayValue()); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - then(ctx, val, - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, new ObjectValue(ctx, Map.of( - "status", "fulfilled", - "value", args[0] - ))); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }), - new NativeFunction(null, (e, th, args) -> { - result.set(ctx, index, new ObjectValue(ctx, Map.of( - "status", "rejected", - "reason", args[0] - ))); - n[0]--; - if (n[0] <= 0) res.fulfill(e, result); - return null; - }) - ); - } - - if (n[0] <= 0) res.fulfill(ctx, result); - - return res; - } - - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native(thisArg=true) public static Object then(Context ctx, Object _thisArg, Object _onFulfill, Object _onReject) { - var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null; - var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null; - - var res = new PromiseLib(); - - var fulfill = onFulfill == null ? new NativeFunction((_ctx, _0, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill; - var reject = onReject == null ? new NativeFunction((_ctx, _0, _args) -> { - throw new EngineException(_args.length > 0 ? _args[0] : null); - }) : (FunctionValue)onReject; - - var thisArg = _thisArg instanceof NativeWrapper && ((NativeWrapper)_thisArg).wrapped instanceof PromiseLib ? - ((NativeWrapper)_thisArg).wrapped : - _thisArg; - - var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> { - try { res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class)); } - catch (EngineException err) { res.reject(ctx, err.value); } - return null; - }); - var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> { - try { res.fulfill(ctx, reject.call(ctx, null, a[0])); } - catch (EngineException err) { res.reject(ctx, err.value); } - if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handled = true; - return null; - }); - - if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handle(ctx, fulfillHandle, rejectHandle); - else { - Object next; - try { next = Values.getMember(ctx, thisArg, "then"); } - catch (IllegalArgumentException e) { next = null; } - - try { - if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, thisArg, fulfillHandle, rejectHandle); - else res.fulfill(ctx, fulfill.call(ctx, null, thisArg)); - } - catch (EngineException err) { - res.reject(ctx, fulfill.call(ctx, null, err.value)); - } - } - - return res; - } - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) { - return then(ctx, thisArg, null, _onReject); - } - /** - * Thread safe - you can call this from anywhere - * HOWEVER, it's strongly recommended to use this only in javascript - */ - @Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) { - return then(ctx, thisArg, - new NativeFunction(null, (e, th, _args) -> { - if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx); - return _args.length > 0 ? _args[0] : null; - }), - new NativeFunction(null, (e, th, _args) -> { - if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx); - throw new EngineException(_args.length > 0 ? _args[0] : null); - }) - ); - } - - private List handles = new ArrayList<>(); - private static final int STATE_PENDING = 0; private static final int STATE_FULFILLED = 1; private static final int STATE_REJECTED = 2; - private int state = STATE_PENDING; - private boolean handled = false; - private Object val; + @Expose(value = "resolve", target = ExposeTarget.STATIC) + public static PromiseLib __ofResolved(Arguments args) { + return ofResolved(args.ctx, args.get(0)); + } + @Expose(value = "reject", target = ExposeTarget.STATIC) + public static PromiseLib __ofRejected(Arguments args) { + return ofRejected(args.ctx, new EngineException(args.get(0)).setCtx(args.ctx)); + } - public synchronized void fulfill(Context ctx, Object val) { - if (this.state != STATE_PENDING) return; + @Expose(target = ExposeTarget.STATIC) + private 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 (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx, - new NativeFunction(null, (e, th, a) -> { this.fulfill(ctx, a[0]); return null; }), - new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }) - ); - else { - Object then; - try { then = Values.getMember(ctx, val, "then"); } - catch (IllegalArgumentException e) { then = null; } + if (promises.size() == 0) return ofRejected(args.ctx, EngineException.ofError("No promises passed to 'Promise.any'.").setCtx(args.ctx)); + var n = new int[] { promises.size() }; + var res = new PromiseLib(); + var errors = new ArrayValue(); - try { - if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val, - new NativeFunction((e, _thisArg, a) -> { this.fulfill(ctx, a.length > 0 ? a[0] : null); return null; }), - new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }) - ); - else { - this.val = val; - this.state = STATE_FULFILLED; + for (var i = 0; i < promises.size(); i++) { + var index = i; + var val = promises.get(i); + if (res.state != STATE_PENDING) break; - ctx.engine.pushMsg(true, ctx.environment, new NativeFunction((_ctx, _thisArg, _args) -> { - for (var handle : handles) { - handle.fulfilled.call(handle.ctx, null, val); - } - handles = null; - return null; - }), null); + handle(args.ctx, val, new Handle() { + public void onFulfil(Object val) { res.fulfill(args.ctx, val); } + public void onReject(EngineException err) { + errors.set(args.ctx, index, err.value); + n[0]--; + if (n[0] <= 0) res.reject(args.ctx, new EngineException(errors).setCtx(args.ctx)); } - } - catch (EngineException err) { - this.reject(ctx, err.value); - } + }); } + + return res; } - public synchronized void reject(Context ctx, Object val) { - if (this.state != STATE_PENDING) return; + @Expose(target = ExposeTarget.STATIC) + private 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(); - if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx, - new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }), - new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }) - ); - else { - Object then; - try { then = Values.getMember(ctx, val, "then"); } - catch (IllegalArgumentException e) { then = null; } + for (var i = 0; i < promises.size(); i++) { + var val = promises.get(i); + if (res.state != STATE_PENDING) break; - try { - if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val, - new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }), - new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }) - ); - else { - this.val = val; - this.state = STATE_REJECTED; + 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); } + }); + } - ctx.engine.pushMsg(true, ctx.environment, new NativeFunction((_ctx, _thisArg, _args) -> { - for (var handle : handles) handle.rejected.call(handle.ctx, null, val); - if (!handled) { - Values.printError(new EngineException(val).setCtx(ctx.environment, ctx.engine), "(in promise)"); - } - handles = null; - return null; - }), null); + return res; + } + @Expose(target = ExposeTarget.STATIC) + private 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.ctx, val, new Handle() { + @Override public void onFulfil(Object val) { + result.set(args.ctx, index, val); + n[0]--; + if (n[0] <= 0) res.fulfill(args.ctx, result); } - } - catch (EngineException err) { - this.reject(ctx, err.value); - } + @Override public void onReject(EngineException err) { + res.reject(args.ctx, err); + } + }); } - } - private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) { - if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx.environment, fulfill, null, val); - else if (state == STATE_REJECTED) { - ctx.engine.pushMsg(true, ctx.environment, reject, null, val); - handled = true; + if (n[0] <= 0) res.fulfill(args.ctx, result); + + return res; + } + @Expose(target = ExposeTarget.STATIC) + private 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.ctx, 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); + + result.set(args.ctx, index, desc); + + n[0]--; + if (n[0] <= 0) res.fulfill(args.ctx, res); + } + @Override public void onReject(EngineException err) { + var desc = new ObjectValue(); + desc.defineProperty(args.ctx, "status", "reject"); + desc.defineProperty(args.ctx, "value", err.value); + + result.set(args.ctx, index, desc); + + n[0]--; + if (n[0] <= 0) res.fulfill(args.ctx, res); + } + }); } - else handles.add(new Handle(ctx, fulfill, reject)); + + if (n[0] <= 0) res.fulfill(args.ctx, result); + + return res; } - @Override @Native public String toString() { - if (state == STATE_PENDING) return "Promise (pending)"; - else if (state == STATE_FULFILLED) return "Promise (fulfilled)"; - else return "Promise (rejected)"; + @Expose + private 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.ctx, 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); } + } + @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); } + } + }.defer(args.ctx.engine)); + + return res; + } + @Expose + private static Object __catch(Arguments args) { + return __then(new Arguments(args.ctx, args.self, null, args.get(0))); + } + @Expose + private static Object __finally(Arguments args) { + var func = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null; + + var res = new PromiseLib(); + + handle(args.ctx, args.self, new Handle() { + @Override public void onFulfil(Object val) { + try { + func.call(args.ctx); + res.fulfill(args.ctx, val); + } + catch (EngineException e) { res.reject(args.ctx, e); } + } + @Override public void onReject(EngineException err) { + try { + func.call(args.ctx); + res.reject(args.ctx, err); + } + catch (EngineException e) { res.reject(args.ctx, e); } + } + }.defer(args.ctx.engine)); + + return res; } - /** - * NOT THREAD SAFE - must be called from the engine executor thread - */ - @Native public PromiseLib(Context ctx, FunctionValue func) { - if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor."); + @ExposeConstructor + private static PromiseLib __constructor(Arguments args) { + var func = args.convert(0, FunctionValue.class); + var res = new PromiseLib(); + try { func.call( - ctx, null, - new NativeFunction(null, (e, th, args) -> { - fulfill(e, args.length > 0 ? args[0] : null); + args.ctx, null, + new NativeFunction(null, _args -> { + res.fulfill(_args.ctx, _args.get(0)); return null; }), - new NativeFunction(null, (e, th, args) -> { - reject(e, args.length > 0 ? args[0] : null); + new NativeFunction(null, _args -> { + res.reject(_args.ctx, new EngineException(_args.get(0)).setCtx(_args.ctx)); return null; }) ); } catch (EngineException e) { - reject(ctx, e.value); + res.reject(args.ctx, e); } + + return res; } - private PromiseLib(int state, Object val) { - this.state = state; - this.val = val; + private List handles = new ArrayList<>(); + + private int state = STATE_PENDING; + private boolean handled = false; + private Object val; + + private void resolveSynchronized(Context ctx, Object val, int newState) { + ctx.engine.pushMsg(() -> { + 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(new EngineException(val).setCtx(ctx.environment, ctx.engine), "(in promise)"); + } + + handles = null; + }, true); + } + private synchronized void resolve(Context ctx, Object val, int newState) { + if (this.state != STATE_PENDING || newState == STATE_PENDING) return; + + handle(ctx, val, new Handle() { + @Override public void onFulfil(Object val) { + resolveSynchronized(ctx, val, newState); + } + @Override public void onReject(EngineException err) { + resolveSynchronized(ctx, val, STATE_REJECTED); + } + }); + } + + public synchronized void fulfill(Context ctx, Object val) { + resolve(ctx, val, STATE_FULFILLED); + } + public synchronized void reject(Context ctx, EngineException val) { + resolve(ctx, 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_PENDING, null); + this.state = STATE_PENDING; + this.val = null; } - public static PromiseLib await(Context ctx, PromiseRunner runner) { + public static PromiseLib await(Context ctx, ResultRunnable runner) { var res = new PromiseLib(); new Thread(() -> { @@ -358,10 +317,70 @@ import me.topchetoeu.jscript.interop.Native; res.fulfill(ctx, runner.run()); } catch (EngineException e) { - res.reject(ctx, e.value); + res.reject(ctx, e); } }, "Promisifier").start(); return res; } + public static PromiseLib await(Context ctx, Runnable runner) { + return await(ctx, () -> { + runner.run(); + return null; + }); + } + + public static void handle(Context ctx, Object obj, Handle handle) { + if (Values.isWrapper(obj, PromiseLib.class)) { + var promise = Values.wrapper(obj, PromiseLib.class); + handle(ctx, promise, handle); + return; + } + if (obj instanceof PromiseLib) { + ((PromiseLib)obj).handle(handle); + return; + } + + var rethrow = new boolean[1]; + + try { + var then = Values.getMember(ctx, obj, "then"); + Values.call(ctx, 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; + }) + ); + + return; + } + catch (Exception e) { + if (rethrow[0]) throw e; + } + + handle.onFulfil(obj); + } + + public static PromiseLib ofResolved(Context ctx, Object value) { + var res = new PromiseLib(); + res.fulfill(ctx, value); + return res; + } + public static PromiseLib ofRejected(Context ctx, EngineException value) { + var res = new PromiseLib(); + res.reject(ctx, value); + return res; + } } diff --git a/src/me/topchetoeu/jscript/lib/RangeErrorLib.java b/src/me/topchetoeu/jscript/lib/RangeErrorLib.java index 3c48d42..86b10ad 100644 --- a/src/me/topchetoeu/jscript/lib/RangeErrorLib.java +++ b/src/me/topchetoeu/jscript/lib/RangeErrorLib.java @@ -1,22 +1,22 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.interop.InitType; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeInit; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; -@Native("RangeError") public class RangeErrorLib extends ErrorLib { - @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) { - var target = ErrorLib.constructor(ctx, thisArg, message); - target.setPrototype(PlaceholderProto.SYNTAX_ERROR); - target.defineProperty(ctx, "name", "RangeError"); +@WrapperName("RangeError") +public class RangeErrorLib extends ErrorLib { + @ExposeField(target = ExposeTarget.STATIC) + 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; } - @NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) { - target.defineProperty(null, "name", "RangeError"); - } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/lib/RegExpLib.java b/src/me/topchetoeu/jscript/lib/RegExpLib.java index cea6970..64c5b1d 100644 --- a/src/me/topchetoeu/jscript/lib/RegExpLib.java +++ b/src/me/topchetoeu/jscript/lib/RegExpLib.java @@ -10,79 +10,63 @@ import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.NativeWrapper; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.ExposeType; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("RegExp") public class RegExpLib { +@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 static String cleanupPattern(Context ctx, 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); - if (res.equals("")) return "(?:)"; - return res; - } - private static String cleanupFlags(Context ctx, Object val) { - if (val == null) return ""; - return Values.toString(ctx, 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; - } - - @Native - public static RegExpLib escape(Context ctx, Object raw, Object flags) { - return escape(Values.toString(ctx, raw), cleanupFlags(ctx, flags)); - } - public static RegExpLib escape(String raw, String flags) { - return new RegExpLib(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags); - } - private Pattern pattern; private String[] namedGroups; private int flags; - - @Native public int lastI = 0; - @Native public final String source; - @Native public final boolean hasIndices; - @Native public final boolean global; - @Native public final boolean sticky; - @Native("@@Symbol.typeName") public final String name = "RegExp"; - @NativeGetter public boolean ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; } - @NativeGetter public boolean multiline() { return (flags & Pattern.MULTILINE) != 0; } - @NativeGetter public boolean unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; } - @NativeGetter public boolean dotAll() { return (flags & Pattern.DOTALL) != 0; } + public int lastI = 0; + public final String source; + public final boolean hasIndices; + public final boolean global; + public final boolean sticky; - @NativeGetter("flags") public final String flags() { + @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 (__ignoreCase()) res += 'i'; + if (__multiline()) res += 'm'; + if (__dotAll()) res += 's'; + if (__unicode()) res += 'u'; if (sticky) res += 'y'; return res; } - @Native public Object exec(String str) { + @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; @@ -126,39 +110,36 @@ import me.topchetoeu.jscript.interop.NativeGetter; return obj; } - @Native public boolean test(String str) { - return this.exec(str) != Values.NULL; - } - @Native public String toString() { - return "/" + source + "/" + flags(); + @Expose public boolean __test(Arguments args) { + return this.__exec(args) != Values.NULL; } - @Native("@@Symbol.match") public Object match(Context ctx, String target) { + @Expose("@@Symbol.match") public Object __match(Arguments args) { if (this.global) { var res = new ArrayValue(); Object val; - while ((val = this.exec(target)) != Values.NULL) { - res.set(ctx, res.size(), Values.getMember(ctx, val, 0)); + while ((val = this.__exec(args)) != Values.NULL) { + res.set(args.ctx, res.size(), Values.getMember(args.ctx, val, 0)); } lastI = 0; return res; } else { - var res = this.exec(target); + var res = this.__exec(args); if (!this.sticky) this.lastI = 0; return res; } } - @Native("@@Symbol.matchAll") public Object matchAll(Context ctx, String target) { - var pattern = new RegExpLib(this.source, this.flags() + "g"); + @Expose("@@Symbol.matchAll") public Object __matchAll(Arguments args) { + var pattern = this.toGlobal(); - return Values.toJSIterator(ctx, new Iterator() { + return Values.toJSIterator(args.ctx, new Iterator() { private Object val = null; private boolean updated = false; private void update() { - if (!updated) val = pattern.exec(target); + if (!updated) val = pattern.__exec(args); } @Override public boolean hasNext() { update(); @@ -172,17 +153,21 @@ import me.topchetoeu.jscript.interop.NativeGetter; }); } - @Native("@@Symbol.split") public ArrayValue split(Context ctx, String target, Object limit, boolean sensible) { - var pattern = new RegExpLib(this.source, this.flags() + "g"); + @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(); - var lim = limit == null ? 0 : Values.toNumber(ctx, limit); - while ((match = pattern.exec(target)) != Values.NULL) { + while ((match = pattern.__exec(args)) != Values.NULL) { var added = new ArrayList(); var arrMatch = (ArrayValue)match; - int index = (int)Values.toNumber(ctx, Values.getMember(ctx, match, "index")); + int index = (int)Values.toNumber(args.ctx, Values.getMember(args.ctx, match, "index")); var matchVal = (String)arrMatch.get(0); if (index >= target.length()) break; @@ -198,31 +183,33 @@ import me.topchetoeu.jscript.interop.NativeGetter; } if (sensible) { - if (limit != null && res.size() + added.size() >= lim) break; - else for (var i = 0; i < added.size(); i++) res.set(ctx, res.size(), added.get(i)); + 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++) { - if (limit != null && res.size() >= lim) return res; - else res.set(ctx, res.size(), added.get(i)); + if (hasLimit && res.size() >= lim) return res; + else res.set(args.ctx, res.size(), added.get(i)); } } lastEnd = pattern.lastI; } if (lastEnd < target.length()) { - res.set(ctx, res.size(), target.substring(lastEnd)); + res.set(args.ctx, res.size(), target.substring(lastEnd)); } return res; } - @Native("@@Symbol.replace") public String replace(Context ctx, String target, Object replacement) { - var pattern = new RegExpLib(this.source, this.flags() + "d"); + @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(target)) != Values.NULL) { - var indices = (ArrayValue)((ArrayValue)Values.getMember(ctx, match, "indices")).get(0); + while ((match = pattern.__exec(args)) != Values.NULL) { + var indices = (ArrayValue)((ArrayValue)Values.getMember(args.ctx, match, "indices")).get(0); var arrMatch = (ArrayValue)match; var start = ((Number)indices.get(0)).intValue(); @@ -230,15 +217,15 @@ import me.topchetoeu.jscript.interop.NativeGetter; res.append(target.substring(lastEnd, start)); if (replacement instanceof FunctionValue) { - var args = new Object[arrMatch.size() + 2]; - args[0] = target.substring(start, end); - arrMatch.copyTo(args, 1, 1, arrMatch.size() - 1); - args[args.length - 2] = start; - args[args.length - 1] = target; - res.append(Values.toString(ctx, ((FunctionValue)replacement).call(ctx, null, args))); + 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.ctx, ((FunctionValue)replacement).call(args.ctx, null, callArgs))); } else { - res.append(Values.toString(ctx, replacement)); + res.append(Values.toString(args.ctx, replacement)); } lastEnd = end; if (!pattern.global) break; @@ -271,9 +258,17 @@ import me.topchetoeu.jscript.interop.NativeGetter; // } // }, - @Native public RegExpLib(Context ctx, Object pattern, Object flags) { - this(cleanupPattern(ctx, pattern), cleanupFlags(ctx, flags)); + 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 = ""; @@ -304,6 +299,56 @@ import me.topchetoeu.jscript.interop.NativeGetter; 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.ctx, args.get(0)), cleanupFlags(args.ctx, 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))); + } + + private static String cleanupPattern(Context ctx, 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); + if (res.equals("")) return "(?:)"; + return res; + } + private static String cleanupFlags(Context ctx, Object val) { + if (val == null) return ""; + return Values.toString(ctx, 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/me/topchetoeu/jscript/lib/SetLib.java b/src/me/topchetoeu/jscript/lib/SetLib.java index 2cc35c2..bda9149 100644 --- a/src/me/topchetoeu/jscript/lib/SetLib.java +++ b/src/me/topchetoeu/jscript/lib/SetLib.java @@ -6,55 +6,64 @@ import java.util.stream.Collectors; import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeType; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Set") public class SetLib { +@WrapperName("Set") +public class SetLib { private LinkedHashSet set = new LinkedHashSet<>(); - @Native("@@Symbol.typeName") public final String name = "Set"; - @Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) { - return this.values(ctx); + @Expose("@@Symbol.iterator") + public ObjectValue __iterator(Arguments args) { + return this.__values(args); } - @Native public ObjectValue entries(Context ctx) { - return ArrayValue.of(ctx, set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList())); + @Expose public ObjectValue __entries(Arguments args) { + return ArrayValue.of(args.ctx, set.stream().map(v -> new ArrayValue(args.ctx, v, v)).collect(Collectors.toList())); } - @Native public ObjectValue keys(Context ctx) { - return ArrayValue.of(ctx, set); + @Expose public ObjectValue __keys(Arguments args) { + return ArrayValue.of(args.ctx, set); } - @Native public ObjectValue values(Context ctx) { - return ArrayValue.of(ctx, set); + @Expose public ObjectValue __values(Arguments args) { + return ArrayValue.of(args.ctx, set); } - @Native public Object add(Object key) { - return set.add(key); + @Expose public Object __add(Arguments args) { + return set.add(args.get(0)); } - @Native public boolean delete(Object key) { - return set.remove(key); + @Expose public boolean __delete(Arguments args) { + return set.remove(args.get(0)); } - @Native public boolean has(Object key) { - return set.contains(key); + @Expose public boolean __has(Arguments args) { + return set.contains(args.get(0)); } - @Native public void clear() { + @Expose public void __clear() { set.clear(); } - @NativeGetter public int size() { + @Expose(type = ExposeType.GETTER) + public int __size() { return set.size(); } - @Native public void forEach(Context ctx, FunctionValue func, Object thisArg) { + @Expose public void __forEach(Arguments args) { var keys = new ArrayList<>(set); - for (var el : keys) func.call(ctx, thisArg, el, el, this); + for (var el : keys) Values.call(args.ctx, args.get(0), args.get(1), el, el, this); } - @Native public SetLib(Context ctx, Object iterable) { - for (var el : Values.fromJSIterator(ctx, iterable)) add(el); + public SetLib(Context ctx, Object iterable) { + for (var el : Values.fromJSIterator(ctx, iterable)) set.add(el); + } + + @ExposeConstructor + public static SetLib __constructor(Arguments args) { + return new SetLib(args.ctx, args.get(0)); } } diff --git a/src/me/topchetoeu/jscript/lib/StringLib.java b/src/me/topchetoeu/jscript/lib/StringLib.java index 620bf1d..c377a38 100644 --- a/src/me/topchetoeu/jscript/lib/StringLib.java +++ b/src/me/topchetoeu/jscript/lib/StringLib.java @@ -2,7 +2,6 @@ package me.topchetoeu.jscript.lib; import java.util.regex.Pattern; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.FunctionValue; @@ -10,15 +9,20 @@ import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.ExposeType; +import me.topchetoeu.jscript.interop.WrapperName; // TODO: implement index wrapping properly -@Native("String") public class StringLib { +@WrapperName("String") +public class StringLib { public final String value; - private static String passThis(Context ctx, String funcName, Object val) { + private static String passThis(Arguments args, String funcName) { + var val = args.self; if (val instanceof StringLib) return ((StringLib)val).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)); @@ -32,170 +36,174 @@ import me.topchetoeu.jscript.interop.NativeGetter; return i; } - @NativeGetter(thisArg = true) public static int length(Context ctx, Object thisArg) { - return passThis(ctx, "substring", thisArg).length(); + @Expose(type = ExposeType.GETTER) + public static int __length(Arguments args) { + return passThis(args, "substring").length(); } - @Native(thisArg = true) public static String substring(Context ctx, Object thisArg, int start, Object _end) { - var val = passThis(ctx, "substring", thisArg); - start = normalizeI(start, val.length(), true); - int end = normalizeI(_end == null ? val.length() : (int)Values.toNumber(ctx, _end), val.length(), true); + @Expose public static String __substring(Arguments args) { + var val = passThis(args, "substring"); + var start = normalizeI(args.getInt(0), val.length(), true); + var end = normalizeI(args.getInt(1, val.length()), val.length(), true); return val.substring(start, end); } - @Native(thisArg = true) public static String substr(Context ctx, Object thisArg, int start, Object _len) { - var val = passThis(ctx, "substr", thisArg); - int len = _len == null ? val.length() - start : (int)Values.toNumber(ctx, _len); - return substring(ctx, val, start, start + len); + @Expose public static String __substr(Arguments args) { + var val = passThis(args, "substr"); + var start = normalizeI(args.getInt(0), val.length(), true); + int len = normalizeI(args.getInt(0), val.length() - start, true); + return val.substring(start, start + len); } - @Native(thisArg = true) public static String toLowerCase(Context ctx, Object thisArg) { - return passThis(ctx, "toLowerCase", thisArg).toLowerCase(); + @Expose public static String __toLowerCase(Arguments args) { + return passThis(args, "toLowerCase").toLowerCase(); } - @Native(thisArg = true) public static String toUpperCase(Context ctx, Object thisArg) { - return passThis(ctx, "toUpperCase", thisArg).toUpperCase(); + @Expose public static String __toUpperCase(Arguments args) { + return passThis(args, "toUpperCase").toUpperCase(); } - @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) { - return passThis(ctx, "charAt", thisArg).charAt(i) + ""; + @Expose public static String __charAt(Arguments args) { + return passThis(args, "charAt").charAt(args.getInt(0)) + ""; } - @Native(thisArg = true) public static double charCodeAt(Context ctx, Object thisArg, int i) { - var str = passThis(ctx, "charCodeAt", thisArg); + @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); } - @Native(thisArg = true) public static double codePointAt(Context ctx, Object thisArg, int i) { - var str = passThis(ctx, "codePointAt", thisArg); + @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); } - @Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) { - return passThis(ctx, "startsWith", thisArg).startsWith(term, pos); + @Expose public static boolean __startsWith(Arguments args) { + return passThis(args, "startsWith").startsWith(args.getString(0), args.getInt(1)); } - @Native(thisArg = true) public static boolean endsWith(Context ctx, Object thisArg, String term, int pos) { - var val = passThis(ctx, "endsWith", thisArg); - return val.lastIndexOf(term, pos) >= 0; + @Expose public static boolean __endsWith(Arguments args) { + return passThis(args, "endsWith").lastIndexOf(args.getString(0), args.getInt(1)) >= 0; } - @Native(thisArg = true) public static int indexOf(Context ctx, Object thisArg, Object term, int start) { - var val = passThis(ctx, "indexOf", thisArg); + @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.ctx, term, Symbol.get("Symbol.search")); - if (term != null && term != Values.NULL && !(term instanceof String)) { - var search = Values.getMember(ctx, term, Symbol.get("Symbol.search")); - if (search instanceof FunctionValue) { - return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, false, start)); - } + if (search instanceof FunctionValue) { + return (int)Values.toNumber(args.ctx, Values.call(args.ctx, search, term, val, false, start)); } - - return val.indexOf(Values.toString(ctx, term), start); + else return val.indexOf(Values.toString(args.ctx, term), start); } - @Native(thisArg = true) public static int lastIndexOf(Context ctx, Object thisArg, Object term, int pos) { - var val = passThis(ctx, "lastIndexOf", thisArg); + @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")); - if (term != null && term != Values.NULL && !(term instanceof String)) { - var search = Values.getMember(ctx, term, Symbol.get("Symbol.search")); - if (search instanceof FunctionValue) { - return (int)Values.toNumber(ctx, ((FunctionValue)search).call(ctx, term, val, true, pos)); - } + if (search instanceof FunctionValue) { + return (int)Values.toNumber(args.ctx, Values.call(args.ctx, search, term, val, true, start)); } - - return val.lastIndexOf(Values.toString(ctx, term), pos); + else return val.lastIndexOf(Values.toString(args.ctx, term), start); } - @Native(thisArg = true) public static boolean includes(Context ctx, Object thisArg, Object term, int pos) { - return indexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0; + @Expose public static boolean __includes(Arguments args) { + return __indexOf(args) >= 0; } - @Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, Object replacement) { - var val = passThis(ctx, "replace", thisArg); + @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.ctx, term, Symbol.get("Symbol.replace")); - if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(ctx, term, Symbol.get("Symbol.replace")); - if (replace instanceof FunctionValue) { - return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement)); - } + if (replace instanceof FunctionValue) { + return Values.toString(args.ctx, Values.call(args.ctx, replace, term, val, replacement)); } - - return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), Values.toString(ctx, replacement)); + else return val.replaceFirst(Pattern.quote(Values.toString(args.ctx, term)), Values.toString(args.ctx, replacement)); } - @Native(thisArg = true) public static String replaceAll(Context ctx, Object thisArg, Object term, Object replacement) { - var val = passThis(ctx, "replaceAll", thisArg); + @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")); - if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(ctx, term, Symbol.get("Symbol.replace")); - if (replace instanceof FunctionValue) { - return Values.toString(ctx, ((FunctionValue)replace).call(ctx, term, val, replacement)); - } + if (replace instanceof FunctionValue) { + return Values.toString(args.ctx, Values.call(args.ctx, replace, term, val, replacement)); } - - return val.replaceFirst(Pattern.quote(Values.toString(ctx, term)), Values.toString(ctx, replacement)); + else return val.replace(Values.toString(args.ctx, term), Values.toString(args.ctx, replacement)); } - @Native(thisArg = true) public static ArrayValue match(Context ctx, Object thisArg, Object term, String replacement) { - var val = passThis(ctx, "match", thisArg); + @Expose public static ArrayValue __match(Arguments args) { + var val = passThis(args, "match"); + var term = args.get(0); FunctionValue match; - + try { - var _match = Values.getMember(ctx, term, Symbol.get("Symbol.match")); + var _match = Values.getMember(args.ctx, term, Symbol.get("Symbol.match")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else if (ctx.has(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), Values.toString(ctx, term), ""); - _match = Values.getMember(ctx, regex, Symbol.get("Symbol.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")); 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(ctx, ""); } + catch (IllegalArgumentException e) { return new ArrayValue(args.ctx, ""); } - var res = match.call(ctx, term, val); + var res = match.call(args.ctx, term, val); if (res instanceof ArrayValue) return (ArrayValue)res; - else return new ArrayValue(ctx, ""); + else return new ArrayValue(args.ctx, ""); } - @Native(thisArg = true) public static Object matchAll(Context ctx, Object thisArg, Object term, String replacement) { - var val = passThis(ctx, "matchAll", thisArg); + @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(ctx, term, Symbol.get("Symbol.matchAll")); + var _match = Values.getMember(args.ctx, term, Symbol.get("Symbol.matchAll")); if (_match instanceof FunctionValue) match = (FunctionValue)_match; } catch (IllegalArgumentException e) { } - if (match == null && ctx.has(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), Values.toString(ctx, term), "g"); - var _match = Values.getMember(ctx, regex, Symbol.get("Symbol.matchAll")); + 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 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(ctx, term, val); + return match.call(args.ctx, term, val); } - @Native(thisArg = true) public static ArrayValue split(Context ctx, Object thisArg, Object term, Object lim, boolean sensible) { - var val = passThis(ctx, "split", thisArg); + @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(ctx, lim); + if (lim != null) lim = Values.toNumber(args.ctx, lim); if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(ctx, term, Symbol.get("Symbol.replace")); + var replace = Values.getMember(args.ctx, term, Symbol.get("Symbol.replace")); if (replace instanceof FunctionValue) { - var tmp = ((FunctionValue)replace).call(ctx, term, val, lim, sensible); + var tmp = ((FunctionValue)replace).call(args.ctx, 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(ctx, i, Values.toString(ctx, ((ArrayValue)tmp).get(i))); + for (int i = 0; i < parts.size(); i++) parts.set(args.ctx, i, Values.toString(args.ctx, ((ArrayValue)tmp).get(i))); return parts; } } } String[] parts; - var pattern = Pattern.quote(Values.toString(ctx, term)); + var pattern = Pattern.quote(Values.toString(args.ctx, term)); if (lim == null) parts = val.split(pattern); else if (sensible) parts = val.split(pattern, (int)(double)lim); @@ -207,7 +215,7 @@ import me.topchetoeu.jscript.interop.NativeGetter; 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(ctx, i, parts[i]); + for (var i = 0; i < parts.length && i < limit; i++) res.set(args.ctx, i, parts[i]); return res; } @@ -217,46 +225,49 @@ import me.topchetoeu.jscript.interop.NativeGetter; for (; i < parts.length; i++) { if (lim != null && (double)lim <= i) break; - res.set(ctx, i, parts[i]); + res.set(args.ctx, i, parts[i]); } return res; } - @Native(thisArg = true) public static String slice(Context ctx, Object thisArg, int start, Object _end) { - return substring(ctx, passThis(ctx, "slice", thisArg), start, _end); + @Expose public static String __slice(Arguments args) { + passThis(args, "slice"); + return __substring(args); } - @Native(thisArg = true) public static String concat(Context ctx, Object thisArg, Object... args) { - var res = new StringBuilder(passThis(ctx, "concat", thisArg)); + @Expose public static String __concat(Arguments args) { + var res = new StringBuilder(passThis(args, "concat")); - for (var el : args) res.append(Values.toString(ctx, el)); + for (var el : args.convert(String.class)) res.append(el); return res.toString(); } - @Native(thisArg = true) public static String trim(Context ctx, Object thisArg) { - return passThis(ctx, "trim", thisArg).trim(); + @Expose public static String __trim(Arguments args) { + return passThis(args, "trim").trim(); } - @Native(thisArg = true) public static String trimStart(Context ctx, Object thisArg) { - return passThis(ctx, "trimStart", thisArg).replaceAll("^\\s+", ""); + @Expose public static String __trimStart(Arguments args) { + return passThis(args, "trimStart").replaceAll("^\\s+", ""); } - @Native(thisArg = true) public static String trimEnd(Context ctx, Object thisArg) { - return passThis(ctx, "trimEnd", thisArg).replaceAll("\\s+$", ""); + @Expose public static String __trimEnd(Arguments args) { + return passThis(args, "trimEnd").replaceAll("\\s+$", ""); } - @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) { - val = Values.toString(ctx, val); - if (thisArg instanceof ObjectValue) return new StringLib((String)val); + @ExposeConstructor public static Object __constructor(Arguments args) { + var val = args.getString(0); + if (args.self instanceof ObjectValue) return new StringLib(val); else return val; } - @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - return passThis(ctx, "toString", thisArg); + @Expose public static String __toString(Arguments args) { + return passThis(args, "toString"); } - @Native(thisArg = true) public static String valueOf(Context ctx, Object thisArg) { - return passThis(ctx, "valueOf", thisArg); + @Expose public static String __valueOf(Arguments args) { + return passThis(args, "valueOf"); } - @Native public static String fromCharCode(int ...val) { + @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/me/topchetoeu/jscript/lib/SymbolLib.java b/src/me/topchetoeu/jscript/lib/SymbolLib.java index 900fb20..7a56ca2 100644 --- a/src/me/topchetoeu/jscript/lib/SymbolLib.java +++ b/src/me/topchetoeu/jscript/lib/SymbolLib.java @@ -3,48 +3,69 @@ package me.topchetoeu.jscript.lib; import java.util.HashMap; import java.util.Map; -import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Symbol; -import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.Expose; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; +import me.topchetoeu.jscript.interop.WrapperName; -@Native("Symbol") public class SymbolLib { +@WrapperName("Symbol") +public class SymbolLib { private static final Map symbols = new HashMap<>(); - @Native public static final Symbol typeName = Symbol.get("Symbol.typeName"); - @Native public static final Symbol replace = Symbol.get("Symbol.replace"); - @Native public static final Symbol match = Symbol.get("Symbol.match"); - @Native public static final Symbol matchAll = Symbol.get("Symbol.matchAll"); - @Native public static final Symbol split = Symbol.get("Symbol.split"); - @Native public static final Symbol search = Symbol.get("Symbol.search"); - @Native public static final Symbol iterator = Symbol.get("Symbol.iterator"); - @Native public static final Symbol asyncIterator = Symbol.get("Symbol.asyncIterator"); - @Native public static final Symbol cause = Symbol.get("Symbol.cause"); + @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(Context ctx, String funcName, Object val) { + private static Symbol passThis(Arguments args, String funcName) { + var val = args.self; if (val instanceof SymbolLib) return ((SymbolLib)val).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)); } - @NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) { - if (thisArg instanceof ObjectValue) throw EngineException.ofType("Symbol constructor may not be called with new."); - if (val == null) return new Symbol(""); - else return new Symbol(Values.toString(ctx, val)); - } - @Native(thisArg = true) public static String toString(Context ctx, Object thisArg) { - return passThis(ctx, "toString", thisArg).value; - } - @Native(thisArg = true) public static Symbol valueOf(Context ctx, Object thisArg) { - return passThis(ctx, "valueOf", thisArg); + public SymbolLib(Symbol val) { + this.value = val; } - @Native("for") public static Symbol _for(String key) { + @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); @@ -52,11 +73,8 @@ import me.topchetoeu.jscript.interop.NativeConstructor; return sym; } } - @Native public static String keyFor(Symbol sym) { - return sym.value; - } - - public SymbolLib(Symbol val) { - this.value = val; + @Expose(target = ExposeTarget.STATIC) + public static String __keyFor(Arguments args) { + return passThis(args.slice(-1), "keyFor").value; } } diff --git a/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java b/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java index d47feb3..b457510 100644 --- a/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java +++ b/src/me/topchetoeu/jscript/lib/SyntaxErrorLib.java @@ -1,21 +1,22 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.interop.InitType; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeInit; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; -@Native("SyntaxError") public class SyntaxErrorLib extends ErrorLib { - @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) { - var target = ErrorLib.constructor(ctx, thisArg, message); +@WrapperName("SyntaxError") +public class SyntaxErrorLib extends ErrorLib { + @ExposeField(target = ExposeTarget.STATIC) + 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; } - @NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) { - target.defineProperty(null, "name", "SyntaxError"); - } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/lib/TypeErrorLib.java b/src/me/topchetoeu/jscript/lib/TypeErrorLib.java index 7e4179c..0fb1c17 100644 --- a/src/me/topchetoeu/jscript/lib/TypeErrorLib.java +++ b/src/me/topchetoeu/jscript/lib/TypeErrorLib.java @@ -1,21 +1,22 @@ package me.topchetoeu.jscript.lib; -import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.Environment; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.interop.InitType; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeConstructor; -import me.topchetoeu.jscript.interop.NativeInit; +import me.topchetoeu.jscript.interop.WrapperName; +import me.topchetoeu.jscript.interop.Arguments; +import me.topchetoeu.jscript.interop.ExposeConstructor; +import me.topchetoeu.jscript.interop.ExposeField; +import me.topchetoeu.jscript.interop.ExposeTarget; -@Native("TypeError") public class TypeErrorLib extends ErrorLib { - @NativeConstructor(thisArg = true) public static ObjectValue constructor(Context ctx, Object thisArg, Object message) { - var target = ErrorLib.constructor(ctx, thisArg, message); - target.setPrototype(PlaceholderProto.SYNTAX_ERROR); +@WrapperName("TypeError") +public class TypeErrorLib extends ErrorLib { + @ExposeField(target = ExposeTarget.STATIC) + 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; } - @NativeInit(InitType.PROTOTYPE) public static void init(Environment env, ObjectValue target) { - target.defineProperty(null, "name", "TypeError"); - } } \ No newline at end of file