diff --git a/gradle.properties b/gradle.properties index a17b580..0181a94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ project_group = me.topchetoeu project_name = jscript project_version = 0.9.41-beta -main_class = me.topchetoeu.jscript.utils.JScriptRepl +main_class = me.topchetoeu.jscript.runtime.SimpleRepl diff --git a/src/java/me/topchetoeu/jscript/runtime/Compiler.java b/src/java/me/topchetoeu/jscript/common/Compiler.java similarity index 82% rename from src/java/me/topchetoeu/jscript/runtime/Compiler.java rename to src/java/me/topchetoeu/jscript/common/Compiler.java index ff87201..01d2bb0 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Compiler.java +++ b/src/java/me/topchetoeu/jscript/common/Compiler.java @@ -1,13 +1,11 @@ -package me.topchetoeu.jscript.runtime; +package me.topchetoeu.jscript.common; -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; public interface Compiler { public Key KEY = new Key<>(); diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java b/src/java/me/topchetoeu/jscript/common/ParseRes.java similarity index 95% rename from src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java rename to src/java/me/topchetoeu/jscript/common/ParseRes.java index 982c504..a373a5d 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/ParseRes.java +++ b/src/java/me/topchetoeu/jscript/common/ParseRes.java @@ -1,9 +1,10 @@ -package me.topchetoeu.jscript.compilation.parsing; +package me.topchetoeu.jscript.common; import java.util.List; import java.util.Map; -import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.compilation.parsing.TestRes; +import me.topchetoeu.jscript.compilation.parsing.Token; import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser; public class ParseRes { diff --git a/src/java/me/topchetoeu/jscript/common/json/JSON.java b/src/java/me/topchetoeu/jscript/common/json/JSON.java index d44270e..b81062b 100644 --- a/src/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/java/me/topchetoeu/jscript/common/json/JSON.java @@ -1,79 +1,16 @@ package me.topchetoeu.jscript.common.json; -import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Operator; -import me.topchetoeu.jscript.compilation.parsing.ParseRes; import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.compilation.parsing.Token; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; public class JSON { - public static Object toJs(JSONElement val) { - if (val.isBoolean()) return val.bool(); - if (val.isString()) return val.string(); - if (val.isNumber()) return val.number(); - if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList())); - if (val.isMap()) { - var res = new ObjectValue(); - for (var el : val.map().entrySet()) { - res.defineProperty(null, el.getKey(), toJs(el.getValue())); - } - return res; - } - if (val.isNull()) return Values.NULL; - return null; - } - private static JSONElement fromJs(Environment ext, Object val, HashSet prev) { - if (val instanceof Boolean) return JSONElement.bool((boolean)val); - if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue()); - if (val instanceof String) return JSONElement.string((String)val); - if (val == Values.NULL) return JSONElement.NULL; - if (val instanceof ArrayValue) { - if (prev.contains(val)) throw new EngineException("Circular dependency in JSON."); - prev.add(val); - - var res = new JSONList(); - - for (var el : ((ArrayValue)val).toArray()) { - var jsonEl = fromJs(ext, el, prev); - if (jsonEl == null) jsonEl = JSONElement.NULL; - res.add(jsonEl); - } - - prev.remove(val); - return JSONElement.of(res); - } - if (val instanceof ObjectValue) { - if (prev.contains(val)) throw new EngineException("Circular dependency in JSON."); - prev.add(val); - - var res = new JSONMap(); - - for (var el : Values.getMembers(ext, val, false, false)) { - var jsonEl = fromJs(ext, Values.getMember(ext, val, el), prev); - if (jsonEl == null) continue; - if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl); - } - - prev.remove(val); - return JSONElement.of(res); - } - if (val == null) return null; - return null; - } - public static JSONElement fromJs(Environment ext, Object val) { - return fromJs(ext, val, new HashSet<>()); - } - public static ParseRes parseIdentifier(List tokens, int i) { return Parsing.parseIdentifier(tokens, i); } diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index b6b992a..153c3a2 100644 --- a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -15,7 +15,6 @@ import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; -import me.topchetoeu.jscript.utils.mapping.SourceMap; public class FunctionMap { public static class FunctionMapBuilder { @@ -131,27 +130,27 @@ public class FunctionMap { return pcToLoc.lastEntry().getValue(); } - public FunctionMap apply(SourceMap map) { - var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames); + // public static FunctionMap apply(FunctionMap funcMap, SourceMap map) { + // var res = new FunctionMap(Map.of(), Map.of(), funcMap.localNames, funcMap.captureNames); - for (var el : pcToLoc.entrySet()) { - res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue())); - } + // for (var el : funcMap.pcToLoc.entrySet()) { + // res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue())); + // } - res.bps.putAll(bps); + // res.bps.putAll(bps); - for (var el : bpLocs.entrySet()) { - for (var loc : el.getValue()) { - loc = map.toCompiled(loc); - if (loc == null) continue; + // for (var el : bpLocs.entrySet()) { + // for (var loc : el.getValue()) { + // loc = map.toCompiled(loc); + // if (loc == null) continue; - if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>()); - res.bpLocs.get(loc.filename()).add(loc); - } - } + // if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>()); + // res.bpLocs.get(loc.filename()).add(loc); + // } + // } - return res; - } + // return res; + // } public FunctionMap clone() { var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames); diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java index 9ec962a..84a467f 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java @@ -11,11 +11,12 @@ import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Operation; +import me.topchetoeu.jscript.common.ParseRes; +import me.topchetoeu.jscript.common.ParseRes.State; import me.topchetoeu.jscript.compilation.*; import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; -import me.topchetoeu.jscript.compilation.parsing.ParseRes.State; import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord; import me.topchetoeu.jscript.compilation.values.*; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java index c399a9e..ec234df 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/TestRes.java @@ -1,7 +1,8 @@ package me.topchetoeu.jscript.compilation.parsing; import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.compilation.parsing.ParseRes.State; +import me.topchetoeu.jscript.common.ParseRes; +import me.topchetoeu.jscript.common.ParseRes.State; public class TestRes { public final State state; diff --git a/src/java/me/topchetoeu/jscript/lib/ArrayLib.java b/src/java/me/topchetoeu/jscript/lib/ArrayLib.java deleted file mode 100644 index ce6a5aa..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ArrayLib.java +++ /dev/null @@ -1,456 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.Iterator; -import java.util.Stack; - -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Array") -public class ArrayLib { - private static int normalizeI(int len, int i, boolean clamp) { - if (i < 0) i += len; - if (clamp) { - if (i < 0) i = 0; - if (i > len) i = len; - } - return i; - } - - @Expose(value = "length", type = ExposeType.GETTER) - public static int __getLength(Arguments args) { - return args.self(ArrayValue.class).size(); - } - @Expose(value = "length", type = ExposeType.SETTER) - public static void __setLength(Arguments args) { - args.self(ArrayValue.class).setSize(args.getInt(0)); - } - - @Expose public static ObjectValue __values(Arguments args) { - return __iterator(args); - } - @Expose public static ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.env, () -> new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < args.self(ArrayValue.class).size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return i++; - } - }); - } - @Expose public static ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.env, () -> new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < args.self(ArrayValue.class).size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return new ArrayValue(args.env, i, args.self(ArrayValue.class).get(i++)); - } - }); - } - - @Expose(value = "@@Symbol.iterator") - public static ObjectValue __iterator(Arguments args) { - return Values.toJSIterator(args.env, args.self(ArrayValue.class)); - } - @Expose(value = "@@Symbol.asyncIterator") - public static ObjectValue __asyncIterator(Arguments args) { - return Values.toJSAsyncIterator(args.env, args.self(ArrayValue.class).iterator()); - } - - @Expose public static ArrayValue __concat(Arguments args) { - // TODO: Fully implement with non-array spreadable objects - var arrs = args.slice(-1); - var size = 0; - - for (int i = 0; i < arrs.n(); i++) { - if (arrs.get(i) instanceof ArrayValue) size += arrs.convert(i, ArrayValue.class).size(); - else i++; - } - - var res = new ArrayValue(size); - - for (int i = 0, j = 0; i < arrs.n(); i++) { - if (arrs.get(i) instanceof ArrayValue) { - var arrEl = arrs.convert(i, ArrayValue.class); - int n = arrEl.size(); - arrEl.copyTo(res, 0, j, n); - j += n; - } - else { - res.set(args.env, j++, arrs.get(i)); - } - } - - return res; - } - @Expose public static ArrayValue __sort(Arguments args) { - var arr = args.self(ArrayValue.class); - var cmp = args.convert(0, FunctionValue.class); - - var defaultCmp = new NativeFunction("", _args -> { - return _args.getString(0).compareTo(_args.getString(1)); - }); - - arr.sort((a, b) -> { - var res = Values.toNumber(args.env, (cmp == null ? defaultCmp : cmp).call(args.env, null, a, b)); - if (res < 0) return -1; - if (res > 0) return 1; - return 0; - }); - return arr; - } - - @Expose public static ArrayValue __fill(Arguments args) { - var arr = args.self(ArrayValue.class); - var val = args.get(0); - var start = normalizeI(arr.size(), args.getInt(1, 0), true); - var end = normalizeI(arr.size(), args.getInt(2, arr.size()), true); - - for (; start < end; start++) arr.set(args.env, start, val); - - return arr; - } - @Expose public static boolean __every(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = 0; i < arr.size(); i++) { - if (arr.has(i) && !Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, arr - ))) return false; - } - - return true; - } - @Expose public static boolean __some(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, arr - ))) return true; - } - - return false; - } - @Expose public static ArrayValue __filter(Arguments args) { - var arr = args.self(ArrayValue.class); - var res = new ArrayValue(arr.size()); - - for (int i = 0, j = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, arr - ))) res.set(args.env, j++, arr.get(i)); - } - - return res; - } - @Expose public static ArrayValue __map(Arguments args) { - var arr = args.self(ArrayValue.class); - var res = new ArrayValue(arr.size()); - res.setSize(arr.size()); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i)) res.set(args.env, i, Values.call(args.env, args.get(0), args.get(1), arr.get(i), i, arr)); - } - return res; - } - @Expose public static void __forEach(Arguments args) { - var arr = args.self(ArrayValue.class); - var func = args.convert(0, FunctionValue.class); - var thisArg = args.get(1); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i)) func.call(args.env, thisArg, arr.get(i), i, arr); - } - } - - @Expose public static Object __reduce(Arguments args) { - var arr = args.self(ArrayValue.class); - var func = args.convert(0, FunctionValue.class); - var res = args.get(1); - var i = 0; - - if (args.n() < 2) { - for (; i < arr.size(); i++) { - if (arr.has(i)){ - res = arr.get(i++); - break; - } - } - } - - for (; i < arr.size(); i++) { - if (arr.has(i)) { - res = func.call(args.env, null, res, arr.get(i), i, arr); - } - } - - return res; - } - @Expose public static Object __reduceRight(Arguments args) { - var arr = args.self(ArrayValue.class); - var func = args.convert(0, FunctionValue.class); - var res = args.get(1); - var i = arr.size(); - - if (args.n() < 2) { - while (!arr.has(i--) && i >= 0) { - res = arr.get(i); - } - } - else i--; - - for (; i >= 0; i--) { - if (arr.has(i)) { - res = func.call(args.env, null, res, arr.get(i), i, arr); - } - } - - return res; - } - - @Expose public static ArrayValue __flat(Arguments args) { - var arr = args.self(ArrayValue.class); - var depth = args.getInt(0, 1); - var res = new ArrayValue(arr.size()); - var stack = new Stack(); - var depths = new Stack(); - - stack.push(arr); - depths.push(-1); - - while (!stack.empty()) { - var el = stack.pop(); - int d = depths.pop(); - - if ((d == -1 || d < depth) && el instanceof ArrayValue) { - var arrEl = (ArrayValue)el; - for (int i = arrEl.size() - 1; i >= 0; i--) { - if (!arrEl.has(i)) continue; - stack.push(arrEl.get(i)); - depths.push(d + 1); - } - } - else res.set(args.env, res.size(), el); - } - - return res; - } - @Expose public static ArrayValue __flatMap(Arguments args) { - return __flat(new Arguments(args.env, __map(args), 1)); - } - - @Expose public static Object __find(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return arr.get(i); - } - - return null; - } - @Expose public static Object __findLast(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = arr.size() - 1; i >= 0; i--) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return arr.get(i); - } - - return null; - } - - @Expose public static int __findIndex(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (int i = 0; i < arr.size(); i++) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return i; - } - - return -1; - } - @Expose public static int __findLastIndex(Arguments args) { - var arr = args.self(ArrayValue.class); - - for (var i = arr.size() - 1; i >= 0; i--) { - if (arr.has(i) && Values.toBoolean(Values.call( - args.env, args.get(0), args.get(1), - arr.get(i), i, args.self - ))) return i; - } - - return -1; - } - - @Expose public static int __indexOf(Arguments args) { - var arr = args.self(ArrayValue.class); - var val = args.get(0); - var start = normalizeI(arr.size(), args.getInt(1), true); - - for (int i = start; i < arr.size(); i++) { - if (Values.strictEquals(args.env, arr.get(i), val)) return i; - } - - return -1; - } - @Expose public static int __lastIndexOf(Arguments args) { - var arr = args.self(ArrayValue.class); - var val = args.get(0); - var start = normalizeI(arr.size(), args.getInt(1), true); - - for (int i = arr.size(); i >= start; i--) { - if (Values.strictEquals(args.env, arr.get(i), val)) return i; - } - - return -1; - } - - @Expose public static boolean __includes(Arguments args) { - return __indexOf(args) >= 0; - } - - @Expose public static Object __pop(Arguments args) { - var arr = args.self(ArrayValue.class); - if (arr.size() == 0) return null; - - var val = arr.get(arr.size() - 1); - arr.shrink(1); - return val; - } - @Expose public static int __push(Arguments args) { - var arr = args.self(ArrayValue.class); - var values = args.args; - - arr.copyFrom(args.env, values, 0, arr.size(), values.length); - return arr.size(); - } - - @Expose public static Object __shift(Arguments args) { - var arr = args.self(ArrayValue.class); - - if (arr.size() == 0) return null; - var val = arr.get(0); - - arr.move(1, 0, arr.size()); - arr.shrink(1); - return val; - } - @Expose public static int __unshift(Arguments args) { - var arr = args.self(ArrayValue.class); - var values = args.slice(0).args; - - arr.move(0, values.length, arr.size()); - arr.copyFrom(args.env, values, 0, 0, values.length); - return arr.size(); - } - - @Expose public static ArrayValue __slice(Arguments args) { - var arr = args.self(ArrayValue.class); - var start = normalizeI(arr.size(), args.getInt(0), true); - var end = normalizeI(arr.size(), args.getInt(1, arr.size()), true); - - var res = new ArrayValue(end - start); - arr.copyTo(res, start, 0, end - start); - return res; - } - - @Expose public static ArrayValue __splice(Arguments args) { - var arr = args.self(ArrayValue.class); - var start = normalizeI(arr.size(), args.getInt(0), true); - var deleteCount = normalizeI(arr.size(), args.getInt(1, arr.size()), true); - var items = args.slice(2).args; - - if (start + deleteCount >= arr.size()) deleteCount = arr.size() - start; - - var size = arr.size() - deleteCount + items.length; - var res = new ArrayValue(deleteCount); - arr.copyTo(res, start, 0, deleteCount); - arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount); - arr.copyFrom(args.env, items, 0, start, items.length); - arr.setSize(size); - - return res; - } - @Expose public static String __toString(Arguments args) { - return __join(new Arguments(args.env, args.self, ",")); - } - - @Expose public static String __join(Arguments args) { - var arr = args.self(ArrayValue.class); - var sep = args.getString(0, ", "); - var res = new StringBuilder(); - var comma = false; - - for (int i = 0; i < arr.size(); i++) { - if (!arr.has(i)) continue; - - if (comma) res.append(sep); - comma = true; - - var el = arr.get(i); - if (el == null || el == Values.NULL) continue; - - res.append(Values.toString(args.env, el)); - } - - return res.toString(); - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isArray(Arguments args) { - return args.get(0) instanceof ArrayValue; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __of(Arguments args) { - return new ArrayValue(args.env, args.slice(0).args); - } - - @ExposeConstructor public static ArrayValue __constructor(Arguments args) { - ArrayValue res; - - if (args.n() == 1 && args.get(0) instanceof Number) { - var len = args.getInt(0); - res = new ArrayValue(len); - res.setSize(len); - } - else { - var val = args.args; - res = new ArrayValue(val.length); - res.copyFrom(args.env, val, 0, 0, val.length); - } - - return res; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java deleted file mode 100644 index af1f178..0000000 --- a/src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.lib.PromiseLib.Handle; -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("AsyncFunction") -public class AsyncFunctionLib extends FunctionValue { - public final CodeFunction func; - - private static class AsyncHelper { - public PromiseLib promise = new PromiseLib(); - public Frame frame; - - private boolean awaiting = false; - - private void next(Environment env, Object inducedValue, EngineException inducedError) { - Object res = null; - - frame.onPush(); - awaiting = false; - while (!awaiting) { - try { - if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); - else if (inducedError != null) res = frame.induceError(inducedError); - else res = frame.next(); - - inducedValue = Values.NO_RETURN; - inducedError = null; - - if (res != Values.NO_RETURN) { - promise.fulfill(env, res); - break; - } - } - catch (EngineException e) { - promise.reject(env, e); - break; - } - } - frame.onPop(); - - if (awaiting) { - PromiseLib.handle(env, frame.pop(), new Handle() { - @Override - public void onFulfil(Object val) { - next(env, val, null); - } - @Override - public void onReject(EngineException err) { - next(env, Values.NO_RETURN, err); - } - }.defer(env)); - } - } - - public Object await(Arguments args) { - this.awaiting = true; - return args.get(0); - } - } - - @Override - public Object call(Environment env, Object thisArg, Object ...args) { - var handler = new AsyncHelper(); - - var newArgs = new Object[args.length + 1]; - newArgs[0] = new NativeFunction("await", handler::await); - System.arraycopy(args, 0, newArgs, 1, args.length); - - handler.frame = new Frame(env, thisArg, newArgs, (CodeFunction)func); - handler.next(env, Values.NO_RETURN, null); - return handler.promise; - } - - public AsyncFunctionLib(FunctionValue func) { - super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); - this.func = (CodeFunction)func; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java deleted file mode 100644 index f6bdbc7..0000000 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorFunctionLib.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("AsyncGeneratorFunction") -public class AsyncGeneratorFunctionLib extends FunctionValue { - public final CodeFunction func; - - @Override - public Object call(Environment ext, Object thisArg, Object ...args) { - var handler = new AsyncGeneratorLib(); - - var newArgs = new Object[args.length + 2]; - newArgs[0] = new NativeFunction("await", handler::await); - newArgs[1] = new NativeFunction("yield", handler::yield); - System.arraycopy(args, 0, newArgs, 2, args.length); - - handler.frame = new Frame(ext, thisArg, newArgs, func); - return handler; - } - - public AsyncGeneratorFunctionLib(CodeFunction func) { - super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a code function."); - this.func = func; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java deleted file mode 100644 index cb1ad6a..0000000 --- a/src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java +++ /dev/null @@ -1,111 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.Map; - -import me.topchetoeu.jscript.lib.PromiseLib.Handle; -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("AsyncGenerator") -public class AsyncGeneratorLib { - private int state = 0; - private boolean done = false; - private PromiseLib currPromise; - public Frame frame; - - private void next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) { - if (done) { - if (inducedError != null) throw inducedError; - currPromise.fulfill(env, new ObjectValue(env, Map.of( - "done", true, - "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn - ))); - return; - } - - Object res = null; - state = 0; - - frame.onPush(); - while (state == 0) { - try { - if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); - else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue); - else if (inducedError != null) res = frame.induceError(inducedError); - else res = frame.next(); - - inducedValue = inducedReturn = Values.NO_RETURN; - inducedError = null; - - if (res != Values.NO_RETURN) { - var obj = new ObjectValue(); - obj.defineProperty(env, "done", true); - obj.defineProperty(env, "value", res); - currPromise.fulfill(env, obj); - break; - } - } - catch (EngineException e) { - currPromise.reject(env, e); - break; - } - } - frame.onPop(); - - if (state == 1) { - PromiseLib.handle(env, frame.pop(), new Handle() { - @Override public void onFulfil(Object val) { - next(env, val, Values.NO_RETURN, null); - } - @Override public void onReject(EngineException err) { - next(env, Values.NO_RETURN, Values.NO_RETURN, err); - } - }.defer(env)); - } - else if (state == 2) { - var obj = new ObjectValue(); - obj.defineProperty(env, "done", false); - obj.defineProperty(env, "value", frame.pop()); - currPromise.fulfill(env, obj); - } - } - - @Override - public String toString() { - if (done) return "Generator [closed]"; - if (state != 0) return "Generator [suspended]"; - return "Generator [running]"; - } - - public Object await(Arguments args) { - this.state = 1; - return args.get(0); - } - public Object yield(Arguments args) { - this.state = 2; - return args.get(0); - } - - @Expose public PromiseLib __next(Arguments args) { - this.currPromise = new PromiseLib(); - if (args.has(0)) next(args.env, args.get(0), Values.NO_RETURN, null); - else next(args.env, Values.NO_RETURN, Values.NO_RETURN, null); - return this.currPromise; - } - @Expose public PromiseLib __return(Arguments args) { - this.currPromise = new PromiseLib(); - next(args.env, Values.NO_RETURN, args.get(0), null); - return this.currPromise; - } - @Expose public PromiseLib __throw(Arguments args) { - this.currPromise = new PromiseLib(); - next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env)); - return this.currPromise; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/BooleanLib.java b/src/java/me/topchetoeu/jscript/lib/BooleanLib.java deleted file mode 100644 index 019a061..0000000 --- a/src/java/me/topchetoeu/jscript/lib/BooleanLib.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Boolean") -public class BooleanLib { - public static final BooleanLib TRUE = new BooleanLib(true); - public static final BooleanLib FALSE = new BooleanLib(false); - - public final boolean value; - - @Override public String toString() { - return value + ""; - } - - public BooleanLib(boolean val) { - this.value = val; - } - - @ExposeConstructor public static Object __constructor(Arguments args) { - var val = args.getBoolean(0); - if (args.self instanceof ObjectValue) return val ? TRUE : FALSE; - else return val; - } - @Expose public static String __toString(Arguments args) { - return args.self(Boolean.class) ? "true" : "false"; - } - @Expose public static boolean __valueOf(Arguments args) { - if (Values.isWrapper(args.self, BooleanLib.class)) return Values.wrapper(args.self, BooleanLib.class).value; - return args.self(Boolean.class); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java b/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java deleted file mode 100644 index cd08dbf..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ConsoleLib.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.io.IOException; - -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.File; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Console") -public class ConsoleLib { - public static interface Writer { - void writeLine(String val) throws IOException; - } - - private File file; - - @Expose - public void __log(Arguments args) { - var res = new StringBuilder(); - var first = true; - - for (var el : args.args) { - if (!first) res.append(" "); - first = false; - res.append(Values.toReadable(args.env, el).getBytes()); - } - - for (var line : res.toString().split("\n", -1)) { - file.write(line.getBytes()); - } - } - - public ConsoleLib(File file) { - this.file = file; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/DateLib.java b/src/java/me/topchetoeu/jscript/lib/DateLib.java deleted file mode 100644 index b4b5082..0000000 --- a/src/java/me/topchetoeu/jscript/lib/DateLib.java +++ /dev/null @@ -1,266 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Date") -public class DateLib { - private Calendar normal; - private Calendar utc; - - private void updateUTC() { - if (utc == null || normal == null) return; - utc.setTimeInMillis(normal.getTimeInMillis()); - } - private void updateNormal() { - if (utc == null || normal == null) return; - normal.setTimeInMillis(utc.getTimeInMillis()); - } - private void invalidate() { - normal = utc = null; - } - - @Expose public double __getYear() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.YEAR) - 1900; - } - @Expose public double __setYeard(Arguments args) { - var real = args.getDouble(0); - if (real >= 0 && real <= 99) real = real + 1900; - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.YEAR, (int)real); - updateUTC(); - return __getTime(); - } - - @Expose public double __getFullYear() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.YEAR); - } - @Expose public double __getMonth() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.MONTH); - } - @Expose public double __getDate() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.DAY_OF_MONTH); - } - @Expose public double __getDay() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.DAY_OF_WEEK); - } - @Expose public double __getHours() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.HOUR_OF_DAY); - } - @Expose public double __getMinutes() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.MINUTE); - } - @Expose public double __getSeconds() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.SECOND); - } - @Expose public double __getMilliseconds() { - if (normal == null) return Double.NaN; - return normal.get(Calendar.MILLISECOND); - } - - @Expose public double __getUTCFullYear() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.YEAR); - } - @Expose public double __getUTCMonth() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.MONTH); - } - @Expose public double __getUTCDate() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.DAY_OF_MONTH); - } - @Expose public double __getUTCDay() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.DAY_OF_WEEK); - } - @Expose public double __getUTCHours() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.HOUR_OF_DAY); - } - @Expose public double __getUTCMinutes() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.MINUTE); - } - @Expose public double __getUTCSeconds() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.SECOND); - } - @Expose public double __getUTCMilliseconds() { - if (utc == null) return Double.NaN; - return utc.get(Calendar.MILLISECOND); - } - - @Expose public double __setFullYear(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.YEAR, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setMonthd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.MONTH, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setDated(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.DAY_OF_MONTH, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setDayd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.DAY_OF_WEEK, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setHoursd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.HOUR_OF_DAY, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setMinutesd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.MINUTE, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setSecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.SECOND, (int)real); - updateUTC(); - return __getTime(); - } - @Expose public double __setMillisecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else normal.set(Calendar.MILLISECOND, (int)real); - updateUTC(); - return __getTime(); - } - - @Expose public double __setUTCFullYeard(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.YEAR, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCMonthd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.MONTH, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCDated(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.DAY_OF_MONTH, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCDayd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.DAY_OF_WEEK, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCHoursd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.HOUR_OF_DAY, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCMinutesd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.MINUTE, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCSecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.SECOND, (int)real); - updateNormal(); - return __getTime(); - } - @Expose public double __setUTCMillisecondsd(Arguments args) { - var real = args.getDouble(0); - if (Double.isNaN(real)) invalidate(); - else utc.set(Calendar.MILLISECOND, (int)real); - updateNormal(); - return __getTime(); - } - - @Expose public double __getTime() { - if (utc == null) return Double.NaN; - return utc.getTimeInMillis(); - } - @Expose public double __getTimezoneOffset() { - if (normal == null) return Double.NaN; - return normal.getTimeZone().getRawOffset() / 60000; - } - - @Expose public double __valueOf() { - if (normal == null) return Double.NaN; - else return normal.getTimeInMillis(); - } - - @Expose public String __toString() { - return normal.getTime().toString(); - } - - @Override public String toString() { - return __toString(); - } - - public DateLib(long timestamp) { - normal = Calendar.getInstance(); - utc = Calendar.getInstance(); - normal.setTimeInMillis(timestamp); - utc.setTimeZone(TimeZone.getTimeZone("UTC")); - utc.setTimeInMillis(timestamp); - } - - public DateLib() { - this(new Date().getTime()); - } - - @ExposeConstructor public static DateLib init(Arguments args) { - if (args.has(0)) return new DateLib(args.getLong(0)); - else return new DateLib(); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __now() { - return new DateLib().__getTime(); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/EncodingLib.java b/src/java/me/topchetoeu/jscript/lib/EncodingLib.java deleted file mode 100644 index e164ef8..0000000 --- a/src/java/me/topchetoeu/jscript/lib/EncodingLib.java +++ /dev/null @@ -1,89 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; - -import me.topchetoeu.jscript.common.Buffer; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Encoding") -public class EncodingLib { - private static final String HEX = "0123456789ABCDEF"; - - public static String encodeUriAny(String str, String keepAlphabet) { - if (str == null) str = "undefined"; - - var bytes = str.getBytes(); - var sb = new StringBuilder(bytes.length); - - for (byte c : bytes) { - if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c); - else { - sb.append('%'); - sb.append(HEX.charAt(c / 16)); - sb.append(HEX.charAt(c % 16)); - } - } - - return sb.toString(); - } - public static String decodeUriAny(String str, String keepAlphabet) { - if (str == null) str = "undefined"; - - var res = new Buffer(); - var bytes = str.getBytes(); - - for (var i = 0; i < bytes.length; i++) { - var c = bytes[i]; - if (c == '%') { - if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed."); - var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]); - if (!Parsing.isAny((char)b, keepAlphabet)) { - i += 2; - res.append((byte)b); - continue; - } - } - res.append(c); - } - - return new String(res.data()); - } - - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __encode(Arguments args) { - var res = new ArrayValue(); - for (var el : args.getString(0).getBytes()) res.set(null, res.size(), (int)el); - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static String __decode(Arguments args) { - var raw = args.convert(0, ArrayList.class); - var res = new byte[raw.size()]; - for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.env, raw.get(i)); - return new String(res); - } - - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURIComponent(Arguments args) { - return EncodingLib.encodeUriAny(args.getString(0), ".-_!~*'()"); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURIComponent(Arguments args) { - return decodeUriAny(args.getString(0), ""); - } - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURI(Arguments args) { - return encodeUriAny(args.getString(0), ";,/?:@&=+$#.-_!~*'()"); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURI(Arguments args) { - return decodeUriAny(args.getString(0), ",/?:@&=+$#."); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/ErrorLib.java b/src/java/me/topchetoeu/jscript/lib/ErrorLib.java deleted file mode 100644 index 431f6bd..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ErrorLib.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.ConvertException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Error") -public class ErrorLib { - private static String toString(Environment ctx, Object name, Object message) { - if (name == null) name = ""; - else name = Values.toString(ctx, name).trim(); - if (message == null) message = ""; - else message = Values.toString(ctx, message).trim(); - StringBuilder res = new StringBuilder(); - - if (!name.equals("")) res.append(name); - if (!message.equals("") && !name.equals("")) res.append(": "); - if (!message.equals("")) res.append(message); - - return res.toString(); - } - - @ExposeField public static final String __name = "Error"; - - @Expose public static String __toString(Arguments args) { - if (args.self instanceof ObjectValue) return toString(args.env, - Values.getMember(args.env, args.self, "name"), - Values.getMember(args.env, args.self, "message") - ); - else return "[Invalid error]"; - } - - @ExposeConstructor public static ObjectValue __constructor(Arguments args) { - var target = new ObjectValue(); - var message = args.getString(0, ""); - - try { - target = args.self(ObjectValue.class); - } - catch (ConvertException e) {} - - target.setPrototype(PlaceholderProto.ERROR); - target.defineProperty(args.env, "message", Values.toString(args.env, message)); - - return target; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/FileLib.java b/src/java/me/topchetoeu/jscript/lib/FileLib.java deleted file mode 100644 index 6a8deb4..0000000 --- a/src/java/me/topchetoeu/jscript/lib/FileLib.java +++ /dev/null @@ -1,84 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.File; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("File") -public class FileLib { - public final File fd; - - @Expose public PromiseLib __pointer(Arguments args) { - return PromiseLib.await(args.env, () -> { - try { - return fd.seek(0, 1); - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose public PromiseLib __length(Arguments args) { - return PromiseLib.await(args.env, () -> { - try { - long curr = fd.seek(0, 1); - long res = fd.seek(0, 2); - fd.seek(curr, 0); - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - - @Expose public PromiseLib __read(Arguments args) { - return PromiseLib.await(args.env, () -> { - var n = args.getInt(0); - try { - var buff = new byte[n]; - var res = new ArrayValue(); - int resI = fd.read(buff); - - for (var i = resI - 1; i >= 0; i--) res.set(args.env, i, (int)buff[i]); - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose public PromiseLib __write(Arguments args) { - return PromiseLib.await(args.env, () -> { - var val = args.convert(0, ArrayValue.class); - try { - var res = new byte[val.size()]; - - for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.env, val.get(i)); - fd.write(res); - - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose public PromiseLib __close(Arguments args) { - return PromiseLib.await(args.env, () -> { - fd.close(); - return null; - }); - } - @Expose public PromiseLib __seek(Arguments args) { - return PromiseLib.await(args.env, () -> { - var ptr = args.getLong(0); - var whence = args.getInt(1); - - try { - return fd.seek(ptr, whence); - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - - public FileLib(File fd) { - this.fd = fd; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java b/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java deleted file mode 100644 index d2f9c3c..0000000 --- a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java +++ /dev/null @@ -1,193 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Stack; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.ActionType; -import me.topchetoeu.jscript.utils.filesystem.EntryType; -import me.topchetoeu.jscript.utils.filesystem.ErrorReason; -import me.topchetoeu.jscript.utils.filesystem.File; -import me.topchetoeu.jscript.utils.filesystem.FileStat; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException; -import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Filesystem") -public class FilesystemLib { - @ExposeField(target = ExposeTarget.STATIC) - public static final int __SEEK_SET = 0; - @ExposeField(target = ExposeTarget.STATIC) - public static final int __SEEK_CUR = 1; - @ExposeField(target = ExposeTarget.STATIC) - public static final int __SEEK_END = 2; - - private static Filesystem fs(Environment env) { - var fs = Filesystem.get(env); - if (fs != null) return fs; - throw EngineException.ofError("Current environment doesn't have a file system."); - } - - @Expose(target = ExposeTarget.STATIC) - public static String __normalize(Arguments args) { - return fs(args.env).normalize(args.convert(String.class)); - } - - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __open(Arguments args) { - return PromiseLib.await(args.env, () -> { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - var _mode = Mode.parse(args.getString(1)); - - try { - if (fs.stat(path).type != EntryType.FILE) { - throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a file").setAction(ActionType.OPEN).setPath(path); - } - - return new FileLib(fs.open(path, _mode)); - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __ls(Arguments args) { - - return Values.toJSAsyncIterator(args.env, new Iterator<>() { - private boolean failed, done; - private File file; - private String nextLine; - - private void update() { - if (done) return; - if (!failed) { - if (file == null) { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - - if (fs.stat(path).type != EntryType.FOLDER) { - throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a directory").setAction(ActionType.OPEN); - } - - file = fs.open(path, Mode.READ); - } - - if (nextLine == null) { - while (true) { - nextLine = file.readLine(); - if (nextLine == null) { - done = true; - return; - } - nextLine = nextLine.trim(); - if (!nextLine.equals("")) break; - } - } - } - } - - @Override - public boolean hasNext() { - try { - update(); - return !done && !failed; - } - catch (FilesystemException e) { throw e.toEngineException(); } - } - @Override - public String next() { - try { - update(); - var res = nextLine; - nextLine = null; - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __mkdir(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - fs(args.env).create(args.getString(0), EntryType.FOLDER); - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __mkfile(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - fs(args.env).create(args.getString(0), EntryType.FILE); - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __rm(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - var recursive = args.getBoolean(1); - - if (!recursive) fs.create(path, EntryType.NONE); - else { - var stack = new Stack(); - stack.push(path); - - while (!stack.empty()) { - var currPath = stack.pop(); - FileStat stat; - - try { stat = fs.stat(currPath); } - catch (FilesystemException e) { continue; } - - if (stat.type == EntryType.FOLDER) { - for (var el : fs.open(currPath, Mode.READ).readToString().split("\n")) stack.push(el); - } - else fs.create(currPath, EntryType.NONE); - } - } - return null; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __stat(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { - var fs = fs(args.env); - var path = fs.normalize(args.getString(0)); - var stat = fs.stat(path); - var res = new ObjectValue(); - - res.defineProperty(args.env, "type", stat.type.name); - res.defineProperty(args.env, "mode", stat.mode.name); - return res; - } - catch (FilesystemException e) { throw e.toEngineException(); } - }); - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __exists(Arguments args) throws IOException { - return PromiseLib.await(args.env, () -> { - try { fs(args.env).stat(args.getString(0)); return true; } - catch (FilesystemException e) { return false; } - }); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/FunctionLib.java b/src/java/me/topchetoeu/jscript/lib/FunctionLib.java deleted file mode 100644 index 278bac3..0000000 --- a/src/java/me/topchetoeu/jscript/lib/FunctionLib.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Function") -public class FunctionLib { - private static int i; - - @Expose public static Object __apply(Arguments args) { - return args.self(FunctionValue.class).call(args.env, args.get(0), args.convert(1, ArrayValue.class).toArray()); - } - @Expose public static Object __call(Arguments args) { - return args.self(FunctionValue.class).call(args.env, args.get(0), args.slice(1).args); - } - @Expose public static FunctionValue __bind(Arguments args) { - var self = args.self(FunctionValue.class); - var thisArg = args.get(0); - var bindArgs = args.slice(1).args; - - return new NativeFunction(self.name + " (bound)", callArgs -> { - Object[] resArgs; - - if (args.n() == 0) resArgs = bindArgs; - else { - resArgs = new Object[bindArgs.length + callArgs.n()]; - System.arraycopy(bindArgs, 0, resArgs, 0, bindArgs.length); - System.arraycopy(callArgs.args, 0, resArgs, bindArgs.length, callArgs.n()); - } - - return self.call(callArgs.env, thisArg, resArgs); - }); - } - @Expose public static String __toString(Arguments args) { - return args.self.toString(); - } - - @Expose(target = ExposeTarget.STATIC) - public static FunctionValue __async(Arguments args) { - return new AsyncFunctionLib(args.convert(0, FunctionValue.class)); - } - @Expose(target = ExposeTarget.STATIC) - public static FunctionValue __asyncGenerator(Arguments args) { - return new AsyncGeneratorFunctionLib(args.convert(0, CodeFunction.class)); - } - @Expose(target = ExposeTarget.STATIC) - public static FunctionValue __generator(Arguments args) { - return new GeneratorFunctionLib(args.convert(0, CodeFunction.class)); - } - - @ExposeConstructor - public static Object __constructor(Arguments args) { - var parts = args.convert(String.class); - if (parts.length == 0) parts = new String[] { "" }; - - var src = "return function("; - - for (var i = 0; i < parts.length - 1; i++) { - if (i != 0) src += ","; - src += parts[i]; - } - - src += "){" + parts[parts.length - 1] + "}"; - - var body = Compiler.get(args.env).compile(new Filename("jscript", "func/" + i++), src); - var func = new CodeFunction(args.env, "", body, new ValueVariable[0]); - return Values.call(args.env, func, null); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java deleted file mode 100644 index 1316cfb..0000000 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorFunctionLib.java +++ /dev/null @@ -1,31 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("GeneratorFunction") -public class GeneratorFunctionLib extends FunctionValue { - public final CodeFunction func; - - @Override public Object call(Environment env, Object thisArg, Object ...args) { - var handler = new GeneratorLib(); - - var newArgs = new Object[args.length + 1]; - newArgs[0] = new NativeFunction("yield", handler::yield); - System.arraycopy(args, 0, newArgs, 1, args.length); - - handler.frame = new Frame(env, thisArg, newArgs, func); - return handler; - } - - public GeneratorFunctionLib(CodeFunction func) { - super(func.name, func.length); - if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function."); - this.func = func; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java deleted file mode 100644 index be8b71c..0000000 --- a/src/java/me/topchetoeu/jscript/lib/GeneratorLib.java +++ /dev/null @@ -1,82 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Generator") -public class GeneratorLib { - private boolean yielding = true; - private boolean done = false; - public Frame frame; - - private ObjectValue next(Environment env, Object inducedValue, Object inducedReturn, EngineException inducedError) { - if (done) { - if (inducedError != Values.NO_RETURN) throw inducedError; - var res = new ObjectValue(); - res.defineProperty(env, "done", true); - res.defineProperty(env, "value", inducedReturn == Values.NO_RETURN ? null : inducedReturn); - return res; - } - - Object res = null; - yielding = false; - - frame.onPush(); - while (!yielding) { - try { - if (inducedValue != Values.NO_RETURN) res = frame.next(inducedValue); - else if (inducedReturn != Values.NO_RETURN) res = frame.induceReturn(inducedValue); - else if (inducedError != null) res = frame.induceError(inducedError); - else res = frame.next(); - - inducedReturn = Values.NO_RETURN; - inducedError = null; - if (res != Values.NO_RETURN) { - done = true; - break; - } - } - catch (EngineException e) { - done = true; - throw e; - } - } - frame.onPop(); - - if (done) frame = null; - else res = frame.pop(); - - var obj = new ObjectValue(); - obj.defineProperty(env, "done", done); - obj.defineProperty(env, "value", res); - return obj; - } - - @Expose public ObjectValue __next(Arguments args) { - if (args.n() == 0) return next(args.env, Values.NO_RETURN, Values.NO_RETURN, null); - else return next(args.env, args.get(0), Values.NO_RETURN, null); - } - @Expose public ObjectValue __throw(Arguments args) { - return next(args.env, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setEnvironment(args.env)); - } - @Expose public ObjectValue __return(Arguments args) { - return next(args.env, Values.NO_RETURN, args.get(0), null); - } - - @Override public String toString() { - if (done) return "Generator [closed]"; - if (yielding) return "Generator [suspended]"; - return "Generator [running]"; - } - - public Object yield(Arguments args) { - this.yielding = true; - return args.get(0); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/Internals.java b/src/java/me/topchetoeu/jscript/lib/Internals.java deleted file mode 100644 index e2633b6..0000000 --- a/src/java/me/topchetoeu/jscript/lib/Internals.java +++ /dev/null @@ -1,229 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.HashMap; - -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; -import me.topchetoeu.jscript.utils.modules.ModuleRepo; - -public class Internals { - private static final Key> THREADS = new Key<>(); - private static final Key I = new Key<>(); - - @Expose(target = ExposeTarget.STATIC) - public static Object __require(Arguments args) { - var repo = ModuleRepo.get(args.env); - - if (repo != null) { - var res = repo.getModule(args.env, ModuleRepo.cwd(args.env), args.getString(0)); - res.load(args.env); - return res.value(); - } - - else throw EngineException.ofError("Modules are not supported."); - } - - @Expose(target = ExposeTarget.STATIC) - public static Thread __setTimeout(Arguments args) { - var func = args.convert(0, FunctionValue.class); - var delay = args.getDouble(1); - var arguments = args.slice(2).args; - - if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - - var thread = new Thread(() -> { - var ms = (long)delay; - var ns = (int)((delay - ms) * 10000000); - - try { Thread.sleep(ms, ns); } - catch (InterruptedException e) { return; } - - args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false); - }); - - thread.start(); - - args.env.init(I, 1); - args.env.init(THREADS, new HashMap<>()); - var i = args.env.get(I); - - args.env.add(I, i + 1); - args.env.get(THREADS).put(i, thread); - - return thread; - } - @Expose(target = ExposeTarget.STATIC) - public static Thread __setInterval(Arguments args) { - var func = args.convert(0, FunctionValue.class); - var delay = args.getDouble(1); - var arguments = args.slice(2).args; - - if (!args.env.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - - var thread = new Thread(() -> { - var ms = (long)delay; - var ns = (int)((delay - ms) * 10000000); - - while (true) { - try { - Thread.sleep(ms, ns); - } - catch (InterruptedException e) { return; } - - args.env.get(EventLoop.KEY).pushMsg(() -> func.call(args.env, null, arguments), false); - } - }); - - thread.start(); - - args.env.init(I, 1); - args.env.init(THREADS, new HashMap<>()); - var i = args.env.get(I); - - args.env.add(I, i + 1); - args.env.get(THREADS).put(i, thread); - - return thread; - } - - @Expose(target = ExposeTarget.STATIC) - public static void __clearTimeout(Arguments args) { - var i = args.getInt(0); - HashMap map = args.env.get(THREADS); - if (map == null) return; - - var thread = map.get(i); - if (thread == null) return; - - thread.interrupt(); - map.remove(i); - } - @Expose(target = ExposeTarget.STATIC) - public static void __clearInterval(Arguments args) { - __clearTimeout(args); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __parseInt(Arguments args) { - return NumberLib.__parseInt(args); - } - @Expose(target = ExposeTarget.STATIC) - public static double __parseFloat(Arguments args) { - return NumberLib.__parseFloat(args); - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isNaN(Arguments args) { - return NumberLib.__isNaN(args); - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isFinite(Arguments args) { - return NumberLib.__isFinite(args); - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isInfinite(Arguments args) { - return NumberLib.__isInfinite(args); - } - - @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) - public static FileLib __stdin(Arguments args) { - return new FileLib(Filesystem.get(args.env).open("std://in", Mode.READ)); - } - @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) - public static FileLib __stdout(Arguments args) { - return new FileLib(Filesystem.get(args.env).open("std://out", Mode.READ)); - } - @Expose(target = ExposeTarget.STATIC, type = ExposeType.GETTER) - public static FileLib __stderr(Arguments args) { - return new FileLib(Filesystem.get(args.env).open("std://err", Mode.READ)); - } - - @ExposeField(target = ExposeTarget.STATIC) - public static double __NaN = Double.NaN; - @ExposeField(target = ExposeTarget.STATIC) - public static double __Infinity = Double.POSITIVE_INFINITY; - - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURIComponent(Arguments args) { - return EncodingLib.__encodeURIComponent(args); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURIComponent(Arguments args) { - return EncodingLib.__decodeURIComponent(args); - } - @Expose(target = ExposeTarget.STATIC) - public static String __encodeURI(Arguments args) { - return EncodingLib.__encodeURI(args); - } - @Expose(target = ExposeTarget.STATIC) - public static String __decodeURI(Arguments args) { - return EncodingLib.__decodeURI(args); - } - - public static Environment apply(Environment env) { - var wp = new NativeWrapperProvider(); - var glob = new GlobalScope(wp.getNamespace(Internals.class)); - - glob.define(null, "Math", false, wp.getNamespace(MathLib.class)); - glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class)); - glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class)); - glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class)); - - glob.define(null, false, wp.getConstr(FileLib.class)); - - glob.define(null, false, wp.getConstr(DateLib.class)); - glob.define(null, false, wp.getConstr(ObjectLib.class)); - glob.define(null, false, wp.getConstr(FunctionLib.class)); - glob.define(null, false, wp.getConstr(ArrayLib.class)); - - glob.define(null, false, wp.getConstr(BooleanLib.class)); - glob.define(null, false, wp.getConstr(NumberLib.class)); - glob.define(null, false, wp.getConstr(StringLib.class)); - glob.define(null, false, wp.getConstr(SymbolLib.class)); - - glob.define(null, false, wp.getConstr(PromiseLib.class)); - glob.define(null, false, wp.getConstr(RegExpLib.class)); - glob.define(null, false, wp.getConstr(MapLib.class)); - glob.define(null, false, wp.getConstr(SetLib.class)); - - glob.define(null, false, wp.getConstr(ErrorLib.class)); - glob.define(null, false, wp.getConstr(SyntaxErrorLib.class)); - glob.define(null, false, wp.getConstr(TypeErrorLib.class)); - glob.define(null, false, wp.getConstr(RangeErrorLib.class)); - - env.add(Environment.OBJECT_PROTO, wp.getProto(ObjectLib.class)); - env.add(Environment.FUNCTION_PROTO, wp.getProto(FunctionLib.class)); - env.add(Environment.ARRAY_PROTO, wp.getProto(ArrayLib.class)); - - env.add(Environment.BOOL_PROTO, wp.getProto(BooleanLib.class)); - env.add(Environment.NUMBER_PROTO, wp.getProto(NumberLib.class)); - env.add(Environment.STRING_PROTO, wp.getProto(StringLib.class)); - env.add(Environment.SYMBOL_PROTO, wp.getProto(SymbolLib.class)); - - env.add(Environment.ERROR_PROTO, wp.getProto(ErrorLib.class)); - env.add(Environment.SYNTAX_ERR_PROTO, wp.getProto(SyntaxErrorLib.class)); - env.add(Environment.TYPE_ERR_PROTO, wp.getProto(TypeErrorLib.class)); - env.add(Environment.RANGE_ERR_PROTO, wp.getProto(RangeErrorLib.class)); - - env.add(Environment.REGEX_CONSTR, wp.getConstr(RegExpLib.class)); - Values.setPrototype(Environment.empty(), wp.getProto(ObjectLib.class), null); - - env.add(NativeWrapperProvider.KEY, wp); - env.add(GlobalScope.KEY, glob); - - return env; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/JSONLib.java b/src/java/me/topchetoeu/jscript/lib/JSONLib.java deleted file mode 100644 index 11bbb55..0000000 --- a/src/java/me/topchetoeu/jscript/lib/JSONLib.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("JSON") -public class JSONLib { - @Expose(target = ExposeTarget.STATIC) - public static Object __parse(Arguments args) { - try { - return JSON.toJs(JSON.parse(null, args.getString(0))); - } - catch (SyntaxException e) { throw EngineException.ofSyntax(e.msg); } - } - @Expose(target = ExposeTarget.STATIC) - public static String __stringify(Arguments args) { - return JSON.stringify(JSON.fromJs(args.env, args.get(0))); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/MapLib.java b/src/java/me/topchetoeu/jscript/lib/MapLib.java deleted file mode 100644 index bfda4f7..0000000 --- a/src/java/me/topchetoeu/jscript/lib/MapLib.java +++ /dev/null @@ -1,87 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Map") -public class MapLib { - private LinkedHashMap map = new LinkedHashMap<>(); - - @Expose("@@Symbol.iterator") - public ObjectValue __iterator(Arguments args) { - return this.__entries(args); - } - - @Expose public void __clear() { - map.clear(); - } - @Expose public boolean __delete(Arguments args) { - var key = args.get(0); - if (map.containsKey(key)) { - map.remove(key); - return true; - } - return false; - } - - @Expose public ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.env, map - .entrySet() - .stream() - .map(v -> new ArrayValue(args.env, v.getKey(), v.getValue())) - .collect(Collectors.toList()) - ); - } - @Expose public ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.env, map.keySet()); - } - @Expose public ObjectValue __values(Arguments args) { - return Values.toJSIterator(args.env, map.values()); - } - - @Expose public Object __get(Arguments args) { - return map.get(args.get(0)); - } - @Expose public MapLib __set(Arguments args) { - map.put(args.get(0), args.get(1)); - return this; - } - @Expose public boolean __has(Arguments args) { - return map.containsKey(args.get(0)); - } - - @Expose(type = ExposeType.GETTER) - public int __size() { - return map.size(); - } - - @Expose public void __forEach(Arguments args) { - var keys = new ArrayList<>(map.keySet()); - - for (var el : keys) Values.call(args.env, args.get(0), args.get(1), map.get(el), el, args.self); - } - - public MapLib(Environment env, Object iterable) { - for (var el : Values.fromJSIterator(env, iterable)) { - try { - map.put(Values.getMember(env, el, 0), Values.getMember(env, el, 1)); - } - catch (IllegalArgumentException e) { } - } - } - - @ExposeConstructor public static MapLib __constructor(Arguments args) { - return new MapLib(args.env, args.get(0)); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/MathLib.java b/src/java/me/topchetoeu/jscript/lib/MathLib.java deleted file mode 100644 index a0b5d53..0000000 --- a/src/java/me/topchetoeu/jscript/lib/MathLib.java +++ /dev/null @@ -1,211 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Math") -public class MathLib { - @ExposeField(target = ExposeTarget.STATIC) - public static final double __E = Math.E; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __PI = Math.PI; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __SQRT2 = Math.sqrt(2); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __SQRT1_2 = Math.sqrt(.5); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LN2 = Math.log(2); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LN10 = Math.log(10); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LOG2E = Math.log(Math.E) / __LN2; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __LOG10E = Math.log10(Math.E); - - @Expose(target = ExposeTarget.STATIC) - public static double __asin(Arguments args) { - return Math.asin(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __acos(Arguments args) { - return Math.acos(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __atan(Arguments args) { - return Math.atan(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __atan2(Arguments args) { - var x = args.getDouble(1); - var y = args.getDouble(0); - - if (x == 0) { - if (y == 0) return Double.NaN; - return Math.signum(y) * Math.PI / 2; - } - else { - var val = Math.atan(y / x); - if (x > 0) return val; - else if (y < 0) return val - Math.PI; - else return val + Math.PI; - } - - } - - @Expose(target = ExposeTarget.STATIC) - public static double __asinh(Arguments args) { - var x = args.getDouble(0); - return Math.log(x + Math.sqrt(x * x + 1)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __acosh(Arguments args) { - var x = args.getDouble(0); - return Math.log(x + Math.sqrt(x * x - 1)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __atanh(Arguments args) { - var x = args.getDouble(0); - - if (x <= -1 || x >= 1) return Double.NaN; - return .5 * Math.log((1 + x) / (1 - x)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sin(Arguments args) { - return Math.sin(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __cos(Arguments args) { - return Math.cos(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __tan(Arguments args) { - return Math.tan(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sinh(Arguments args) { - return Math.sinh(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __cosh(Arguments args) { - return Math.cosh(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __tanh(Arguments args) { - return Math.tanh(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sqrt(Arguments args) { - return Math.sqrt(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __cbrt(Arguments args) { - return Math.cbrt(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __hypot(Arguments args) { - var res = 0.; - for (var i = 0; i < args.n(); i++) { - var val = args.getDouble(i); - res += val * val; - } - return Math.sqrt(res); - } - @Expose(target = ExposeTarget.STATIC) - public static int __imul(Arguments args) { return args.getInt(0) * args.getInt(1); } - - @Expose(target = ExposeTarget.STATIC) - public static double __exp(Arguments args) { - return Math.exp(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __expm1(Arguments args) { - return Math.expm1(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __pow(Arguments args) { return Math.pow(args.getDouble(0), args.getDouble(1)); } - - @Expose(target = ExposeTarget.STATIC) - public static double __log(Arguments args) { - return Math.log(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __log10(Arguments args) { - return Math.log10(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __log1p(Arguments args) { - return Math.log1p(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __log2(Arguments args) { - return Math.log(args.getDouble(0)) / __LN2; - } - - @Expose(target = ExposeTarget.STATIC) - public static double __ceil(Arguments args) { - return Math.ceil(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __floor(Arguments args) { - return Math.floor(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static double __round(Arguments args) { - return Math.round(args.getDouble(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static float __fround(Arguments args) { - return (float)args.getDouble(0); - } - @Expose(target = ExposeTarget.STATIC) - public static double __trunc(Arguments args) { - var x = args.getDouble(0); - return Math.floor(Math.abs(x)) * Math.signum(x); - } - @Expose(target = ExposeTarget.STATIC) - public static double __abs(Arguments args) { - return Math.abs(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __max(Arguments args) { - var res = Double.NEGATIVE_INFINITY; - - for (var i = 0; i < args.n(); i++) { - var el = args.getDouble(i); - if (el > res) res = el; - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static double __min(Arguments args) { - var res = Double.POSITIVE_INFINITY; - - for (var i = 0; i < args.n(); i++) { - var el = args.getDouble(i); - if (el < res) res = el; - } - - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static double __sign(Arguments args) { - return Math.signum(args.getDouble(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static double __random() { return Math.random(); } - @Expose(target = ExposeTarget.STATIC) - public static int __clz32(Arguments args) { - return Integer.numberOfLeadingZeros(args.getInt(0)); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/NumberLib.java b/src/java/me/topchetoeu/jscript/lib/NumberLib.java deleted file mode 100644 index 1a7aac1..0000000 --- a/src/java/me/topchetoeu/jscript/lib/NumberLib.java +++ /dev/null @@ -1,103 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.text.NumberFormat; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Number") -public class NumberLib { - @ExposeField(target = ExposeTarget.STATIC) - public static final double __EPSILON = Math.ulp(1.0); - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MAX_SAFE_INTEGER = 9007199254740991.; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MIN_SAFE_INTEGER = -__MAX_SAFE_INTEGER; - // lmao big number go brrr - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MAX_VALUE = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __MIN_VALUE = -__MAX_VALUE; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __NaN = 0. / 0; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __NEGATIVE_INFINITY = -1. / 0; - @ExposeField(target = ExposeTarget.STATIC) - public static final double __POSITIVE_INFINITY = 1. / 0; - - public final double value; - - @Override public String toString() { return value + ""; } - - public NumberLib(double val) { - this.value = val; - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isFinite(Arguments args) { return Double.isFinite(args.getDouble(0)); } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isInfinite(Arguments args) { return Double.isInfinite(args.getDouble(0)); } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isNaN(Arguments args) { return Double.isNaN(args.getDouble(0)); } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isSafeInteger(Arguments args) { - return args.getDouble(0) > __MIN_SAFE_INTEGER && args.getDouble(0) < __MAX_SAFE_INTEGER; - } - - @Expose(target = ExposeTarget.STATIC) - public static double __parseFloat(Arguments args) { - return args.getDouble(0); - } - @Expose(target = ExposeTarget.STATIC) - public static double __parseInt(Arguments args) { - var radix = args.getInt(1, 10); - - if (radix < 2 || radix > 36) return Double.NaN; - else { - long res = 0; - - for (var c : args.getString(0).toCharArray()) { - var digit = 0; - - if (c >= '0' && c <= '9') digit = c - '0'; - else if (c >= 'a' && c <= 'z') digit = c - 'a' + 10; - else if (c >= 'A' && c <= 'Z') digit = c - 'A' + 10; - else break; - - if (digit > radix) break; - - res *= radix; - res += digit; - } - - return res; - } - } - - @ExposeConstructor public static Object __constructor(Arguments args) { - if (args.self instanceof ObjectValue) return new NumberLib(args.getDouble(0)); - else return args.getDouble(0); - } - @Expose public static String __toString(Arguments args) { - return Values.toString(args.env, args.self); - } - @Expose public static String __toFixed(Arguments args) { - var digits = args.getInt(0, 0); - - var nf = NumberFormat.getNumberInstance(); - nf.setMinimumFractionDigits(digits); - nf.setMaximumFractionDigits(digits); - - return nf.format(args.getDouble(-1)); - } - @Expose public static double __valueOf(Arguments args) { - if (Values.isWrapper(args.self, NumberLib.class)) return Values.wrapper(args.self, NumberLib.class).value; - else return Values.toNumber(args.env, args.self); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/ObjectLib.java b/src/java/me/topchetoeu/jscript/lib/ObjectLib.java deleted file mode 100644 index 2dfbafa..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ObjectLib.java +++ /dev/null @@ -1,273 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Object") -public class ObjectLib { - @Expose(target = ExposeTarget.STATIC) - public static Object __assign(Arguments args) { - for (var obj : args.slice(1).args) { - for (var key : Values.getMembers(args.env, obj, true, true)) { - Values.setMember(args.env, args.get(0), key, Values.getMember(args.env, obj, key)); - } - } - return args.get(0); - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __create(Arguments args) { - var obj = new ObjectValue(); - Values.setPrototype(args.env, obj, args.get(0)); - - if (args.n() >= 1) { - var newArgs = new Object[args.n()]; - System.arraycopy(args.args, 1, args, 1, args.n() - 1); - newArgs[0] = obj; - - __defineProperties(new Arguments(args.env, null, newArgs)); - } - - return obj; - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __defineProperty(Arguments args) { - var obj = args.convert(0, ObjectValue.class); - var key = args.get(1); - var attrib = args.convert(2, ObjectValue.class); - - var hasVal = Values.hasMember(args.env, attrib, "value", false); - var hasGet = Values.hasMember(args.env, attrib, "get", false); - var hasSet = Values.hasMember(args.env, attrib, "set", false); - - if (hasVal) { - if (hasGet || hasSet) throw EngineException.ofType("Cannot specify a value and accessors for a property."); - if (!obj.defineProperty( - args.env, key, - Values.getMember(args.env, attrib, "value"), - Values.toBoolean(Values.getMember(args.env, attrib, "writable")), - Values.toBoolean(Values.getMember(args.env, attrib, "configurable")), - Values.toBoolean(Values.getMember(args.env, attrib, "enumerable")) - )) throw EngineException.ofType("Can't define property '" + key + "'."); - } - else { - var get = Values.getMember(args.env, attrib, "get"); - var set = Values.getMember(args.env, attrib, "set"); - if (get != null && !(get instanceof FunctionValue)) throw EngineException.ofType("Get accessor must be a function."); - if (set != null && !(set instanceof FunctionValue)) throw EngineException.ofType("Set accessor must be a function."); - - if (!obj.defineProperty( - args.env, key, - (FunctionValue)get, (FunctionValue)set, - Values.toBoolean(Values.getMember(args.env, attrib, "configurable")), - Values.toBoolean(Values.getMember(args.env, attrib, "enumerable")) - )) throw EngineException.ofType("Can't define property '" + key + "'."); - } - - return obj; - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __defineProperties(Arguments args) { - var obj = args.convert(0, ObjectValue.class); - var attrib = args.get(1); - - for (var key : Values.getMembers(null, attrib, false, false)) { - __defineProperty(new Arguments(args.env, null, obj, key, Values.getMember(args.env, attrib, key))); - } - - return obj; - } - - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __keys(Arguments args) { - var obj = args.get(0); - var all = args.getBoolean(1); - var res = new ArrayValue(); - - for (var key : Values.getMembers(args.env, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __entries(Arguments args) { - var res = new ArrayValue(); - var obj = args.get(0); - var all = args.getBoolean(1); - - for (var key : Values.getMembers(args.env, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), new ArrayValue(args.env, key, Values.getMember(args.env, obj, key))); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __values(Arguments args) { - var res = new ArrayValue(); - var obj = args.get(0); - var all = args.getBoolean(1); - - for (var key : Values.getMembers(args.env, obj, true, false)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), Values.getMember(args.env, obj, key)); - } - - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __getOwnPropertyDescriptor(Arguments args) { - return Values.getMemberDescriptor(args.env, args.get(0), args.get(1)); - } - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __getOwnPropertyDescriptors(Arguments args) { - var res = new ObjectValue(); - var obj = args.get(0); - for (var key : Values.getMembers(args.env, obj, true, true)) { - res.defineProperty(args.env, key, Values.getMemberDescriptor(args.env, obj, key)); - } - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __getOwnPropertyNames(Arguments args) { - var res = new ArrayValue(); - var obj = args.get(0); - var all = args.getBoolean(1); - - for (var key : Values.getMembers(args.env, obj, true, true)) { - if (all || !(key instanceof Symbol)) res.set(args.env, res.size(), key); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static ArrayValue __getOwnPropertySymbols(Arguments args) { - var obj = args.get(0); - var res = new ArrayValue(); - - for (var key : Values.getMembers(args.env, obj, true, true)) { - if (key instanceof Symbol) res.set(args.env, res.size(), key); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __hasOwn(Arguments args) { - return Values.hasMember(args.env, args.get(0), args.get(1), true); - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __getPrototypeOf(Arguments args) { - return Values.getPrototype(args.env, args.get(0)); - } - @Expose(target = ExposeTarget.STATIC) - public static Object __setPrototypeOf(Arguments args) { - Values.setPrototype(args.env, args.get(0), args.get(1)); - return args.get(0); - } - - @Expose(target = ExposeTarget.STATIC) - public static ObjectValue __fromEntries(Arguments args) { - var res = new ObjectValue(); - - for (var el : Values.fromJSIterator(args.env, args.get(0))) { - if (el instanceof ArrayValue) { - res.defineProperty(args.env, ((ArrayValue)el).get(0), ((ArrayValue)el).get(1)); - } - } - - return res; - } - - @Expose(target = ExposeTarget.STATIC) - public static Object __preventExtensions(Arguments args) { - if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).preventExtensions(); - return args.get(0); - } - @Expose(target = ExposeTarget.STATIC) - public static Object __seal(Arguments args) { - if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).seal(); - return args.get(0); - } - @Expose(target = ExposeTarget.STATIC) - public static Object __freeze(Arguments args) { - if (args.get(0) instanceof ObjectValue) args.convert(0, ObjectValue.class).freeze(); - return args.get(0); - } - - @Expose(target = ExposeTarget.STATIC) - public static boolean __isExtensible(Arguments args) { - var obj = args.get(0); - if (!(obj instanceof ObjectValue)) return false; - return ((ObjectValue)obj).extensible(); - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isSealed(Arguments args) { - var obj = args.get(0); - - if (!(obj instanceof ObjectValue)) return true; - var _obj = (ObjectValue)obj; - - if (_obj.extensible()) return false; - - for (var key : _obj.keys(true)) { - if (_obj.memberConfigurable(key)) return false; - } - - return true; - } - @Expose(target = ExposeTarget.STATIC) - public static boolean __isFrozen(Arguments args) { - var obj = args.get(0); - - if (!(obj instanceof ObjectValue)) return true; - var _obj = (ObjectValue)obj; - - if (_obj.extensible()) return false; - - for (var key : _obj.keys(true)) { - if (_obj.memberConfigurable(key)) return false; - if (_obj.memberWritable(key)) return false; - } - - return true; - } - - @Expose - public static Object __valueOf(Arguments args) { - return args.self; - } - @Expose - public static String __toString(Arguments args) { - var name = Values.getMember(args.env, args.self, Symbol.get("Symbol.typeName")); - if (name == null) name = "Unknown"; - else name = Values.toString(args.env, name); - - return "[object " + name + "]"; - } - @Expose - public static boolean __hasOwnProperty(Arguments args) { - return Values.hasMember(args.env, args.self, args.get(0), true); - } - - @ExposeConstructor - public static Object __constructor(Arguments args) { - var arg = args.get(0); - if (arg == null || arg == Values.NULL) return new ObjectValue(); - else if (arg instanceof Boolean) return new BooleanLib((boolean)arg); - else if (arg instanceof Number) return new NumberLib(((Number)arg).doubleValue()); - else if (arg instanceof String) return new StringLib((String)arg); - else if (arg instanceof Symbol) return new SymbolLib((Symbol)arg); - else return arg; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java b/src/java/me/topchetoeu/jscript/lib/PromiseLib.java deleted file mode 100644 index 5bd95cb..0000000 --- a/src/java/me/topchetoeu/jscript/lib/PromiseLib.java +++ /dev/null @@ -1,403 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.List; - -import me.topchetoeu.jscript.common.ResultRunnable; -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Promise") -public class PromiseLib { - public static interface Handle { - void onFulfil(Object val); - void onReject(EngineException err); - - default Handle defer(Environment loop) { - var self = this; - - return new Handle() { - @Override public void onFulfil(Object val) { - if (!loop.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - loop.get(EventLoop.KEY).pushMsg(() -> self.onFulfil(val), true); - } - @Override public void onReject(EngineException val) { - if (!loop.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - loop.get(EventLoop.KEY).pushMsg(() -> self.onReject(val), true); - } - }; - } - } - - private static final int STATE_PENDING = 0; - private static final int STATE_FULFILLED = 1; - private static final int STATE_REJECTED = 2; - - private List handles = new ArrayList<>(); - - private int state = STATE_PENDING; - private boolean handled = false; - private Object val; - - private void resolveSynchronized(Environment env, Object val, int newState) { - this.val = val; - this.state = newState; - - for (var handle : handles) { - if (newState == STATE_FULFILLED) handle.onFulfil(val); - if (newState == STATE_REJECTED) { - handle.onReject((EngineException)val); - handled = true; - } - } - - if (state == STATE_REJECTED && !handled) { - Values.printError(((EngineException)val).setEnvironment(env), "(in promise)"); - } - - handles = null; - - // ctx.get(EventLoop.KEY).pushMsg(() -> { - // if (!ctx.hasNotNull(EventLoop.KEY)) throw EngineException.ofError("No event loop"); - - - // handles = null; - // }, true); - - } - private synchronized void resolve(Environment env, Object val, int newState) { - if (this.state != STATE_PENDING || newState == STATE_PENDING) return; - - handle(env, val, new Handle() { - @Override public void onFulfil(Object val) { - resolveSynchronized(env, val, newState); - } - @Override public void onReject(EngineException err) { - resolveSynchronized(env, val, STATE_REJECTED); - } - }); - } - - public synchronized void fulfill(Environment env, Object val) { - resolve(env, val, STATE_FULFILLED); - } - public synchronized void reject(Environment env, EngineException val) { - resolve(env, val, STATE_REJECTED); - } - - private void handle(Handle handle) { - if (state == STATE_FULFILLED) handle.onFulfil(val); - else if (state == STATE_REJECTED) { - handle.onReject((EngineException)val); - handled = true; - } - else handles.add(handle); - } - - @Override public String toString() { - if (state == STATE_PENDING) return "Promise (pending)"; - else if (state == STATE_FULFILLED) return "Promise (fulfilled)"; - else return "Promise (rejected)"; - } - - public PromiseLib() { - this.state = STATE_PENDING; - this.val = null; - } - - public static PromiseLib await(Environment env, ResultRunnable runner) { - var res = new PromiseLib(); - - new Thread(() -> { - try { - res.fulfill(env, runner.run()); - } - catch (EngineException e) { - res.reject(env, e); - } - catch (Exception e) { - if (e instanceof InterruptException) throw e; - else { - res.reject(env, EngineException.ofError("Native code failed with " + e.getMessage())); - } - } - }, "Promisifier").start(); - - return res; - } - public static PromiseLib await(Environment env, Runnable runner) { - return await(env, () -> { - runner.run(); - return null; - }); - } - - public static void handle(Environment env, Object obj, Handle handle) { - if (Values.isWrapper(obj, PromiseLib.class)) { - var promise = Values.wrapper(obj, PromiseLib.class); - handle(env, promise, handle); - return; - } - if (obj instanceof PromiseLib) { - ((PromiseLib)obj).handle(handle); - return; - } - - var rethrow = new boolean[1]; - - try { - var then = Values.getMember(env, obj, "then"); - - if (then instanceof FunctionValue) Values.call(env, then, obj, - new NativeFunction(args -> { - try { handle.onFulfil(args.get(0)); } - catch (Exception e) { - rethrow[0] = true; - throw e; - } - return null; - }), - new NativeFunction(args -> { - try { handle.onReject(new EngineException(args.get(0))); } - catch (Exception e) { - rethrow[0] = true; - throw e; - } - return null; - }) - ); - else handle.onFulfil(obj); - - return; - } - catch (Exception e) { - if (rethrow[0]) throw e; - } - - handle.onFulfil(obj); - } - - public static PromiseLib ofResolved(Environment ctx, Object value) { - var res = new PromiseLib(); - res.fulfill(ctx, value); - return res; - } - public static PromiseLib ofRejected(Environment ctx, EngineException value) { - var res = new PromiseLib(); - res.reject(ctx, value); - return res; - } - - @Expose(value = "resolve", target = ExposeTarget.STATIC) - public static PromiseLib __ofResolved(Arguments args) { - return ofResolved(args.env, args.get(0)); - } - @Expose(value = "reject", target = ExposeTarget.STATIC) - public static PromiseLib __ofRejected(Arguments args) { - return ofRejected(args.env, new EngineException(args.get(0)).setEnvironment(args.env)); - } - - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __any(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - - if (promises.size() == 0) return ofRejected(args.env, EngineException.ofError("No promises passed to 'Promise.any'.").setEnvironment(args.env)); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - var errors = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - var index = i; - var val = promises.get(i); - if (res.state != STATE_PENDING) break; - - handle(args.env, val, new Handle() { - public void onFulfil(Object val) { res.fulfill(args.env, val); } - public void onReject(EngineException err) { - errors.set(args.env, index, err.value); - n[0]--; - if (n[0] <= 0) res.reject(args.env, new EngineException(errors).setEnvironment(args.env)); - } - }); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __race(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - var res = new PromiseLib(); - - for (var i = 0; i < promises.size(); i++) { - var val = promises.get(i); - if (res.state != STATE_PENDING) break; - - handle(args.env, val, new Handle() { - @Override public void onFulfil(Object val) { res.fulfill(args.env, val); } - @Override public void onReject(EngineException err) { res.reject(args.env, err); } - }); - } - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __all(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - if (res.state != STATE_PENDING) break; - - var index = i; - var val = promises.get(i); - - handle(args.env, val, new Handle() { - @Override public void onFulfil(Object val) { - result.set(args.env, index, val); - n[0]--; - if (n[0] <= 0) res.fulfill(args.env, result); - } - @Override public void onReject(EngineException err) { - res.reject(args.env, err); - } - }); - } - - if (n[0] <= 0) res.fulfill(args.env, result); - - return res; - } - @Expose(target = ExposeTarget.STATIC) - public static PromiseLib __allSettled(Arguments args) { - if (!(args.get(0) instanceof ArrayValue)) throw EngineException.ofType("Expected argument for any to be an array."); - var promises = args.convert(0, ArrayValue.class); - var n = new int[] { promises.size() }; - var res = new PromiseLib(); - var result = new ArrayValue(); - - for (var i = 0; i < promises.size(); i++) { - if (res.state != STATE_PENDING) break; - - var index = i; - - handle(args.env, promises.get(i), new Handle() { - @Override public void onFulfil(Object val) { - var desc = new ObjectValue(); - desc.defineProperty(args.env, "status", "fulfilled"); - desc.defineProperty(args.env, "value", val); - - result.set(args.env, index, desc); - - n[0]--; - if (n[0] <= 0) res.fulfill(args.env, res); - } - @Override public void onReject(EngineException err) { - var desc = new ObjectValue(); - desc.defineProperty(args.env, "status", "reject"); - desc.defineProperty(args.env, "value", err.value); - - result.set(args.env, index, desc); - - n[0]--; - if (n[0] <= 0) res.fulfill(args.env, res); - } - }); - } - - if (n[0] <= 0) res.fulfill(args.env, result); - - return res; - } - - @Expose - public static Object __then(Arguments args) { - var onFulfill = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null; - var onReject = args.get(1) instanceof FunctionValue ? args.convert(1, FunctionValue.class) : null; - - var res = new PromiseLib(); - - handle(args.env, args.self, new Handle() { - @Override public void onFulfil(Object val) { - try { res.fulfill(args.env, onFulfill.call(args.env, null, val)); } - catch (EngineException e) { res.reject(args.env, e); } - } - @Override public void onReject(EngineException err) { - try { res.fulfill(args.env, onReject.call(args.env, null, err.value)); } - catch (EngineException e) { res.reject(args.env, e); } - } - }.defer(args.env)); - - return res; - } - @Expose - public static Object __catch(Arguments args) { - return __then(new Arguments(args.env, args.self, null, args.get(0))); - } - @Expose - public static Object __finally(Arguments args) { - var func = args.get(0) instanceof FunctionValue ? args.convert(0, FunctionValue.class) : null; - - var res = new PromiseLib(); - - handle(args.env, args.self, new Handle() { - @Override public void onFulfil(Object val) { - try { - func.call(args.env); - res.fulfill(args.env, val); - } - catch (EngineException e) { res.reject(args.env, e); } - } - @Override public void onReject(EngineException err) { - try { - func.call(args.env); - res.reject(args.env, err); - } - catch (EngineException e) { res.reject(args.env, e); } - } - }.defer(args.env)); - - return res; - } - - @ExposeConstructor - public static PromiseLib __constructor(Arguments args) { - var func = args.convert(0, FunctionValue.class); - var res = new PromiseLib(); - - try { - func.call( - args.env, null, - new NativeFunction(null, _args -> { - res.fulfill(_args.env, _args.get(0)); - return null; - }), - new NativeFunction(null, _args -> { - res.reject(_args.env, new EngineException(_args.get(0)).setEnvironment(_args.env)); - return null; - }) - ); - } - catch (EngineException e) { - res.reject(args.env, e); - } - - return res; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java b/src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java deleted file mode 100644 index 78877ce..0000000 --- a/src/java/me/topchetoeu/jscript/lib/RangeErrorLib.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("RangeError") -public class RangeErrorLib extends ErrorLib { - @ExposeField public static final String __name = "RangeError"; - - @ExposeConstructor public static ObjectValue constructor(Arguments args) { - var target = ErrorLib.__constructor(args); - target.setPrototype(PlaceholderProto.RANGE_ERROR); - return target; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/RegExpLib.java b/src/java/me/topchetoeu/jscript/lib/RegExpLib.java deleted file mode 100644 index d4cf695..0000000 --- a/src/java/me/topchetoeu/jscript/lib/RegExpLib.java +++ /dev/null @@ -1,354 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.regex.Pattern; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeWrapper; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("RegExp") -public class RegExpLib { - // I used Regex to analyze Regex - private static final Pattern NAMED_PATTERN = Pattern.compile("\\(\\?<([^=!].*?)>", Pattern.DOTALL); - private static final Pattern ESCAPE_PATTERN = Pattern.compile("[/\\-\\\\^$*+?.()|\\[\\]{}]"); - - private Pattern pattern; - private String[] namedGroups; - private int flags; - - public int lastI = 0; - public final String source; - public final boolean hasIndices; - public final boolean global; - public final boolean sticky; - - @Expose(type = ExposeType.GETTER) - public int __lastIndex() { return lastI; } - @Expose(type = ExposeType.SETTER) - public void __setLastIndex(Arguments args) { lastI = args.getInt(0); } - @Expose(type = ExposeType.GETTER) - public String __source() { return source; } - - @Expose(type = ExposeType.GETTER) - public boolean __ignoreCase() { return (flags & Pattern.CASE_INSENSITIVE) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __multiline() { return (flags & Pattern.MULTILINE) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __unicode() { return (flags & Pattern.UNICODE_CHARACTER_CLASS) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __dotAll() { return (flags & Pattern.DOTALL) != 0; } - @Expose(type = ExposeType.GETTER) - public boolean __global() { return global; } - @Expose(type = ExposeType.GETTER) - public boolean __sticky() { return sticky; } - @Expose(type = ExposeType.GETTER) - public final String __flags() { - String res = ""; - if (hasIndices) res += 'd'; - if (global) res += 'g'; - if (__ignoreCase()) res += 'i'; - if (__multiline()) res += 'm'; - if (__dotAll()) res += 's'; - if (__unicode()) res += 'u'; - if (sticky) res += 'y'; - return res; - } - - @Expose public Object __exec(Arguments args) { - var str = args.getString(0); - var matcher = pattern.matcher(str); - if (lastI > str.length() || !matcher.find(lastI) || sticky && matcher.start() != lastI) { - lastI = 0; - return Values.NULL; - } - if (sticky || global) { - lastI = matcher.end(); - if (matcher.end() == matcher.start()) lastI++; - } - - var obj = new ArrayValue(); - ObjectValue groups = null; - - for (var el : namedGroups) { - if (groups == null) groups = new ObjectValue(); - try { groups.defineProperty(null, el, matcher.group(el)); } - catch (IllegalArgumentException e) { } - } - - - for (int i = 0; i < matcher.groupCount() + 1; i++) { - obj.set(null, i, matcher.group(i)); - } - obj.defineProperty(null, "groups", groups); - obj.defineProperty(null, "index", matcher.start()); - obj.defineProperty(null, "input", str); - - if (hasIndices) { - var indices = new ArrayValue(); - for (int i = 0; i < matcher.groupCount() + 1; i++) { - indices.set(null, i, new ArrayValue(null, matcher.start(i), matcher.end(i))); - } - var groupIndices = new ObjectValue(); - for (var el : namedGroups) { - groupIndices.defineProperty(null, el, new ArrayValue(null, matcher.start(el), matcher.end(el))); - } - indices.defineProperty(null, "groups", groupIndices); - obj.defineProperty(null, "indices", indices); - } - - return obj; - } - - @Expose public boolean __test(Arguments args) { - return this.__exec(args) != Values.NULL; - } - - @Expose("@@Symbol.match") public Object __match(Arguments args) { - if (this.global) { - var res = new ArrayValue(); - Object val; - while ((val = this.__exec(args)) != Values.NULL) { - res.set(args.env, res.size(), Values.getMember(args.env, val, 0)); - } - lastI = 0; - return res; - } - else { - var res = this.__exec(args); - if (!this.sticky) this.lastI = 0; - return res; - } - } - - @Expose("@@Symbol.matchAll") public Object __matchAll(Arguments args) { - var pattern = this.toGlobal(); - - return Values.toJSIterator(args.env, new Iterator() { - private Object val = null; - private boolean updated = false; - - private void update() { - if (!updated) val = pattern.__exec(args); - } - @Override public boolean hasNext() { - update(); - return val != Values.NULL; - } - @Override public Object next() { - update(); - updated = false; - return val; - } - }); - } - - @Expose("@@Symbol.split") public ArrayValue __split(Arguments args) { - var pattern = this.toGlobal(); - var target = args.getString(0); - var hasLimit = args.get(1) != null; - var lim = args.getInt(1); - var sensible = args.getBoolean(2); - - Object match; - int lastEnd = 0; - var res = new ArrayValue(); - - while ((match = pattern.__exec(args)) != Values.NULL) { - var added = new ArrayList(); - var arrMatch = (ArrayValue)match; - int index = (int)Values.toNumber(args.env, Values.getMember(args.env, match, "index")); - var matchVal = (String)arrMatch.get(0); - - if (index >= target.length()) break; - - if (matchVal.length() == 0 || index - lastEnd > 0) { - added.add(target.substring(lastEnd, pattern.lastI)); - if (pattern.lastI < target.length()) { - for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i)); - } - } - else { - for (var i = 1; i < arrMatch.size(); i++) added.add((String)arrMatch.get(i)); - } - - if (sensible) { - if (hasLimit && res.size() + added.size() >= lim) break; - else for (var i = 0; i < added.size(); i++) res.set(args.env, res.size(), added.get(i)); - } - else { - for (var i = 0; i < added.size(); i++) { - if (hasLimit && res.size() >= lim) return res; - else res.set(args.env, res.size(), added.get(i)); - } - } - lastEnd = pattern.lastI; - } - if (lastEnd < target.length()) { - res.set(args.env, res.size(), target.substring(lastEnd)); - } - return res; - } - - @Expose("@@Symbol.replace") public String __replace(Arguments args) { - var pattern = this.toIndexed(); - var target = args.getString(0); - var replacement = args.get(1); - Object match; - var lastEnd = 0; - var res = new StringBuilder(); - - while ((match = pattern.__exec(args)) != Values.NULL) { - var indices = (ArrayValue)((ArrayValue)Values.getMember(args.env, match, "indices")).get(0); - var arrMatch = (ArrayValue)match; - - var start = ((Number)indices.get(0)).intValue(); - var end = ((Number)indices.get(1)).intValue(); - - res.append(target.substring(lastEnd, start)); - if (replacement instanceof FunctionValue) { - var callArgs = new Object[arrMatch.size() + 2]; - callArgs[0] = target.substring(start, end); - arrMatch.copyTo(callArgs, 1, 1, arrMatch.size() - 1); - callArgs[callArgs.length - 2] = start; - callArgs[callArgs.length - 1] = target; - res.append(Values.toString(args.env, ((FunctionValue)replacement).call(args.env, null, callArgs))); - } - else { - res.append(Values.toString(args.env, replacement)); - } - lastEnd = end; - if (!pattern.global) break; - } - if (lastEnd < target.length()) { - res.append(target.substring(lastEnd)); - } - return res.toString(); - } - - // [Symbol.search](target, reverse, start) { - // const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp; - // if (!reverse) { - // pattern.lastIndex = (start as any) | 0; - // const res = pattern.exec(target); - // if (res) return res.index; - // else return -1; - // } - // else { - // start ??= target.length; - // start |= 0; - // let res: RegExpResult | null = null; - // while (true) { - // const tmp = pattern.exec(target); - // if (tmp === null || tmp.index > start) break; - // res = tmp; - // } - // if (res && res.index <= start) return res.index; - // else return -1; - // } - // }, - - public RegExpLib toGlobal() { - return new RegExpLib(pattern, namedGroups, flags, source, hasIndices, true, sticky); - } - public RegExpLib toIndexed() { - return new RegExpLib(pattern, namedGroups, flags, source, true, global, sticky); - } - - public String toString() { - return "/" + source + "/" + __flags(); - } - - public RegExpLib(String pattern, String flags) { - if (pattern == null || pattern.equals("")) pattern = "(?:)"; - if (flags == null || flags.equals("")) flags = ""; - - this.flags = 0; - this.hasIndices = flags.contains("d"); - this.global = flags.contains("g"); - this.sticky = flags.contains("y"); - this.source = pattern; - - if (flags.contains("i")) this.flags |= Pattern.CASE_INSENSITIVE; - if (flags.contains("m")) this.flags |= Pattern.MULTILINE; - if (flags.contains("s")) this.flags |= Pattern.DOTALL; - if (flags.contains("u")) this.flags |= Pattern.UNICODE_CHARACTER_CLASS; - - if (pattern.equals("{(\\d+)}")) pattern = "\\{([0-9]+)\\}"; - this.pattern = Pattern.compile(pattern.replace("\\d", "[0-9]"), this.flags); - - var matcher = NAMED_PATTERN.matcher(source); - var groups = new ArrayList(); - - while (matcher.find()) { - if (!checkEscaped(source, matcher.start() - 1)) { - groups.add(matcher.group(1)); - } - } - - namedGroups = groups.toArray(String[]::new); - } - - private RegExpLib(Pattern pattern, String[] namedGroups, int flags, String source, boolean hasIndices, boolean global, boolean sticky) { - this.pattern = pattern; - this.namedGroups = namedGroups; - this.flags = flags; - this.source = source; - this.hasIndices = hasIndices; - this.global = global; - this.sticky = sticky; - } - public RegExpLib(String pattern) { this(pattern, null); } - public RegExpLib() { this(null, null); } - - @ExposeConstructor - public static RegExpLib __constructor(Arguments args) { - return new RegExpLib(cleanupPattern(args.env, args.get(0)), cleanupFlags(args.env, args.get(1))); - } - @Expose(target = ExposeTarget.STATIC) - public static RegExpLib __escape(Arguments args) { - return escape(Values.toString(args.env, args.get(0)), cleanupFlags(args.env, args.get(1))); - } - - private static String cleanupPattern(Environment env, Object val) { - if (val == null) return "(?:)"; - if (val instanceof RegExpLib) return ((RegExpLib)val).source; - if (val instanceof NativeWrapper && ((NativeWrapper)val).wrapped instanceof RegExpLib) { - return ((RegExpLib)((NativeWrapper)val).wrapped).source; - } - var res = Values.toString(env, val); - if (res.equals("")) return "(?:)"; - return res; - } - private static String cleanupFlags(Environment env, Object val) { - if (val == null) return ""; - return Values.toString(env, val); - } - - private static boolean checkEscaped(String s, int pos) { - int n = 0; - - while (true) { - if (pos <= 0) break; - if (s.charAt(pos) != '\\') break; - n++; - pos--; - } - - return (n % 2) != 0; - } - - public static RegExpLib escape(String raw, String flags) { - return new RegExpLib(ESCAPE_PATTERN.matcher(raw).replaceAll("\\\\$0"), flags); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/SetLib.java b/src/java/me/topchetoeu/jscript/lib/SetLib.java deleted file mode 100644 index d76113a..0000000 --- a/src/java/me/topchetoeu/jscript/lib/SetLib.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Set") -public class SetLib { - private LinkedHashSet set = new LinkedHashSet<>(); - - @Expose("@@Symbol.iterator") - public ObjectValue __iterator(Arguments args) { - return this.__values(args); - } - - @Expose public ObjectValue __entries(Arguments args) { - return Values.toJSIterator(args.env, set.stream().map(v -> new ArrayValue(args.env, v, v)).collect(Collectors.toList())); - } - @Expose public ObjectValue __keys(Arguments args) { - return Values.toJSIterator(args.env, set); - } - @Expose public ObjectValue __values(Arguments args) { - return Values.toJSIterator(args.env, set); - } - - @Expose public Object __add(Arguments args) { - return set.add(args.get(0)); - } - @Expose public boolean __delete(Arguments args) { - return set.remove(args.get(0)); - } - @Expose public boolean __has(Arguments args) { - return set.contains(args.get(0)); - } - - @Expose public void __clear() { - set.clear(); - } - - @Expose(type = ExposeType.GETTER) - public int __size() { - return set.size(); - } - - @Expose public void __forEach(Arguments args) { - var keys = new ArrayList<>(set); - - for (var el : keys) Values.call(args.env, args.get(0), args.get(1), el, el, args.self); - } - - public SetLib(Environment env, Object iterable) { - for (var el : Values.fromJSIterator(env, iterable)) set.add(el); - } - - @ExposeConstructor - public static SetLib __constructor(Arguments args) { - return new SetLib(args.env, args.get(0)); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/StringLib.java b/src/java/me/topchetoeu/jscript/lib/StringLib.java deleted file mode 100644 index dba6546..0000000 --- a/src/java/me/topchetoeu/jscript/lib/StringLib.java +++ /dev/null @@ -1,291 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.regex.Pattern; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.ExposeType; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -// TODO: implement index wrapping properly -@WrapperName("String") -public class StringLib { - public final String value; - - @Override public String toString() { return value; } - - public StringLib(String val) { - this.value = val; - } - - private static String passThis(Arguments args, String funcName) { - var val = args.self; - if (Values.isWrapper(val, StringLib.class)) return Values.wrapper(val, StringLib.class).value; - else if (val instanceof String) return (String)val; - else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve strings.", funcName)); - } - private static int normalizeI(int i, int len, boolean clamp) { - if (i < 0) i += len; - if (clamp) { - if (i < 0) i = 0; - if (i > len) i = len; - } - return i; - } - - @Expose(type = ExposeType.GETTER) - public static int __length(Arguments args) { - return passThis(args, "length").length(); - } - - @Expose public static String __substring(Arguments args) { - var val = passThis(args, "substring"); - var start = Math.max(0, Math.min(val.length(), args.getInt(0))); - var end = Math.max(0, Math.min(val.length(), args.getInt(1, val.length()))); - - if (end < start) { - var tmp = end; - end = start; - start = tmp; - } - - return val.substring(start, end); - } - @Expose public static String __substr(Arguments args) { - var val = passThis(args, "substr"); - var start = normalizeI(args.getInt(0), val.length(), true); - int end = normalizeI(args.getInt(1, val.length() - start) + start, val.length(), true); - return val.substring(start, end); - } - - @Expose public static String __toLowerCase(Arguments args) { - return passThis(args, "toLowerCase").toLowerCase(); - } - @Expose public static String __toUpperCase(Arguments args) { - return passThis(args, "toUpperCase").toUpperCase(); - } - - @Expose public static String __charAt(Arguments args) { - return passThis(args, "charAt").charAt(args.getInt(0)) + ""; - } - @Expose public static double __charCodeAt(Arguments args) { - var str = passThis(args, "charCodeAt"); - var i = args.getInt(0); - if (i < 0 || i >= str.length()) return Double.NaN; - else return str.charAt(i); - } - @Expose public static double __codePointAt(Arguments args) { - var str = passThis(args, "codePointAt"); - var i = args.getInt(0); - if (i < 0 || i >= str.length()) return Double.NaN; - else return str.codePointAt(i); - } - - @Expose public static boolean __startsWith(Arguments args) { - return passThis(args, "startsWith").startsWith(args.getString(0), args.getInt(1)); - } - @Expose public static boolean __endsWith(Arguments args) { - return passThis(args, "endsWith").lastIndexOf(args.getString(0), args.getInt(1)) >= 0; - } - - @Expose public static int __indexOf(Arguments args) { - var val = passThis(args, "indexOf"); - var term = args.get(0); - var start = args.getInt(1); - var search = Values.getMember(args.env, term, Symbol.get("Symbol.search")); - - if (search instanceof FunctionValue) { - return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, false, start)); - } - else return val.indexOf(Values.toString(args.env, term), start); - } - @Expose public static int __lastIndexOf(Arguments args) { - var val = passThis(args, "lastIndexOf"); - var term = args.get(0); - var start = args.getInt(1); - var search = Values.getMember(args.env, term, Symbol.get("Symbol.search")); - - if (search instanceof FunctionValue) { - return (int)Values.toNumber(args.env, Values.call(args.env, search, term, val, true, start)); - } - else return val.lastIndexOf(Values.toString(args.env, term), start); - } - - @Expose public static boolean __includes(Arguments args) { - return __indexOf(args) >= 0; - } - - @Expose public static String __replace(Arguments args) { - var val = passThis(args, "replace"); - var term = args.get(0); - var replacement = args.get(1); - var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); - - if (replace instanceof FunctionValue) { - return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement)); - } - else return val.replaceFirst(Pattern.quote(Values.toString(args.env, term)), Values.toString(args.env, replacement)); - } - @Expose public static String __replaceAll(Arguments args) { - var val = passThis(args, "replaceAll"); - var term = args.get(0); - var replacement = args.get(1); - var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); - - if (replace instanceof FunctionValue) { - return Values.toString(args.env, Values.call(args.env, replace, term, val, replacement)); - } - else return val.replace(Values.toString(args.env, term), Values.toString(args.env, replacement)); - } - - @Expose public static ArrayValue __match(Arguments args) { - var val = passThis(args, "match"); - var term = args.get(0); - - FunctionValue match; - - try { - var _match = Values.getMember(args.env, term, Symbol.get("Symbol.match")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else if (args.env.hasNotNull(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), ""); - _match = Values.getMember(args.env, regex, Symbol.get("Symbol.match")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else throw EngineException.ofError("Regular expressions don't support matching."); - } - else throw EngineException.ofError("Regular expressions not supported."); - } - catch (IllegalArgumentException e) { return new ArrayValue(args.env, ""); } - - var res = match.call(args.env, term, val); - if (res instanceof ArrayValue) return (ArrayValue)res; - else return new ArrayValue(args.env, ""); - } - @Expose public static Object __matchAll(Arguments args) { - var val = passThis(args, "matchAll"); - var term = args.get(0); - - FunctionValue match = null; - - try { - var _match = Values.getMember(args.env, term, Symbol.get("Symbol.matchAll")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - } - catch (IllegalArgumentException e) { } - - if (match == null && args.env.hasNotNull(Environment.REGEX_CONSTR)) { - var regex = Values.callNew(args.env, args.env.get(Environment.REGEX_CONSTR), Values.toString(args.env, term), "g"); - var _match = Values.getMember(args.env, regex, Symbol.get("Symbol.matchAll")); - if (_match instanceof FunctionValue) match = (FunctionValue)_match; - else throw EngineException.ofError("Regular expressions don't support matching."); - } - else throw EngineException.ofError("Regular expressions not supported."); - - return match.call(args.env, term, val); - } - - @Expose public static ArrayValue __split(Arguments args) { - var val = passThis(args, "split"); - var term = args.get(0); - var lim = args.get(1); - var sensible = args.getBoolean(2); - - if (lim != null) lim = Values.toNumber(args.env, lim); - - if (term != null && term != Values.NULL && !(term instanceof String)) { - var replace = Values.getMember(args.env, term, Symbol.get("Symbol.replace")); - if (replace instanceof FunctionValue) { - var tmp = ((FunctionValue)replace).call(args.env, term, val, lim, sensible); - - if (tmp instanceof ArrayValue) { - var parts = new ArrayValue(((ArrayValue)tmp).size()); - for (int i = 0; i < parts.size(); i++) parts.set(args.env, i, Values.toString(args.env, ((ArrayValue)tmp).get(i))); - return parts; - } - } - } - - String[] parts; - var pattern = Pattern.quote(Values.toString(args.env, term)); - - if (lim == null) parts = val.split(pattern); - else if ((double)lim < 1) return new ArrayValue(); - else if (sensible) parts = val.split(pattern, (int)(double)lim); - else { - var limit = (int)(double)lim; - parts = val.split(pattern, limit + 1); - ArrayValue res; - - if (parts.length > limit) res = new ArrayValue(limit); - else res = new ArrayValue(parts.length); - - for (var i = 0; i < parts.length && i < limit; i++) res.set(args.env, i, parts[i]); - - return res; - } - - var res = new ArrayValue(parts.length); - var i = 0; - - for (; i < parts.length; i++) { - if (lim != null && (double)lim <= i) break; - res.set(args.env, i, parts[i]); - } - - return res; - } - - @Expose public static String __slice(Arguments args) { - var self = passThis(args, "slice"); - var start = normalizeI(args.getInt(0), self.length(), false); - var end = normalizeI(args.getInt(1, self.length()), self.length(), false); - - return __substring(new Arguments(args.env, self, start, end)); - } - - @Expose public static String __concat(Arguments args) { - var res = new StringBuilder(passThis(args, "concat")); - - for (var el : args.convert(String.class)) res.append(el); - - return res.toString(); - } - @Expose public static String __trim(Arguments args) { - return passThis(args, "trim").trim(); - } - @Expose public static String __trimStart(Arguments args) { - return passThis(args, "trimStart").replaceAll("^\\s+", ""); - } - @Expose public static String __trimEnd(Arguments args) { - return passThis(args, "trimEnd").replaceAll("\\s+$", ""); - } - - @ExposeConstructor public static Object __constructor(Arguments args) { - var val = args.getString(0, ""); - if (args.self instanceof ObjectValue) return new StringLib(val); - else return val; - } - @Expose public static String __toString(Arguments args) { - return passThis(args, "toString"); - } - @Expose public static String __valueOf(Arguments args) { - return passThis(args, "valueOf"); - } - - @Expose(target = ExposeTarget.STATIC) - public static String __fromCharCode(Arguments args) { - var val = args.convertInt(); - char[] arr = new char[val.length]; - for (var i = 0; i < val.length; i++) arr[i] = (char)val[i]; - return new String(arr); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/SymbolLib.java b/src/java/me/topchetoeu/jscript/lib/SymbolLib.java deleted file mode 100644 index e6a553d..0000000 --- a/src/java/me/topchetoeu/jscript/lib/SymbolLib.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import java.util.HashMap; -import java.util.Map; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.ExposeTarget; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("Symbol") -public class SymbolLib { - private static final Map symbols = new HashMap<>(); - - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __typeName = Symbol.get("Symbol.typeName"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __replace = Symbol.get("Symbol.replace"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __match = Symbol.get("Symbol.match"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __matchAll = Symbol.get("Symbol.matchAll"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __split = Symbol.get("Symbol.split"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __search = Symbol.get("Symbol.search"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __iterator = Symbol.get("Symbol.iterator"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __asyncIterator = Symbol.get("Symbol.asyncIterator"); - @ExposeField(target = ExposeTarget.STATIC) - public static final Symbol __cause = Symbol.get("Symbol.cause"); - - public final Symbol value; - - private static Symbol passThis(Arguments args, String funcName) { - var val = args.self; - if (Values.isWrapper(val, SymbolLib.class)) return Values.wrapper(val, SymbolLib.class).value; - else if (val instanceof Symbol) return (Symbol)val; - else throw EngineException.ofType(String.format("'%s' may only be called upon object and primitve symbols.", funcName)); - } - - public SymbolLib(Symbol val) { - this.value = val; - } - - @Expose public static String __toString(Arguments args) { - return passThis(args, "toString").value; - } - @Expose public static Symbol __valueOf(Arguments args) { - return passThis(args, "valueOf"); - } - - @ExposeConstructor - public static Object __constructor(Arguments args) { - if (args.self instanceof ObjectValue) throw EngineException.ofType("Symbol constructor may not be called with new."); - if (args.get(0) == null) return new Symbol(""); - else return new Symbol(args.getString(0)); - } - - @Expose(target = ExposeTarget.STATIC) - public static Symbol __for(Arguments args) { - var key = args.getString(0); - if (symbols.containsKey(key)) return symbols.get(key); - else { - var sym = new Symbol(key); - symbols.put(key, sym); - return sym; - } - } - @Expose(target = ExposeTarget.STATIC) - public static String __keyFor(Arguments args) { - return passThis(new Arguments(args.env, args.get(0)), "keyFor").value; - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java b/src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java deleted file mode 100644 index 1557729..0000000 --- a/src/java/me/topchetoeu/jscript/lib/SyntaxErrorLib.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("SyntaxError") -public class SyntaxErrorLib extends ErrorLib { - @ExposeField public static final String __name = "SyntaxError"; - - @ExposeConstructor public static ObjectValue __constructor(Arguments args) { - var target = ErrorLib.__constructor(args); - target.setPrototype(PlaceholderProto.SYNTAX_ERROR); - return target; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/lib/ThrowableLib.java b/src/java/me/topchetoeu/jscript/lib/ThrowableLib.java deleted file mode 100644 index 960229f..0000000 --- a/src/java/me/topchetoeu/jscript/lib/ThrowableLib.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.Expose; - -public class ThrowableLib { - @Expose public static String __message(Arguments args) { - if (args.self instanceof Throwable) return ((Throwable)args.self).getMessage(); - else return null; - } - @Expose public static String __name(Arguments args) { - return args.self.getClass().getSimpleName(); - } - - @Expose public static String __toString(Arguments args) { - return __name(args) + ": " + __message(args); - } -} diff --git a/src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java b/src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java deleted file mode 100644 index 98107ae..0000000 --- a/src/java/me/topchetoeu/jscript/lib/TypeErrorLib.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.lib; - -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; -import me.topchetoeu.jscript.utils.interop.Arguments; -import me.topchetoeu.jscript.utils.interop.ExposeConstructor; -import me.topchetoeu.jscript.utils.interop.ExposeField; -import me.topchetoeu.jscript.utils.interop.WrapperName; - -@WrapperName("TypeError") -public class TypeErrorLib extends ErrorLib { - @ExposeField public static final String __name = "TypeError"; - - @ExposeConstructor public static ObjectValue __constructor(Arguments args) { - var target = ErrorLib.__constructor(args); - target.setPrototype(PlaceholderProto.TYPE_ERROR); - return target; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java index 9502c7d..adbc6ff 100644 --- a/src/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -1,12 +1,14 @@ package me.topchetoeu.jscript.runtime; +import me.topchetoeu.jscript.common.Compiler; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.ResultRunnable; import me.topchetoeu.jscript.common.events.DataNotifier; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; public interface EventLoop { public static final Key KEY = new Key<>(); @@ -25,10 +27,10 @@ public interface EventLoop { return pushMsg(() -> { runnable.run(); return null; }, micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) { + public default DataNotifier pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { return pushMsg(() -> func.call(env, thisArg, args), micro); } - public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) { + public default DataNotifier pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { return pushMsg(() -> Compiler.compile(env, filename, raw).call(env, thisArg, args), micro); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/Frame.java b/src/java/me/topchetoeu/jscript/runtime/Frame.java index 69e7c19..02f1360 100644 --- a/src/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.runtime; -import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Stack; import me.topchetoeu.jscript.common.Instruction; @@ -11,11 +12,13 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.InterruptException; import me.topchetoeu.jscript.runtime.scope.LocalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.ScopeValue; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.objects.ScopeValue; public class Frame { public static final Key KEY = new Key<>(); @@ -64,12 +67,12 @@ public class Frame { private static class PendingResult { public final boolean isReturn, isJump, isThrow; - public final Object value; + public final Value value; public final EngineException error; public final int ptr; public final Instruction instruction; - private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) { + private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Value value, EngineException error, int ptr) { this.instruction = instr; this.isReturn = isReturn; this.isJump = isJump; @@ -82,7 +85,7 @@ public class Frame { public static PendingResult ofNone() { return new PendingResult(null, false, false, false, null, null, 0); } - public static PendingResult ofReturn(Object value, Instruction instr) { + public static PendingResult ofReturn(Value value, Instruction instr) { return new PendingResult(instr, true, false, false, value, null, 0); } public static PendingResult ofThrow(EngineException error, Instruction instr) { @@ -100,7 +103,7 @@ public class Frame { public final CodeFunction function; public final Environment env; - public Object[] stack = new Object[32]; + public Value[] stack = new Value[32]; public int stackPtr = 0; public int codePtr = 0; public boolean jumpFlag = false; @@ -113,52 +116,54 @@ public class Frame { tryStack.add(res); } - public Object peek() { + public Value peek() { return peek(0); } - public Object peek(int offset) { + public Value peek(int offset) { if (stackPtr <= offset) return null; else return stack[stackPtr - 1 - offset]; } - public Object pop() { + public Value pop() { if (stackPtr == 0) return null; return stack[--stackPtr]; } - public Object[] take(int n) { + public Value[] take(int n) { int srcI = stackPtr - n; if (srcI < 0) srcI = 0; int dstI = n + srcI - stackPtr; int copyN = stackPtr - srcI; - Object[] res = new Object[n]; + Value[] res = new Value[n]; System.arraycopy(stack, srcI, res, dstI, copyN); stackPtr -= copyN; return res; } - public void push(Object val) { + public void push(Value val) { if (stack.length <= stackPtr) { - var newStack = new Object[stack.length * 2]; + var newStack = new Value[stack.length * 2]; System.arraycopy(stack, 0, newStack, 0, stack.length); stack = newStack; } - stack[stackPtr++] = Values.normalize(env, val); + + stack[stackPtr++] = val; } - private Object next(Object value, Object returnValue, EngineException error) { - if (value != Values.NO_RETURN) push(value); + // for the love of christ don't touch this + private Value next(Value value, Value returnValue, EngineException error) { + if (value != null) push(value); Instruction instr = null; if (codePtr >= 0 && codePtr < function.body.instructions.length) instr = function.body.instructions[codePtr]; - if (returnValue == Values.NO_RETURN && error == null) { + if (returnValue == null && error == null) { try { if (Thread.interrupted()) throw new InterruptException(); if (instr == null) returnValue = null; else { - DebugContext.get(env).onInstruction(env, this, instr, Values.NO_RETURN, null, false); + DebugContext.get(env).onInstruction(env, this, instr, null, null, false); try { this.jumpFlag = this.popTryFlag = false; @@ -181,7 +186,7 @@ public class Frame { if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error); else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr)); } - else if (returnValue != Values.NO_RETURN) { + else if (returnValue != null) { if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr)); } else if (jumpFlag && !tryCtx.inBounds(codePtr)) { @@ -209,7 +214,7 @@ public class Frame { } error = null; - returnValue = Values.NO_RETURN; + returnValue = null; break; } else { @@ -227,7 +232,7 @@ public class Frame { tryStack.pop(); codePtr = tryCtx.end; if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction; - if (!jumpFlag && returnValue == Values.NO_RETURN && error == null) { + if (!jumpFlag && returnValue == null && error == null) { if (tryCtx.result.isJump) { codePtr = tryCtx.result.ptr; jumpFlag = true; @@ -255,19 +260,19 @@ public class Frame { DebugContext.get(env).onInstruction(env, this, instr, null, error, caught); throw error; } - if (returnValue != Values.NO_RETURN) { + if (returnValue != null) { DebugContext.get(env).onInstruction(env, this, instr, returnValue, null, false); return returnValue; } - return Values.NO_RETURN; + return null; } /** * Executes the next instruction in the frame */ - public Object next() { - return next(Values.NO_RETURN, Values.NO_RETURN, null); + public Value next() { + return next(null, null, null); } /** * Induces a value on the stack (as if it were returned by the last function call) @@ -275,8 +280,8 @@ public class Frame { * * @param value The value to induce */ - public Object next(Object value) { - return next(value, Values.NO_RETURN, null); + public Value next(Value value) { + return next(value, null, null); } /** * Induces a thrown error and executes the next instruction. @@ -286,8 +291,8 @@ public class Frame { * * @param error The error to induce */ - public Object induceError(EngineException error) { - return next(Values.NO_RETURN, Values.NO_RETURN, error); + public Value induceError(EngineException error) { + return next(null, null, error); } /** * Induces a return, as if there was a return statement before @@ -298,8 +303,8 @@ public class Frame { * * @param value The retunr value to induce */ - public Object induceReturn(Object value) { - return next(Values.NO_RETURN, value, null); + public Value induceReturn(Value value) { + return next(null, value, null); } public void onPush() { @@ -348,35 +353,47 @@ public class Frame { */ public ObjectValue getValStackScope() { return new ObjectValue() { - @Override - protected Object getField(Environment ext, Object key) { - var i = (int)Values.toNumber(ext, key); + @Override public Member getOwnMember(Environment env, Value key) { + var res = super.getOwnMember(env, key); + if (res != null) return res; + + var f = key.toNumber(env).value; + var i = (int)f; + if (i < 0 || i >= stackPtr) return null; - else return stack[i]; + else return new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { return stack[i]; } + @Override public boolean set(Environment env, Value val, Value self) { + stack[i] = val; + return true; + } + }; } - @Override - protected boolean hasField(Environment ext, Object key) { - return true; - } - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - for (var i = 0; i < stackPtr; i++) res.add(i); + @Override public Map getOwnMembers(Environment env) { + var res = new LinkedHashMap(); + + for (var i = 0; i < stackPtr; i++) { + var _i = i; + res.put(i + "", new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { return stack[_i]; } + @Override public boolean set(Environment env, Value val, Value self) { + stack[_i] = val; + return true; + } + }); + } + return res; } }; } - public Frame(Environment env, Object thisArg, Object[] args, CodeFunction func) { + public Frame(Environment env, Value thisArg, Value[] args, CodeFunction func) { this.env = env; this.args = args.clone(); this.scope = new LocalScope(func.body.localsN, func.captures); - this.scope.get(0).set(null, thisArg); - var argsObj = new ArrayValue(); - for (var i = 0; i < args.length; i++) { - argsObj.set(env, i, args[i]); - } - this.scope.get(1).value = argsObj; + this.scope.get(0).set(thisArg); + this.scope.get(1).value = new ArrayValue(args); this.thisArg = thisArg; this.function = func; diff --git a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index c7bcfb8..bd25878 100644 --- a/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.runtime; +import java.util.ArrayList; import java.util.Collections; import me.topchetoeu.jscript.common.Instruction; @@ -8,85 +9,96 @@ import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.scope.GlobalScope; import me.topchetoeu.jscript.runtime.scope.ValueVariable; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class InstructionRunner { - private static Object execReturn(Environment ext, Instruction instr, Frame frame) { + private static Value execReturn(Environment env, Instruction instr, Frame frame) { return frame.pop(); } - private static Object execThrow(Environment ext, Instruction instr, Frame frame) { + private static Value execThrow(Environment env, Instruction instr, Frame frame) { throw new EngineException(frame.pop()); } - private static Object execThrowSyntax(Environment ext, Instruction instr, Frame frame) { + private static Value execThrowSyntax(Environment env, Instruction instr, Frame frame) { throw EngineException.ofSyntax((String)instr.get(0)); } - private static Object execCall(Environment ext, Instruction instr, Frame frame) { + private static Value execCall(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); - frame.push(Values.call(ext, func, thisArg, callArgs)); + frame.push(func.call(env, thisArg, callArgs)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execCallNew(Environment ext, Instruction instr, Frame frame) { + private static Value execCallNew(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); - frame.push(Values.callNew(ext, funcObj, callArgs)); + frame.push(funcObj.callNew(env, callArgs)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execMakeVar(Environment ext, Instruction instr, Frame frame) { + private static Value execMakeVar(Environment env, Instruction instr, Frame frame) { var name = (String)instr.get(0); - GlobalScope.get(ext).define(ext, name); + GlobalScope.get(env).define(env, false, name); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execDefProp(Environment ext, Instruction instr, Frame frame) { - var setter = frame.pop(); - var getter = frame.pop(); + private static Value execDefProp(Environment env, Instruction instr, Frame frame) { + var setterVal = frame.pop(); + var getterVal = frame.pop(); var name = frame.pop(); var obj = frame.pop(); - if (getter != null && !(getter instanceof FunctionValue)) throw EngineException.ofType("Getter must be a function or undefined."); - if (setter != null && !(setter instanceof FunctionValue)) throw EngineException.ofType("Setter must be a function or undefined."); - if (!(obj instanceof ObjectValue)) throw EngineException.ofType("Property apply target must be an object."); - ((ObjectValue)obj).defineProperty(ext, name, (FunctionValue)getter, (FunctionValue)setter, false, false); + FunctionValue getter, setter; + + if (getterVal == VoidValue.UNDEFINED) getter = null; + else if (getterVal instanceof FunctionValue) getter = (FunctionValue)getterVal; + else throw EngineException.ofType("Getter must be a function or undefined."); + + if (setterVal == VoidValue.UNDEFINED) setter = null; + else if (setterVal instanceof FunctionValue) setter = (FunctionValue)setterVal; + else throw EngineException.ofType("Setter must be a function or undefined."); + + obj.defineOwnMember(env, name, new PropertyMember(getter, setter, true, true)); frame.push(obj); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execKeys(Environment ext, Instruction instr, Frame frame) { + private static Value execKeys(Environment env, Instruction instr, Frame frame) { var val = frame.pop(); - var members = Values.getMembers(ext, val, false, false); + var members = new ArrayList<>(val.getMembers(env, false, true).keySet()); Collections.reverse(members); frame.push(null); for (var el : members) { - if (el instanceof Symbol) continue; var obj = new ObjectValue(); - obj.defineProperty(ext, "value", el); + obj.defineOwnMember(env, new StringValue("value"), FieldMember.of(new StringValue(el))); frame.push(obj); } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execTryStart(Environment ext, Instruction instr, Frame frame) { + private static Value execTryStart(Environment env, Instruction instr, Frame frame) { int start = frame.codePtr + 1; int catchStart = (int)instr.get(0); int finallyStart = (int)instr.get(1); @@ -95,14 +107,14 @@ public class InstructionRunner { int end = (int)instr.get(2) + start; frame.addTry(start, end, catchStart, finallyStart); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execTryEnd(Environment ext, Instruction instr, Frame frame) { + private static Value execTryEnd(Environment env, Instruction instr, Frame frame) { frame.popTryFlag = true; - return Values.NO_RETURN; + return null; } - private static Object execDup(Environment ext, Instruction instr, Frame frame) { + private static Value execDup(Environment env, Instruction instr, Frame frame) { int count = instr.get(0); for (var i = 0; i < count; i++) { @@ -110,45 +122,48 @@ public class InstructionRunner { } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadValue(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadValue(Environment env, Instruction instr, Frame frame) { switch (instr.type) { - case PUSH_UNDEFINED: frame.push(null); break; - case PUSH_NULL: frame.push(Values.NULL); break; - default: frame.push(instr.get(0)); break; + case PUSH_UNDEFINED: frame.push(VoidValue.UNDEFINED); break; + case PUSH_NULL: frame.push(VoidValue.NULL); break; + case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); break; + case PUSH_NUMBER: frame.push(new NumberValue(instr.get(0))); break; + case PUSH_STRING: frame.push(new StringValue(instr.get(0))); break; + default: } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadVar(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadVar(Environment env, Instruction instr, Frame frame) { var i = instr.get(0); - if (i instanceof String) frame.push(GlobalScope.get(ext).get(ext, (String)i)); - else frame.push(frame.scope.get((int)i).get(ext)); + if (i instanceof String) frame.push(GlobalScope.get(env).get(env, (String)i)); + else frame.push(frame.scope.get((int)i).get(env)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadObj(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadObj(Environment env, Instruction instr, Frame frame) { frame.push(new ObjectValue()); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadGlob(Environment ext, Instruction instr, Frame frame) { - frame.push(GlobalScope.get(ext).obj); + private static Value execLoadGlob(Environment env, Instruction instr, Frame frame) { + frame.push(GlobalScope.get(env).object); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadArr(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadArr(Environment env, Instruction instr, Frame frame) { var res = new ArrayValue(); res.setSize(instr.get(0)); frame.push(res); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadFunc(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { int id = instr.get(0); var captures = new ValueVariable[instr.params.length - 1]; @@ -156,174 +171,175 @@ public class InstructionRunner { captures[i - 1] = frame.scope.get(instr.get(i)); } - var func = new CodeFunction(ext, "", frame.function.body.children[id], captures); + var func = new CodeFunction(env, "", frame.function.body.children[id], captures); frame.push(func); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadMember(Environment ext, Instruction instr, Frame frame) { + private static Value execLoadMember(Environment env, Instruction instr, Frame frame) { var key = frame.pop(); var obj = frame.pop(); try { - frame.push(Values.getMember(ext, obj, key)); + frame.push(obj.getMember(env, key)); } catch (IllegalArgumentException e) { throw EngineException.ofType(e.getMessage()); } frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execLoadRegEx(Environment ext, Instruction instr, Frame frame) { - if (ext.hasNotNull(Environment.REGEX_CONSTR)) { - frame.push(Values.callNew(ext, ext.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1))); + private static Value execLoadRegEx(Environment env, Instruction instr, Frame frame) { + if (env.hasNotNull(Environment.REGEX_CONSTR)) { + frame.push(env.get(Environment.REGEX_CONSTR).callNew(env, instr.get(0), instr.get(1))); } else { throw EngineException.ofSyntax("Regex is not supported."); } + frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execDiscard(Environment ext, Instruction instr, Frame frame) { + private static Value execDiscard(Environment env, Instruction instr, Frame frame) { frame.pop(); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execStoreMember(Environment ext, Instruction instr, Frame frame) { + private static Value execStoreMember(Environment env, Instruction instr, Frame frame) { var val = frame.pop(); var key = frame.pop(); var obj = frame.pop(); - if (!Values.setMember(ext, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'."); + if (!obj.setMember(env, key, val)) throw EngineException.ofSyntax("Can't set member '" + key.toReadable(env) + "'."); if ((boolean)instr.get(0)) frame.push(val); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execStoreVar(Environment ext, Instruction instr, Frame frame) { + private static Value execStoreVar(Environment env, Instruction instr, Frame frame) { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var i = instr.get(0); - if (i instanceof String) GlobalScope.get(ext).set(ext, (String)i, val); - else frame.scope.get((int)i).set(ext, val); + if (i instanceof String) GlobalScope.get(env).set(env, (String)i, val); + else frame.scope.get((int)i).set(env, val); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execStoreSelfFunc(Environment ext, Instruction instr, Frame frame) { - frame.scope.locals[(int)instr.get(0)].set(ext, frame.function); + private static Value execStoreSelfFunc(Environment env, Instruction instr, Frame frame) { + frame.scope.locals[(int)instr.get(0)].set(env, frame.function); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execJmp(Environment ext, Instruction instr, Frame frame) { + private static Value execJmp(Environment env, Instruction instr, Frame frame) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; - return Values.NO_RETURN; + return null; } - private static Object execJmpIf(Environment ext, Instruction instr, Frame frame) { - if (Values.toBoolean(frame.pop())) { + private static Value execJmpIf(Environment env, Instruction instr, Frame frame) { + if (frame.pop().toBoolean().value) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; } else frame.codePtr ++; - return Values.NO_RETURN; + return null; } - private static Object execJmpIfNot(Environment ext, Instruction instr, Frame frame) { - if (Values.not(frame.pop())) { + private static Value execJmpIfNot(Environment env, Instruction instr, Frame frame) { + if (frame.pop().not().value) { frame.codePtr += (int)instr.get(0); frame.jumpFlag = true; } else frame.codePtr ++; - return Values.NO_RETURN; + return null; } - private static Object execTypeof(Environment ext, Instruction instr, Frame frame) { + private static Value execTypeof(Environment env, Instruction instr, Frame frame) { String name = instr.get(0); - Object obj; + Value obj; if (name != null) { - if (GlobalScope.get(ext).has(ext, name)) { - obj = GlobalScope.get(ext).get(ext, name); + if (GlobalScope.get(env).has(env, name)) { + obj = GlobalScope.get(env).get(env, name); } else obj = null; } else obj = frame.pop(); - frame.push(Values.type(obj)); + frame.push(obj.type()); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execNop(Environment ext, Instruction instr, Frame frame) { + private static Value execNop(Environment env, Instruction instr, Frame frame) { frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execDelete(Environment ext, Instruction instr, Frame frame) { + private static Value execDelete(Environment env, Instruction instr, Frame frame) { var key = frame.pop(); var val = frame.pop(); - if (!Values.deleteMember(ext, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); + if (!val.deleteMember(env, key)) throw EngineException.ofSyntax("Can't delete member '" + key.toReadable(env) + "'."); frame.codePtr++; - return Values.NO_RETURN; + return null; } - private static Object execOperation(Environment ext, Instruction instr, Frame frame) { + private static Value execOperation(Environment env, Instruction instr, Frame frame) { Operation op = instr.get(0); - var args = new Object[op.operands]; + var args = new Value[op.operands]; for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); - frame.push(Values.operation(ext, op, args)); + frame.push(Value.operation(env, op, args)); frame.codePtr++; - return Values.NO_RETURN; + return null; } - public static Object exec(Environment ext, Instruction instr, Frame frame) { + public static Value exec(Environment env, Instruction instr, Frame frame) { switch (instr.type) { - case NOP: return execNop(ext, instr, frame); - case RETURN: return execReturn(ext, instr, frame); - case THROW: return execThrow(ext, instr, frame); - case THROW_SYNTAX: return execThrowSyntax(ext, instr, frame); - case CALL: return execCall(ext, instr, frame); - case CALL_NEW: return execCallNew(ext, instr, frame); - case TRY_START: return execTryStart(ext, instr, frame); - case TRY_END: return execTryEnd(ext, instr, frame); + case NOP: return execNop(env, instr, frame); + case RETURN: return execReturn(env, instr, frame); + case THROW: return execThrow(env, instr, frame); + case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); + case CALL: return execCall(env, instr, frame); + case CALL_NEW: return execCallNew(env, instr, frame); + case TRY_START: return execTryStart(env, instr, frame); + case TRY_END: return execTryEnd(env, instr, frame); - case DUP: return execDup(ext, instr, frame); + case DUP: return execDup(env, instr, frame); case PUSH_UNDEFINED: case PUSH_NULL: case PUSH_STRING: case PUSH_NUMBER: case PUSH_BOOL: - return execLoadValue(ext, instr, frame); - case LOAD_VAR: return execLoadVar(ext, instr, frame); - case LOAD_OBJ: return execLoadObj(ext, instr, frame); - case LOAD_ARR: return execLoadArr(ext, instr, frame); - case LOAD_FUNC: return execLoadFunc(ext, instr, frame); - case LOAD_MEMBER: return execLoadMember(ext, instr, frame); - case LOAD_REGEX: return execLoadRegEx(ext, instr, frame); - case LOAD_GLOB: return execLoadGlob(ext, instr, frame); + return execLoadValue(env, instr, frame); + case LOAD_VAR: return execLoadVar(env, instr, frame); + case LOAD_OBJ: return execLoadObj(env, instr, frame); + case LOAD_ARR: return execLoadArr(env, instr, frame); + case LOAD_FUNC: return execLoadFunc(env, instr, frame); + case LOAD_MEMBER: return execLoadMember(env, instr, frame); + case LOAD_REGEX: return execLoadRegEx(env, instr, frame); + case LOAD_GLOB: return execLoadGlob(env, instr, frame); - case DISCARD: return execDiscard(ext, instr, frame); - case STORE_MEMBER: return execStoreMember(ext, instr, frame); - case STORE_VAR: return execStoreVar(ext, instr, frame); - case STORE_SELF_FUNC: return execStoreSelfFunc(ext, instr, frame); - case MAKE_VAR: return execMakeVar(ext, instr, frame); + case DISCARD: return execDiscard(env, instr, frame); + case STORE_MEMBER: return execStoreMember(env, instr, frame); + case STORE_VAR: return execStoreVar(env, instr, frame); + case STORE_SELF_FUNC: return execStoreSelfFunc(env, instr, frame); + case MAKE_VAR: return execMakeVar(env, instr, frame); - case KEYS: return execKeys(ext, instr, frame); - case DEF_PROP: return execDefProp(ext, instr, frame); - case TYPEOF: return execTypeof(ext, instr, frame); - case DELETE: return execDelete(ext, instr, frame); + case KEYS: return execKeys(env, instr, frame); + case DEF_PROP: return execDefProp(env, instr, frame); + case TYPEOF: return execTypeof(env, instr, frame); + case DELETE: return execDelete(env, instr, frame); - case JMP: return execJmp(ext, instr, frame); - case JMP_IF: return execJmpIf(ext, instr, frame); - case JMP_IFN: return execJmpIfNot(ext, instr, frame); + case JMP: return execJmp(env, instr, frame); + case JMP_IF: return execJmpIf(env, instr, frame); + case JMP_IFN: return execJmpIfNot(env, instr, frame); - case OPERATION: return execOperation(ext, instr, frame); + case OPERATION: return execOperation(env, instr, frame); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } diff --git a/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java new file mode 100644 index 0000000..84b390a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/JSONConverter.java @@ -0,0 +1,86 @@ +package me.topchetoeu.jscript.runtime; + +import java.util.HashSet; +import java.util.stream.Collectors; + +import me.topchetoeu.jscript.common.json.JSONElement; +import me.topchetoeu.jscript.common.json.JSONList; +import me.topchetoeu.jscript.common.json.JSONMap; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +public class JSONConverter { + public static Value toJs(JSONElement val) { + if (val.isBoolean()) return BoolValue.of(val.bool()); + if (val.isString()) return new StringValue(val.string()); + if (val.isNumber()) return new NumberValue(val.number()); + if (val.isList()) return ArrayValue.of(val.list().stream().map(JSONConverter::toJs).collect(Collectors.toList())); + if (val.isMap()) { + var res = new ObjectValue(); + + for (var el : val.map().entrySet()) { + res.defineOwnMember(null, new StringValue(el.getKey()), FieldMember.of(toJs(el.getValue()))); + } + + return res; + } + if (val.isNull()) return VoidValue.NULL; + return VoidValue.UNDEFINED; + } + + public static JSONElement fromJs(Environment ext, Value val) { + var res = JSONConverter.fromJs(ext, val, new HashSet<>()); + if (res == null) return JSONElement.NULL; + else return res; + } + + public static JSONElement fromJs(Environment env, Value val, HashSet prev) { + if (val instanceof BoolValue) return JSONElement.bool(((BoolValue)val).value); + if (val instanceof NumberValue) return JSONElement.number(((NumberValue)val).value); + if (val instanceof StringValue) return JSONElement.string(((StringValue)val).value); + if (val == VoidValue.NULL) return JSONElement.NULL; + if (val instanceof VoidValue) return null; + + if (val instanceof ArrayValue) { + if (prev.contains(val)) throw EngineException.ofError("Circular dependency in JSON."); + prev.add(val); + + var res = new JSONList(); + + for (var el : ((ArrayValue)val).toArray()) { + var jsonEl = fromJs(env, el, prev); + if (jsonEl == null) continue; + res.add(jsonEl); + } + + prev.remove(val); + return JSONElement.of(res); + } + if (val instanceof ObjectValue) { + if (prev.contains(val)) throw EngineException.ofError("Circular dependency in JSON."); + prev.add(val); + + var res = new JSONMap(); + + for (var el : val.getMembers(env, true, true).entrySet()) { + var jsonEl = fromJs(env, el.getValue().get(env, val), prev); + if (jsonEl == null) continue; + res.put(el.getKey(), jsonEl); + } + + prev.remove(val); + return JSONElement.of(res); + } + if (val == null) return null; + return null; + } + +} diff --git a/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java new file mode 100644 index 0000000..6cf802b --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -0,0 +1,136 @@ +package me.topchetoeu.jscript.runtime; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import me.topchetoeu.jscript.common.Compiler; +import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.Metadata; +import me.topchetoeu.jscript.common.Reading; +import me.topchetoeu.jscript.compilation.parsing.Parsing; +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.exceptions.InterruptException; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; +import me.topchetoeu.jscript.runtime.scope.GlobalScope; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; + +public class SimpleRepl { + static Thread engineTask, debugTask; + static Engine engine = new Engine(); + static Environment environment = Environment.empty(); + + static int j = 0; + static String[] args; + + private static void reader() { + try { + for (var arg : args) { + try { + var file = Path.of(arg); + var raw = Files.readString(file); + var res = engine.pushMsg( + false, environment, + Filename.fromFile(file.toFile()), + raw, null + ).await(); + + System.err.println(res.toReadable(environment)); + } + catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } + } + + for (var i = 0; ; i++) { + try { + var raw = Reading.readline(); + + if (raw == null) break; + var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); + var res = engine.pushMsg(false, environment, func, null).await(); + System.err.println(res.toReadable(environment)); + } + catch (EngineException | SyntaxException e) { System.err.println(Value.errorToReadable(e, null)); } + } + } + catch (IOException e) { + System.out.println(e.toString()); + engine.thread().interrupt(); + } + catch (RuntimeException ex) { + if (ex instanceof InterruptException) return; + else { + System.out.println("Internal error ocurred:"); + ex.printStackTrace(); + } + } + } + + private static void initEnv() { + var glob = GlobalScope.get(environment); + + glob.define(null, false, new NativeFunction("exit", args -> { + throw new InterruptException(); + })); + // glob.define(null, false, new NativeFunction("go", args -> { + // try { + // var f = Path.of("do.js"); + // var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); + // return func.call(args.env); + // } + // catch (IOException e) { + // throw new EngineException("Couldn't open do.js"); + // } + // })); + glob.define(null, false, new NativeFunction("log", args -> { + for (var el : args.args) { + System.out.print(el.toReadable(args.env)); + } + + return null; + })); + + // var fs = new RootFilesystem(PermissionsProvider.get(environment)); + // fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); + // fs.protocols.put("file", new PhysicalFilesystem(".")); + // fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err)); + + // environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); + // environment.add(Filesystem.KEY, fs); + // environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); + // environment.add(Compiler.KEY, new JSCompiler(environment)); + environment.add(EventLoop.KEY, engine); + environment.add(GlobalScope.KEY, new GlobalScope()); + // environment.add(EventLoop.KEY, engine); + environment.add(Compiler.KEY, (filename, source) -> { + return Parsing.compile(filename, source).body(); + }); + } + private static void initEngine() { + // var ctx = new DebugContext(); + // environment.add(DebugContext.KEY, ctx); + + // debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx)); + engineTask = engine.start(); + // debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); + } + + public static void main(String args[]) throws InterruptedException { + System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); + + SimpleRepl.args = args; + var reader = new Thread(SimpleRepl::reader); + + initEnv(); + initEngine(); + + reader.setDaemon(true); + reader.setName("STD Reader"); + reader.start(); + + engine.thread().join(); + debugTask.interrupt(); + engineTask.interrupt(); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java index ac0555b..f97fa15 100644 --- a/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java +++ b/src/java/me/topchetoeu/jscript/runtime/WrapperProvider.java @@ -1,8 +1,8 @@ package me.topchetoeu.jscript.runtime; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public interface WrapperProvider { public ObjectValue getProto(Class obj); diff --git a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java index bb9be07..a1c5e26 100644 --- a/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/runtime/debug/DebugContext.java @@ -14,8 +14,8 @@ import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.CodeFunction; -import me.topchetoeu.jscript.runtime.values.FunctionValue; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; public class DebugContext { public static final Key KEY = new Key<>(); @@ -71,6 +71,7 @@ public class DebugContext { return getMapOrEmpty(((CodeFunction)func).body); } public List getStackFrames() { + if (debugger == null) return List.of(); return this.debugger.getStackFrame(); } diff --git a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java index 24c246e..11e10e6 100644 --- a/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java +++ b/src/java/me/topchetoeu/jscript/runtime/environment/Environment.java @@ -7,9 +7,9 @@ import java.util.Random; import java.util.Set; import java.util.function.Supplier; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; +import me.topchetoeu.jscript.common.Compiler; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public class Environment { public static final Key COMPILE_FUNC = new Key<>(); diff --git a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java index 15f0aed..975db01 100644 --- a/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java +++ b/src/java/me/topchetoeu/jscript/runtime/exceptions/EngineException.java @@ -5,9 +5,11 @@ import java.util.List; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.runtime.values.ObjectValue.PlaceholderProto; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue.PrototypeProvider; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; public class EngineException extends RuntimeException { public static class StackElement { @@ -40,7 +42,7 @@ public class EngineException extends RuntimeException { } } - public final Object value; + public final Value value; public EngineException cause; public Environment env = null; public final List stackTrace = new ArrayList<>(); @@ -61,10 +63,10 @@ public class EngineException extends RuntimeException { return this; } - public String toString(Environment ext) { + public String toString(Environment env) { var ss = new StringBuilder(); try { - ss.append(Values.toString(ext, value)).append('\n'); + ss.append(value.toString(env)).append('\n'); } catch (EngineException e) { ss.append("[Error while stringifying]\n"); @@ -72,38 +74,40 @@ public class EngineException extends RuntimeException { for (var line : stackTrace) { if (line.visible()) ss.append(" ").append(line.toString()).append("\n"); } - if (cause != null) ss.append("Caused by ").append(cause.toString(ext)).append('\n'); + if (cause != null) ss.append("Caused by ").append(cause.toString(env)).append('\n'); ss.deleteCharAt(ss.length() - 1); return ss.toString(); } - private static Object err(String name, String msg, PlaceholderProto proto) { - var res = new ObjectValue(proto); - if (name != null) res.defineProperty(null, "name", name); - res.defineProperty(null, "message", msg); + private static ObjectValue err(String name, String msg, PrototypeProvider proto) { + var res = new ObjectValue(); + res.setPrototype(proto); + + if (name != null) res.defineOwnMember(Environment.empty(), new StringValue("name"), FieldMember.of(new StringValue(name))); + res.defineOwnMember(Environment.empty(), new StringValue("message"), FieldMember.of(new StringValue(msg))); return res; } - public EngineException(Object error) { - super(error == null ? "null" : error.toString()); + public EngineException(Value error) { + super(error.toReadable(Environment.empty())); this.value = error; this.cause = null; } public static EngineException ofError(String name, String msg) { - return new EngineException(err(name, msg, PlaceholderProto.ERROR)); + return new EngineException(err(name, msg, env -> env.get(Environment.ERROR_PROTO))); } public static EngineException ofError(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.ERROR_PROTO))); } public static EngineException ofSyntax(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.SYNTAX_ERR_PROTO))); } public static EngineException ofType(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.TYPE_ERR_PROTO))); } public static EngineException ofRange(String msg) { - return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR)); + return new EngineException(err(null, msg, env -> env.get(Environment.RANGE_ERR_PROTO))); } } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java index eb26e61..a571bac 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/GlobalScope.java @@ -6,55 +6,48 @@ import java.util.Set; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.environment.Key; import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; public class GlobalScope { public static final Key KEY = new Key<>(); - public final ObjectValue obj; + public final ObjectValue object; public boolean has(Environment ext, String name) { - return Values.hasMember(ext, obj, name, false); + return object.hasMember(ext, new StringValue(name), false); } public GlobalScope child() { - var obj = new ObjectValue(); - Values.setPrototype(null, obj, this.obj); - return new GlobalScope(obj); + var res = new GlobalScope(); + res.object.setPrototype(null, this.object); + return res; } - public Object define(Environment ext, String name) { - if (Values.hasMember(ext, obj, name, false)) return name; - obj.defineProperty(ext, name, null); - return name; + public void define(Environment ext, String name, Variable variable) { + object.defineOwnMember(ext, new StringValue(name), variable.toField(true, true)); } - public void define(Environment ext, String name, Variable val) { - obj.defineProperty(ext, name, - new NativeFunction("get " + name, args -> val.get(args.env)), - new NativeFunction("set " + name, args -> { val.set(args.env, args.get(0)); return null; }), - true, true - ); + public void define(Environment ext, boolean readonly, String name, Value val) { + object.defineOwnMember(ext, new StringValue(name), FieldMember.of(val, !readonly)); } - public void define(Environment ext, String name, boolean readonly, Object val) { - obj.defineProperty(ext, name, val, readonly, true, true); - } - public void define(Environment ext, String ...names) { - for (var n : names) define(ext, n); + public void define(Environment ext, boolean readonly, String ...names) { + for (var name : names) define(ext, name, new ValueVariable(readonly, VoidValue.UNDEFINED)); } public void define(Environment ext, boolean readonly, FunctionValue val) { - define(ext, val.name, readonly, val); + define(ext, readonly, val.name, val); } - public Object get(Environment ext, String name) { - if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - else return Values.getMember(ext, obj, name); + public Value get(Environment env, String name) { + if (!object.hasMember(env, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); + else return object.getMember(env, new StringValue(name)); } - public void set(Environment ext, String name, Object val) { - if (!Values.hasMember(ext, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); - if (!Values.setMember(ext, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly."); + public void set(Environment ext, String name, Value val) { + if (!object.hasMember(ext, new StringValue(name), false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist."); + if (!object.setMember(ext, new StringValue(name), val)) throw EngineException.ofSyntax("The global '" + name + "' is read-only."); } public Set keys() { @@ -68,10 +61,11 @@ public class GlobalScope { } public GlobalScope() { - this.obj = new ObjectValue(); + this.object = new ObjectValue(); + this.object.setPrototype(null, null); } public GlobalScope(ObjectValue val) { - this.obj = val; + this.object = val; } public static GlobalScope get(Environment ext) { diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java index 60b30fb..a4b4a43 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/ValueVariable.java @@ -1,27 +1,24 @@ package me.topchetoeu.jscript.runtime.scope; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.Values; +import me.topchetoeu.jscript.runtime.values.Value; public class ValueVariable implements Variable { public boolean readonly; - public Object value; + public Value value; - @Override - public boolean readonly() { return readonly; } + @Override public boolean readonly() { return readonly; } + @Override public final Value get(Environment env) { return get(); } + @Override public final boolean set(Environment env, Value val) { return set(val); } - @Override - public Object get(Environment ext) { - return value; + public Value get() { return value; } + public boolean set(Value val) { + if (readonly) return false; + this.value = val; + return true; } - @Override - public void set(Environment ext, Object val) { - if (readonly) return; - this.value = Values.normalize(ext, val); - } - - public ValueVariable(boolean readonly, Object val) { + public ValueVariable(boolean readonly, Value val) { this.readonly = readonly; this.value = val; } diff --git a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java index db5fb89..85f5f5b 100644 --- a/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java +++ b/src/java/me/topchetoeu/jscript/runtime/scope/Variable.java @@ -1,9 +1,24 @@ package me.topchetoeu.jscript.runtime.scope; import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; public interface Variable { - Object get(Environment ext); + Value get(Environment env); default boolean readonly() { return true; } - default void set(Environment ext, Object val) { } + default boolean set(Environment env, Value val) { return false; } + + default FieldMember toField(boolean configurable, boolean enumerable) { + var self = this; + + return new FieldMember(!readonly(), configurable, enumerable) { + @Override public Value get(Environment env, Value _self) { + return self.get(env); + } + @Override public boolean set(Environment env, Value val, Value _self) { + return self.set(env, val); + } + }; + } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java deleted file mode 100644 index 0214505..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ArrayValue.java +++ /dev/null @@ -1,227 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -// TODO: Make methods generic -public class ArrayValue extends ObjectValue implements Iterable { - private static final Object UNDEFINED = new Object(); - private Object[] values; - private int size; - - private Object[] alloc(int index) { - index++; - if (index < values.length) return values; - if (index < values.length * 2) index = values.length * 2; - - var arr = new Object[index]; - System.arraycopy(values, 0, arr, 0, values.length); - return arr; - } - - public int size() { return size; } - public boolean setSize(int val) { - if (val < 0) return false; - if (size > val) shrink(size - val); - else { - values = alloc(val); - size = val; - } - return true; - } - - public Object get(int i) { - if (i < 0 || i >= size) return null; - var res = values[i]; - if (res == UNDEFINED) return null; - else return res; - } - public void set(Environment ext, int i, Object val) { - if (i < 0) return; - - values = alloc(i); - - val = Values.normalize(ext, val); - if (val == null) val = UNDEFINED; - values[i] = val; - if (i >= size) size = i + 1; - } - public boolean has(int i) { - return i >= 0 && i < size && values[i] != null; - } - public void remove(int i) { - if (i < 0 || i >= values.length) return; - values[i] = null; - } - public void shrink(int n) { - if (n >= values.length) { - values = new Object[16]; - size = 0; - } - else { - for (int i = 0; i < n; i++) { - values[--size] = null; - } - } - } - - public Object[] toArray() { - Object[] res = new Object[size]; - copyTo(res, 0, 0, size); - return res; - } - public void copyTo(Object[] arr, int sourceStart, int destStart, int count) { - for (var i = 0; i < count; i++) { - if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null; - if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null; - else arr[i + sourceStart] = values[i + destStart]; - } - } - public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) { - if (arr == this) { - move(sourceStart, destStart, count); - return; - } - - // Iterate in reverse to reallocate at most once - if (destStart + count > arr.size) arr.size = destStart + count; - - for (var i = count - 1; i >= 0; i--) { - if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart); - if (values[i + sourceStart] == UNDEFINED) arr.set(null, i + destStart, null); - else if (values[i + sourceStart] == null) arr.remove(i + destStart); - else arr.set(null, i + destStart, values[i + sourceStart]); - } - } - - public void copyFrom(Environment ext, Object[] arr, int sourceStart, int destStart, int count) { - for (var i = 0; i < count; i++) { - set(ext, i + destStart, arr[i + sourceStart]); - } - } - - public void move(int srcI, int dstI, int n) { - values = alloc(dstI + n); - - System.arraycopy(values, srcI, values, dstI, n); - - if (dstI + n >= size) size = dstI + n; - } - - public void sort(Comparator comparator) { - Arrays.sort(values, 0, size, (a, b) -> { - var _a = 0; - var _b = 0; - - if (a == UNDEFINED) _a = 1; - if (a == null) _a = 2; - - if (b == UNDEFINED) _b = 1; - if (b == null) _b = 2; - - if (_a != 0 || _b != 0) return Integer.compare(_a, _b); - - return comparator.compare(a, b); - }); - } - - @Override - protected Object getField(Environment ext, Object key) { - if (key instanceof Number) { - var i = ((Number)key).doubleValue(); - if (i >= 0 && i - Math.floor(i) == 0) { - return get((int)i); - } - } - - return super.getField(ext, key); - } - @Override - protected boolean setField(Environment ext, Object key, Object val) { - if (key instanceof Number) { - var i = Values.number(key); - if (i >= 0 && i - Math.floor(i) == 0) { - set(ext, (int)i, val); - return true; - } - } - - return super.setField(ext, key, val); - } - @Override - protected boolean hasField(Environment ext, Object key) { - if (key instanceof Number) { - var i = Values.number(key); - if (i >= 0 && i - Math.floor(i) == 0) { - return has((int)i); - } - } - - return super.hasField(ext, key); - } - @Override - protected void deleteField(Environment ext, Object key) { - if (key instanceof Number) { - var i = Values.number(key); - if (i >= 0 && i - Math.floor(i) == 0) { - remove((int)i); - return; - } - } - - super.deleteField(ext, key); - } - - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - for (var i = 0; i < size(); i++) { - if (has(i)) res.add(i); - } - return res; - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return i < size(); - } - @Override - public Object next() { - if (!hasNext()) return null; - return get(i++); - } - }; - } - - public ArrayValue() { - super(PlaceholderProto.ARRAY); - values = new Object[16]; - size = 0; - } - public ArrayValue(int cap) { - super(PlaceholderProto.ARRAY); - values = new Object[cap]; - size = 0; - } - public ArrayValue(Environment ext, Object ...values) { - this(); - this.values = new Object[values.length]; - size = values.length; - - for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ext, values[i]); - } - - public static ArrayValue of(Environment ext, Collection values) { - return new ArrayValue(ext, values.toArray(Object[]::new)); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java deleted file mode 100644 index 115f855..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/FunctionValue.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.List; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -public abstract class FunctionValue extends ObjectValue { - public String name = ""; - public int length; - - @Override - public String toString() { - return String.format("function %s(...)", name); - } - - public abstract Object call(Environment ext, Object thisArg, Object ...args); - public Object call(Environment ext) { - return call(ext, null); - } - - @Override - protected Object getField(Environment ext, Object key) { - if ("name".equals(key)) return name; - if ("length".equals(key)) return length; - return super.getField(ext, key); - } - @Override - protected boolean setField(Environment ext, Object key, Object val) { - if ("name".equals(key)) name = Values.toString(ext, val); - else if ("length".equals(key)) length = (int)Values.toNumber(ext, val); - else return super.setField(ext, key, val); - return true; - } - @Override - protected boolean hasField(Environment ext, Object key) { - if ("name".equals(key)) return true; - if ("length".equals(key)) return true; - return super.hasField(ext, key); - } - - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - if (includeNonEnumerable) { - res.add("name"); - res.add("length"); - } - return res; - } - - public FunctionValue(String name, int length) { - super(PlaceholderProto.FUNCTION); - - if (name == null) name = ""; - this.length = length; - this.name = name; - - nonConfigurableSet.add("name"); - nonEnumerableSet.add("name"); - nonWritableSet.add("length"); - nonConfigurableSet.add("length"); - nonEnumerableSet.add("length"); - - var proto = new ObjectValue(); - proto.defineProperty(null, "constructor", this, true, false, false); - this.defineProperty(null, "prototype", proto, true, false, false); - } -} - diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/java/me/topchetoeu/jscript/runtime/values/Member.java new file mode 100644 index 0000000..91de993 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/Member.java @@ -0,0 +1,132 @@ +package me.topchetoeu.jscript.runtime.values; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +public interface Member { + public static final class PropertyMember implements Member { + public final FunctionValue getter; + public final FunctionValue setter; + public final boolean configurable; + public final boolean enumerable; + + @Override public Value get(Environment env, Value self) { + if (getter != null) return getter.call(env, self); + else return VoidValue.UNDEFINED; + } + @Override public boolean set(Environment env, Value val, Value self) { + if (setter == null) return false; + setter.call(env, self, val); + return true; + } + + @Override public boolean configurable() { return configurable; } + @Override public boolean enumerable() { return enumerable; } + + @Override public boolean configure(Environment env, Member newMember, Value self) { + if (!(newMember instanceof PropertyMember)) return false; + var prop = (PropertyMember)newMember; + + if (prop.configurable != configurable) return false; + if (prop.enumerable != enumerable) return false; + + if (prop.getter == getter) return true; + if (prop.setter == setter) return true; + return false; + } + + @Override public ObjectValue descriptor(Environment env, Value self) { + var res = new ObjectValue(); + + if (getter == null) res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(VoidValue.UNDEFINED)); + else res.defineOwnMember(env, new StringValue("getter"), FieldMember.of(getter)); + + if (setter == null) res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(VoidValue.UNDEFINED)); + else res.defineOwnMember(env, new StringValue("setter"), FieldMember.of(setter)); + + res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); + res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); + return res; + } + + public PropertyMember(FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { + this.getter = getter; + this.setter = setter; + this.configurable = configurable; + this.enumerable = enumerable; + } + } + + public static abstract class FieldMember implements Member { + private static class SimpleFieldMember extends FieldMember { + public Value value; + + @Override public Value get(Environment env, Value self) { return value; } + @Override public boolean set(Environment env, Value val, Value self) { + if (!writable) return false; + value = val; + return true; + } + public SimpleFieldMember(Value value, boolean configurable, boolean enumerable, boolean writable) { + super(configurable, enumerable, writable); + this.value = value; + } + } + + public boolean configurable; + public boolean enumerable; + public boolean writable; + + @Override public final boolean configurable() { return configurable; } + @Override public final boolean enumerable() { return enumerable; } + @Override public final boolean configure(Environment env, Member newMember, Value self) { + if (!(newMember instanceof FieldMember)) return false; + var field = (FieldMember)newMember; + + if (field.configurable != configurable) return false; + if (field.enumerable != enumerable) return false; + if (!writable) return field.get(env, self).strictEquals(env, get(env, self)); + + set(env, field.get(env, self), self); + writable = field.writable; + return true; + } + + @Override public ObjectValue descriptor(Environment env, Value self) { + var res = new ObjectValue(); + res.defineOwnMember(env, new StringValue("value"), FieldMember.of(get(env, self))); + res.defineOwnMember(env, new StringValue("writable"), FieldMember.of(BoolValue.of(writable))); + res.defineOwnMember(env, new StringValue("enumerable"), FieldMember.of(BoolValue.of(enumerable))); + res.defineOwnMember(env, new StringValue("configurable"), FieldMember.of(BoolValue.of(configurable))); + return res; + } + + public FieldMember(boolean configurable, boolean enumerable, boolean writable) { + this.configurable = configurable; + this.enumerable = enumerable; + this.writable = writable; + } + + public static FieldMember of(Value value) { + return new SimpleFieldMember(value, true, true, true); + } + public static FieldMember of(Value value, boolean writable) { + return new SimpleFieldMember(value, true, true, writable); + } + public static FieldMember of(Value value, boolean configurable, boolean enumerable, boolean writable) { + return new SimpleFieldMember(value, configurable, enumerable, writable); + } + } + + public boolean configurable(); + public boolean enumerable(); + public boolean configure(Environment env, Member newMember, Value self); + public ObjectValue descriptor(Environment env, Value self); + + public Value get(Environment env, Value self); + public boolean set(Environment env, Value val, Value self); +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java b/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java deleted file mode 100644 index c2fbf55..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeWrapper.java +++ /dev/null @@ -1,77 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.WeakHashMap; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; - -public class NativeWrapper extends ObjectValue { - private static class MapKey { - public final Object key; - - @Override - public int hashCode() { - return System.identityHashCode(key); - } - @Override - public boolean equals(Object obj) { - if (this == null || obj == null) return this == null && obj == null; - if (!(obj instanceof MapKey)) return false; - var other = (MapKey)obj; - - return other.key == key; - } - - public MapKey(Object key) { - this.key = key; - } - } - - private static final Key> WRAPPER_MAP = new Key<>(); - private static final Object NATIVE_PROTO = new Object(); - public final Object wrapped; - - @Override - public ObjectValue getPrototype(Environment ext) { - if (ext != null && prototype == NATIVE_PROTO) { - var clazz = wrapped.getClass(); - var res = NativeWrapperProvider.get(ext).getProto(clazz); - if (res != null) return res; - } - return super.getPrototype(ext); - } - - @Override - public String toString() { - return wrapped.toString(); - } - @Override - public boolean equals(Object obj) { - return wrapped.equals(obj); - } - @Override - public int hashCode() { - return wrapped.hashCode(); - } - - private NativeWrapper(Object wrapped) { - this.wrapped = wrapped; - prototype = NATIVE_PROTO; - } - - public static NativeWrapper of(Environment env, Object wrapped) { - if (env == null) return new NativeWrapper(wrapped); - - var wrappers = env.get(WRAPPER_MAP); - if (wrappers == null) return new NativeWrapper(wrapped); - - var key = new MapKey(wrapped); - if (wrappers.containsKey(key)) return wrappers.get(key); - - var res = new NativeWrapper(wrapped); - wrappers.put(key, res); - - return res; - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java deleted file mode 100644 index aabb1f1..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ObjectValue.java +++ /dev/null @@ -1,353 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -public class ObjectValue { - public static enum PlaceholderProto { - NONE, - OBJECT, - ARRAY, - FUNCTION, - ERROR, - SYNTAX_ERROR, - TYPE_ERROR, - RANGE_ERROR, - } - public static enum State { - NORMAL, - NO_EXTENSIONS, - SEALED, - FROZEN, - } - - public static class Property { - public final FunctionValue getter; - public final FunctionValue setter; - - public Property(FunctionValue getter, FunctionValue setter) { - this.getter = getter; - this.setter = setter; - } - } - - private static final Object OBJ_PROTO = new Object(); - private static final Object ARR_PROTO = new Object(); - private static final Object FUNC_PROTO = new Object(); - private static final Object ERR_PROTO = new Object(); - private static final Object SYNTAX_ERR_PROTO = new Object(); - private static final Object TYPE_ERR_PROTO = new Object(); - private static final Object RANGE_ERR_PROTO = new Object(); - - protected Object prototype; - - public State state = State.NORMAL; - public LinkedHashMap values = new LinkedHashMap<>(); - public LinkedHashMap properties = new LinkedHashMap<>(); - public LinkedHashSet nonWritableSet = new LinkedHashSet<>(); - public LinkedHashSet nonConfigurableSet = new LinkedHashSet<>(); - public LinkedHashSet nonEnumerableSet = new LinkedHashSet<>(); - - private Property getProperty(Environment env, Object key) { - if (properties.containsKey(key)) return properties.get(key); - var proto = getPrototype(env); - if (proto != null) return proto.getProperty(env, key); - else return null; - } - - public final boolean memberWritable(Object key) { - if (state == State.FROZEN) return false; - return !values.containsKey(key) || !nonWritableSet.contains(key); - } - public final boolean memberConfigurable(Object key) { - if (state == State.SEALED || state == State.FROZEN) return false; - return !nonConfigurableSet.contains(key); - } - public final boolean memberEnumerable(Object key) { - return !nonEnumerableSet.contains(key); - } - public final boolean extensible() { - return state == State.NORMAL; - } - - public final void preventExtensions() { - if (state == State.NORMAL) state = State.NO_EXTENSIONS; - } - public final void seal() { - if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED; - } - public final void freeze() { - state = State.FROZEN; - } - - public final boolean defineProperty(Environment env, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) { - key = Values.normalize(env, key); val = Values.normalize(env, val); - boolean reconfigured = - writable != memberWritable(key) || - configurable != memberConfigurable(key) || - enumerable != memberEnumerable(key); - - if (!reconfigured) { - if (!memberWritable(key)) { - var a = values.get(key); - var b = val; - if (a == null || b == null) return a == null && b == null; - return a == b || a.equals(b); - } - values.put(key, val); - return true; - } - - if ( - properties.containsKey(key) && - values.get(key) == val && - !reconfigured - ) return true; - - if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false; - if (!memberConfigurable(key)) return false; - - nonWritableSet.remove(key); - nonEnumerableSet.remove(key); - properties.remove(key); - values.remove(key); - - if (!writable) nonWritableSet.add(key); - if (!configurable) nonConfigurableSet.add(key); - if (!enumerable) nonEnumerableSet.add(key); - - values.put(key, val); - return true; - } - public final boolean defineProperty(Environment env, Object key, Object val) { - return defineProperty(env, key, val, true, true, true); - } - public final boolean defineProperty(Environment env, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) { - key = Values.normalize(env, key); - if ( - properties.containsKey(key) && - properties.get(key).getter == getter && - properties.get(key).setter == setter && - !configurable == nonConfigurableSet.contains(key) && - !enumerable == nonEnumerableSet.contains(key) - ) return true; - if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false; - if (!memberConfigurable(key)) return false; - - nonWritableSet.remove(key); - nonEnumerableSet.remove(key); - properties.remove(key); - values.remove(key); - - if (!configurable) nonConfigurableSet.add(key); - if (!enumerable) nonEnumerableSet.add(key); - - properties.put(key, new Property(getter, setter)); - return true; - } - - public ObjectValue getPrototype(Environment env) { - if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype; - - try { - if (prototype == ARR_PROTO) return env.get(Environment.ARRAY_PROTO); - if (prototype == FUNC_PROTO) return env.get(Environment.FUNCTION_PROTO); - if (prototype == ERR_PROTO) return env.get(Environment.ERROR_PROTO); - if (prototype == RANGE_ERR_PROTO) return env.get(Environment.RANGE_ERR_PROTO); - if (prototype == SYNTAX_ERR_PROTO) return env.get(Environment.SYNTAX_ERR_PROTO); - if (prototype == TYPE_ERR_PROTO) return env.get(Environment.TYPE_ERR_PROTO); - return env.get(Environment.OBJECT_PROTO); - } - catch (NullPointerException e) { return null; } - } - public final boolean setPrototype(PlaceholderProto val) { - if (!extensible()) return false; - switch (val) { - case OBJECT: prototype = OBJ_PROTO; break; - case FUNCTION: prototype = FUNC_PROTO; break; - case ARRAY: prototype = ARR_PROTO; break; - case ERROR: prototype = ERR_PROTO; break; - case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break; - case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break; - case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break; - case NONE: prototype = null; break; - } - return true; - } - - /** - * A method, used to get the value of a field. If a property is bound to - * this key, but not a field, this method should return null. - */ - protected Object getField(Environment env, Object key) { - if (values.containsKey(key)) return values.get(key); - var proto = getPrototype(env); - if (proto != null) return proto.getField(env, key); - else return null; - } - /** - * Changes the value of a field, that is bound to the given key. If no field is - * bound to this key, a new field should be created with the given value - * @return Whether or not the operation was successful - */ - protected boolean setField(Environment env, Object key, Object val) { - if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) { - ((FunctionValue)val).name = Values.toString(env, key); - } - - values.put(key, val); - return true; - } - /** - * Deletes the field bound to the given key. - */ - protected void deleteField(Environment env, Object key) { - values.remove(key); - } - /** - * Returns whether or not there is a field bound to the given key. - * This must ignore properties - */ - protected boolean hasField(Environment env, Object key) { - return values.containsKey(key); - } - - public final Object getMember(Environment env, Object key, Object thisArg) { - key = Values.normalize(env, key); - - if ("__proto__".equals(key)) { - var res = getPrototype(env); - return res == null ? Values.NULL : res; - } - - var prop = getProperty(env, key); - - if (prop != null) { - if (prop.getter == null) return null; - else return prop.getter.call(env, Values.normalize(env, thisArg)); - } - else return getField(env, key); - } - public final boolean setMember(Environment env, Object key, Object val, Object thisArg, boolean onlyProps) { - key = Values.normalize(env, key); val = Values.normalize(env, val); - - var prop = getProperty(env, key); - if (prop != null) { - if (prop.setter == null) return false; - prop.setter.call(env, Values.normalize(env, thisArg), val); - return true; - } - else if (onlyProps) return false; - else if (!extensible() && !values.containsKey(key)) return false; - else if (key == null) { - values.put(key, val); - return true; - } - else if ("__proto__".equals(key)) return setPrototype(env, val); - else if (nonWritableSet.contains(key)) return false; - else return setField(env, key, val); - } - public final boolean hasMember(Environment env, Object key, boolean own) { - key = Values.normalize(env, key); - - if (key != null && "__proto__".equals(key)) return true; - if (hasField(env, key)) return true; - if (properties.containsKey(key)) return true; - if (own) return false; - var proto = getPrototype(env); - return proto != null && proto.hasMember(env, key, own); - } - public final boolean deleteMember(Environment env, Object key) { - key = Values.normalize(env, key); - - if (!memberConfigurable(key)) return false; - properties.remove(key); - nonWritableSet.remove(key); - nonEnumerableSet.remove(key); - deleteField(env, key); - return true; - } - public final boolean setPrototype(Environment env, Object val) { - val = Values.normalize(env, val); - - if (!extensible()) return false; - if (val == null || val == Values.NULL) { - prototype = null; - return true; - } - else if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - - if (env != null) { - if (obj == env.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO; - else if (obj == env.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO; - else if (obj == env.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO; - else if (obj == env.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO; - else if (obj == env.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO; - else if (obj == env.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO; - else if (obj == env.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO; - else prototype = obj; - } - else prototype = obj; - - return true; - } - return false; - } - - public final ObjectValue getMemberDescriptor(Environment env, Object key) { - key = Values.normalize(env, key); - - var prop = properties.get(key); - var res = new ObjectValue(); - - res.defineProperty(env, "configurable", memberConfigurable(key)); - res.defineProperty(env, "enumerable", memberEnumerable(key)); - - if (prop != null) { - res.defineProperty(env, "get", prop.getter); - res.defineProperty(env, "set", prop.setter); - } - else if (hasField(env, key)) { - res.defineProperty(env, "value", values.get(key)); - res.defineProperty(env, "writable", memberWritable(key)); - } - else return null; - return res; - } - - public List keys(boolean includeNonEnumerable) { - var res = new ArrayList(); - - for (var key : values.keySet()) { - if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue; - res.add(key); - } - for (var key : properties.keySet()) { - if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue; - res.add(key); - } - - return res; - } - - public ObjectValue(Environment env, Map values) { - this(PlaceholderProto.OBJECT); - for (var el : values.entrySet()) { - defineProperty(env, el.getKey(), el.getValue()); - } - } - public ObjectValue(PlaceholderProto proto) { - nonConfigurableSet.add("__proto__"); - nonEnumerableSet.add("__proto__"); - setPrototype(proto); - } - public ObjectValue() { - this(PlaceholderProto.OBJECT); - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java deleted file mode 100644 index 3abf2ee..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/ScopeValue.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.HashMap; -import java.util.List; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.scope.ValueVariable; - -public class ScopeValue extends ObjectValue { - public final ValueVariable[] variables; - public final HashMap names = new HashMap<>(); - - @Override - protected Object getField(Environment ext, Object key) { - if (names.containsKey(key)) return variables[names.get(key)].get(ext); - return super.getField(ext, key); - } - @Override - protected boolean setField(Environment ext, Object key, Object val) { - if (names.containsKey(key)) { - variables[names.get(key)].set(ext, val); - return true; - } - - var proto = getPrototype(ext); - if (proto != null && proto.hasMember(ext, key, false) && proto.setField(ext, key, val)) return true; - - return super.setField(ext, key, val); - } - @Override - protected void deleteField(Environment ext, Object key) { - if (names.containsKey(key)) return; - super.deleteField(ext, key); - } - @Override - protected boolean hasField(Environment ext, Object key) { - if (names.containsKey(key)) return true; - return super.hasField(ext, key); - } - @Override - public List keys(boolean includeNonEnumerable) { - var res = super.keys(includeNonEnumerable); - res.addAll(names.keySet()); - return res; - } - - public ScopeValue(ValueVariable[] variables, String[] names) { - this.variables = variables; - for (var i = 0; i < names.length && i < variables.length; i++) { - this.names.put(names[i], i); - this.nonConfigurableSet.add(names[i]); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Symbol.java b/src/java/me/topchetoeu/jscript/runtime/values/Symbol.java deleted file mode 100644 index efb8f32..0000000 --- a/src/java/me/topchetoeu/jscript/runtime/values/Symbol.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.topchetoeu.jscript.runtime.values; - -import java.util.HashMap; - -public final class Symbol { - private static final HashMap registry = new HashMap<>(); - - public final String value; - - public Symbol(String value) { - this.value = value; - } - - @Override - public String toString() { - if (value == null) return "Symbol"; - else return "@@" + value; - } - - public static Symbol get(String name) { - if (registry.containsKey(name)) return registry.get(name); - else { - var res = new Symbol(name); - registry.put(name, res); - return res; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/java/me/topchetoeu/jscript/runtime/values/Value.java index a240ec0..480fd1e 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -2,26 +2,34 @@ package me.topchetoeu.jscript.runtime.values; import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.lang.reflect.Array; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; import me.topchetoeu.jscript.common.Operation; -// import me.topchetoeu.jscript.lib.PromiseLib; +import me.topchetoeu.jscript.common.json.JSON; +import me.topchetoeu.jscript.common.json.JSONElement; +import me.topchetoeu.jscript.runtime.EventLoop; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.ConvertException; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; -public interface Value { +public abstract class Value { public static enum CompareResult { NOT_EQUAL, EQUAL, @@ -43,666 +51,490 @@ public interface Value { public static final Object NULL = new Object(); public static final Object NO_RETURN = new Object(); - public static double number(Object val) { - if (val instanceof Number) return ((Number)val).doubleValue(); - else return Double.NaN; + public abstract StringValue type(); + public abstract boolean isPrimitive(); + public abstract BoolValue toBoolean(); + + public final boolean isNaN() { + return this instanceof NumberValue && Double.isNaN(((NumberValue)this).value); } - @SuppressWarnings("unchecked") - public static T wrapper(Object val, Class clazz) { - if (isWrapper(val)) val = ((NativeWrapper)val).wrapped; - if (val != null && clazz.isInstance(val)) return (T)val; - else return null; - } - - public static String type(Object val) { - if (val == null) return "undefined"; - if (val instanceof String) return "string"; - if (val instanceof Number) return "number"; - if (val instanceof Boolean) return "boolean"; - if (val instanceof Symbol) return "symbol"; - if (val instanceof FunctionValue) return "function"; - return "object"; - } - - public boolean isPrimitive(); - public BooleanValue toBoolean(); - - public default Value call(Environment env, Value self, Value ...args) { + public Value call(Environment env, Value self, Value ...args) { throw EngineException.ofType("Tried to call a non-function value."); } - public default Value callNew(Environment env, Value ...args) { + public final Value callNew(Environment env, Value ...args) { var res = new ObjectValue(); + var proto = getMember(env, new StringValue("prototype")); - try { - var proto = Values.getMember(env, this, "prototype"); - setPrototype(env, res, proto); + if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); + else res.setPrototype(env, null); - var ret = this.call(env, res, args); + var ret = this.call(env, res, args); - if (!ret.isPrimitive()) return ret; - return res; - } - catch (IllegalArgumentException e) { - throw EngineException.ofType("Tried to call new on an invalid constructor."); + if (!ret.isPrimitive()) return ret; + return res; + } + + public abstract Value toPrimitive(Environment env); + public abstract NumberValue toNumber(Environment env); + public abstract StringValue toString(Environment env); + + public final int toInt(Environment env) { return (int)toNumber(env).value; } + public final long toLong(Environment env) { return (long)toNumber(env).value; } + + public Value add(Environment env, Value other) { + if (this instanceof StringValue || other instanceof StringValue) { + return new StringValue(this.toString(env).value + other.toString(env).value); } + else return new NumberValue(this.toNumber(env).value + other.toNumber(env).value); + } + public NumberValue subtract(Environment env, Value other) { + return new NumberValue(toNumber(env).value - other.toNumber(env).value); + } + public NumberValue multiply(Environment env, Value other) { + return new NumberValue(toNumber(env).value - other.toNumber(env).value); + } + public NumberValue divide(Environment env, Value other) { + return new NumberValue(toNumber(env).value / other.toNumber(env).value); + } + public NumberValue modulo(Environment env, Value other) { + return new NumberValue(toNumber(env).value % other.toNumber(env).value); + } + public NumberValue negative(Environment env) { + return new NumberValue(-toNumber(env).value); } - public default Value toPrimitive(Environment env, Value val) { - if (val.isPrimitive()) return val; - - if (env != null) { - var valueOf = getMember(env, val, "valueOf"); - - if (valueOf instanceof FunctionValue) { - var res = valueOf.call(env, val); - if (res.isPrimitive()) return res; - } - - var toString = getMember(env, val, "toString"); - if (toString instanceof FunctionValue) { - var res = toString.call(env, val); - if (res.isPrimitive()) return res; - } - } - - throw EngineException.ofType("Value couldn't be converted to a primitive."); + public NumberValue and(Environment env, Value other) { + return new NumberValue(this.toInt(env) & other.toInt(env)); } - public default NumberValue toNumber(Environment ext, Object obj) { - var val = this.toPrimitive(ext, obj, ConvertHint.VALUEOF); - - if (val instanceof NumberValue) return number(val); - if (val instanceof Boolean) return ((Boolean)val) ? 1 : 0; - if (val instanceof String) { - try { return Double.parseDouble((String)val); } - catch (NumberFormatException e) { return Double.NaN; } - } - return Double.NaN; + public NumberValue or(Environment env, Value other) { + return new NumberValue(this.toInt(env) | other.toInt(env)); } - public default StringValue toString(Environment ext, Object obj) { - var val = toPrimitive(ext, obj, ConvertHint.VALUEOF); - - if (val == null) return "undefined"; - if (val == NULL) return "null"; - - if (val instanceof Number) { - var d = number(val); - if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; - if (d == Double.POSITIVE_INFINITY) return "Infinity"; - if (Double.isNaN(d)) return "NaN"; - return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); - } - if (val instanceof Boolean) return (Boolean)val ? "true" : "false"; - if (val instanceof String) return (String)val; - if (val instanceof Symbol) return val.toString(); - - return "Unknown value"; + public NumberValue xor(Environment env, Value other) { + return new NumberValue(this.toInt(env) ^ other.toInt(env)); + } + public NumberValue bitwiseNot(Environment env) { + return new NumberValue(~this.toInt(env)); } - public static Object add(Environment ext, Object a, Object b) { - if (a instanceof String || b instanceof String) return toString(ext, a) + toString(ext, b); - else return toNumber(ext, a) + toNumber(ext, b); + public NumberValue shiftLeft(Environment env, Value other) { + return new NumberValue(this.toInt(env) << other.toInt(env)); } - public static double subtract(Environment ext, Object a, Object b) { - return toNumber(ext, a) - toNumber(ext, b); + public NumberValue shiftRight(Environment env, Value other) { + return new NumberValue(this.toInt(env) >> other.toInt(env)); } - public static double multiply(Environment ext, Object a, Object b) { - return toNumber(ext, a) * toNumber(ext, b); - } - public static double divide(Environment ext, Object a, Object b) { - return toNumber(ext, a) / toNumber(ext, b); - } - public static double modulo(Environment ext, Object a, Object b) { - return toNumber(ext, a) % toNumber(ext, b); - } - - public static double negative(Environment ext, Object obj) { - return -toNumber(ext, obj); + public NumberValue unsignedShiftRight(Environment env, Value other) { + long a = this.toInt(env); + long b = other.toInt(env); + + if (a < 0) a += 0x100000000l; + if (b < 0) b += 0x100000000l; + + return new NumberValue(a >>> b); } - public static int and(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) & (int)toNumber(ext, b); - } - public static int or(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) | (int)toNumber(ext, b); - } - public static int xor(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) ^ (int)toNumber(ext, b); - } - public static int bitwiseNot(Environment ext, Object obj) { - return ~(int)toNumber(ext, obj); + public CompareResult compare(Environment env, Value other) { + return toPrimitive(env).compare(env, other); } - public static int shiftLeft(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) << (int)toNumber(ext, b); - } - public static int shiftRight(Environment ext, Object a, Object b) { - return (int)toNumber(ext, a) >> (int)toNumber(ext, b); - } - public static long unsignedShiftRight(Environment ext, Object a, Object b) { - long _a = (long)toNumber(ext, a); - long _b = (long)toNumber(ext, b); - - if (_a < 0) _a += 0x100000000l; - if (_b < 0) _b += 0x100000000l; - return _a >>> _b; + public final BoolValue not() { + return toBoolean().value ? BoolValue.FALSE : BoolValue.TRUE; } - public static CompareResult compare(Environment ext, Object a, Object b) { - a = toPrimitive(ext, a, ConvertHint.VALUEOF); - b = toPrimitive(ext, b, ConvertHint.VALUEOF); - - if (a instanceof String && b instanceof String) CompareResult.from(((String)a).compareTo((String)b)); - - var _a = toNumber(ext, a); - var _b = toNumber(ext, b); - - if (Double.isNaN(_a) || Double.isNaN(_b)) return CompareResult.NOT_EQUAL; - - return CompareResult.from(Double.compare(_a, _b)); - } - - public static boolean not(Object obj) { - return !toBoolean(obj); - } - - public static boolean isInstanceOf(Environment ext, Object obj, Object proto) { - if (obj == null || obj == NULL || proto == null || proto == NULL) return false; - var val = getPrototype(ext, obj); - - while (val != null) { + public final boolean isInstanceOf(Environment env, Value proto) { + for (var val = getPrototype(env); val != null; val = getPrototype(env)) { if (val.equals(proto)) return true; - val = val.getPrototype(ext); } return false; } - public static Object operation(Environment ext, Operation op, Object ...args) { + public static Value operation(Environment env, Operation op, Value ...args) { switch (op) { - case ADD: return add(ext, args[0], args[1]); - case SUBTRACT: return subtract(ext, args[0], args[1]); - case DIVIDE: return divide(ext, args[0], args[1]); - case MULTIPLY: return multiply(ext, args[0], args[1]); - case MODULO: return modulo(ext, args[0], args[1]); + case ADD: return args[0].add(env, args[1]); + case SUBTRACT: return args[0].subtract(env, args[1]); + case DIVIDE: return args[0].divide(env, args[1]); + case MULTIPLY: return args[0].multiply(env, args[1]); + case MODULO: return args[0].modulo(env, args[1]); - case AND: return and(ext, args[0], args[1]); - case OR: return or(ext, args[0], args[1]); - case XOR: return xor(ext, args[0], args[1]); + case AND: return args[0].and(env, args[1]); + case OR: return args[0].or(env, args[1]); + case XOR: return args[0].xor(env, args[1]); - case EQUALS: return strictEquals(ext, args[0], args[1]); - case NOT_EQUALS: return !strictEquals(ext, args[0], args[1]); - case LOOSE_EQUALS: return looseEqual(ext, args[0], args[1]); - case LOOSE_NOT_EQUALS: return !looseEqual(ext, args[0], args[1]); + case EQUALS: return BoolValue.of(args[0].strictEquals(env, args[1])); + case NOT_EQUALS: return BoolValue.of(!args[0].strictEquals(env, args[1])); + case LOOSE_EQUALS: return BoolValue.of(args[0].looseEqual(env, args[1])); + case LOOSE_NOT_EQUALS: return BoolValue.of(!args[0].looseEqual(env, args[1])); - case GREATER: return compare(ext, args[0], args[1]).greater(); - case GREATER_EQUALS: return compare(ext, args[0], args[1]).greaterOrEqual(); - case LESS: return compare(ext, args[0], args[1]).less(); - case LESS_EQUALS: return compare(ext, args[0], args[1]).lessOrEqual(); + case GREATER: return BoolValue.of(args[0].compare(env, args[1]).greater()); + case GREATER_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).greaterOrEqual()); + case LESS: return BoolValue.of(args[0].compare(env, args[1]).less()); + case LESS_EQUALS: return BoolValue.of(args[0].compare(env, args[1]).lessOrEqual()); - case INVERSE: return bitwiseNot(ext, args[0]); - case NOT: return not(args[0]); - case POS: return toNumber(ext, args[0]); - case NEG: return negative(ext, args[0]); + case INVERSE: return args[0].bitwiseNot(env); + case NOT: return args[0].not(); + case POS: return args[0].toNumber(env); + case NEG: return args[0].negative(env); - case SHIFT_LEFT: return shiftLeft(ext, args[0], args[1]); - case SHIFT_RIGHT: return shiftRight(ext, args[0], args[1]); - case USHIFT_RIGHT: return unsignedShiftRight(ext, args[0], args[1]); + case SHIFT_LEFT: return args[0].shiftLeft(env, args[1]); + case SHIFT_RIGHT: return args[0].shiftRight(env, args[1]); + case USHIFT_RIGHT: return args[0].unsignedShiftRight(env, args[1]); - case IN: return hasMember(ext, args[1], args[0], false); - case INSTANCEOF: { - var proto = getMember(ext, args[1], "prototype"); - return isInstanceOf(ext, args[0], proto); - } + case IN: return BoolValue.of(args[0].hasMember(env, args[1], false)); + case INSTANCEOF: return BoolValue.of(args[0].isInstanceOf(env, args[1].getMember(env, new StringValue("prototype")))); default: return null; } } - public static Object getMember(Environment ctx, Object obj, Object key) { - obj = normalize(ctx, obj); key = normalize(ctx, key); - if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); - if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null."); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj); + public abstract Member getOwnMember(Environment env, Value key); + public abstract Map getOwnMembers(Environment env); + public abstract Map getOwnSymbolMembers(Environment env); + public abstract boolean defineOwnMember(Environment env, Value key, Member member); + public abstract boolean deleteOwnMember(Environment env, Value key); - if (obj instanceof String && key instanceof Number) { - var i = number(key); - var s = (String)obj; - if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) { - return s.charAt((int)i) + ""; + public abstract ObjectValue getPrototype(Environment env); + public abstract boolean setPrototype(Environment env, ObjectValue val); + + public final Value getMember(Environment env, Value key) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + var member = obj.getOwnMember(env, key); + if (member != null) return member.get(env, obj); + } + + return VoidValue.UNDEFINED; + } + public final boolean setMember(Environment env, Value key, Value val) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + var member = obj.getOwnMember(env, key); + if (member != null) return member.set(env, val, obj); + } + + return defineOwnMember(env, key, FieldMember.of(val)); + } + public final boolean hasMember(Environment env, Value key, boolean own) { + for (Value obj = this; obj != null; obj = obj.getPrototype(env)) { + if (obj.getOwnMember(env, key) != null) return true; + if (own) return false; + } + + return false; + } + public final boolean deleteMember(Environment env, Value key) { + if (!hasMember(env, key, true)) return true; + return deleteOwnMember(env, key); + } + public final Map getMembers(Environment env, boolean own, boolean onlyEnumerable) { + var res = new LinkedHashMap(); + var protos = new ArrayList(); + + for (var proto = this; proto != null; proto = proto.getPrototype(env)) { + protos.add(proto); + if (own) break; + } + + Collections.reverse(protos); + + for (var proto : protos) { + if (onlyEnumerable) { + for (var el : proto.getOwnMembers(env).entrySet()) { + if (!el.getValue().enumerable()) continue; + res.put(el.getKey(), el.getValue()); + } } + else res.putAll(proto.getOwnMembers(env)); } - var proto = getPrototype(ctx, obj); - - if (proto == null) return "__proto__".equals(key) ? NULL : null; - else if (key != null && "__proto__".equals(key)) return proto; - else return proto.getMember(ctx, key, obj); - } - public static Object getMemberPath(Environment ctx, Object obj, Object ...path) { - var res = obj; - for (var key : path) res = getMember(ctx, res, key); - return res; - } - public static boolean setMember(Environment ctx, Object obj, Object key, Object val) { - obj = normalize(ctx, obj); key = normalize(ctx, key); val = normalize(ctx, val); - if (obj == null) throw EngineException.ofType("Tried to access member of undefined."); - if (obj == NULL) throw EngineException.ofType("Tried to access member of null."); - if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false); - - var proto = getPrototype(ctx, obj); - return proto.setMember(ctx, key, val, obj, true); - } - public static boolean hasMember(Environment ctx, Object obj, Object key, boolean own) { - if (obj == null || obj == NULL) return false; - obj = normalize(ctx, obj); key = normalize(ctx, key); - - if ("__proto__".equals(key)) return true; - if (obj instanceof ObjectValue) return ((ObjectValue)obj).hasMember(ctx, key, own); - - if (obj instanceof String && key instanceof Number) { - var i = number(key); - var s = (String)obj; - if (i >= 0 && i < s.length() && i - Math.floor(i) == 0) return true; - } - - if (own) return false; - - var proto = getPrototype(ctx, obj); - return proto != null && proto.hasMember(ctx, key, own); - } - public static boolean deleteMember(Environment ext, Object obj, Object key) { - if (obj == null || obj == NULL) return false; - obj = normalize(ext, obj); key = normalize(ext, key); - - if (obj instanceof ObjectValue) return ((ObjectValue)obj).deleteMember(ext, key); - else return false; - } - public static ObjectValue getPrototype(Environment ext, Object obj) { - if (obj == null || obj == NULL) return null; - obj = normalize(ext, obj); - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ext); - if (ext == null) return null; - - if (obj instanceof String) return ext.get(Environment.STRING_PROTO); - else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO); - else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO); - else if (obj instanceof Symbol) return ext.get(Environment.SYMBOL_PROTO); - - return null; - } - public static boolean setPrototype(Environment ext, Object obj, Object proto) { - obj = normalize(ext, obj); - return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ext, proto); - } - public static void makePrototypeChain(Environment ext, Object... chain) { - for(var i = 1; i < chain.length; i++) { - setPrototype(ext, chain[i], chain[i - 1]); - } - } - public static List getMembers(Environment ext, Object obj, boolean own, boolean includeNonEnumerable) { - List res = new ArrayList<>(); - - if (obj instanceof ObjectValue) res = ((ObjectValue)obj).keys(includeNonEnumerable); - if (obj instanceof String) { - for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i); - } - - if (!own) { - var proto = getPrototype(ext, obj); - - while (proto != null) { - res.addAll(proto.keys(includeNonEnumerable)); - proto = getPrototype(ext, proto); - } - } - - return res; } - public static ObjectValue getMemberDescriptor(Environment ext, Object obj, Object key) { - if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMemberDescriptor(ext, key); - else if (obj instanceof String && key instanceof Number) { - var i = ((Number)key).intValue(); - var _i = ((Number)key).doubleValue(); - if (i - _i != 0) return null; - if (i < 0 || i >= ((String)obj).length()) return null; + public final Map getSymbolMembers(Environment env, boolean own, boolean onlyEnumerable) { + var res = new LinkedHashMap(); + var protos = new ArrayList(); - return new ObjectValue(ext, Map.of( - "value", ((String)obj).charAt(i) + "", - "writable", false, - "enumerable", true, - "configurable", false - )); + for (var proto = this; proto != null; proto = proto.getPrototype(env)) { + protos.add(proto); + if (own) break; } + + Collections.reverse(protos); + + for (var proto : protos) { + if (onlyEnumerable) { + for (var el : proto.getOwnSymbolMembers(env).entrySet()) { + if (!el.getValue().enumerable()) continue; + res.put(el.getKey(), el.getValue()); + } + } + else res.putAll(proto.getOwnSymbolMembers(env)); + } + + return res; + } + + public final Value getMemberPath(Environment env, Value ...path) { + var res = this; + for (var key : path) res = res.getMember(env, key); + return res; + } + public final ObjectValue getMemberDescriptor(Environment env, Value key) { + var member = getOwnMember(env, key); + + if (member != null) return member.descriptor(env, this); else return null; } - public static boolean strictEquals(Environment ext, Object a, Object b) { - a = normalize(ext, a); - b = normalize(ext, b); + public abstract boolean strictEquals(Environment env, Value other); - if (a == null || b == null) return a == null && b == null; - if (isNan(a) || isNan(b)) return false; - if (a instanceof Number && number(a) == -0.) a = 0.; - if (b instanceof Number && number(b) == -0.) b = 0.; - - return a == b || a.equals(b); - } - public static boolean looseEqual(Environment ext, Object a, Object b) { - a = normalize(ext, a); b = normalize(ext, b); + public final boolean looseEqual(Environment env, Value other) { + var a = this; + var b = other; // In loose equality, null is equivalent to undefined - if (a == NULL) a = null; - if (b == NULL) b = null; + if (a instanceof VoidValue || b instanceof VoidValue) return a instanceof VoidValue && b instanceof VoidValue; - if (a == null || b == null) return a == null && b == null; // If both are objects, just compare their references - if (!isPrimitive(a) && !isPrimitive(b)) return a == b; + if (!a.isPrimitive() && !b.isPrimitive()) return a.strictEquals(env, b); // Convert values to primitives - a = toPrimitive(ext, a, ConvertHint.VALUEOF); - b = toPrimitive(ext, b, ConvertHint.VALUEOF); + a = a.toPrimitive(env); + b = b.toPrimitive(env); // Compare symbols by reference - if (a instanceof Symbol || b instanceof Symbol) return a == b; - if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); - if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); + if (a instanceof SymbolValue || b instanceof SymbolValue) return a.strictEquals(env, b); + // Compare booleans as numbers + if (a instanceof BoolValue || b instanceof BoolValue) return a.toNumber(env).strictEquals(env, b.toNumber(env)); + // Comparse numbers as numbers + if (a instanceof NumberValue || b instanceof NumberValue) return a.toNumber(env).strictEquals(env, b.toNumber(env)); // Default to strings - return toString(ext, a).equals(toString(ext, b)); + return a.toString(env).strictEquals(env, b.toString(env)); } - public static Object normalize(Environment ext, Object val) { - if (val instanceof Number) return number(val); - if (isPrimitive(val) || val instanceof ObjectValue) return val; - if (val instanceof Character) return val + ""; + // @SuppressWarnings("unchecked") + // public static T convert(Environment ext, Class clazz) { + // if (clazz == Void.class) return null; - if (val instanceof Map) { - var res = new ObjectValue(); + // if (this instanceof NativeWrapper) { + // var res = ((NativeWrapper)this).wrapped; + // if (clazz.isInstance(res)) return (T)res; + // } - for (var entry : ((Map)val).entrySet()) { - res.defineProperty(ext, entry.getKey(), entry.getValue()); - } + // if (clazz == null || clazz == Object.class) return (T)this; - return res; - } + // if (this instanceof ArrayValue) { + // if (clazz.isAssignableFrom(ArrayList.class)) { + // var raw = ((ArrayValue)this).toArray(); + // var res = new ArrayList<>(); + // for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); + // return (T)new ArrayList<>(res); + // } + // if (clazz.isAssignableFrom(HashSet.class)) { + // var raw = ((ArrayValue)this).toArray(); + // var res = new HashSet<>(); + // for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); + // return (T)new HashSet<>(res); + // } + // if (clazz.isArray()) { + // var raw = ((ArrayValue)this).toArray(); + // Object res = Array.newInstance(clazz.getComponentType(), raw.length); + // for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); + // return (T)res; + // } + // } - if (val instanceof Iterable) { - var res = new ArrayValue(); + // if (this instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { + // var res = new HashMap<>(); + // for (var el : ((ObjectValue)this).values.entrySet()) res.put( + // convert(ext, el.getKey(), null), + // convert(ext, el.getValue(), null) + // ); + // return (T)res; + // } - for (var entry : ((Iterable)val)) { - res.set(ext, res.size(), entry); - } + // if (clazz == String.class) return (T)toString(ext, this); + // if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(this); + // if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, this); + // if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, this); + // if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, this); + // if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, this); + // if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, this); + // if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, this); - return res; - } + // if (clazz == Character.class || clazz == char.class) { + // if (this instanceof Number) return (T)(Character)(char)number(this); + // else { + // var res = toString(ext, this); + // if (res.length() == 0) throw new ConvertException("\"\"", "Character"); + // else return (T)(Character)res.charAt(0); + // } + // } - if (val instanceof Class) { - if (ext == null) return null; - else return NativeWrapperProvider.get(ext).getConstr((Class)val); - } + // if (this == null) return null; + // if (clazz.isInstance(this)) return (T)this; + // if (clazz.isAssignableFrom(NativeWrapper.class)) { + // return (T)NativeWrapper.of(ext, this); + // } - return NativeWrapper.of(ext, val); - } + // throw new ConvertException(type(this), clazz.getSimpleName()); + // } - @SuppressWarnings("unchecked") - public static T convert(Environment ext, Object obj, Class clazz) { - if (clazz == Void.class) return null; - - if (obj instanceof NativeWrapper) { - var res = ((NativeWrapper)obj).wrapped; - if (clazz.isInstance(res)) return (T)res; - } - - if (clazz == null || clazz == Object.class) return (T)obj; - - if (obj instanceof ArrayValue) { - if (clazz.isAssignableFrom(ArrayList.class)) { - var raw = ((ArrayValue)obj).toArray(); - var res = new ArrayList<>(); - for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - return (T)new ArrayList<>(res); - } - if (clazz.isAssignableFrom(HashSet.class)) { - var raw = ((ArrayValue)obj).toArray(); - var res = new HashSet<>(); - for (var i = 0; i < raw.length; i++) res.add(convert(ext, raw[i], Object.class)); - return (T)new HashSet<>(res); - } - if (clazz.isArray()) { - var raw = ((ArrayValue)obj).toArray(); - Object res = Array.newInstance(clazz.getComponentType(), raw.length); - for (var i = 0; i < raw.length; i++) Array.set(res, i, convert(ext, raw[i], Object.class)); - return (T)res; - } - } - - if (obj instanceof ObjectValue && clazz.isAssignableFrom(HashMap.class)) { - var res = new HashMap<>(); - for (var el : ((ObjectValue)obj).values.entrySet()) res.put( - convert(ext, el.getKey(), null), - convert(ext, el.getValue(), null) - ); - return (T)res; - } - - if (clazz == String.class) return (T)toString(ext, obj); - if (clazz == Boolean.class || clazz == Boolean.TYPE) return (T)(Boolean)toBoolean(obj); - if (clazz == Byte.class || clazz == byte.class) return (T)(Byte)(byte)toNumber(ext, obj); - if (clazz == Integer.class || clazz == int.class) return (T)(Integer)(int)toNumber(ext, obj); - if (clazz == Long.class || clazz == long.class) return (T)(Long)(long)toNumber(ext, obj); - if (clazz == Short.class || clazz == short.class) return (T)(Short)(short)toNumber(ext, obj); - if (clazz == Float.class || clazz == float.class) return (T)(Float)(float)toNumber(ext, obj); - if (clazz == Double.class || clazz == double.class) return (T)(Double)toNumber(ext, obj); - - if (clazz == Character.class || clazz == char.class) { - if (obj instanceof Number) return (T)(Character)(char)number(obj); - else { - var res = toString(ext, obj); - if (res.length() == 0) throw new ConvertException("\"\"", "Character"); - else return (T)(Character)res.charAt(0); - } - } - - if (obj == null) return null; - if (clazz.isInstance(obj)) return (T)obj; - if (clazz.isAssignableFrom(NativeWrapper.class)) { - return (T)NativeWrapper.of(ext, obj); - } - - throw new ConvertException(type(obj), clazz.getSimpleName()); - } - - public static Iterable fromJSIterator(Environment ext, Object obj) { + public Iterable toIterable(Environment env) { return () -> { - try { - var symbol = Symbol.get("Symbol.iterator"); + if (!(this instanceof FunctionValue)) return Collections.emptyIterator(); + var func = (FunctionValue)this; - var iteratorFunc = getMember(ext, obj, symbol); - if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); - var iterator = iteratorFunc instanceof FunctionValue ? - ((FunctionValue)iteratorFunc).call(ext, obj, obj) : - iteratorFunc; - var nextFunc = getMember(ext, call(ext, iteratorFunc, obj), "next"); + return new Iterator() { + private Object value = null; + public boolean consumed = true; + private FunctionValue supplier = func; - if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator(); + private void loadNext() { + if (supplier == null) value = null; + else if (consumed) { + var curr = supplier.call(env, VoidValue.UNDEFINED); - return new Iterator() { - private Object value = null; - public boolean consumed = true; - private FunctionValue next = (FunctionValue)nextFunc; - - private void loadNext() { - if (next == null) value = null; - else if (consumed) { - var curr = next.call(ext, iterator); - if (curr == null) { next = null; value = null; } - if (toBoolean(Values.getMember(ext, curr, "done"))) { next = null; value = null; } - else { - this.value = Values.getMember(ext, curr, "value"); - consumed = false; - } + if (curr == null) { supplier = null; value = null; } + if (curr.getMember(env, new StringValue("done")).toBoolean().value) { supplier = null; value = null; } + else { + this.value = curr.getMember(env, new StringValue("value")); + consumed = false; } } + } - @Override - public boolean hasNext() { - loadNext(); - return next != null; - } - @Override - public Object next() { - loadNext(); - var res = value; - value = null; - consumed = true; - return res; - } - }; - } - catch (IllegalArgumentException | NullPointerException e) { - return Collections.emptyIterator(); - } + @Override + public boolean hasNext() { + loadNext(); + return supplier != null; + } + @Override + public Object next() { + loadNext(); + var res = value; + value = null; + consumed = true; + return res; + } + }; }; } - public static ObjectValue toJSIterator(Environment ext, Iterator it) { - var res = new ObjectValue(); + public static FunctionValue fromIterator(Environment ext, Iterable iterable) { + var it = iterable.iterator(); - try { - var key = getMember(ext, getMember(ext, ext.get(Environment.SYMBOL_PROTO), "constructor"), "iterator"); - res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); + return new NativeFunction("", args -> { + var obj = new ObjectValue(); + + if (!it.hasNext()) obj.defineOwnMember(args.env, new StringValue("done"), FieldMember.of(BoolValue.TRUE)); + else obj.defineOwnMember(args.env, new StringValue("value"), FieldMember.of(it.next())); + + return obj; + }); + } + + public void callWith(Environment env, Iterable it) { + for (var el : it) { + this.call(env, VoidValue.UNDEFINED, el); } - catch (IllegalArgumentException | NullPointerException e) { } - - res.defineProperty(ext, "next", new NativeFunction("", args -> { - if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); - else { - var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; - } - })); - - return res; } - - public static ObjectValue toJSIterator(Environment ext, Iterable it) { - return toJSIterator(ext, it.iterator()); - } - - public static ObjectValue toJSAsyncIterator(Environment ext, Iterator it) { - var res = new ObjectValue(); - - try { - var key = getMemberPath(ext, ext.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator"); - res.defineProperty(ext, key, new NativeFunction("", args -> args.self)); + public void callWithAsync(Environment env, Iterable it, boolean async) { + for (var el : it) { + env.get(EventLoop.KEY).pushMsg(() -> this.call(env, VoidValue.UNDEFINED, el), true); } - catch (IllegalArgumentException | NullPointerException e) { } - - res.defineProperty(ext, "next", new NativeFunction("", args -> { - return PromiseLib.await(args.env, () -> { - if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); - else { - var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; - } - }); - })); - - return res; } - private static boolean isEmptyFunc(ObjectValue val) { + private final boolean isEmptyFunc(Environment env, ObjectValue val) { if (!(val instanceof FunctionValue)) return false; - if (!val.values.containsKey("prototype") || val.values.size() + val.properties.size() > 1) return false; - var proto = val.values.get("prototype"); + if (val.members.size() + val.symbolMembers.size() > 1) return false; + + var proto = ((FunctionValue)val).prototype; if (!(proto instanceof ObjectValue)) return false; var protoObj = (ObjectValue)proto; - if (protoObj.values.get("constructor") != val) return false; - if (protoObj.values.size() + protoObj.properties.size() != 1) return false; + + if (protoObj.getMember(env, new StringValue("constructor")) != val) return false; + if (protoObj.getOwnMembers(env).size() + protoObj.getOwnSymbolMembers(env).size() != 1) return false; + return true; } - private static String toReadable(Environment ext, Object val, HashSet passed, int tab) { - if (tab == 0 && val instanceof String) return (String)val; + private final String toReadable(Environment env, HashSet passed, int tab) { + if (passed.contains(this)) return "[circular]"; - if (passed.contains(val)) return "[circular]"; + if (this instanceof ObjectValue) { + var res = new StringBuilder(); + var dbg = DebugContext.get(env); + var printed = true; - var printed = true; - var res = new StringBuilder(); - var dbg = DebugContext.get(ext); + if (this instanceof FunctionValue) { + res.append(this.toString()); + var loc = this instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)this).start() : null; - if (val instanceof FunctionValue) { - res.append(val.toString()); - var loc = val instanceof CodeFunction ? dbg.getMapOrEmpty((CodeFunction)val).start() : null; - - if (loc != null) res.append(" @ " + loc); - } - else if (val instanceof ArrayValue) { - res.append("["); - var obj = ((ArrayValue)val); - for (int i = 0; i < obj.size(); i++) { - if (i != 0) res.append(", "); - else res.append(" "); - if (obj.has(i)) res.append(toReadable(ext, obj.get(i), passed, tab)); - else res.append(""); + if (loc != null) res.append(" @ " + loc); } - res.append(" ] "); - } - else if (val instanceof NativeWrapper) { - var obj = ((NativeWrapper)val).wrapped; - res.append("Native " + obj.toString() + " "); - } - else printed = false; + else if (this instanceof ArrayValue) { + res.append("["); + var arr = (ArrayValue)this; - if (val instanceof ObjectValue) { - if (tab > 3) { - return "{...}"; + for (int i = 0; i < arr.size(); i++) { + if (i != 0) res.append(", "); + else res.append(" "); + if (arr.has(i)) res.append(arr.get(i).toReadable(env, passed, tab)); + else res.append(""); + } + + res.append(" ] "); } + else printed = false; - passed.add(val); + if (tab > 3) return "{...}"; - var obj = (ObjectValue)val; - if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) { + passed.add(this); + + var obj = (ObjectValue)this; + if (obj.getOwnSymbolMembers(env).size() + obj.getOwnMembers(env).size() == 0 || isEmptyFunc(env, obj)) { if (!printed) res.append("{}\n"); } else { res.append("{\n"); - for (var el : obj.values.entrySet()) { + for (var entry : obj.getOwnSymbolMembers(env).entrySet()) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ext, el.getKey(), passed, tab + 1)); - res.append(": "); - res.append(toReadable(ext, el.getValue(), passed, tab + 1)); + res.append("[" + entry.getKey().value + "]" + ": "); + + var member = entry.getValue(); + if (member instanceof FieldMember) res.append(((FieldMember)member).get(env, obj).toReadable(env, passed, tab + 1)); + else res.append("[property]"); + res.append(",\n"); } - for (var el : obj.properties.entrySet()) { + for (var entry : obj.getOwnMembers(env).entrySet()) { for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(toReadable(ext, el.getKey(), passed, tab + 1)); - res.append(": [prop],\n"); + res.append(entry.getKey() + ": "); + + var member = entry.getValue(); + if (member instanceof FieldMember) res.append(((FieldMember)member).get(env, obj).toReadable(env, passed, tab + 1)); + else res.append("[property]"); + + res.append(",\n"); } for (int i = 0; i < tab; i++) res.append(" "); res.append("}"); } - passed.remove(val); + passed.remove(this); + return res.toString(); } - else if (val == null) return "undefined"; - else if (val == Values.NULL) return "null"; - else if (val instanceof String) return "'" + val + "'"; - else return Values.toString(ext, val); - - return res.toString(); + else if (this instanceof VoidValue) return ((VoidValue)this).name; + else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value)); + else return this.toString(env).value; } - public static String toReadable(Environment ext, Object val) { - return toReadable(ext, val, new HashSet<>(), 0); + public final String toReadable(Environment ext) { + if (this instanceof StringValue) return ((StringValue)this).value; + return toReadable(ext, new HashSet<>(), 0); } - public static String errorToReadable(RuntimeException err, String prefix) { + + public static final String errorToReadable(RuntimeException err, String prefix) { prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix; if (err instanceof EngineException) { var ee = ((EngineException)err); @@ -710,7 +542,7 @@ public interface Value { return prefix + " " + ee.toString(ee.env); } catch (EngineException ex) { - return prefix + " " + toReadable(ee.env, ee.value); + return prefix + " " + ee.value.toReadable(ee.env); } } else if (err instanceof SyntaxException) { @@ -724,10 +556,4 @@ public interface Value { return prefix + " internal error " + str.toString(); } } - public static void printValue(Environment ext, Object val) { - System.out.print(toReadable(ext, val)); - } - public static void printError(RuntimeException err, String prefix) { - System.out.println(errorToReadable(err, prefix)); - } } diff --git a/src/java/me/topchetoeu/jscript/runtime/values/Values.java b/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old similarity index 97% rename from src/java/me/topchetoeu/jscript/runtime/values/Values.java rename to src/java/me/topchetoeu/jscript/runtime/values/Values.java.old index 0e527f6..a2482b7 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/Values.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/Values.java.old @@ -19,6 +19,11 @@ import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.ConvertException; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; +import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; +import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider; public class Values { @@ -68,7 +73,7 @@ public class Values { if (val instanceof String) return "string"; if (val instanceof Number) return "number"; if (val instanceof Boolean) return "boolean"; - if (val instanceof Symbol) return "symbol"; + if (val instanceof SymbolValue) return "symbol"; if (val instanceof FunctionValue) return "function"; return "object"; } @@ -89,7 +94,7 @@ public class Values { obj instanceof Number || obj instanceof String || obj instanceof Boolean || - obj instanceof Symbol || + obj instanceof SymbolValue || obj == null || obj == NULL; } @@ -141,7 +146,7 @@ public class Values { } if (val instanceof Boolean) return (Boolean)val ? "true" : "false"; if (val instanceof String) return (String)val; - if (val instanceof Symbol) return val.toString(); + if (val instanceof SymbolValue) return val.toString(); return "Unknown value"; } @@ -335,7 +340,7 @@ public class Values { if (obj instanceof String) return ext.get(Environment.STRING_PROTO); else if (obj instanceof Number) return ext.get(Environment.NUMBER_PROTO); else if (obj instanceof Boolean) return ext.get(Environment.BOOL_PROTO); - else if (obj instanceof Symbol) return ext.get(Environment.SYMBOL_PROTO); + else if (obj instanceof SymbolValue) return ext.get(Environment.SYMBOL_PROTO); return null; } @@ -433,7 +438,7 @@ public class Values { b = toPrimitive(ext, b, ConvertHint.VALUEOF); // Compare symbols by reference - if (a instanceof Symbol || b instanceof Symbol) return a == b; + if (a instanceof SymbolValue || b instanceof SymbolValue) return a == b; if (a instanceof Boolean || b instanceof Boolean) return toBoolean(a) == toBoolean(b); if (a instanceof Number || b instanceof Number) return strictEquals(ext, toNumber(ext, a), toNumber(ext, b)); @@ -545,7 +550,7 @@ public class Values { public static Iterable fromJSIterator(Environment ext, Object obj) { return () -> { try { - var symbol = Symbol.get("Symbol.iterator"); + var symbol = SymbolValue.get("Symbol.iterator"); var iteratorFunc = getMember(ext, obj, symbol); if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator(); @@ -634,8 +639,8 @@ public class Values { if (!it.hasNext()) return new ObjectValue(ext, Map.of("done", true)); else { var obj = new ObjectValue(); - obj.defineProperty(args.env, "value", it.next()); - return obj; + object.defineProperty(args.env, "value", it.next()); + return object; } }); })); diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java new file mode 100644 index 0000000..7acb9e1 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java @@ -0,0 +1,39 @@ +package me.topchetoeu.jscript.runtime.values.functions; + + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +public class Arguments { + public final Value self; + public final Value[] args; + public final Environment env; + + public int n() { + return args.length; + } + + public boolean has(int i) { + return i == -1 || i >= 0 && i < args.length; + } + + public Value self() { + return get(-1); + } + public Value get(int i) { + if (i >= args.length || i < -1) return VoidValue.UNDEFINED; + else if (i == -1) return self; + else return args[i]; + } + public Value getOrDefault(int i, Value def) { + if (i < -1 || i >= args.length) return def; + else return get(i); + } + + public Arguments(Environment env, Value thisArg, Value... args) { + this.env = env; + this.args = args; + this.self = thisArg; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java similarity index 58% rename from src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java rename to src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index ba5a909..daf35f9 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/CodeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -1,36 +1,24 @@ -package me.topchetoeu.jscript.runtime.values; +package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.environment.Environment; import me.topchetoeu.jscript.runtime.scope.ValueVariable; +import me.topchetoeu.jscript.runtime.values.Value; public class CodeFunction extends FunctionValue { public final FunctionBody body; public final ValueVariable[] captures; public Environment env; - // public Location loc() { - // for (var instr : body.instructions) { - // if (instr.location != null) return instr.location; - // } - // return null; - // } - // public String readable() { - // var loc = loc(); - // if (loc == null) return name; - // else if (name.equals("")) return loc.toString(); - // else return name + "@" + loc; - // } - - @Override public Object call(Environment env, Object thisArg, Object ...args) { + @Override public Value call(Environment env, Value thisArg, Value ...args) { var frame = new Frame(env, thisArg, args, this); frame.onPush(); try { while (true) { var res = frame.next(); - if (res != Values.NO_RETURN) return res; + if (res != null) return res; } } finally { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java new file mode 100644 index 0000000..63a778b --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -0,0 +1,77 @@ +package me.topchetoeu.jscript.runtime.values.functions; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; + +public abstract class FunctionValue extends ObjectValue { + public String name = ""; + public int length; + public Value prototype = new ObjectValue(); + + private final FieldMember nameField = new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { + return new StringValue(name); + } + @Override public boolean set(Environment env, Value val, Value self) { + name = val.toString(env).value; + return true; + } + }; + private final FieldMember lengthField = new FieldMember(false, true, false) { + @Override public Value get(Environment env, Value self) { + return new NumberValue(length); + } + @Override public boolean set(Environment env, Value val, Value self) { + return false; + } + }; + private final FieldMember prototypeField = new FieldMember(false, true, true) { + @Override public Value get(Environment env, Value self) { + return prototype; + } + @Override public boolean set(Environment env, Value val, Value self) { + prototype = val; + return true; + } + }; + + @Override public String toString() { return String.format("function %s(...)", name); } + @Override public abstract Value call(Environment ext, Value thisArg, Value ...args); + + @Override public Member getOwnMember(Environment env, Value key) { + var el = key.toString(env).value; + + if (el.equals("length")) return lengthField; + if (el.equals("name")) return nameField; + if (el.equals("prototype")) return prototypeField; + + return super.getOwnMember(env, key); + } + @Override public boolean deleteOwnMember(Environment env, Value key) { + if (!super.deleteMember(env, key)) return false; + + var el = key.toString(env).value; + + if (el.equals("length")) return false; + if (el.equals("name")) return false; + if (el.equals("prototype")) return false; + + return true; + } + + public FunctionValue(String name, int length) { + setPrototype(Environment.FUNCTION_PROTO); + + if (name == null) name = ""; + this.length = length; + this.name = name; + + prototype.defineOwnMember(Environment.empty(), new StringValue("constructor"), FieldMember.of(this)); + } +} + diff --git a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java similarity index 62% rename from src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java rename to src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index 3746568..895552d 100644 --- a/src/java/me/topchetoeu/jscript/runtime/values/NativeFunction.java +++ b/src/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -1,18 +1,17 @@ -package me.topchetoeu.jscript.runtime.values; +package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.utils.interop.Arguments; +import me.topchetoeu.jscript.runtime.values.Value; public class NativeFunction extends FunctionValue { public static interface NativeFunctionRunner { - Object run(Arguments args); + Value run(Arguments args); } public final NativeFunctionRunner action; - @Override - public Object call(Environment env, Object thisArg, Object ...args) { - return action.run(new Arguments(env, thisArg, args)); + @Override public Value call(Environment env, Value self, Value ...args) { + return action.run(new Arguments(env, self, args)); } public NativeFunction(String name, NativeFunctionRunner action) { diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java new file mode 100644 index 0000000..542837e --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ArrayValue.java @@ -0,0 +1,217 @@ +package me.topchetoeu.jscript.runtime.values.objects; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.VoidValue; + +// TODO: Make methods generic +public class ArrayValue extends ObjectValue implements Iterable { + private Value[] values; + private int size; + + private class IndexField extends FieldMember { + private int i; + private ArrayValue arr; + + @Override public Value get(Environment env, Value self) { + return arr.get(i); + } + @Override public boolean set(Environment env, Value val, Value self) { + arr.set(i, val); + return true; + } + public IndexField(int i, ArrayValue arr) { + super(true, true, true); + this.arr = arr; + this.i = i; + } + } + + private Value[] alloc(int index) { + index++; + if (index < values.length) return values; + if (index < values.length * 2) index = values.length * 2; + + var arr = new Value[index]; + System.arraycopy(values, 0, arr, 0, values.length); + return arr; + } + + public int size() { return size; } + public boolean setSize(int val) { + if (val < 0) return false; + if (size > val) shrink(size - val); + else { + values = alloc(val); + size = val; + } + return true; + } + + public Value get(int i) { + if (i < 0 || i >= size) return null; + var res = values[i]; + + if (res == null) return VoidValue.UNDEFINED; + else return res; + } + public void set(int i, Value val) { + if (i < 0) return; + + alloc(i)[i] = val; + if (i >= size) size = i + 1; + } + public boolean has(int i) { + return i >= 0 && i < size && values[i] != null; + } + public void remove(int i) { + if (i < 0 || i >= values.length) return; + values[i] = null; + } + public void shrink(int n) { + if (n >= values.length) { + values = new Value[16]; + size = 0; + } + else { + for (int i = 0; i < n; i++) values[--size] = null; + } + } + + public Value[] toArray() { + var res = new Value[size]; + copyTo(res, 0, 0, size); + return res; + } + + public void copyTo(Value[] arr, int sourceStart, int destStart, int count) { + var nullFill = values.length - destStart + count; + count -= nullFill; + + System.arraycopy(values, sourceStart, arr, destStart, count); + Arrays.fill(arr, count, nullFill, null); + } + public void copyTo(ArrayValue arr, int sourceStart, int destStart, int count) { + if (arr == this) { + move(sourceStart, destStart, count); + return; + } + + arr.copyFrom(values, sourceStart, destStart, count); + } + public void copyFrom(Value[] arr, int sourceStart, int destStart, int count) { + alloc(destStart + count); + System.arraycopy(arr, sourceStart, values, destStart, count); + if (size < destStart + count) size = destStart + count; + } + + public void move(int srcI, int dstI, int n) { + values = alloc(dstI + n); + System.arraycopy(values, srcI, values, dstI, n); + if (dstI + n >= size) size = dstI + n; + } + + public void sort(Comparator comparator) { + Arrays.sort(values, 0, size, (a, b) -> { + var _a = 0; + var _b = 0; + + if (a == null) _a = 2; + if (a instanceof VoidValue) _a = 1; + + if (b == null) _b = 2; + if (b instanceof VoidValue) _b = 1; + + if (_a != 0 || _b != 0) return Integer.compare(_a, _b); + + return comparator.compare(a, b); + }); + } + + @Override public Member getOwnMember(Environment env, Value key) { + var res = super.getOwnMember(env, key); + if (res != null) return res; + + var num = key.toNumber(env); + var i = num.toInt(env); + + if (i == num.value && i >= 0 && i < size) return new IndexField(i, this); + else return null; + } + @Override public boolean defineOwnMember(Environment env, Value key, Member member) { + if (!(member instanceof FieldMember) || hasMember(env, key, true)) return super.defineOwnMember(env, key, member); + if (!extensible) return false; + + var num = key.toNumber(env); + var i = num.toInt(env); + + if (i == num.value && i >= 0) { + set(i, ((FieldMember)member).get(env, this)); + return true; + } + else return super.defineOwnMember(env, key, member); + } + @Override public boolean deleteOwnMember(Environment env, Value key) { + if (!super.deleteOwnMember(env, key)) return false; + + var num = key.toNumber(env); + var i = num.toInt(env); + + if (i == num.value && i >= 0 && i < size) return super.deleteOwnMember(env, key); + else return true; + } + + @Override public Map getOwnMembers(Environment env) { + var res = new LinkedHashMap(); + + for (var i = 0; i < size; i++) { + res.put(i + "", getOwnMember(env, new NumberValue(i))); + } + + res.putAll(super.getOwnMembers(env)); + + return res; + } + @Override public Iterator iterator() { + return new Iterator<>() { + private int i = 0; + + @Override + public boolean hasNext() { + return i < size(); + } + @Override + public Value next() { + if (!hasNext()) return null; + return get(i++); + } + }; + } + + public ArrayValue() { + this(16); + } + public ArrayValue(int cap) { + setPrototype(env -> env.get(Environment.ARRAY_PROTO)); + values = new Value[Math.min(cap, 16)]; + size = 0; + } + public ArrayValue(Value ...values) { + this(); + copyFrom(values, 0, 0, values.length); + } + + public static ArrayValue of(Collection values) { + return new ArrayValue(values.toArray(Value[]::new)); + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java new file mode 100644 index 0000000..efc2000 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -0,0 +1,134 @@ +package me.topchetoeu.jscript.runtime.values.objects; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.environment.Key; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; +import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; +import me.topchetoeu.jscript.runtime.values.primitives.NumberValue; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; +import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; + +public class ObjectValue extends Value { + public static interface PrototypeProvider { + public ObjectValue get(Environment env); + } + + public static enum State { + NORMAL, + NO_EXTENSIONS, + SEALED, + FROZEN, + } + + public static class Property { + public final FunctionValue getter; + public final FunctionValue setter; + + public Property(FunctionValue getter, FunctionValue setter) { + this.getter = getter; + this.setter = setter; + } + } + + private static final StringValue typeString = new StringValue("object"); + + protected PrototypeProvider prototype; + + public boolean extensible = true; + + public LinkedHashMap members = new LinkedHashMap<>(); + public LinkedHashMap symbolMembers = new LinkedHashMap<>(); + + @Override public boolean isPrimitive() { return false; } + @Override public Value toPrimitive(Environment env) { + if (env != null) { + var valueOf = getMember(env, new StringValue("valueOf")); + + if (valueOf instanceof FunctionValue) { + var res = valueOf.call(env, this); + if (res.isPrimitive()) return res; + } + + var toString = getMember(env, new StringValue("toString")); + if (toString instanceof FunctionValue) { + var res = toString.call(env, this); + if (res.isPrimitive()) return res; + } + } + + throw EngineException.ofType("Value couldn't be converted to a primitive."); + } + @Override public StringValue toString(Environment env) { return toPrimitive(env).toString(env); } + @Override public BoolValue toBoolean() { return BoolValue.TRUE; } + @Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); } + @Override public StringValue type() { return typeString; } + + @Override public boolean strictEquals(Environment ext, Value other) { return this == other; } + + public final void preventExtensions() { + extensible = false; + } + + @Override public Member getOwnMember(Environment env, Value key) { + if (key instanceof SymbolValue) return symbolMembers.get(key); + else return members.get(key.toString(env).value); + } + @Override public boolean defineOwnMember(Environment env, Value key, Member member) { + if (!(key instanceof SymbolValue)) key = key.toString(env); + + var old = getOwnMember(env, key); + if (old != null && old.configure(env, member, this)) return true; + if (old != null && !old.configurable()) return false; + + if (key instanceof SymbolValue) symbolMembers.put((SymbolValue)key, member); + else members.put(key.toString(env).value, member); + + return true; + } + @Override public boolean deleteOwnMember(Environment env, Value key) { + if (!extensible) return false; + + if (!(key instanceof SymbolValue)) key = key.toString(env); + + var member = getOwnMember(env, key); + if (member == null) return true; + if (member.configurable()) return false; + + if (key instanceof SymbolValue) symbolMembers.remove(key); + else members.remove(key.toString(env).value); + return true; + } + + @Override public Map getOwnMembers(Environment env) { + return members; + } + @Override public Map getOwnSymbolMembers(Environment env) { + return Collections.unmodifiableMap(symbolMembers); + } + + @Override public ObjectValue getPrototype(Environment env) { + if (prototype == null) return null; + else return prototype.get(env); + } + @Override public final boolean setPrototype(Environment env, ObjectValue val) { + return setPrototype(_env -> val); + } + + public final boolean setPrototype(PrototypeProvider val) { + if (!extensible) return false; + prototype = val; + return true; + } + public final boolean setPrototype(Key key) { + if (!extensible) return false; + prototype = env -> env.get(key); + return true; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java new file mode 100644 index 0000000..bfd3a91 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/objects/ScopeValue.java @@ -0,0 +1,35 @@ +package me.topchetoeu.jscript.runtime.values.objects; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.scope.ValueVariable; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; +import me.topchetoeu.jscript.runtime.values.primitives.StringValue; + +public class ScopeValue extends ObjectValue { + private class VariableField extends FieldMember { + private int i; + + public VariableField(int i) { + super(false, true, true); + this.i = i; + } + + @Override public Value get(Environment env, Value self) { + return variables[i].get(env); + } + + @Override public boolean set(Environment env, Value val, Value self) { + return variables[i].set(env, val); + } + } + + public final ValueVariable[] variables; + + public ScopeValue(ValueVariable[] variables, String[] names) { + this.variables = variables; + for (var i = 0; i < names.length && i < variables.length; i++) { + defineOwnMember(Environment.empty(), new StringValue(i + ""), new VariableField(i)); + } + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java new file mode 100644 index 0000000..5e9d0c0 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -0,0 +1,38 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class BoolValue extends PrimitiveValue { + public static final BoolValue TRUE = new BoolValue(true); + public static final BoolValue FALSE = new BoolValue(false); + private static final StringValue typeString = new StringValue("boolean"); + + public final boolean value; + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return this; } + @Override public NumberValue toNumber(Environment ext) { + return value ? new NumberValue(1) : new NumberValue(0); + } + @Override public StringValue toString(Environment ext) { return new StringValue(value ? "true" : "false"); } + + @Override public ObjectValue getPrototype(Environment env) { + return env.get(Environment.BOOL_PROTO); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + if (other instanceof BoolValue) return value == ((BoolValue)other).value; + else return false; + } + + private BoolValue(boolean val) { + this.value = val; + } + + public static BoolValue of(boolean val) { + return val ? TRUE : FALSE; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java new file mode 100644 index 0000000..608516a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/NumberValue.java @@ -0,0 +1,80 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.math.BigDecimal; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class NumberValue extends PrimitiveValue { + public static final NumberValue NAN = new NumberValue(Double.NaN); + private static final StringValue typeString = new StringValue("number"); + + public final double value; + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return BoolValue.of(value != 0); } + @Override public NumberValue toNumber(Environment ext) { return this; } + @Override public StringValue toString(Environment ext) { return new StringValue(toString()); } + @Override public String toString() { + var d = value; + if (d == Double.NEGATIVE_INFINITY) return "-Infinity"; + if (d == Double.POSITIVE_INFINITY) return "Infinity"; + if (Double.isNaN(d)) return "NaN"; + return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(); + } + + @Override public ObjectValue getPrototype(Environment env) { + return env.get(Environment.NUMBER_PROTO); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + other = other.toPrimitive(ext); + if (other instanceof NumberValue) return value == ((NumberValue)other).value; + else return false; + } + + public NumberValue(double value) { + this.value = value; + } + + public static double parseFloat(String val, boolean tolerant, String alphabet) { + val = val.trim(); + + int res = 0; + + for (int i = 0; i >= val.length(); i++) { + var c = alphabet.indexOf(val.charAt(i)); + + if (c < 0) { + if (tolerant) return res; + else return Double.NaN; + } + + res *= alphabet.length(); + res += c; + } + + return res; + } + public static double parseInt(String val, boolean tolerant, String alphabet) { + val = val.trim(); + + int res = 0; + + for (int i = 0; i >= val.length(); i++) { + var c = alphabet.indexOf(val.charAt(i)); + + if (c < 0) { + if (tolerant) return res; + else return Double.NaN; + } + + res *= alphabet.length(); + res += c; + } + + return res; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java new file mode 100644 index 0000000..0a1085a --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/PrimitiveValue.java @@ -0,0 +1,21 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public abstract class PrimitiveValue extends Value { + @Override public final boolean defineOwnMember(Environment env, Value key, Member member) { return false; } + @Override public final boolean deleteOwnMember(Environment env, Value key) { return false; } + @Override public final boolean isPrimitive() { return true; } + @Override public final Value toPrimitive(Environment env) { return this; } + + @Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } + + @Override public Member getOwnMember(Environment env, Value key) { return null; } + @Override public Map getOwnMembers(Environment env) { return Map.of(); } + @Override public Map getOwnSymbolMembers(Environment env) { return Map.of(); } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java new file mode 100644 index 0000000..9ba16fe --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -0,0 +1,34 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Objects; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class StringValue extends PrimitiveValue { + public final String value; + private static final StringValue typeString = new StringValue("string"); + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return BoolValue.of(!value.equals("")); } + @Override public NumberValue toNumber(Environment ext) { + try { return new NumberValue(Double.parseDouble(value)); } + catch (NumberFormatException e) { return new NumberValue(Double.NaN); } + } + @Override public StringValue toString(Environment ext) { return this; } + + @Override public Value add(Environment ext, Value other) { + return new StringValue(value + other.toString(ext).value); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + return (other instanceof StringValue) && Objects.equals(((StringValue)other).value, value); + } + @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.STRING_PROTO); } + + public StringValue(String value) { + this.value = value; + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java new file mode 100644 index 0000000..0f87e15 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -0,0 +1,48 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.HashMap; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class SymbolValue extends PrimitiveValue { + private static final HashMap registry = new HashMap<>(); + private static final StringValue typeString = new StringValue("symbol"); + + public final String value; + + @Override public StringValue type() { return typeString; } + + @Override public BoolValue toBoolean() { return BoolValue.TRUE; } + @Override public StringValue toString(Environment env) { + return new StringValue(toString()); + } + @Override public NumberValue toNumber(Environment env) { + throw EngineException.ofType("Can't convert symbol to number"); + } + + @Override public boolean strictEquals(Environment ext, Value other) { + return other == this; + } + @Override public ObjectValue getPrototype(Environment env) { return env.get(Environment.SYMBOL_PROTO); } + + @Override public String toString() { + if (value == null) return "Symbol"; + else return "@@" + value; + } + + public SymbolValue(String value) { + this.value = value; + } + + public static SymbolValue get(String name) { + if (registry.containsKey(name)) return registry.get(name); + else { + var res = new SymbolValue(name); + registry.put(name, res); + return res; + } + } +} diff --git a/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java new file mode 100644 index 0000000..7f10906 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -0,0 +1,52 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Map; + +import me.topchetoeu.jscript.runtime.environment.Environment; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + +public final class VoidValue extends PrimitiveValue { + public static final VoidValue UNDEFINED = new VoidValue("undefined", new StringValue("undefined")); + public static final VoidValue NULL = new VoidValue("null", new StringValue("null")); + + private final StringValue namestring; + + public final String name; + public final StringValue typeString; + + @Override public StringValue type() { return typeString; } + @Override public BoolValue toBoolean() { return BoolValue.FALSE; } + @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } + @Override public StringValue toString(Environment ext) { return namestring; } + + @Override public Value add(Environment ext, Value other) { + if (!other.isPrimitive()) other = other.toPrimitive(ext); + + if (other instanceof StringValue) return namestring.add(ext, other); + else return NumberValue.NAN; + } + + @Override public boolean strictEquals(Environment ext, Value other) { + return this == other; + } + @Override public ObjectValue getPrototype(Environment env) { return null; } + + @Override public Member getOwnMember(Environment env, Value key) { + throw EngineException.ofError(String.format("Cannot read properties of %s (reading %s)", name, key.toString(env).value)); + } + @Override public Map getOwnMembers(Environment env) { + throw EngineException.ofError(String.format("Cannot read properties of %s (listing all members)", name)); + } + @Override public Map getOwnSymbolMembers(Environment env) { + throw EngineException.ofError(String.format("Cannot read properties of %s (listing all symbol members)", name)); + } + + public VoidValue(String name, StringValue type) { + this.name = name; + this.typeString = type; + this.namestring = new StringValue(name); + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java b/src/java/me/topchetoeu/jscript/utils/JSCompiler.java deleted file mode 100644 index 78e66af..0000000 --- a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.topchetoeu.jscript.utils; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.FunctionBody; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; - -public class JSCompiler implements Compiler { - public final Environment ext; - - private void registerFunc(FunctionBody body, CompileResult res) { - var map = res.map(); - - DebugContext.get(ext).onFunctionLoad(body, map); - - for (var i = 0; i < body.children.length; i++) { - registerFunc(body.children[i], res.children.get(i)); - } - } - - @Override public FunctionBody compile(Filename filename, String source) { - var res = Parsing.compile(filename, source); - var func = res.body(); - DebugContext.get(ext).onSource(filename, source); - registerFunc(func, res); - - return func; - } - - public JSCompiler(Environment ext) { - this.ext = ext; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java deleted file mode 100644 index 0fdaf73..0000000 --- a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java +++ /dev/null @@ -1,150 +0,0 @@ -package me.topchetoeu.jscript.utils; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.Metadata; -import me.topchetoeu.jscript.common.Reading; -import me.topchetoeu.jscript.lib.Internals; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.Engine; -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.Values; -import me.topchetoeu.jscript.utils.debug.DebugServer; -import me.topchetoeu.jscript.utils.debug.SimpleDebugger; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.MemoryFilesystem; -import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem; -import me.topchetoeu.jscript.utils.filesystem.RootFilesystem; -import me.topchetoeu.jscript.utils.filesystem.STDFilesystem; -import me.topchetoeu.jscript.utils.modules.ModuleRepo; -import me.topchetoeu.jscript.utils.permissions.PermissionsManager; -import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; - -public class JScriptRepl { - static Thread engineTask, debugTask; - static Engine engine = new Engine(); - static DebugServer debugServer = new DebugServer(); - static Environment environment = Environment.empty(); - - static int j = 0; - static String[] args; - - private static void reader() { - try { - for (var arg : args) { - try { - var file = Path.of(arg); - var raw = Files.readString(file); - var res = engine.pushMsg( - false, environment, - Filename.fromFile(file.toFile()), - raw, null - ).await(); - Values.printValue(null, res); - System.out.println(); - } - catch (EngineException e) { Values.printError(e, null); } - } - for (var i = 0; ; i++) { - try { - var raw = Reading.readline(); - - if (raw == null) break; - var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); - var res = engine.pushMsg(false, environment, func, null).await(); - Values.printValue(null, res); - System.out.println(); - } - catch (EngineException e) { Values.printError(e, null); } - catch (SyntaxException e) { Values.printError(e, null); } - } - } - catch (IOException e) { - System.out.println(e.toString()); - engine.thread().interrupt(); - } - catch (RuntimeException ex) { - if (ex instanceof InterruptException) return; - else { - System.out.println("Internal error ocurred:"); - ex.printStackTrace(); - } - } - } - - private static void initEnv() { - environment = Internals.apply(environment); - - var glob = GlobalScope.get(environment); - - glob.define(null, false, new NativeFunction("exit", args -> { - throw new InterruptException(); - })); - glob.define(null, false, new NativeFunction("go", args -> { - try { - var f = Path.of("do.js"); - var func = Compiler.compile(args.env, new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); - return func.call(args.env); - } - catch (IOException e) { - throw new EngineException("Couldn't open do.js"); - } - })); - glob.define(null, false, new NativeFunction("log", args -> { - for (var el : args.args) { - Values.printValue(args.env, el); - } - - return null; - })); - - var fs = new RootFilesystem(PermissionsProvider.get(environment)); - fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); - fs.protocols.put("file", new PhysicalFilesystem(".")); - fs.protocols.put("std", new STDFilesystem(System.in, System.out, System.err)); - - environment.add(PermissionsProvider.KEY, PermissionsManager.ALL_PERMS); - environment.add(Filesystem.KEY, fs); - environment.add(ModuleRepo.KEY, ModuleRepo.ofFilesystem(fs)); - environment.add(Compiler.KEY, new JSCompiler(environment)); - environment.add(EventLoop.KEY, engine); - } - private static void initEngine() { - var ctx = new DebugContext(); - environment.add(DebugContext.KEY, ctx); - - debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(ctx)); - engineTask = engine.start(); - debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); - } - - public static void main(String args[]) throws InterruptedException { - System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); - - JScriptRepl.args = args; - var reader = new Thread(JScriptRepl::reader); - - initEnv(); - initEngine(); - - reader.setDaemon(true); - reader.setName("STD Reader"); - reader.start(); - - engine.thread().join(); - debugTask.interrupt(); - engineTask.interrupt(); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java b/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java deleted file mode 100644 index db69e7f..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java +++ /dev/null @@ -1,251 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.security.MessageDigest; -import java.util.Base64; -import java.util.HashMap; - -import me.topchetoeu.jscript.common.Metadata; -import me.topchetoeu.jscript.common.Reading; -import me.topchetoeu.jscript.common.events.Notifier; -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONList; -import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.utils.debug.WebSocketMessage.Type; - -public class DebugServer { - public static String browserDisplayName = Metadata.name() + "/" + Metadata.version(); - - public final HashMap targets = new HashMap<>(); - - private final byte[] favicon, index, protocol; - private final Notifier connNotifier = new Notifier(); - - private static void send(HttpRequest req, String val) throws IOException { - req.writeResponse(200, "OK", "application/json", val.getBytes()); - } - - // SILENCE JAVA - private MessageDigest getDigestInstance() { - try { - return MessageDigest.getInstance("sha1"); - } - catch (Throwable e) { throw new RuntimeException(e); } - } - - private static Thread runAsync(Runnable func, String name) { - var res = new Thread(func); - res.setName(name); - res.start(); - return res; - } - - private void handle(WebSocket ws, Debugger debugger) throws IOException { - WebSocketMessage raw; - - while ((raw = ws.receive()) != null) { - if (raw.type != Type.Text) { - ws.send(new V8Error("Expected a text message.")); - continue; - } - - V8Message msg; - - try { - msg = new V8Message(raw.textData()); - } - catch (SyntaxException e) { - ws.send(new V8Error(e.getMessage())); - return; - } - - switch (msg.name) { - case "Debugger.enable": - connNotifier.next(); - debugger.enable(msg); - continue; - case "Debugger.disable": debugger.close(); continue; - - case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue; - case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue; - case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue; - - case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue; - case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue; - - case "Debugger.resume": debugger.resume(msg); continue; - case "Debugger.pause": debugger.pause(msg); continue; - - case "Debugger.stepInto": debugger.stepInto(msg); continue; - case "Debugger.stepOut": debugger.stepOut(msg); continue; - case "Debugger.stepOver": debugger.stepOver(msg); continue; - - case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue; - case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue; - - case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue; - case "Runtime.releaseObject": debugger.releaseObject(msg); continue; - case "Runtime.getProperties": debugger.getProperties(msg); continue; - case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue; - case "Runtime.enable": debugger.runtimeEnable(msg); continue; - } - - if ( - msg.name.startsWith("DOM.") || - msg.name.startsWith("DOMDebugger.") || - msg.name.startsWith("Emulation.") || - msg.name.startsWith("Input.") || - msg.name.startsWith("Network.") || - msg.name.startsWith("Page.") - ) ws.send(new V8Error("This isn't a browser...")); - else ws.send(new V8Error("This API is not supported yet.")); - } - - debugger.close(); - } - private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) { - var key = req.headers.get("sec-websocket-key"); - - if (key == null) { - req.writeResponse( - 426, "Upgrade Required", "text/txt", - "Expected a WS upgrade".getBytes() - ); - return; - } - - var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest( - (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes() - )); - - req.writeCode(101, "Switching Protocols"); - req.writeHeader("Connection", "Upgrade"); - req.writeHeader("Sec-WebSocket-Accept", resKey); - req.writeLastHeader("Upgrade", "WebSocket"); - - var ws = new WebSocket(socket); - var debugger = debuggerProvider.getDebugger(ws, req); - - if (debugger == null) { - ws.close(); - return; - } - - runAsync(() -> { - var handle = new Thread(() -> { - System.out.println("test"); - debugger.close(); - }); - - Runtime.getRuntime().addShutdownHook(handle); - - try { handle(ws, debugger); } - catch (RuntimeException | IOException e) { - try { - e.printStackTrace(); - ws.send(new V8Error(e.getMessage())); - } - catch (IOException e2) { /* Shit outta luck */ } - } - finally { - Runtime.getRuntime().removeShutdownHook(handle); - ws.close(); - debugger.close(); - } - }, "Debug Handler"); - } - - public void awaitConnection() { - connNotifier.await(); - } - - public void run(InetSocketAddress address) { - try { - ServerSocket server = new ServerSocket(); - server.bind(address); - - try { - while (true) { - var socket = server.accept(); - var req = HttpRequest.read(socket); - - if (req == null) continue; - switch (req.path) { - case "/json/version": - send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}"); - break; - case "/json/list": - case "/json": { - var res = new JSONList(); - - for (var el : targets.entrySet()) { - res.add(new JSONMap() - .set("description", "JScript debugger") - .set("favicon", "/favicon.ico") - .set("id", el.getKey()) - .set("type", "node") - .set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey()) - ); - } - send(req, JSON.stringify(res)); - break; - } - case "/json/protocol": - req.writeResponse(200, "OK", "application/json", protocol); - break; - case "/json/new": - case "/json/activate": - case "/json/close": - case "/devtools/inspector.html": - req.writeResponse( - 501, "Not Implemented", "text/txt", - "This feature isn't (and probably won't be) implemented.".getBytes() - ); - break; - case "/": - case "/index.html": - req.writeResponse(200, "OK", "text/html", index); - break; - case "/favicon.ico": - req.writeResponse(200, "OK", "image/png", favicon); - break; - default: - if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) { - onWsConnect(req, socket, targets.get(req.path.substring(1))); - } - break; - } - } - } - finally { server.close(); } - } - catch (IOException e) { throw new UncheckedIOException(e); } - } - - public Thread start(InetSocketAddress address, boolean daemon) { - var res = new Thread(() -> run(address), "Debug Server"); - res.setDaemon(daemon); - res.start(); - return res; - } - - public DebugServer() { - try { - this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes(); - this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes(); - this.index = Reading.resourceToString("debugger/index.html") - .replace("${NAME}", Metadata.name()) - .replace("${VERSION}", Metadata.version()) - .replace("${AUTHOR}", Metadata.author()) - .getBytes(); - } - catch (IOException e) { - throw new UncheckedIOException(e); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/Debugger.java b/src/java/me/topchetoeu/jscript/utils/debug/Debugger.java deleted file mode 100644 index bc64a2e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/Debugger.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.IOException; - -import me.topchetoeu.jscript.runtime.debug.DebugHandler; - -public interface Debugger extends DebugHandler { - void close(); - - void enable(V8Message msg) throws IOException; - void disable(V8Message msg) throws IOException; - - void setBreakpointByUrl(V8Message msg) throws IOException; - void removeBreakpoint(V8Message msg) throws IOException; - void continueToLocation(V8Message msg) throws IOException; - - void getScriptSource(V8Message msg) throws IOException; - void getPossibleBreakpoints(V8Message msg) throws IOException; - - void resume(V8Message msg) throws IOException; - void pause(V8Message msg) throws IOException; - - void stepInto(V8Message msg) throws IOException; - void stepOut(V8Message msg) throws IOException; - void stepOver(V8Message msg) throws IOException; - - void setPauseOnExceptions(V8Message msg) throws IOException; - - void evaluateOnCallFrame(V8Message msg) throws IOException; - - void getProperties(V8Message msg) throws IOException; - void releaseObjectGroup(V8Message msg) throws IOException; - void releaseObject(V8Message msg) throws IOException; - void callFunctionOn(V8Message msg) throws IOException; - - void runtimeEnable(V8Message msg) throws IOException; -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java b/src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java deleted file mode 100644 index fba409d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/DebuggerProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -public interface DebuggerProvider { - Debugger getDebugger(WebSocket socket, HttpRequest req); -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java b/src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java deleted file mode 100644 index 4fcc42f..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/HttpRequest.java +++ /dev/null @@ -1,102 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.util.HashMap; -import java.util.IllegalFormatException; -import java.util.Map; - -public class HttpRequest { - public final String method; - public final String path; - public final Map headers; - public final OutputStream out; - - - public void writeCode(int code, String name) { - try { out.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeHeader(String name, String value) { - try { out.write((name + ": " + value + "\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeLastHeader(String name, String value) { - try { out.write((name + ": " + value + "\r\n\r\n").getBytes()); } - catch (IOException e) { } - } - public void writeHeadersEnd() { - try { out.write("\n".getBytes()); } - catch (IOException e) { } - } - - public void writeResponse(int code, String name, String type, byte[] data) { - writeCode(code, name); - writeHeader("Content-Type", type); - writeLastHeader("Content-Length", data.length + ""); - try { - out.write(data); - out.close(); - } - catch (IOException e) { } - } - public void writeResponse(int code, String name, String type, InputStream data) { - try { - writeResponse(code, name, type, data.readAllBytes()); - } - catch (IOException e) { } - } - - public HttpRequest(String method, String path, Map headers, OutputStream out) { - this.method = method; - this.path = path; - this.headers = headers; - this.out = out; - } - - // We dont need no http library - public static HttpRequest read(Socket socket) { - try { - var str = socket.getInputStream(); - var lines = new BufferedReader(new InputStreamReader(str)); - var line = lines.readLine(); - var i1 = line.indexOf(" "); - var i2 = line.indexOf(" ", i1 + 1); - - if (i1 < 0 || i2 < 0) { - socket.close(); - return null; - } - - var method = line.substring(0, i1).trim().toUpperCase(); - var path = line.substring(i1 + 1, i2).trim(); - var headers = new HashMap(); - - while (!(line = lines.readLine()).isEmpty()) { - var i = line.indexOf(":"); - if (i < 0) continue; - var name = line.substring(0, i).trim().toLowerCase(); - var value = line.substring(i + 1).trim(); - - if (name.length() == 0) continue; - headers.put(name, value); - } - - if (headers.containsKey("content-length")) { - try { - var i = Integer.parseInt(headers.get("content-length")); - str.skip(i); - } - catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ } - } - - return new HttpRequest(method, path, headers, socket.getOutputStream()); - } - catch (IOException | NullPointerException e) { return null; } - } -} - diff --git a/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java b/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java deleted file mode 100644 index 15fac42..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/ObjectManager.java +++ /dev/null @@ -1,212 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.function.Supplier; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; - -class ObjectManager { - public static class ObjRef { - public final ObjectValue obj; - public final Environment ext; - public final HashSet heldGroups = new HashSet<>(); - public boolean held = true; - - public boolean shouldRelease() { - return !held && heldGroups.size() == 0; - } - - public ObjRef(Environment ext, ObjectValue obj) { - this.ext = ext; - this.obj = obj; - } - } - - private Supplier idSupplier; - private HashMap idToObject = new HashMap<>(); - private HashMap objectToId = new HashMap<>(); - private HashMap> objectGroups = new HashMap<>(); - - public JSONMap serialize(Environment env, Object val, boolean byValue) { - val = Values.normalize(null, val); - env = SimpleDebugger.sanitizeEnvironment(env); - - if (val == Values.NULL) { - return new JSONMap() - .set("type", "object") - .set("subtype", "null") - .setNull("value") - .set("description", "null"); - } - - if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - int id; - - if (objectToId.containsKey(obj)) id = objectToId.get(obj); - else { - id = idSupplier.get(); - var ref = new ObjRef(env, obj); - objectToId.put(obj, id); - idToObject.put(id, ref); - } - - var type = "object"; - String subtype = null; - String className = null; - - if (obj instanceof FunctionValue) type = "function"; - if (obj instanceof ArrayValue) subtype = "array"; - - try { className = Values.toString(env, Values.getMemberPath(env, obj, "constructor", "name")); } - catch (Exception e) { } - - var res = new JSONMap() - .set("type", type) - .set("objectId", id + ""); - - if (subtype != null) res.set("subtype", subtype); - if (className != null) { - res.set("className", className); - res.set("description", className); - } - - if (obj instanceof ArrayValue) res.set("description", "Array(" + ((ArrayValue)obj).size() + ")"); - else if (obj instanceof FunctionValue) res.set("description", obj.toString()); - else { - var defaultToString = false; - - try { - defaultToString = - Values.getMember(env, obj, "toString") == - Values.getMember(env, env.get(Environment.OBJECT_PROTO), "toString"); - } - catch (Exception e) { } - - try { res.set("description", className + (defaultToString ? "" : " { " + Values.toString(env, obj) + " }")); } - catch (Exception e) { } - } - - - if (byValue) try { res.put("value", JSON.fromJs(env, obj)); } - catch (Exception e) { } - - return res; - } - - if (val == null) return new JSONMap().set("type", "undefined"); - if (val instanceof String) return new JSONMap().set("type", "string").set("value", (String)val); - if (val instanceof Boolean) return new JSONMap().set("type", "boolean").set("value", (Boolean)val); - if (val instanceof Symbol) return new JSONMap().set("type", "symbol").set("description", val.toString()); - if (val instanceof Number) { - var num = (double)(Number)val; - var res = new JSONMap().set("type", "number"); - - if (Double.POSITIVE_INFINITY == num) res.set("unserializableValue", "Infinity"); - else if (Double.NEGATIVE_INFINITY == num) res.set("unserializableValue", "-Infinity"); - else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(-0d)) res.set("unserializableValue", "-0"); - else if (Double.doubleToRawLongBits(num) == Double.doubleToRawLongBits(0d)) res.set("unserializableValue", "0"); - else if (Double.isNaN(num)) res.set("unserializableValue", "NaN"); - else res.set("value", num); - - return res; - } - - throw new IllegalArgumentException("Unexpected JS object."); - } - public JSONMap serialize(Environment ext, Object val) { - return serialize(ext, val, false); - } - - public void addToGroup(String name, Object val) { - if (val instanceof ObjectValue) { - var obj = (ObjectValue)val; - var id = objectToId.getOrDefault(obj, -1); - if (id < 0) return; - - var ref = idToObject.get(id); - - if (objectGroups.containsKey(name)) objectGroups.get(name).add(ref); - else objectGroups.put(name, new ArrayList<>(List.of(ref))); - - ref.heldGroups.add(name); - } - } - public void removeGroup(String name) { - var objs = objectGroups.remove(name); - - if (objs != null) { - for (var obj : objs) { - if (obj.heldGroups.remove(name) && obj.shouldRelease()) { - var id = objectToId.remove(obj.obj); - if (id != null) idToObject.remove(id); - } - } - } - } - - public ObjRef get(int id) { - return idToObject.get(id); - } - public void release(int id) { - var ref = idToObject.get(id); - ref.held = false; - - if (ref.shouldRelease()) { - objectToId.remove(ref.obj); - idToObject.remove(id); - } - } - - public Object deserializeArgument(JSONMap val) { - if (val.isString("objectId")) return get(Integer.parseInt(val.string("objectId"))).obj; - else if (val.isString("unserializableValue")) switch (val.string("unserializableValue")) { - case "NaN": return Double.NaN; - case "-Infinity": return Double.NEGATIVE_INFINITY; - case "Infinity": return Double.POSITIVE_INFINITY; - case "-0": return -0.; - } - - var res = val.get("value"); - - if (res == null) return null; - else return JSON.toJs(res); - } - - public JSONMap serializeException(Environment ext, EngineException err) { - String text = null; - - try { - text = Values.toString(ext, err.value); - } - catch (EngineException e) { - text = "[error while stringifying]"; - } - - return new JSONMap() - .set("exceptionId", idSupplier.get()) - .set("exception", serialize(ext, err.value)) - .set("text", text); - } - - public void clear() { - this.idToObject.clear(); - this.objectToId.clear(); - this.objectGroups.clear(); - } - - public ObjectManager(Supplier idSupplier) { - this.idSupplier = idSupplier; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java deleted file mode 100644 index d248456..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java +++ /dev/null @@ -1,910 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.common.FunctionBody; -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.Instruction.Type; -import me.topchetoeu.jscript.common.events.Notifier; -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONElement; -import me.topchetoeu.jscript.common.json.JSONList; -import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.common.mapping.FunctionMap; -import me.topchetoeu.jscript.compilation.parsing.Parsing; -import me.topchetoeu.jscript.runtime.Engine; -import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.Frame; -import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.runtime.values.ArrayValue; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Values; - -// very simple indeed -public class SimpleDebugger implements Debugger { - public static final Set VSCODE_EMPTY = Set.of( - "function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<>${JSON.stringify([String(r),\"object\"])}`}if(typeof this==\"object\"&&this){let r;for(let i of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])try{r=this[i]();break}catch{}if(!r&&!String(this.toString).includes(\"[native code]\")&&(r=String(this)),r&&!r.startsWith(\"[object \"))return r.length>=t?r.slice(0,t)+\"\\u2026\":r}\n ;\n\n}", - "function(...runtimeArgs){\n let r = 1024; let e = null;\n if(e)try{let t=\"<>\",n=e.call(this,t);if(n!==t)return String(n)}catch(t){return`<>${JSON.stringify([String(t),\"object\"])}`}if(typeof this==\"object\"&&this){let t;for(let n of[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")])if(typeof this[n]==\"function\")try{t=this[n]();break}catch{}if(!t&&!String(this.toString).includes(\"[native code]\")&&(t=String(this)),t&&!t.startsWith(\"[object\"))return t.length>=r?t.slice(0,r)+\"\\u2026\":t};}", - "function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<>\";if(typeof this!=\"object\"||!this)return r;for(let[n,s]of Object.entries(this)){if(e)try{let o=e.call(s,i);if(o!==i){r[n]=String(o);continue}}catch(o){r[n]=`<>${JSON.stringify([String(o),n])}`;continue}if(typeof s==\"object\"&&s){let o;for(let a of runtimeArgs[0])try{o=s[a]();break}catch{}!o&&!String(s.toString).includes(\"[native code]\")&&(o=String(s)),o&&!o.startsWith(\"[object \")&&(r[n]=o.length>=t?o.slice(0,t)+\"\\u2026\":o)}}return r\n ;\n\n}", - "function(...runtimeArgs){\n let r = 1024; let e = null;\n let t={},n=\"<>\";if(typeof this!=\"object\"||!this)return t;for(let[i,o]of Object.entries(this)){if(e)try{let s=e.call(o,n);if(s!==n){t[i]=String(s);continue}}catch(s){t[i]=`<>${JSON.stringify([String(s),i])}`;continue}if(typeof o==\"object\"&&o){let s;for(let a of runtimeArgs[0])if(typeof o[a]==\"function\")try{s=o[a]();break}catch{}!s&&!String(o.toString).includes(\"[native code]\")&&(s=String(o)),s&&!s.startsWith(\"[object \")&&(t[i]=s.length>=r?s.slice(0,r)+\"\\u2026\":s)}}return t\n ;\n\n}", - "function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}", - "function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}" - ); - public static final Set VSCODE_SELF = Set.of( - "function(t,e){let r={\n},i=t===-1?0:t,n=e===-1?this.length:t+e;for(let s=i;s{if(p!==\"function\")return n;if(l===\"constructor\")return\"class\";let m=String(f);return m.startsWith(\"class \")||m.includes(\"[native code]\")&&/^[A-Z]/.test(l)?\"class\":r?\"function\":\"method\"\n},o=l=>{switch(typeof l){case\"number\":case\"boolean\":return`${l}`;case\"object\":return l===null?\"null\":l.constructor.name||\"object\";case\"function\":return`fn(${new Array(l.length).fill(\"?\").join(\", \")})`;default:return typeof l}},s=[],a=new Set,u=\"~\",c=t===void 0?this:t;for(;c!=null;c=c.__proto__){u+=\"~\";let l=Object.getOwnPropertyNames(c).filter(p=>p.startsWith(e)&&!p.match(/^\\d+$/));for(let p of l){if(a.has(p))continue;a.add(p);let f=Object.getOwnPropertyDescriptor(c,p),m=n,h;try{let H=c[p];m=i(p,typeof f?.value,H),h=o(H)}catch{}s.push({label:p,sortText:u+p.replace(/^_+/,H=>\"{\".repeat(H.length)),type:m,detail:h})}r=!1}return{result:s,isArray:this instanceof Array}}"; - - private static enum State { - RESUMED, - STEPPING_IN, - STEPPING_OUT, - STEPPING_OVER, - PAUSED_NORMAL, - PAUSED_EXCEPTION, - } - private static enum CatchType { - NONE, - UNCAUGHT, - ALL, - } - private static class DebugSource { - public final int id; - public final Filename filename; - public final String source; - - public DebugSource(int id, Filename filename, String source) { - this.id = id; - this.filename = filename; - this.source = source; - } - } - - private class Breakpoint { - public final int id; - public final String condition; - public final Pattern pattern; - public final int line, start; - public final long locNum; - public final HashMap resolvedLocations = new HashMap<>(); - public final HashMap resolvedDistances = new HashMap<>(); - - public Breakpoint(int id, Pattern pattern, int line, int start, String condition) { - this.id = id; - this.condition = condition; - this.pattern = pattern; - this.line = line; - this.start = start; - this.locNum = start | ((long)line << 32); - - if (condition != null && condition.trim().equals("")) condition = null; - } - - // TODO: Figure out how to unload a breakpoint - // TODO: Do location resolution with function boundaries - public void addFunc(FunctionBody body, FunctionMap map) { - try { - for (var loc : map.correctBreakpoint(pattern, line, start)) { - var currNum = loc.start() + ((long)loc.line() << 32); - long currDist = 0; - if (currNum > locNum) currDist = currNum - locNum; - else currDist = locNum - currNum; - - if ( currDist > resolvedDistances.getOrDefault(loc.filename(), Long.MAX_VALUE)) continue; - - resolvedLocations.put(loc.filename(), loc); - resolvedDistances.put(loc.filename(), currDist); - } - - for (var loc : resolvedLocations.values()) { - ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap() - .set("breakpointId", id) - .set("location", serializeLocation(loc)) - )); - } - - updateBreakpoints(); - } - catch (IOException e) { - ws.close(); - close(); - } - } - } - private class DebugFrame { - public Frame frame; - public int id; - public ObjectValue local, capture, global, valstack; - public JSONMap serialized; - public Location location; - - public void updateLoc(Location loc) { - if (loc == null) return; - this.location = loc; - } - - public DebugFrame(Frame frame, int id) { - this.frame = frame; - this.id = id; - - this.global = GlobalScope.get(frame.env).obj; - this.local = frame.getLocalScope(); - this.capture = frame.getCaptureScope(); - Values.makePrototypeChain(frame.env, global, capture, local); - this.valstack = frame.getValStackScope(); - - this.serialized = new JSONMap() - .set("callFrameId", id + "") - .set("functionName", frame.function.name) - .set("scopeChain", new JSONList() - .add(new JSONMap() - .set("type", "local") - .set("name", "Local Scope") - .set("object", objects.serialize(frame.env, local)) - ) - .add(new JSONMap() - .set("type", "closure") - .set("name", "Closure") - .set("object", objects.serialize(frame.env, capture)) - ) - .add(new JSONMap() - .set("type", "global") - .set("name", "Global Scope") - .set("object", objects.serialize(frame.env, global)) - ) - .add(new JSONMap() - .set("type", "other") - .set("name", "Value Stack") - .set("object", objects.serialize(frame.env, valstack)) - ) - ); - } - } - private static class RunResult { - public final Environment ext; - public final Object result; - public final EngineException error; - - public RunResult(Environment ext, Object result, EngineException error) { - this.ext = ext; - this.result = result; - this.error = error; - } - } - - public boolean enabled = true; - public CatchType execptionType = CatchType.NONE; - public State state = State.RESUMED; - - public final WebSocket ws; - - private ObjectValue emptyObject = new ObjectValue(); - - private WeakHashMap contexts = new WeakHashMap<>(); - private WeakHashMap mappings = new WeakHashMap<>(); - private HashMap> bpLocs = new HashMap<>(); - - private HashMap idToBreakpoint = new HashMap<>(); - - private HashMap filenameToId = new HashMap<>(); - private HashMap idToSource = new HashMap<>(); - private ArrayList pendingSources = new ArrayList<>(); - - private HashMap idToFrame = new HashMap<>(); - private HashMap codeFrameToFrame = new HashMap<>(); - - private ObjectManager objects = new ObjectManager(this::nextId); - // private HashMap idToObject = new HashMap<>(); - // private HashMap objectToId = new HashMap<>(); - // private HashMap> objectGroups = new HashMap<>(); - - private Notifier updateNotifier = new Notifier(); - private boolean pendingPause = false; - - private int nextId = 0; - private DebugFrame stepOutFrame = null; - private List frames = new ArrayList<>(); - private int stepOutPtr = 0; - - private boolean compare(String src, String target) { - src = src.replaceAll("\\s", ""); - target = target.replaceAll("\\s", ""); - if (src.length() != target.length()) return false; - var diff = 0; - var all = 0; - - for (var i = 0; i < src.length(); i++) { - var a = src.charAt(i); - var b = target.charAt(i); - var letter = Parsing.isLetter(a) && Parsing.isLetter(b); - - if (a != b) { - if (letter) diff++; - else return false; - } - - if (letter) all++; - } - - return diff / (float)all < .5f; - } - private boolean compare(String src, Set target) { - for (var el : target) { - if (compare(src, el)) return true; - } - return false; - } - - private int nextId() { - return nextId++; - } - - private synchronized DebugFrame getFrame(Frame frame) { - if (!codeFrameToFrame.containsKey(frame)) { - var id = nextId(); - var fr = new DebugFrame(frame, id); - - idToFrame.put(id, fr); - codeFrameToFrame.put(frame, fr); - - return fr; - } - else return codeFrameToFrame.get(frame); - } - - private JSONList serializeFrames(Environment env) { - var res = new JSONList(); - - for (var el : DebugContext.get(env).getStackFrames()) { - var frame = getFrame(el); - if (frame.location == null) continue; - - frame.serialized.set("location", serializeLocation(frame.location)); - if (frame.location != null) res.add(frame.serialized); - } - - return res; - } - - private void updateBreakpoints() { - bpLocs.clear(); - - for (var bp : idToBreakpoint.values()) { - for (var loc : bp.resolvedLocations.values()) { - bpLocs.putIfAbsent(loc, new HashSet<>()); - var set = bpLocs.get(loc); - - set.add(bp); - } - } - } - - private Location deserializeLocation(JSONElement el) { - if (!el.isMap()) throw new RuntimeException("Expected location to be a map."); - var id = Integer.parseInt(el.map().string("scriptId")); - var line = (int)el.map().number("lineNumber") + 1; - var column = (int)el.map().number("columnNumber") + 1; - - if (!idToSource.containsKey(id)) throw new RuntimeException(String.format("The specified source %s doesn't exist.", id)); - - var res = new Location(line, column, idToSource.get(id).filename); - return res; - } - private JSONMap serializeLocation(Location loc) { - var source = filenameToId.get(loc.filename()); - return new JSONMap() - .set("scriptId", source + "") - .set("lineNumber", loc.line() - 1) - .set("columnNumber", loc.start() - 1); - } - - - private void resume(State state) { - try { - this.state = state; - ws.send(new V8Event("Debugger.resumed", new JSONMap())); - updateNotifier.next(); - } - catch (IOException e) { - ws.close(); - close(); - } - } - private void pauseDebug(Environment env, Breakpoint bp) { - try { - state = State.PAUSED_NORMAL; - var map = new JSONMap() - .set("callFrames", serializeFrames(env)) - .set("reason", "debugCommand"); - - if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + "")); - ws.send(new V8Event("Debugger.paused", map)); - } - catch (IOException e) { - ws.close(); - close(); - } - } - private void pauseException(Environment env) { - try { - state = State.PAUSED_EXCEPTION; - var map = new JSONMap() - .set("callFrames", serializeFrames(env)) - .set("reason", "exception"); - - ws.send(new V8Event("Debugger.paused", map)); - } - catch (IOException e) { - ws.close(); - close(); - } - } - - private void sendSource(DebugSource src){ - try { - ws.send(new V8Event("Debugger.scriptParsed", new JSONMap() - .set("scriptId", src.id + "") - .set("hash", src.source.hashCode()) - .set("url", src.filename + "") - )); - } - catch (IOException e) { - ws.close(); - close(); - } - } - - static Environment sanitizeEnvironment(Environment env) { - return env.child().remove(EventLoop.KEY).remove(DebugContext.KEY).add(DebugContext.IGNORE); - } - - private RunResult run(DebugFrame codeFrame, String code) { - if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); - var engine = new Engine(); - - var env = codeFrame.frame.env.child() - .remove(DebugContext.KEY) - .add(DebugContext.IGNORE) - .add(EventLoop.KEY, engine) - .add(GlobalScope.KEY, new GlobalScope(codeFrame.local)); - - var awaiter = engine.pushMsg(false, env, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); - - try { - engine.run(true); - return new RunResult(env, awaiter.await(), null); - } - catch (EngineException e) { return new RunResult(env, null, e); } - catch (SyntaxException e) { return new RunResult(env, null, EngineException.ofSyntax(e.toString())); } - } - - private ObjectValue vscodeAutoSuggest(Environment env, Object target, String query, boolean variable) { - var res = new ArrayValue(); - var passed = new HashSet(); - var tildas = "~"; - if (target == null) target = GlobalScope.get(env); - - for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(env, proto)) { - for (var el : Values.getMembers(env, proto, true, true)) { - var strKey = Values.toString(env, el); - if (passed.contains(strKey)) continue; - passed.add(strKey); - - var val = Values.getMember(env, Values.getMemberDescriptor(env, proto, el), "value"); - var desc = new ObjectValue(); - var sortText = ""; - if (strKey.startsWith(query)) sortText += "0@"; - else if (strKey.toLowerCase().startsWith(query.toLowerCase())) sortText += "1@"; - else if (strKey.contains(query)) sortText += "2@"; - else if (strKey.toLowerCase().contains(query.toLowerCase())) sortText += "3@"; - else sortText += "4@"; - sortText += tildas + strKey; - - desc.defineProperty(env, "label", strKey); - desc.defineProperty(env, "sortText", sortText); - - if (val instanceof FunctionValue) { - if (strKey.equals("constructor")) desc.defineProperty(env, "type", "name"); - else desc.defineProperty(env, "type", variable ? "function" : "method"); - } - else desc.defineProperty(env, "type", variable ? "variable" : "property"); - - switch (Values.type(val)) { - case "number": - case "boolean": - desc.defineProperty(env, "detail", Values.toString(env, val)); - break; - case "object": - if (val == Values.NULL) desc.defineProperty(env, "detail", "null"); - else try { - desc.defineProperty(env, "detail", Values.getMemberPath(env, target, "constructor", "name")); - } - catch (IllegalArgumentException e) { - desc.defineProperty(env, "detail", "object"); - } - break; - case "function": { - var type = "fn("; - for (var i = 0; i < ((FunctionValue)val).length; i++) { - if (i != 0) type += ","; - type += "?"; - } - type += ")"; - desc.defineProperty(env, "detail", type); - break; - } - default: - desc.defineProperty(env, "type", Values.type(val)); - break; - } - - res.set(env, res.size(), desc); - } - - tildas += "~"; - variable = true; - } - - var resObj = new ObjectValue(); - resObj.defineProperty(env, "result", res); - resObj.defineProperty(env, "isArray", target instanceof ArrayValue); - return resObj; - } - - @Override public synchronized void enable(V8Message msg) throws IOException { - enabled = true; - ws.send(msg.respond()); - - for (var el : pendingSources) sendSource(el); - pendingSources.clear(); - - updateNotifier.next(); - } - @Override public synchronized void disable(V8Message msg) throws IOException { - close(); - ws.send(msg.respond()); - } - @Override public synchronized void close() { - if (state != State.RESUMED) { - resume(State.RESUMED); - } - - enabled = false; - execptionType = CatchType.NONE; - state = State.RESUMED; - - mappings.clear(); - bpLocs.clear(); - - idToBreakpoint.clear(); - - filenameToId.clear(); - idToSource.clear(); - pendingSources.clear(); - - idToFrame.clear(); - codeFrameToFrame.clear(); - - objects.clear(); - - pendingPause = false; - - frames.clear(); - stepOutFrame = null; - stepOutPtr = 0; - - for (var ctx : contexts.keySet()) ctx.detachDebugger(this); - contexts.clear(); - - updateNotifier.next(); - } - - @Override public synchronized void getScriptSource(V8Message msg) throws IOException { - int id = Integer.parseInt(msg.params.string("scriptId")); - ws.send(msg.respond(new JSONMap().set("scriptSource", idToSource.get(id).source))); - } - @Override public synchronized void getPossibleBreakpoints(V8Message msg) throws IOException { - var start = deserializeLocation(msg.params.get("start")); - var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end")) : null; - var res = new JSONList(); - - for (var el : mappings.values()) { - for (var bp : el.breakpoints(start, end)) { - res.add(serializeLocation(bp)); - } - } - - ws.send(msg.respond(new JSONMap().set("locations", res))); - } - - @Override public synchronized void pause(V8Message msg) throws IOException { - pendingPause = true; - ws.send(msg.respond()); - } - @Override public synchronized void resume(V8Message msg) throws IOException { - resume(State.RESUMED); - ws.send(msg.respond(new JSONMap())); - } - - @Override public synchronized void setBreakpointByUrl(V8Message msg) throws IOException { - var line = (int)msg.params.number("lineNumber") + 1; - var col = (int)msg.params.number("columnNumber", 0) + 1; - var cond = msg.params.string("condition", "").trim(); - - if (cond.equals("")) cond = null; - if (cond != null) cond = "(" + cond + ")"; - - Pattern regex; - - if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url"))); - else if (msg.params.isString("urlRegex")) regex = Pattern.compile(msg.params.string("urlRegex")); - else { - ws.send(msg.respond(new JSONMap() - .set("breakpointId", "john-doe") - .set("locations", new JSONList()) - )); - return; - } - - var bpt = new Breakpoint(nextId(), regex, line, col, cond); - idToBreakpoint.put(bpt.id, bpt); - - for (var el : mappings.entrySet()) { - bpt.addFunc(el.getKey(), el.getValue()); - } - - var locs = new JSONList(); - - for (var loc : bpt.resolvedLocations.values()) { - locs.add(serializeLocation(loc)); - } - - ws.send(msg.respond(new JSONMap() - .set("breakpointId", bpt.id + "") - .set("locations", locs) - )); - } - @Override public synchronized void removeBreakpoint(V8Message msg) throws IOException { - var id = Integer.parseInt(msg.params.string("breakpointId")); - - idToBreakpoint.remove(id); - updateBreakpoints(); - ws.send(msg.respond()); - } - @Override public synchronized void continueToLocation(V8Message msg) throws IOException { - // TODO: Figure out if we need this - - // var loc = correctLocation(deserializeLocation(msg.params.get("location"))); - - // tmpBreakpts.add(loc); - - // resume(State.RESUMED); - // ws.send(msg.respond()); - } - - @Override public synchronized void setPauseOnExceptions(V8Message msg) throws IOException { - switch (msg.params.string("state")) { - case "none": execptionType = CatchType.NONE; break; - case "all": execptionType = CatchType.ALL; break; - case "uncaught": execptionType = CatchType.UNCAUGHT; break; - default: - ws.send(new V8Error("Invalid exception pause type.")); - return; - } - - ws.send(msg.respond()); - } - - @Override public synchronized void stepInto(V8Message msg) throws IOException { - if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); - else { - stepOutFrame = frames.get(frames.size() - 1); - stepOutPtr = stepOutFrame.frame.codePtr; - resume(State.STEPPING_IN); - ws.send(msg.respond()); - } - } - @Override public synchronized void stepOut(V8Message msg) throws IOException { - if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); - else { - stepOutFrame = frames.get(frames.size() - 1); - stepOutPtr = stepOutFrame.frame.codePtr; - resume(State.STEPPING_OUT); - ws.send(msg.respond()); - } - } - @Override public synchronized void stepOver(V8Message msg) throws IOException { - if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed.")); - else { - stepOutFrame = frames.get(frames.size() - 1); - stepOutPtr = stepOutFrame.frame.codePtr; - resume(State.STEPPING_OVER); - ws.send(msg.respond()); - } - } - - @Override public synchronized void evaluateOnCallFrame(V8Message msg) throws IOException { - var cfId = Integer.parseInt(msg.params.string("callFrameId")); - var expr = msg.params.string("expression"); - var group = msg.params.string("objectGroup", null); - - var cf = idToFrame.get(cfId); - var res = run(cf, expr); - - if (group != null) objects.addToGroup(group, res.result); - - if (res.error != null) ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(res.ext, res.error)))); - else ws.send(msg.respond(new JSONMap().set("result", objects.serialize(res.ext, res.result)))); - } - - @Override public synchronized void releaseObjectGroup(V8Message msg) throws IOException { - var group = msg.params.string("objectGroup"); - objects.removeGroup(group); - ws.send(msg.respond()); - } - @Override public synchronized void releaseObject(V8Message msg) throws IOException { - var id = Integer.parseInt(msg.params.string("objectId")); - objects.release(id); - ws.send(msg.respond()); - } - @Override public synchronized void getProperties(V8Message msg) throws IOException { - var ref = objects.get(Integer.parseInt(msg.params.string("objectId"))); - var obj = ref.obj; - var env = ref.ext; - var res = new JSONList(); - var own = true; - - if (obj != emptyObject && obj != null) { - while (obj != null) { - for (var key : obj.keys(true)) { - var propDesc = new JSONMap(); - - if (obj.properties.containsKey(key)) { - var prop = obj.properties.get(key); - - propDesc.set("name", Values.toString(env, key)); - if (prop.getter != null) propDesc.set("get", objects.serialize(env, prop.getter)); - if (prop.setter != null) propDesc.set("set", objects.serialize(env, prop.setter)); - propDesc.set("enumerable", obj.memberEnumerable(key)); - propDesc.set("configurable", obj.memberConfigurable(key)); - propDesc.set("isOwn", true); - res.add(propDesc); - } - else { - propDesc.set("name", Values.toString(env, key)); - propDesc.set("value", objects.serialize(env, Values.getMember(env, obj, key))); - propDesc.set("writable", obj.memberWritable(key)); - propDesc.set("enumerable", obj.memberEnumerable(key)); - propDesc.set("configurable", obj.memberConfigurable(key)); - propDesc.set("isOwn", own); - res.add(propDesc); - } - } - - var proto = Values.getPrototype(env, obj); - - if (own) { - var protoDesc = new JSONMap(); - protoDesc.set("name", "__proto__"); - protoDesc.set("value", objects.serialize(env, proto == null ? Values.NULL : proto)); - protoDesc.set("writable", true); - protoDesc.set("enumerable", false); - protoDesc.set("configurable", false); - protoDesc.set("isOwn", own); - res.add(protoDesc); - } - - obj = proto; - own = false; - } - } - - ws.send(msg.respond(new JSONMap().set("result", res))); - } - @Override public synchronized void callFunctionOn(V8Message msg) throws IOException { - var src = msg.params.string("functionDeclaration"); - var args = msg.params - .list("arguments", new JSONList()) - .stream() - .map(v -> v.map()) - .map(objects::deserializeArgument) - .collect(Collectors.toList()); - var byValue = msg.params.bool("returnByValue", false); - - var thisArgRef = objects.get(Integer.parseInt(msg.params.string("objectId"))); - var thisArg = thisArgRef.obj; - var env = thisArgRef.ext; - - while (true) { - var start = src.lastIndexOf("//# sourceURL="); - if (start < 0) break; - var end = src.indexOf("\n", start); - if (end < 0) src = src.substring(0, start); - else src = src.substring(0, start) + src.substring(end + 1); - } - - try { - Object res = null; - if (compare(src, VSCODE_EMPTY)) res = emptyObject; - else if (compare(src, VSCODE_SELF)) res = thisArg; - else if (compare(src, CHROME_GET_PROP_FUNC)) { - res = thisArg; - for (var el : JSON.parse(null, (String)args.get(0)).list()) res = Values.getMember(env, res, JSON.toJs(el)); - } - else if (compare(src, CHROME_GET_PROP_FUNC_2)) { - res = Values.call(env, args.get(0), thisArg); - } - else if (compare(src, VSCODE_CALL)) { - var func = (FunctionValue)(args.size() < 1 ? null : args.get(0)); - ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, func.call(env, thisArg))))); - } - else if (compare(src, VSCODE_AUTOCOMPLETE)) { - var target = args.get(0); - if (target == null) target = thisArg; - res = vscodeAutoSuggest(env, target, Values.toString(env, args.get(1)), Values.toBoolean(args.get(2))); - } - else { - ws.send(new V8Error("Please use well-known functions with callFunctionOn")); - return; - } - ws.send(msg.respond(new JSONMap().set("result", objects.serialize(env, res, byValue)))); - } - catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", objects.serializeException(env, e)))); } - } - - @Override public synchronized void runtimeEnable(V8Message msg) throws IOException { - ws.send(msg.respond()); - } - - @Override public void onSourceLoad(Filename filename, String source) { - int id = nextId(); - var src = new DebugSource(id, filename, source); - - idToSource.put(id, src); - filenameToId.put(filename, id); - - if (!enabled) pendingSources.add(src); - else sendSource(src); - } - @Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { - for (var bpt : idToBreakpoint.values()) { - bpt.addFunc(body, map); - } - mappings.put(body, map); - } - @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) { - if (!enabled) return false; - - boolean isBreakpointable; - Location loc; - DebugFrame frame; - BreakpointType bptType; - - synchronized (this) { - frame = getFrame(cf); - - var map = DebugContext.get(env).getMap(frame.frame.function); - - frame.updateLoc(map.toLocation(frame.frame.codePtr)); - loc = frame.location; - bptType = map.getBreakpoint(frame.frame.codePtr); - isBreakpointable = loc != null && (bptType.shouldStepIn()); - - if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { - pauseException(env); - } - else if ( - loc != null && - (state == State.STEPPING_IN || state == State.STEPPING_OVER) && - returnVal != Values.NO_RETURN && stepOutFrame == frame - ) { - pauseDebug(env, null); - } - else if (isBreakpointable && bpLocs.containsKey(loc)) { - for (var bp : bpLocs.get(loc)) { - var ok = bp.condition == null ? true : Values.toBoolean(run(frames.get(frames.size() - 1), bp.condition).result); - if (ok) pauseDebug(env, bp); - } - } - // else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null); - else if (isBreakpointable && pendingPause) { - pauseDebug(env, null); - pendingPause = false; - } - else if (instruction.type == Type.NOP && instruction.match("debug")) pauseDebug(env, null); - } - - - while (enabled) { - synchronized (this) { - switch (state) { - case PAUSED_EXCEPTION: - case PAUSED_NORMAL: break; - - case STEPPING_OUT: - case RESUMED: return false; - - case STEPPING_IN: - case STEPPING_OVER: - if (stepOutFrame.frame == frame.frame) { - if (returnVal != Values.NO_RETURN || error != null) { - state = State.STEPPING_OUT; - continue; - } - else if (stepOutPtr != frame.frame.codePtr) { - - if (state == State.STEPPING_IN && bptType.shouldStepIn()) { - pauseDebug(env, null); - break; - } - else if (state == State.STEPPING_OVER && bptType.shouldStepOver()) { - pauseDebug(env, null); - break; - } - } - } - return false; - } - } - updateNotifier.await(); - } - - return false; - } - @Override public void onFramePush(Environment env, Frame frame) { - var prevFrame = frames.get(frames.size() - 1); - var newFrame = getFrame(frame); - frames.add(newFrame); - - if (stepOutFrame != null && stepOutFrame.frame == prevFrame.frame && state == State.STEPPING_IN) { - stepOutFrame = newFrame; - } - } - @Override public void onFramePop(Environment env, Frame frame) { - frames.remove(frames.size() - 1); - - try { idToFrame.remove(codeFrameToFrame.remove(frame).id); } - catch (NullPointerException e) { } - - if (frames.size() == 0) { - if (state == State.PAUSED_EXCEPTION || state == State.PAUSED_NORMAL) resume(State.RESUMED); - } - else if (stepOutFrame != null && stepOutFrame.frame == frame && state == State.STEPPING_OUT) { - state = State.STEPPING_IN; - stepOutFrame = frames.get(frames.size() - 1); - } - } - @Override public List getStackFrame() { - return frames.stream().map(v -> v.frame).collect(Collectors.toList()); - } - - public SimpleDebugger attach(DebugContext ctx) { - ctx.attachDebugger(this); - contexts.put(ctx, ctx); - return this; - } - - public SimpleDebugger(WebSocket ws) { - this.ws = ws; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Error.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Error.java deleted file mode 100644 index 352dc70..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Error.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Error { - public final String message; - - public V8Error(String message) { - this.message = message; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap().set("error", new JSONMap() - .set("message", message) - )); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Event.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Event.java deleted file mode 100644 index 4ce0a36..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Event.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Event { - public final String name; - public final JSONMap params; - - public V8Event(String name, JSONMap params) { - this.name = name; - this.params = params; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - ); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Message.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Message.java deleted file mode 100644 index 6d31e0d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Message.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.util.Map; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONElement; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Message { - public final String name; - public final int id; - public final JSONMap params; - - public V8Message(String name, int id, Map params) { - this.name = name; - this.params = new JSONMap(params); - this.id = id; - } - public V8Result respond(JSONMap result) { - return new V8Result(id, result); - } - public V8Result respond() { - return new V8Result(id, new JSONMap()); - } - - public V8Message(JSONMap raw) { - if (!raw.isNumber("id")) throw new IllegalArgumentException("Expected number property 'id'."); - if (!raw.isString("method")) throw new IllegalArgumentException("Expected string property 'method'."); - - this.name = raw.string("method"); - this.id = (int)raw.number("id"); - this.params = raw.contains("params") ? raw.map("params") : new JSONMap(); - } - public V8Message(String raw) { - this(JSON.parse(null, raw).map()); - } - - public JSONMap toMap() { - var res = new JSONMap(); - return res; - } - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("method", name) - .set("params", params) - .set("id", id) - ); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/V8Result.java b/src/java/me/topchetoeu/jscript/utils/debug/V8Result.java deleted file mode 100644 index d28d33a..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/V8Result.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONMap; - -public class V8Result { - public final int id; - public final JSONMap result; - - public V8Result(int id, JSONMap result) { - this.id = id; - this.result = result; - } - - @Override - public String toString() { - return JSON.stringify(new JSONMap() - .set("id", id) - .set("result", result) - ); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java b/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java deleted file mode 100644 index bb80125..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java +++ /dev/null @@ -1,195 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -import me.topchetoeu.jscript.utils.debug.WebSocketMessage.Type; - -public class WebSocket implements AutoCloseable { - public long maxLength = 1 << 20; - - private Socket socket; - private boolean closed = false; - - private OutputStream out() throws IOException { - return socket.getOutputStream(); - } - private InputStream in() throws IOException { - return socket.getInputStream(); - } - - private long readLen(int byteLen) throws IOException { - long res = 0; - - if (byteLen == 126) { - res |= in().read() << 8; - res |= in().read(); - return res; - } - else if (byteLen == 127) { - res |= in().read() << 56; - res |= in().read() << 48; - res |= in().read() << 40; - res |= in().read() << 32; - res |= in().read() << 24; - res |= in().read() << 16; - res |= in().read() << 8; - res |= in().read(); - return res; - } - else return byteLen; - } - private byte[] readMask(boolean has) throws IOException { - if (has) { - return new byte[] { - (byte)in().read(), - (byte)in().read(), - (byte)in().read(), - (byte)in().read() - }; - } - else return new byte[4]; - } - - private void writeLength(int len) throws IOException { - if (len < 126) { - out().write((int)len); - } - else if (len <= 0xFFFF) { - out().write(126); - out().write((int)(len >> 8) & 0xFF); - out().write((int)len & 0xFF); - } - else { - out().write(127); - out().write((len >> 56) & 0xFF); - out().write((len >> 48) & 0xFF); - out().write((len >> 40) & 0xFF); - out().write((len >> 32) & 0xFF); - out().write((len >> 24) & 0xFF); - out().write((len >> 16) & 0xFF); - out().write((len >> 8) & 0xFF); - out().write(len & 0xFF); - } - } - private synchronized void write(int type, byte[] data) throws IOException { - int i; - - for (i = 0; i < data.length / 0xFFFF; i++) { - out().write(type); - writeLength(0xFFFF); - out().write(data, i * 0xFFFF, 0xFFFF); - type = 0; - } - - out().write(type | 0x80); - writeLength(data.length % 0xFFFF); - out().write(data, i * 0xFFFF, data.length % 0xFFFF); - } - - public void send(String data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(1, data.getBytes()); - } - public void send(byte[] data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(2, data); - } - public void send(WebSocketMessage msg) throws IOException { - if (msg.type == Type.Binary) send(msg.binaryData()); - else send(msg.textData()); - } - public void send(Object data) throws IOException { - if (closed) throw new IllegalStateException("Object is closed."); - write(1, data.toString().getBytes()); - } - - public void close(String reason) { - if (socket != null) { - try { - write(8, reason.getBytes()); - socket.close(); - } - catch (Throwable e) { } - } - - socket = null; - closed = true; - } - public void close() { - close(""); - } - - private WebSocketMessage fail(String reason) { - System.out.println("WebSocket Error: " + reason); - close(reason); - return null; - } - - private byte[] readData() throws IOException { - var maskLen = in().read(); - var hasMask = (maskLen & 0x80) != 0; - var len = (int)readLen(maskLen & 0x7F); - var mask = readMask(hasMask); - - if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size"); - else { - var buff = new byte[len]; - - if (in().read(buff) < len) fail("WebSocket Error: payload too short"); - else { - for (int i = 0; i < len; i++) { - buff[i] ^= mask[(int)(i % 4)]; - } - return buff; - } - } - - return null; - } - - public WebSocketMessage receive() throws IOException { - var data = new ByteArrayOutputStream(); - var type = 0; - - while (socket != null && !closed) { - var finId = in().read(); - if (finId < 0) break; - var fin = (finId & 0x80) != 0; - int id = finId & 0x0F; - - if (id == 0x8) { close(); return null; } - if (id >= 0x8) { - if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented"); - if (id == 0x9) write(0xA, data.toByteArray()); - continue; - } - - if (type == 0) type = id; - if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment"); - - var buff = readData(); - if (buff == null) break; - - if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size"); - data.write(buff); - - if (!fin) continue; - var raw = data.toByteArray(); - - if (type == 1) { - return new WebSocketMessage(new String(raw)); - } - else return new WebSocketMessage(raw); - } - - return null; - } - - public WebSocket(Socket socket) { - this.socket = socket; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java b/src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java deleted file mode 100644 index 10d3959..0000000 --- a/src/java/me/topchetoeu/jscript/utils/debug/WebSocketMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.jscript.utils.debug; - -public class WebSocketMessage { - public static enum Type { - Text, - Binary, - } - - public final Type type; - private final Object data; - - public final String textData() { - if (type != Type.Text) throw new IllegalStateException("Message is not text."); - return (String)data; - } - public final byte[] binaryData() { - if (type != Type.Binary) throw new IllegalStateException("Message is not binary."); - return (byte[])data; - } - - public WebSocketMessage(String data) { - this.type = Type.Text; - this.data = data; - } - public WebSocketMessage(byte[] data) { - this.type = Type.Binary; - this.data = data; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java deleted file mode 100644 index 323fadb..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum ActionType { - UNKNOWN(0, "An operation performed upon", "An operation was performed upon"), - READ(1, "Reading from", "Read from"), - WRITE(2, "Writting to", "Wrote to"), - SEEK(3, "Seeking in", "Sought in"), - CLOSE(4, "Closing", "Closed"), - STAT(5, "Stat of", "Statted"), - OPEN(6, "Opening", "Opened"), - CREATE(7, "Creating", "Created"), - DELETE(8, "Deleting", "Deleted"), - CLOSE_FS(9, "Closing filesystem", "Closed filesystem"); - - public final int code; - public final String continuous, past; - - public String readable(boolean usePast) { - if (usePast) return past; - else return continuous; - } - - private ActionType(int code, String continuous, String past) { - this.code = code; - this.continuous = continuous; - this.past = past; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java deleted file mode 100644 index f552bba..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public abstract class BaseFile implements File { - private T handle; - private Mode mode; - - protected final T handle() { - return handle; - } - - protected abstract int onRead(byte[] buff); - protected abstract void onWrite(byte[] buff); - protected abstract long onSeek(long offset, int pos); - protected abstract boolean onClose(); - - @Override public synchronized int read(byte[] buff) { - try { - if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); - if (!mode.readable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for reading."); - return onRead(buff); - } - catch (FilesystemException e) { throw e.setAction(ActionType.READ); } - } - @Override public synchronized void write(byte[] buff) { - try { - if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); - if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for writting."); - onWrite(buff); - } - catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } - } - @Override public synchronized long seek(long offset, int pos) { - try { - if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); - if (mode == Mode.NONE) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for seeking."); - return onSeek(offset, pos); - } - catch (FilesystemException e) { throw e.setAction(ActionType.SEEK); } - } - @Override public synchronized boolean close() { - if (handle != null) { - try { - var res = onClose(); - handle = null; - mode = Mode.NONE; - return res; - } - catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE); } - } - else return false; - } - - public BaseFile(T handle, Mode mode) { - this.mode = mode; - this.handle = handle; - - if (mode == Mode.NONE) this.handle = null; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java b/src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java deleted file mode 100644 index 09bd373..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/EntryType.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum EntryType { - NONE("none"), - FILE("file"), - FOLDER("folder"); - - public final String name; - - private EntryType(String name) { - this.name = name; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java deleted file mode 100644 index 91cdc2b..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum ErrorReason { - UNKNOWN(0, "failed", false), - NO_PERMISSION(1, "is not allowed", false), - CLOSED(1, "that was closed", true), - UNSUPPORTED(2, "is not supported", false), - ILLEGAL_ARGS(3, "with illegal arguments", true), - DOESNT_EXIST(4, "that doesn't exist", true), - ALREADY_EXISTS(5, "that already exists", true), - ILLEGAL_PATH(6, "with illegal path", true), - NO_PARENT(7, "with a missing parent folder", true); - - public final int code; - public final boolean usePast; - public final String readable; - - private ErrorReason(int code, String readable, boolean usePast) { - this.code = code; - this.readable = readable; - this.usePast = usePast; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/File.java b/src/java/me/topchetoeu/jscript/utils/filesystem/File.java deleted file mode 100644 index f98c566..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/File.java +++ /dev/null @@ -1,169 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Iterator; -import java.util.LinkedList; - -import me.topchetoeu.jscript.common.Buffer; -public interface File { - default int read(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.READ); } - default void write(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.WRITE); } - default long seek(long offset, int pos) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.SEEK); } - default boolean close() { return false; } - - default byte[] readAll() { - var parts = new LinkedList(); - var sizes = new LinkedList(); - var buff = new byte[1024]; - var size = 0; - - while (true) { - var n = read(buff); - if (n < 0) break; - else if (n == 0) continue; - - parts.add(buff); - sizes.add(n); - size += n; - buff = new byte[1024]; - } - - buff = new byte[size]; - - var i = 0; - var j = 0; - - for (var part : parts) { - var currSize = sizes.get(j++); - - System.arraycopy(part, 0, buff, i, currSize); - i += currSize; - } - - return buff; - } - default String readToString() { - return new String(readAll()); - } - default String readLine() { - var res = new Buffer(); - var buff = new byte[1]; - - while (true) { - if (read(buff) == 0) { - if (res.length() == 0) return null; - else break; - } - - if (buff[0] == '\n') break; - - res.write(res.length(), buff); - } - return new String(res.data()); - } - - public static File ofStream(InputStream str) { - return new File() { - @Override public synchronized int read(byte[] buff) { - try { - try { return str.read(buff); } - catch (NullPointerException e) { throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); } - catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.READ); } - } - }; - } - public static File ofStream(OutputStream str) { - return new File() { - @Override public synchronized void write(byte[] buff) { - try { - try { str.write(buff); } - catch (NullPointerException e) {throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); } - catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } - } - }; - } - public static File ofLineWriter(LineWriter writer) { - var buff = new Buffer(); - return new File() { - @Override public synchronized void write(byte[] val) { - try { - if (val == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null."); - - for (var b : val) { - if (b == '\n') { - try { - writer.writeLine(new String(buff.data())); - buff.clear(); - } - catch (IOException e) { - throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); - } - } - else buff.append(b); - } - } - catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } - } - }; - } - public static File ofLineReader(LineReader reader) { - return new File() { - private int offset = 0; - private byte[] prev = new byte[0]; - - @Override - public synchronized int read(byte[] buff) { - try { - if (buff == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null."); - var ptr = 0; - - while (true) { - if (prev == null) break; - if (offset >= prev.length) { - try { - var line = reader.readLine(); - - if (line == null) { - prev = null; - break; - } - else prev = (line + "\n").getBytes(); - - offset = 0; - } - catch (IOException e) { - throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); - } - } - - if (ptr + prev.length - offset > buff.length) { - var n = buff.length - ptr; - System.arraycopy(prev, offset, buff, ptr, buff.length - ptr); - offset += n; - ptr += n; - break; - } - else { - var n = prev.length - offset; - System.arraycopy(prev, offset, buff, ptr, n); - offset += n; - ptr += n; - } - } - - return ptr; - } - catch (FilesystemException e) { throw e.setAction(ActionType.READ); } - } - }; - } - public static File ofIterator(Iterator it) { - return ofLineReader(LineReader.ofIterator(it)); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java b/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java deleted file mode 100644 index 903bde1..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public class FileStat { - public final Mode mode; - public final EntryType type; - - public FileStat(Mode mode, EntryType type) { - if (mode == Mode.NONE) type = EntryType.NONE; - if (type == EntryType.NONE) mode = Mode.NONE; - - this.mode = mode; - this.type = type; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java deleted file mode 100644 index 283d633..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; - -public interface Filesystem { - public static final Key KEY = new Key<>(); - - default String normalize(String... path) { return Paths.normalize(path); } - default boolean create(String path, EntryType type) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.CREATE); } - File open(String path, Mode mode); - FileStat stat(String path); - void close(); - - public static Filesystem get(Environment exts) { - return exts.get(KEY); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java b/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java deleted file mode 100644 index 802179a..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.ArrayList; - -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.values.Values; - -public class FilesystemException extends RuntimeException { - public final ErrorReason reason; - public final String details; - private ActionType action; - private EntryType entry = EntryType.FILE; - private String path; - - public FilesystemException setPath(String path) { - this.path = path; - return this; - } - public FilesystemException setAction(ActionType action) { - if (action == null) action = ActionType.UNKNOWN; - - this.action = action; - return this; - } - public FilesystemException setEntry(EntryType entry) { - if (entry == null) entry = EntryType.NONE; - - this.entry = entry; - return this; - } - - public ActionType action() { - return action; - } - public String path() { - return path; - } - public EntryType entry() { - return entry; - } - - public EngineException toEngineException() { - var res = EngineException.ofError("IOError", getMessage()); - - Values.setMember(null, res.value, "action", action.code); - Values.setMember(null, res.value, "reason", reason.code); - Values.setMember(null, res.value, "path", path); - Values.setMember(null, res.value, "entry", entry.name); - if (details != null) Values.setMember(null, res.value, "details", details); - - return res; - } - - @Override public String getMessage() { - var parts = new ArrayList(10); - - parts.add(action == null ? "An action performed upon " : action.readable(reason.usePast)); - - if (entry == EntryType.FILE) parts.add("file"); - if (entry == EntryType.FOLDER) parts.add("folder"); - - if (path != null && !path.isBlank()) parts.add(path.trim()); - - parts.add(reason.readable); - - var msg = String.join(" ", parts); - if (details != null) msg += ": " + details; - - return msg; - } - - public FilesystemException(ErrorReason type, String details) { - super(); - if (type == null) type = ErrorReason.UNKNOWN; - - this.details = details; - this.reason = type; - } - public FilesystemException(ErrorReason type) { - this(type, null); - } - public FilesystemException() { - this(null); - } - -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java b/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java deleted file mode 100644 index c6dbacb..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.HashSet; -import java.util.Set; - -public class HandleManager { - private Set files = new HashSet<>(); - - public File put(File val) { - var handle = new File() { - @Override public int read(byte[] buff) { - return val.read(buff); - } - @Override public void write(byte[] buff) { - val.write(buff); - } - @Override public long seek(long offset, int pos) { - return val.seek(offset, pos); - } - @Override public boolean close() { - return files.remove(this) && val.close(); - } - }; - files.add(handle); - return handle; - } - public void close() { - while (!files.isEmpty()) { - files.stream().findFirst().get().close(); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java b/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java deleted file mode 100644 index 6d84450..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; -import java.util.Iterator; - - -public interface LineReader { - String readLine() throws IOException; - - public static LineReader ofIterator(Iterator it) { - return () -> { - if (it.hasNext()) return it.next(); - else return null; - }; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java b/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java deleted file mode 100644 index 501631a..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; - -public interface LineWriter { - void writeLine(String value) throws IOException; -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java deleted file mode 100644 index cb89d1d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java +++ /dev/null @@ -1,36 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import me.topchetoeu.jscript.common.Buffer; - -class MemoryFile extends BaseFile { - private int ptr; - - @Override protected int onRead(byte[] buff) { - if (ptr >= handle().length()) return -1; - var res = handle().read(ptr, buff); - ptr += res; - return res; - } - @Override protected void onWrite(byte[] buff) { - handle().write(ptr, buff); - ptr += buff.length; - } - @Override protected long onSeek(long offset, int pos) { - if (pos == 0) ptr = (int)offset; - else if (pos == 1) ptr += (int)offset; - else if (pos == 2) ptr = handle().length() - (int)offset; - - if (ptr < 0) ptr = 0; - if (ptr > handle().length()) ptr = handle().length(); - - return pos; - } - @Override protected boolean onClose() { - ptr = 0; - return true; - } - - public MemoryFile(Buffer buff, Mode mode) { - super(buff, mode); - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java deleted file mode 100644 index 68b1e4c..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java +++ /dev/null @@ -1,100 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.nio.file.Path; -import java.util.HashMap; -import java.util.HashSet; - -import me.topchetoeu.jscript.common.Buffer; -import me.topchetoeu.jscript.common.Filename; - -public class MemoryFilesystem implements Filesystem { - public final Mode mode; - private HashMap files = new HashMap<>(); - private HashSet folders = new HashSet<>(); - private HandleManager handles = new HandleManager(); - - private Path realPath(String path) { - return Filename.normalize(path); - } - - @Override public String normalize(String... path) { - return Paths.normalize(path); - } - @Override public synchronized File open(String _path, Mode perms) { - try { - var path = realPath(_path); - var pcount = path.getNameCount(); - - if (files.containsKey(path)) return handles.put(new MemoryFile(files.get(path), perms)); - else if (folders.contains(path)) { - var res = new StringBuilder(); - - for (var folder : folders) { - if (pcount + 1 != folder.getNameCount()) continue; - if (!folder.startsWith(path)) continue; - res.append(folder.toFile().getName()).append('\n'); - } - - for (var file : files.keySet()) { - if (pcount + 1 != file.getNameCount()) continue; - if (!file.startsWith(path)) continue; - res.append(file.toFile().getName()).append('\n'); - } - - return handles.put(new MemoryFile(new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ))); - } - else throw new FilesystemException(ErrorReason.DOESNT_EXIST); - } - catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.OPEN); } - } - @Override public synchronized boolean create(String _path, EntryType type) { - try { - var path = realPath(_path); - - switch (type) { - case FILE: - if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT); - if (folders.contains(path) || files.containsKey(path)) return false; - files.put(path, new Buffer()); - return true; - case FOLDER: - if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT); - if (folders.contains(path) || files.containsKey(path)) return false; - folders.add(path); - return true; - default: - case NONE: - return folders.remove(path) || files.remove(path) != null; - } - } - catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.CREATE); } - } - @Override public synchronized FileStat stat(String _path) { - var path = realPath(_path); - - if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE); - else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER); - else return new FileStat(Mode.NONE, EntryType.NONE); - } - @Override public synchronized void close() throws FilesystemException { - handles.close(); - } - - public MemoryFilesystem put(String path, byte[] data) { - var _path = realPath(path); - var _curr = "/"; - - for (var seg : _path) { - create(_curr, EntryType.FOLDER); - _curr += seg + "/"; - } - - files.put(_path, new Buffer(data)); - return this; - } - - public MemoryFilesystem(Mode mode) { - this.mode = mode; - folders.add(Path.of("/")); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java deleted file mode 100644 index b64d288..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -public enum Mode { - NONE("", false, false), - READ("r", true, false), - WRITE("rw", false, true), - READ_WRITE("rw", true, true); - - public final String name; - public final boolean readable; - public final boolean writable; - - public Mode intersect(Mode other) { - return of(readable && other.readable, writable && other.writable); - } - - private Mode(String mode, boolean r, boolean w) { - this.name = mode; - this.readable = r; - this.writable = w; - } - - public static Mode of(boolean read, boolean write) { - if (read && write) return READ_WRITE; - if (read) return READ; - if (write) return WRITE; - return NONE; - } - - public static Mode parse(String mode) { - switch (mode.toLowerCase()) { - case "r": return READ; - case "w": return WRITE; - case "r+": - case "w+": - case "wr": - case "rw": return READ_WRITE; - default: return NONE; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java deleted file mode 100644 index 0bc3967..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Paths.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.ArrayList; - -public class Paths { - public static String normalize(String... path) { - var parts = String.join("/", path).split("[\\\\/]"); - var res = new ArrayList(); - - for (var part : parts) { - if (part.equals("...")) res.clear(); - else if (part.equals("..")) { - if (res.size() > 0) res.remove(res.size() - 1); - } - else if (!part.equals(".") && !part.isEmpty()) res.add(part); - } - - var sb = new StringBuilder(); - - for (var el : res) sb.append("/").append(el); - - if (sb.length() == 0) return "/"; - else return sb.toString(); - } - - public static String chroot(String root, String path) { - return normalize(root) + normalize(path); - } - - public static String cwd(String cwd, String path) { - return normalize(cwd + "/" + path); - } - - public static String filename(String path) { - var i = path.lastIndexOf('/'); - if (i < 0) i = path.lastIndexOf('\\'); - - if (i < 0) return path; - else return path.substring(i + 1); - } - - public static String extension(String path) { - var i = path.lastIndexOf('.'); - - if (i < 0) return ""; - else return path.substring(i + 1); - } - - public static String dir(String path) { - return normalize(path + "/.."); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java deleted file mode 100644 index 67efb80..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.file.Path; - -public class PhysicalFile extends BaseFile { - @Override protected int onRead(byte[] buff) { - try { return handle().read(buff); } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.READ); } - } - @Override protected void onWrite(byte[] buff) { - try { handle().write(buff); } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.WRITE); } - } - @Override protected long onSeek(long offset, int pos) { - try { - if (pos == 1) offset += handle().getFilePointer(); - else if (pos == 2) offset += handle().length(); - handle().seek(offset); - return offset; - } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.SEEK); } - } - @Override protected boolean onClose() { - try { handle().close(); } - catch (IOException e) {} // SHUT - return true; - } - - public PhysicalFile(Path path, Mode mode) throws FileNotFoundException { - super(new RandomAccessFile(path.toFile(), mode.name), mode); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java deleted file mode 100644 index 9ff0c0e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java +++ /dev/null @@ -1,92 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; - -public class PhysicalFilesystem implements Filesystem { - public final String root; - private HandleManager handles = new HandleManager(); - - private void checkMode(Path path, Mode mode) { - if (!path.startsWith(root)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "Tried to jailbreak the sandbox."); - - if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions"); - if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No write permissions"); - } - - private Path realPath(String path) { - return Path.of(Paths.chroot(root, path)); - } - - @Override public String normalize(String... paths) { - return Paths.normalize(paths); - } - @Override public synchronized File open(String _path, Mode perms) { - try { - var path = realPath(normalize(_path)); - checkMode(path, perms); - - try { - if (Files.isDirectory(path)) return handles.put(File.ofIterator( - Files.list(path).map(v -> v.getFileName().toString()).iterator() - )); - else return handles.put(new PhysicalFile(path, perms)); - } - catch (FileNotFoundException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.OPEN).setPath(_path); } - } - @Override public synchronized boolean create(String _path, EntryType type) { - try { - var path = realPath(_path); - - try { - switch (type) { - case FILE: - Files.createFile(path); - break; - case FOLDER: - Files.createDirectories(path); - break; - case NONE: - default: - Files.delete(path); - } - } - catch (FileAlreadyExistsException | NoSuchFileException e) { return false; } - catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PARENT); } - } - catch (FilesystemException e) { throw e.setAction(ActionType.CREATE).setPath(_path); } - - return true; - } - @Override public synchronized FileStat stat(String _path) { - var path = realPath(_path); - - if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE); - - var perms = Mode.of(Files.isReadable(path), Files.isWritable(path)); - - if (perms == Mode.NONE) return new FileStat(Mode.NONE, EntryType.NONE); - - return new FileStat( - perms, - Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE - ); - } - @Override public synchronized void close() throws FilesystemException { - try { - handles.close(); - } - catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); } - } - - public PhysicalFilesystem(String root) { - this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString()); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java deleted file mode 100644 index 1db7355..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java +++ /dev/null @@ -1,99 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.util.HashMap; -import java.util.Map; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.utils.permissions.Matcher; -import me.topchetoeu.jscript.utils.permissions.Permission; -import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; - -public class RootFilesystem implements Filesystem { - public final Map protocols = new HashMap<>(); - public final PermissionsProvider perms; - - public static final Permission PERM_READ = new Permission("jscript.file.read", Matcher.fileWildcard()); - public static final Permission PERM_WRITE = new Permission("jscript.file.read", Matcher.fileWildcard()); - - private boolean canRead(String _path) { - return perms.hasPermission(PERM_READ, _path); - } - private boolean canWrite(String _path) { - return perms.hasPermission(PERM_WRITE, _path); - } - - private void modeAllowed(String _path, Mode mode) throws FilesystemException { - if (mode.readable && perms != null && !canRead(_path)) { - throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions").setPath(_path); - } - if (mode.writable && perms != null && !canWrite(_path)) { - throw new FilesystemException(ErrorReason.NO_PERMISSION, "No wtrite permissions").setPath(_path); - } - } - - private Filesystem getProtocol(Filename filename) { - var protocol = protocols.get(filename.protocol); - - if (protocol == null) { - throw new FilesystemException(ErrorReason.DOESNT_EXIST, "The protocol '" + filename.protocol + "' doesn't exist."); - } - - return protocol; - } - - @Override public String normalize(String... paths) { - if (paths.length == 0) return "file://"; - else { - var filename = Filename.parse(paths[0]); - var protocol = protocols.get(filename.protocol); - paths[0] = filename.path; - - - if (protocol == null) return Paths.normalize(paths); - else return filename.protocol + "://" + protocol.normalize(paths); - } - } - @Override public synchronized File open(String path, Mode perms) throws FilesystemException { - try { - var filename = Filename.parse(path); - var protocol = getProtocol(filename); - - modeAllowed(filename.toString(), perms); - return protocol.open(filename.path, perms); - } - catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.OPEN); } - } - @Override public synchronized boolean create(String path, EntryType type) throws FilesystemException { - try { - var filename = Filename.parse(path); - var protocol = getProtocol(filename); - - modeAllowed(filename.toString(), Mode.WRITE); - return protocol.create(filename.path, type); - } - catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.CREATE); } - } - @Override public synchronized FileStat stat(String path) throws FilesystemException { - try { - var filename = Filename.parse(path); - var protocol = getProtocol(filename); - - return protocol.stat(filename.path); - } - catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.STAT); } - } - @Override public synchronized void close() throws FilesystemException { - try { - for (var protocol : protocols.values()) { - protocol.close(); - } - - protocols.clear(); - } - catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); } - } - - public RootFilesystem(PermissionsProvider perms) { - this.perms = perms; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java deleted file mode 100644 index 5b82c0e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.InputStream; -import java.io.OutputStream; - -public class STDFilesystem implements Filesystem { - private File in; - private File out; - private File err; - - @Override - public String normalize(String... path) { - var res = Paths.normalize(path); - while (res.startsWith("/")) res = res.substring(1); - while (res.endsWith("/")) res = res.substring(0, res.length() - 1); - return res; - } - - @Override public synchronized File open(String path, Mode mode) { - path = normalize(path); - if (in != null && path.equals("in")) return in; - else if (out != null && path.equals("out")) return out; - else if (err != null && path.equals("err")) return err; - else throw new FilesystemException(ErrorReason.DOESNT_EXIST).setAction(ActionType.OPEN).setPath(path); - } - @Override public synchronized FileStat stat(String path) { - path = normalize(path); - if (path.equals("in") || path.equals("out") || path.equals("err")) return new FileStat(Mode.READ_WRITE, EntryType.FILE); - else return new FileStat(Mode.NONE, EntryType.NONE); - } - @Override public synchronized void close() { - in = out = err = null; - } - - public STDFilesystem(File in, File out, File err) { - this.in = in; - this.out = out; - this.err = err; - } - public STDFilesystem(InputStream in, OutputStream out, OutputStream err) { - if (in != null) this.in = File.ofStream(in); - if (out != null) this.out = File.ofStream(out); - if (err != null) this.err = File.ofStream(err); - } - public STDFilesystem(LineReader in, LineWriter out) { - if (in != null) this.in = File.ofLineReader(in); - if (out != null) { - this.out = File.ofLineWriter(out); - this.err = File.ofLineWriter(out); - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java b/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java deleted file mode 100644 index 2ced1aa..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/Arguments.java +++ /dev/null @@ -1,121 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.reflect.Array; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.values.NativeWrapper; -import me.topchetoeu.jscript.runtime.values.Values; - -public class Arguments { - public final Object self; - public final Object[] args; - public final Environment env; - - public int n() { - return args.length; - } - - public boolean has(int i) { - return i == -1 || i >= 0 && i < args.length; - } - - public T self(Class type) { - return convert(-1, type); - } - public T convert(int i, Class type) { - return Values.convert(env, get(i), type); - } - public Object get(int i, boolean unwrap) { - Object res = null; - - if (i == -1) res = self; - if (i >= 0 && i < args.length) res = args[i]; - if (unwrap && res instanceof NativeWrapper) res = ((NativeWrapper)res).wrapped; - - return res; - } - public Object get(int i) { - return get(i, false); - } - public Object getOrDefault(int i, Object def) { - if (i < 0 || i >= args.length) return def; - else return get(i); - } - - public Arguments slice(int start) { - var res = new Object[Math.max(0, args.length - start)]; - for (int j = start; j < args.length; j++) res[j - start] = get(j); - return new Arguments(env, args, res); - } - - @SuppressWarnings("unchecked") - public T[] convert(Class type) { - var res = Array.newInstance(type, args.length); - for (int i = 0; i < args.length; i++) Array.set(res, i, convert(i, type)); - return (T[])res; - } - public int[] convertInt() { - var res = new int[args.length]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Integer.class); - return res; - } - public long[] convertLong() { - var res = new long[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Long.class); - return res; - } - public short[] sliceShort() { - var res = new short[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Short.class); - return res; - } - public float[] sliceFloat() { - var res = new float[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Float.class); - return res; - } - public double[] sliceDouble() { - var res = new double[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Double.class); - return res; - } - public byte[] sliceByte() { - var res = new byte[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Byte.class); - return res; - } - public char[] sliceChar() { - var res = new char[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Character.class); - return res; - } - public boolean[] sliceBool() { - var res = new boolean[Math.max(0, args.length)]; - for (int i = 0; i < args.length; i++) res[i] = convert(i, Boolean.class); - return res; - } - - public String getString(int i) { return Values.toString(env, get(i)); } - public boolean getBoolean(int i) { return Values.toBoolean(get(i)); } - public int getInt(int i) { return (int)Values.toNumber(env, get(i)); } - public long getLong(int i) { return (long)Values.toNumber(env, get(i)); } - public double getDouble(int i) { return Values.toNumber(env, get(i)); } - public float getFloat(int i) { return (float)Values.toNumber(env, get(i)); } - - public int getInt(int i, int def) { - var res = get(i); - if (res == null) return def; - else return (int)Values.toNumber(env, res); - } - public String getString(int i, String def) { - var res = get(i); - if (res == null) return def; - else return Values.toString(env, res); - } - - public Arguments(Environment env, Object thisArg, Object... args) { - this.env = env; - this.args = args; - this.self = thisArg; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/Expose.java b/src/java/me/topchetoeu/jscript/utils/interop/Expose.java deleted file mode 100644 index 5ef6815..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/Expose.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface Expose { - public String value() default ""; - public ExposeType type() default ExposeType.METHOD; - public ExposeTarget target() default ExposeTarget.MEMBER; -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java deleted file mode 100644 index f5b97e6..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeConstructor.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ExposeConstructor { } diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java deleted file mode 100644 index a66330e..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeField.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD, ElementType.FIELD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface ExposeField { - public String value() default ""; - public ExposeTarget target() default ExposeTarget.MEMBER; -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java deleted file mode 100644 index 311a627..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeTarget.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -public enum ExposeTarget { - STATIC(true, true, false), - MEMBER(false, false, true), - NAMESPACE(false, true, false), - CONSTRUCTOR(true, false, false), - PROTOTYPE(false, false, true), - ALL(true, true, true); - - public final boolean constructor; - public final boolean namespace; - public final boolean prototype; - - public boolean shouldApply(ExposeTarget other) { - if (other.constructor && !constructor) return false; - if (other.namespace && !namespace) return false; - if (other.prototype && !prototype) return false; - - return true; - } - - private ExposeTarget(boolean constructor, boolean namespace, boolean prototype) { - this.constructor = constructor; - this.namespace = namespace; - this.prototype = prototype; - } -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java b/src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java deleted file mode 100644 index 15e91a9..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/ExposeType.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -public enum ExposeType { - METHOD, - GETTER, - SETTER, -} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java b/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java deleted file mode 100644 index 1022ce4..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/NativeWrapperProvider.java +++ /dev/null @@ -1,454 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; -import me.topchetoeu.jscript.runtime.exceptions.InterruptException; -import me.topchetoeu.jscript.runtime.values.FunctionValue; -import me.topchetoeu.jscript.runtime.values.NativeFunction; -import me.topchetoeu.jscript.runtime.values.ObjectValue; -import me.topchetoeu.jscript.runtime.values.Symbol; -import me.topchetoeu.jscript.runtime.values.Values; - -public class NativeWrapperProvider { - public static final Key KEY = new Key<>(); - - private final HashMap, FunctionValue> constructors = new HashMap<>(); - private final HashMap, ObjectValue> prototypes = new HashMap<>(); - private final HashMap, ObjectValue> namespaces = new HashMap<>(); - private final HashMap, Class> classToProxy = new HashMap<>(); - private final HashMap, Class> proxyToClass = new HashMap<>(); - private final HashMap, Class> interfaceToProxy = new HashMap<>(); - private final HashSet> ignore = new HashSet<>(); - - private static Object call(Environment ctx, String name, Method method, Object thisArg, Object... args) { - try { - var realArgs = new Object[method.getParameterCount()]; - System.arraycopy(args, 0, realArgs, 0, realArgs.length); - if (Modifier.isStatic(method.getModifiers())) thisArg = null; - return Values.normalize(ctx, method.invoke(Values.convert(ctx, thisArg, method.getDeclaringClass()), realArgs)); - } - catch (InvocationTargetException e) { - if (e.getTargetException() instanceof EngineException) { - throw ((EngineException)e.getTargetException()).add(ctx, name, Location.INTERNAL); - } - else if (e.getTargetException() instanceof NullPointerException) { - // e.getTargetException().printStackTrace(); - throw EngineException.ofType("Unexpected value of 'undefined'.").add(ctx, name, Location.INTERNAL); - } - else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) { - throw new InterruptException(); - } - else throw new EngineException(e.getTargetException()).add(ctx, name, Location.INTERNAL); - } - catch (ReflectiveOperationException e) { - throw EngineException.ofError(e.getMessage()).add(ctx, name, Location.INTERNAL); - } - } - private static FunctionValue create(String name, Method method) { - return new NativeFunction(name, args -> call(args.env, name, method, args.self, args)); - } - private static void checkSignature(Method method, boolean forceStatic, Class ...params) { - if (forceStatic && !Modifier.isStatic(method.getModifiers())) throw new IllegalArgumentException(String.format( - "Method %s must be static.", - method.getDeclaringClass().getName() + "." + method.getName() - )); - - var actual = method.getParameterTypes(); - - boolean failed = actual.length > params.length; - - if (!failed) for (var i = 0; i < actual.length; i++) { - if (!actual[i].isAssignableFrom(params[i])) { - failed = true; - break; - } - } - - if (failed) throw new IllegalArgumentException(String.format( - "Method %s was expected to have a signature of '%s', found '%s' instead.", - method.getDeclaringClass().getName() + "." + method.getName(), - String.join(", ", Arrays.stream(params).map(v -> v.getName()).collect(Collectors.toList())), - String.join(", ", Arrays.stream(actual).map(v -> v.getName()).collect(Collectors.toList())) - )); - } - private static String getName(Class ...classes) { - String last = null; - - for (var clazz : classes) { - var classNat = clazz.getAnnotation(WrapperName.class); - if (classNat != null && !classNat.value().trim().equals("")) return classNat.value().trim(); - else if (last != null) last = clazz.getSimpleName(); - } - - return last; - } - - private static void checkUnderscore(Member member) { - if (!member.getName().startsWith("__")) { - System.out.println(String.format("WARNING: The name of the exposed member '%s.%s' doesn't start with '__'.", - member.getDeclaringClass().getName(), - member.getName() - )); - } - } - private static String getName(Member member, String overrideName) { - if (overrideName == null) overrideName = ""; - if (overrideName.isBlank()) { - var res = member.getName(); - if (res.startsWith("__")) res = res.substring(2); - return res; - } - else return overrideName.trim(); - } - private static Object getKey(String name) { - if (name.startsWith("@@")) return Symbol.get(name.substring(2)); - else return name; - } - - private static boolean apply(ObjectValue obj, ExposeTarget target, Class clazz) { - var getters = new HashMap(); - var setters = new HashMap(); - var props = new HashSet(); - var nonProps = new HashSet(); - var any = false; - - for (var method : clazz.getDeclaredMethods()) { - for (var annotation : method.getAnnotationsByType(Expose.class)) { - any = true; - if (!annotation.target().shouldApply(target)) continue; - - checkUnderscore(method); - var name = getName(method, annotation.value()); - var key = getKey(name); - var repeat = false; - - switch (annotation.type()) { - case METHOD: - if (props.contains(key) || nonProps.contains(key)) repeat = true; - else { - checkSignature(method, false, Arguments.class); - obj.defineProperty(null, key, create(name, method), true, true, false); - nonProps.add(key); - } - break; - case GETTER: - if (nonProps.contains(key) || getters.containsKey(key)) repeat = true; - else { - checkSignature(method, false, Arguments.class); - getters.put(key, create(name, method)); - props.add(key); - } - break; - case SETTER: - if (nonProps.contains(key) || setters.containsKey(key)) repeat = true; - else { - checkSignature(method, false, Arguments.class); - setters.put(key, create(name, method)); - props.add(key); - } - break; - } - - if (repeat) - throw new IllegalArgumentException(String.format( - "A member '%s' in the wrapper for '%s' of type '%s' is already present.", - name, clazz.getName(), target.toString() - )); - } - for (var annotation : method.getAnnotationsByType(ExposeField.class)) { - any = true; - if (!annotation.target().shouldApply(target)) continue; - - checkUnderscore(method); - var name = getName(method, annotation.value()); - var key = getKey(name); - var repeat = false; - - if (props.contains(key) || nonProps.contains(key)) repeat = true; - else { - checkSignature(method, true); - try { - obj.defineProperty(null, key, method.invoke(null), true, true, false); - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new RuntimeException(e); - } - nonProps.add(key); - } - - if (repeat) - throw new IllegalArgumentException(String.format( - "A member '%s' in the wrapper for '%s' of type '%s' is already present.", - name, clazz.getName(), target.toString() - )); - } - } - for (var field : clazz.getDeclaredFields()) { - for (var annotation : field.getAnnotationsByType(ExposeField.class)) { - any = true; - if (!annotation.target().shouldApply(target)) continue; - - checkUnderscore(field); - var name = getName(field, annotation.value()); - var key = getKey(name); - var repeat = false; - - if (props.contains(key) || nonProps.contains(key)) repeat = true; - else { - try { - if (Modifier.isStatic(field.getModifiers())) { - obj.defineProperty(null, key, Values.normalize(null, field.get(null)), true, true, false); - } - else { - obj.defineProperty( - null, key, - new NativeFunction("get " + key, args -> { - try { return field.get(args.self(clazz)); } - catch (IllegalAccessException e) { e.printStackTrace(); return null; } - }), - Modifier.isFinal(field.getModifiers()) ? null : new NativeFunction("get " + key, args -> { - try { field.set(args.self(clazz), args.convert(0, field.getType())); } - catch (IllegalAccessException e) { e.printStackTrace(); } - return null; - }), - true, false - ); - } - nonProps.add(key); - } - catch (IllegalArgumentException | IllegalAccessException e) { } - } - - if (repeat) - throw new IllegalArgumentException(String.format( - "A member '%s' in the wrapper for '%s' of type '%s' is already present.", - name, clazz.getName(), target.toString() - )); - } - } - - for (var key : props) obj.defineProperty(null, key, getters.get(key), setters.get(key), true, false); - - return any; - } - private static boolean apply(ObjectValue obj, ExposeTarget target, Class ...appliers) { - var res = false; - - for (var i = appliers.length - 1; i >= 0; i--) { - res |= apply(obj, target, appliers[i]); - } - - return res; - } - - private static Method getConstructor(Class ...appliers) { - for (var clazz : appliers) { - for (var method : clazz.getDeclaredMethods()) { - if (!method.isAnnotationPresent(ExposeConstructor.class)) continue; - checkSignature(method, true, Arguments.class); - return method; - } - } - - return null; - } - - /** - * Generates a prototype for the given class. - * The returned object will have appropriate wrappers for all instance members. - * All accessors and methods will expect the this argument to be a native wrapper of the given class type. - * @param clazz The class for which a prototype should be generated - */ - public static ObjectValue makeProto(Class ...appliers) { - var res = new ObjectValue(); - res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(appliers)); - if (!apply(res, ExposeTarget.PROTOTYPE, appliers)) return null; - return res; - } - /** - * Generates a constructor for the given class. - * The returned function will have appropriate wrappers for all static members. - * When the function gets called, the underlying constructor will get called, unless the constructor is inaccessible. - * @param clazz The class for which a constructor should be generated - */ - public static FunctionValue makeConstructor(Class ...appliers) { - var constr = getConstructor(appliers); - - FunctionValue res = constr == null ? - new NativeFunction(getName(appliers), args -> { throw EngineException.ofError("This constructor is not invokable."); }) : - create(getName(appliers), constr); - - if (!apply(res, ExposeTarget.CONSTRUCTOR, appliers) && constr == null) return null; - - return res; - } - /** - * Generates a namespace for the given class. - * The returned function will have appropriate wrappers for all static members. - * This method behaves almost like {@link NativeWrapperProvider#makeConstructor}, but will return an object instead. - * @param clazz The class for which a constructor should be generated - */ - public static ObjectValue makeNamespace(Class ...appliers) { - var res = new ObjectValue(); - - if (!apply(res, ExposeTarget.NAMESPACE, appliers)) return null; - - return res; - } - - private Class[] getAppliers(Class clazz) { - var res = new ArrayList>(); - - res.add(clazz); - - if (classToProxy.containsKey(clazz)) res.add(classToProxy.get(clazz)); - for (var intf : interfaceToProxy.keySet()) { - if (intf.isAssignableFrom(clazz)) res.add(interfaceToProxy.get(intf)); - } - - return res.toArray(Class[]::new); - } - - private void updateProtoChain(Class clazz, ObjectValue proto, FunctionValue constr) { - var parent = clazz; - - while (true) { - parent = parent.getSuperclass(); - if (parent == null) break; - - var parentProto = getProto(parent); - var parentConstr = getConstr(parent); - - if (parentProto != null && parentConstr != null) { - Values.setPrototype(Environment.empty(), proto, parentProto); - Values.setPrototype(Environment.empty(), constr, parentConstr); - - return; - } - } - } - - private void initType(Class clazz, FunctionValue constr, ObjectValue proto) { - if (constr != null && proto != null || ignore.contains(clazz)) return; - // i vomit - if ( - clazz == Object.class || - clazz == Void.class || - clazz == Number.class || clazz == Double.class || clazz == Float.class || - clazz == Long.class || clazz == Integer.class || clazz == Short.class || - clazz == Character.class || clazz == Byte.class || clazz == Boolean.class || - clazz.isPrimitive() || - clazz.isArray() || - clazz.isAnonymousClass() || - clazz.isEnum() || - clazz.isInterface() || - clazz.isSynthetic() - ) return; - - var appliers = getAppliers(clazz); - - if (constr == null) constr = makeConstructor(appliers); - if (proto == null) proto = makeProto(appliers); - - if (constr == null || proto == null) return; - - proto.defineProperty(null, "constructor", constr, true, false, false); - constr.defineProperty(null, "prototype", proto, true, false, false); - - prototypes.put(clazz, proto); - constructors.put(clazz, constr); - - updateProtoChain(clazz, proto, constr); - } - - public ObjectValue getProto(Class clazz) { - if (proxyToClass.containsKey(clazz)) return getProto(proxyToClass.get(clazz)); - - initType(clazz, constructors.get(clazz), prototypes.get(clazz)); - while (clazz != null) { - var res = prototypes.get(clazz); - if (res != null) return res; - clazz = clazz.getSuperclass(); - } - return null; - } - public ObjectValue getNamespace(Class clazz) { - if (proxyToClass.containsKey(clazz)) return getNamespace(proxyToClass.get(clazz)); - - if (!namespaces.containsKey(clazz)) namespaces.put(clazz, makeNamespace(clazz)); - while (clazz != null) { - var res = namespaces.get(clazz); - if (res != null) return res; - clazz = clazz.getSuperclass(); - } - return null; - } - public FunctionValue getConstr(Class clazz) { - if (proxyToClass.containsKey(clazz)) return getConstr(proxyToClass.get(clazz)); - - initType(clazz, constructors.get(clazz), prototypes.get(clazz)); - while (clazz != null) { - var res = constructors.get(clazz); - if (res != null) return res; - clazz = clazz.getSuperclass(); - } - return null; - } - - public NativeWrapperProvider copy() { - var res = new NativeWrapperProvider(); - - for (var pair : classToProxy.entrySet()) { - res.set(pair.getKey(), pair.getValue()); - } - - return this; - } - - public void set(Class clazz, Class wrapper) { - if (clazz == null) return; - - if (clazz.isInterface()) { - if (wrapper == null || wrapper == clazz) interfaceToProxy.remove(clazz); - else interfaceToProxy.put(clazz, wrapper); - } - else { - if (wrapper == null || wrapper == clazz) classToProxy.remove(clazz); - else classToProxy.put(clazz, wrapper); - } - - var classes = Stream.concat( - Stream.of(clazz), - prototypes.keySet().stream().filter(clazz::isAssignableFrom) - ).toArray(Class[]::new); - - for (var el : classes) { - prototypes.remove(el); - constructors.remove(el); - namespaces.remove(el); - } - - for (var el : classes) { - initType(el, null, null); - } - } - - public NativeWrapperProvider() { } - - public static NativeWrapperProvider get(Environment ext) { - return ext.get(KEY); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java b/src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java deleted file mode 100644 index 7b0e67d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/OnWrapperInit.java +++ /dev/null @@ -1,12 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -public @interface OnWrapperInit { - -} diff --git a/src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java b/src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java deleted file mode 100644 index daf078c..0000000 --- a/src/java/me/topchetoeu/jscript/utils/interop/WrapperName.java +++ /dev/null @@ -1,12 +0,0 @@ -package me.topchetoeu.jscript.utils.interop; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -public @interface WrapperName { - String value(); -} diff --git a/src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java b/src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java deleted file mode 100644 index a7523b3..0000000 --- a/src/java/me/topchetoeu/jscript/utils/mapping/SourceMap.java +++ /dev/null @@ -1,109 +0,0 @@ -package me.topchetoeu.jscript.utils.mapping; - -import java.util.ArrayList; -import java.util.List; -import java.util.TreeMap; -import java.util.stream.Collectors; - -import me.topchetoeu.jscript.common.Location; -import me.topchetoeu.jscript.common.json.JSON; - -public class SourceMap { - private final TreeMap origToComp = new TreeMap<>(); - private final TreeMap compToOrig = new TreeMap<>(); - - public Location toCompiled(Location loc) { return convert(loc, origToComp); } - public Location toOriginal(Location loc) { return convert(loc, compToOrig); } - - private void add(long orig, long comp) { - var a = origToComp.remove(orig); - var b = compToOrig.remove(comp); - - if (b != null) origToComp.remove(b); - if (a != null) compToOrig.remove(a); - - origToComp.put(orig, comp); - compToOrig.put(comp, orig); - } - - public SourceMap apply(SourceMap map) { - var res = new SourceMap(); - - for (var el : new ArrayList<>(origToComp.entrySet())) { - var mapped = convert(el.getValue(), map.origToComp); - res.origToComp.put(el.getKey(), mapped); - } - for (var el : new ArrayList<>(compToOrig.entrySet())) { - var mapped = convert(el.getKey(), map.compToOrig); - res.compToOrig.put(mapped, el.getValue()); - res.add(el.getValue(), mapped); - } - - return res; - } - - public SourceMap clone() { - var res = new SourceMap(); - res.origToComp.putAll(this.origToComp); - res.compToOrig.putAll(this.compToOrig); - return res; - } - - public static SourceMap parse(String raw) { - var mapping = VLQ.decodeMapping(raw); - var res = new SourceMap(); - - var compRow = 0l; - var compCol = 0l; - - for (var origRow = 0; origRow < mapping.length; origRow++) { - var origCol = 0; - - for (var rawSeg : mapping[origRow]) { - if (rawSeg.length > 1 && rawSeg[1] != 0) throw new IllegalArgumentException("Source mapping is to more than one files."); - origCol += rawSeg.length > 0 ? rawSeg[0] : 0; - compRow += rawSeg.length > 2 ? rawSeg[2] : 0; - compCol += rawSeg.length > 3 ? rawSeg[3] : 0; - - var compPacked = ((long)compRow << 32) | compCol; - var origPacked = ((long)origRow << 32) | origCol; - - res.add(origPacked, compPacked); - } - } - - return res; - } - public static List getSources(String raw) { - var json = JSON.parse(null, raw).map(); - return json - .list("sourcesContent") - .stream() - .map(v -> v.string()) - .collect(Collectors.toList()); - } - - public static SourceMap chain(SourceMap ...maps) { - if (maps.length == 0) return null; - var res = maps[0]; - - for (var i = 1; i < maps.length; i++) res = res.apply(maps[i]); - - return res; - } - - private static Long convert(long packed, TreeMap map) { - if (map.containsKey(packed)) return map.get(packed); - var key = map.floorKey(packed); - if (key == null) return null; - else return map.get(key); - } - - private static Location convert(Location loc, TreeMap map) { - var packed = ((loc.line() - 1l) << 32) | (loc.start() - 1); - var resPacked = convert(packed, map); - - if (resPacked == null) return null; - else return new Location((int)(resPacked >> 32) + 1, (int)(resPacked & 0xFFFF) + 1, loc.filename()); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java b/src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java deleted file mode 100644 index 5f49aea..0000000 --- a/src/java/me/topchetoeu/jscript/utils/mapping/VLQ.java +++ /dev/null @@ -1,95 +0,0 @@ -package me.topchetoeu.jscript.utils.mapping; - -import java.util.ArrayList; -import java.util.List; - -public class VLQ { - private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - private static long[] toArray(List list) { - var arr = new long[list.size()]; - for (var i = 0; i < list.size(); i++) arr[i] = list.get(i); - return arr; - } - - public static String encode(long... arr) { - var raw = new StringBuilder(); - - for (var data : arr) { - var b = data < 0 ? 1 : 0; - data = Math.abs(data); - b |= (int)(data & 0b1111) << 1; - data >>= 4; - b |= data > 0 ? 0x20 : 0;; - raw.append(ALPHABET.charAt(b)); - - while (data > 0) { - b = (int)(data & 0b11111); - data >>= 5; - b |= data > 0 ? 0x20 : 0; - raw.append(ALPHABET.charAt(b)); - } - } - - return raw.toString(); - } - public static long[] decode(String val) { - if (val.length() == 0) return new long[0]; - - var list = new ArrayList(); - - for (var i = 0; i < val.length();) { - var sign = 1; - var curr = ALPHABET.indexOf(val.charAt(i++)); - var cont = (curr & 0x20) == 0x20; - if ((curr & 1) == 1) sign = -1; - long res = (curr & 0b11110) >> 1; - var n = 4; - - for (; i < val.length() && cont;) { - curr = ALPHABET.indexOf(val.charAt(i++)); - cont = (curr & 0x20) == 0x20; - res |= (curr & 0b11111) << n; - n += 5; - if (!cont) break; - } - - list.add(res * sign); - } - - return toArray(list); - } - - public static String encodeMapping(long[][][] arr) { - var res = new StringBuilder(); - var semicolon = false; - - for (var line : arr) { - var comma = false; - - if (semicolon) res.append(";"); - semicolon = true; - - for (var el : line) { - if (comma) res.append(","); - comma = true; - res.append(encode(el)); - } - } - - return res.toString(); - } - public static long[][][] decodeMapping(String val) { - var lines = new ArrayList(); - - for (var line : val.split(";", -1)) { - var elements = new ArrayList(); - for (var el : line.split(",", -1)) { - elements.add(decode(el)); - } - lines.add(elements.toArray(long[][]::new)); - } - - return lines.toArray(long[][][]::new); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/modules/Module.java b/src/java/me/topchetoeu/jscript/utils/modules/Module.java deleted file mode 100644 index 63b1164..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/Module.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import me.topchetoeu.jscript.runtime.environment.Environment; - -public abstract class Module { - private Object value; - private boolean loaded; - - public Object value() { return value; } - public boolean loaded() { return loaded; } - - protected abstract Object onLoad(Environment ctx); - - public void load(Environment ctx) { - if (loaded) return; - this.value = onLoad(ctx); - this.loaded = true; - } -} - diff --git a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java deleted file mode 100644 index 99cffdc..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/ModuleRepo.java +++ /dev/null @@ -1,46 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import java.util.HashMap; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; -import me.topchetoeu.jscript.runtime.scope.GlobalScope; -import me.topchetoeu.jscript.utils.filesystem.Filesystem; -import me.topchetoeu.jscript.utils.filesystem.Mode; - -public interface ModuleRepo { - public static final Key KEY = new Key<>(); - public static final Key CWD = new Key<>(); - - public Module getModule(Environment ctx, String cwd, String name); - - public static ModuleRepo ofFilesystem(Filesystem fs) { - var modules = new HashMap(); - - return (env, cwd, name) -> { - name = fs.normalize(cwd, name); - var filename = Filename.parse(name); - var src = fs.open(name, Mode.READ).readToString(); - - if (modules.containsKey(name)) return modules.get(name); - - var moduleEnv = env.child() - .add(CWD, fs.normalize(name, "..")) - .add(GlobalScope.KEY, env.hasNotNull(GlobalScope.KEY) ? env.get(GlobalScope.KEY).child() : new GlobalScope()); - - var mod = new SourceModule(filename, src, moduleEnv); - modules.put(name, mod); - - return mod; - }; - } - - public static String cwd(Environment exts) { - exts.init(CWD, "/"); - return "/"; - } - public static ModuleRepo get(Environment exts) { - return exts.get(KEY); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java b/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java deleted file mode 100644 index 2644941..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/RootModuleRepo.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import java.util.HashMap; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; - -public class RootModuleRepo implements ModuleRepo { - public final HashMap repos = new HashMap<>(); - - @Override - public Module getModule(Environment env, String cwd, String name) { - var i = name.indexOf(":"); - String repoName, modName; - - if (i < 0) { - repoName = "file"; - modName = name; - } - else { - repoName = name.substring(0, i); - modName = name.substring(i + 1); - } - - var repo = repos.get(repoName); - if (repo == null) throw EngineException.ofError("ModuleError", "Couldn't find module repo '" + repoName + "'."); - - return repo.getModule(env, cwd, modName); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java b/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java deleted file mode 100644 index 36aeb0d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/modules/SourceModule.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.topchetoeu.jscript.utils.modules; - -import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.runtime.Compiler; -import me.topchetoeu.jscript.runtime.environment.Environment; - -public class SourceModule extends Module { - public final Filename filename; - public final String source; - public final Environment ext; - - @Override - protected Object onLoad(Environment env) { - return Compiler.compile(env, filename, source).call(env); - } - - public SourceModule(Filename filename, String source, Environment ext) { - this.filename = filename; - this.source = source; - this.ext = ext; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java b/src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java deleted file mode 100644 index fc1138c..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/Matcher.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -import java.util.LinkedList; - -public interface Matcher { - static class State { - public final int predI, trgI, wildcardI; - public final boolean wildcard; - - @Override - public String toString() { - return String.format("State [pr=%s;trg=%s;wildN=%s;wild=%s]", predI, trgI, wildcardI, wildcard); - } - - public State(int predicateI, int targetI, int wildcardI, boolean wildcard) { - this.predI = predicateI; - this.trgI = targetI; - this.wildcardI = wildcardI; - this.wildcard = wildcard; - } - } - - boolean match(String predicate, String value); - - public static Matcher fileWildcard() { - return (predicate, value) -> execWildcard(predicate, value, '/'); - } - public static Matcher namespaceWildcard() { - return (predicate, value) -> execWildcard(predicate, value, '.'); - } - public static Matcher wildcard() { - return (predicate, value) -> execWildcard(predicate, value, '\0'); - } - - public static boolean execWildcard(String predicate, String target, char delim) { - if (predicate.equals("")) return target.equals(""); - - var queue = new LinkedList(); - queue.push(new State(0, 0, 0, false)); - - while (!queue.isEmpty()) { - var state = queue.poll(); - var predEnd = state.predI >= predicate.length(); - - if (state.trgI >= target.length()) return predEnd; - var predC = predEnd ? 0 : predicate.charAt(state.predI); - var trgC = target.charAt(state.trgI); - - if (state.wildcard) { - if (state.wildcardI == 2 || trgC != delim) { - queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true)); - } - queue.add(new State(state.predI, state.trgI, 0, false)); - } - else if (predC == '*') { - queue.add(new State(state.predI + 1, state.trgI, state.wildcardI + 1, false)); - } - else if (state.wildcardI > 0) { - if (state.wildcardI > 2) throw new IllegalArgumentException("Too many sequential stars."); - queue.add(new State(state.predI, state.trgI, state.wildcardI, true)); - } - else if (!predEnd && (predC == '?' || predC == trgC)) { - queue.add(new State(state.predI + 1, state.trgI + 1, 0, false)); - } - } - - return false; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/Permission.java b/src/java/me/topchetoeu/jscript/utils/permissions/Permission.java deleted file mode 100644 index f120500..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/Permission.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - - -public class Permission { - public final String namespace; - public final Matcher matcher; - - @Override public String toString() { - return namespace; - } - - public Permission(String namespace, Matcher matcher) { - this.namespace = namespace; - this.matcher = matcher; - } - public Permission(String raw) { - this(raw, null); - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java deleted file mode 100644 index 5bedf8d..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionPredicate.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -public class PermissionPredicate { - public final String namespace; - public final String value; - public final boolean denies; - - public boolean match(Permission permission, String value) { - if (!match(permission)) return false; - if (this.value == null || value == null) return true; - if (permission.matcher == null) return true; - else return permission.matcher.match(this.value, value); - } - public boolean match(Permission permission) { - return Matcher.namespaceWildcard().match(namespace, permission.namespace); - } - - @Override - public String toString() { - if (value != null) return namespace + ":" + value; - else return namespace; - } - - public PermissionPredicate(String raw) { - raw = raw.trim(); - - if (raw.startsWith("!")) { - denies = true; - raw = raw.substring(1).trim(); - } - else denies = false; - - var i = raw.indexOf(':'); - - if (i > 0) { - value = raw.substring(i + 1); - namespace = raw.substring(0, i); - } - else { - value = null; - namespace = raw; - } - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java deleted file mode 100644 index 72468cc..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; - -public class PermissionsManager implements PermissionsProvider { - public final ArrayList predicates = new ArrayList<>(); - - public PermissionsProvider add(PermissionPredicate perm) { - predicates.add(perm); - return this; - } - public PermissionsProvider add(String perm) { - predicates.add(new PermissionPredicate(perm)); - return this; - } - - @Override public boolean hasPermission(Permission perm, String value) { - for (var el : predicates) { - if (el.match(perm, value)) { - if (el.denies) return false; - else return true; - } - } - - return false; - } - @Override public boolean hasPermission(Permission perm) { - for (var el : predicates) { - if (el.match(perm)) { - if (el.denies) return false; - else return true; - } - } - - return false; - } - - public PermissionsProvider addFromStream(InputStream stream) throws IOException { - var reader = new BufferedReader(new InputStreamReader(stream)); - String line; - - while ((line = reader.readLine()) != null) { - var i = line.indexOf('#'); - if (i >= 0) line = line.substring(0, i); - - line = line.trim(); - - if (line.isEmpty()) continue; - - add(line); - } - - return this; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java b/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java deleted file mode 100644 index 19f474b..0000000 --- a/src/java/me/topchetoeu/jscript/utils/permissions/PermissionsProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.topchetoeu.jscript.utils.permissions; - -import me.topchetoeu.jscript.runtime.environment.Environment; -import me.topchetoeu.jscript.runtime.environment.Key; - -public interface PermissionsProvider { - public static final Key KEY = new Key<>(); - public static final PermissionsProvider ALL_PERMS = (perm, value) -> true; - - boolean hasPermission(Permission perm, String value); - - default boolean hasPermission(Permission perm) { - return hasPermission(perm, null); - } - - default boolean hasPermission(String perm, String value, Matcher matcher) { - return hasPermission(new Permission(perm, matcher), value); - } - default boolean hasPermission(String perm, Matcher matcher) { - return hasPermission(new Permission(perm, matcher)); - } - - public static PermissionsProvider get(Environment exts) { - return (perm, value) -> { - if (exts.hasNotNull(KEY)) return exts.get(KEY).hasPermission(perm); - else return true; - }; - } -} \ No newline at end of file