From 078d7ed95f065727faed427ffe98a7afd5560682 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Dec 2023 14:26:42 +0200 Subject: [PATCH] refactor: improve Engine API --- src/me/topchetoeu/jscript/Main.java | 338 +++++++------- src/me/topchetoeu/jscript/engine/Context.java | 3 +- src/me/topchetoeu/jscript/engine/Engine.java | 8 +- .../jscript/engine/Environment.java | 4 +- .../jscript/engine/debug/SimpleDebugger.java | 2 +- src/me/topchetoeu/jscript/lib/Internals.java | 442 +++++++++--------- src/me/topchetoeu/jscript/lib/PromiseLib.java | 8 +- 7 files changed, 402 insertions(+), 403 deletions(-) diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 2143316..db9211c 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -1,170 +1,168 @@ -package me.topchetoeu.jscript; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; - -import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.Engine; -import me.topchetoeu.jscript.engine.Environment; -import me.topchetoeu.jscript.engine.debug.DebugServer; -import me.topchetoeu.jscript.engine.debug.SimpleDebugger; -import me.topchetoeu.jscript.engine.values.ArrayValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; -import me.topchetoeu.jscript.engine.values.ObjectValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.events.Observer; -import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.exceptions.InterruptException; -import me.topchetoeu.jscript.exceptions.SyntaxException; -import me.topchetoeu.jscript.filesystem.MemoryFilesystem; -import me.topchetoeu.jscript.filesystem.Mode; -import me.topchetoeu.jscript.filesystem.PhysicalFilesystem; -import me.topchetoeu.jscript.lib.Internals; - -public class Main { - public static class Printer implements Observer { - public void next(Object data) { - Values.printValue(null, data); - System.out.println(); - } - - public void error(RuntimeException err) { - Values.printError(err, null); - } - - public void finish() { - engineTask.interrupt(); - } - } - - static Thread engineTask, debugTask; - static Engine engine = new Engine(true); - static DebugServer debugServer = new DebugServer(); - static Environment environment = new Environment(null, null, null); - - static int j = 0; - static boolean exited = false; - static String[] args; - - private static void reader() { - try { - for (var arg : args) { - try { - if (arg.equals("--ts")) initTypescript(); - else { - var file = Path.of(arg); - var raw = Files.readString(file); - var res = engine.pushMsg( - false, new Context(engine, 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.read(); - - if (raw == null) break; - var res = engine.pushMsg( - false, new Context(engine, environment), - new Filename("jscript", "repl/" + i + ".js"), - raw, 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()); - exited = true; - } - catch (RuntimeException ex) { - if (!exited) { - System.out.println("Internal error ocurred:"); - ex.printStackTrace(); - } - } - if (exited) { - debugTask.interrupt(); - engineTask.interrupt(); - } - } - - private static void initEnv() { - environment = Internals.apply(environment); - - environment.global.define(false, new NativeFunction("exit", (_ctx, th, args) -> { - exited = true; - throw new InterruptException(); - })); - environment.global.define(false, new NativeFunction("go", (_ctx, th, args) -> { - try { - var f = Path.of("do.js"); - var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); - return func.call(_ctx); - } - catch (IOException e) { - throw new EngineException("Couldn't open do.js"); - } - })); - - environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); - environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath())); - } - private static void initEngine() { - debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine)); - engineTask = engine.start(); - debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); - } - private static void initTypescript() { - try { - var tsEnv = Internals.apply(new Environment(null, null, null)); - tsEnv.stackVisible = false; - tsEnv.global.define(null, "module", false, new ObjectValue()); - var bsEnv = Internals.apply(new Environment(null, null, null)); - bsEnv.stackVisible = false; - - engine.pushMsg( - false, new Context(engine, tsEnv), - new Filename("jscript", "ts.js"), - Reading.resourceToString("js/ts.js"), null - ).await(); - System.out.println("Loaded typescript!"); - - var ctx = new Context(engine, bsEnv); - - engine.pushMsg( - false, ctx, - new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null, - tsEnv.global.get(ctx, "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts")) - ).await(); - } - catch (EngineException e) { - Values.printError(e, "(while initializing TS)"); - } - } - - public static void main(String args[]) { - System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); - - Main.args = args; - var reader = new Thread(Main::reader); - - initEnv(); - initEngine(); - - reader.setDaemon(true); - reader.setName("STD Reader"); - reader.start(); - } -} +package me.topchetoeu.jscript; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; + +import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.Engine; +import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.debug.DebugServer; +import me.topchetoeu.jscript.engine.debug.SimpleDebugger; +import me.topchetoeu.jscript.engine.values.ArrayValue; +import me.topchetoeu.jscript.engine.values.NativeFunction; +import me.topchetoeu.jscript.engine.values.ObjectValue; +import me.topchetoeu.jscript.engine.values.Values; +import me.topchetoeu.jscript.events.Observer; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.exceptions.InterruptException; +import me.topchetoeu.jscript.exceptions.SyntaxException; +import me.topchetoeu.jscript.filesystem.MemoryFilesystem; +import me.topchetoeu.jscript.filesystem.Mode; +import me.topchetoeu.jscript.filesystem.PhysicalFilesystem; +import me.topchetoeu.jscript.lib.Internals; + +public class Main { + public static class Printer implements Observer { + public void next(Object data) { + Values.printValue(null, data); + System.out.println(); + } + + public void error(RuntimeException err) { + Values.printError(err, null); + } + + public void finish() { + engineTask.interrupt(); + } + } + + static Thread engineTask, debugTask; + static Engine engine = new Engine(true); + static DebugServer debugServer = new DebugServer(); + static Environment environment = new Environment(null, null, null); + + static int j = 0; + static boolean exited = false; + static String[] args; + + private static void reader() { + try { + for (var arg : args) { + try { + if (arg.equals("--ts")) initTypescript(); + else { + 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.read(); + + if (raw == null) break; + var res = engine.pushMsg( + false, environment, + new Filename("jscript", "repl/" + i + ".js"), + raw, 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()); + exited = true; + } + catch (RuntimeException ex) { + if (!exited) { + System.out.println("Internal error ocurred:"); + ex.printStackTrace(); + } + } + if (exited) { + debugTask.interrupt(); + engineTask.interrupt(); + } + } + + private static void initEnv() { + environment = Internals.apply(environment); + + environment.global.define(false, new NativeFunction("exit", (_ctx, th, args) -> { + exited = true; + throw new InterruptException(); + })); + environment.global.define(false, new NativeFunction("go", (_ctx, th, args) -> { + try { + var f = Path.of("do.js"); + var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f))); + return func.call(_ctx); + } + catch (IOException e) { + throw new EngineException("Couldn't open do.js"); + } + })); + + environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); + environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath())); + } + private static void initEngine() { + debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine)); + engineTask = engine.start(); + debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); + } + private static void initTypescript() { + try { + var tsEnv = Internals.apply(new Environment(null, null, null)); + tsEnv.stackVisible = false; + tsEnv.global.define(null, "module", false, new ObjectValue()); + var bsEnv = Internals.apply(new Environment(null, null, null)); + bsEnv.stackVisible = false; + + engine.pushMsg( + false, tsEnv, + new Filename("jscript", "ts.js"), + Reading.resourceToString("js/ts.js"), null + ).await(); + System.out.println("Loaded typescript!"); + + engine.pushMsg( + false, bsEnv, + new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null, + tsEnv.global.get(new Context(engine, bsEnv), "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts")) + ).await(); + } + catch (EngineException e) { + Values.printError(e, "(while initializing TS)"); + } + } + + public static void main(String args[]) { + System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); + + Main.args = args; + var reader = new Thread(Main::reader); + + initEnv(); + initEngine(); + + reader.setDaemon(true); + reader.setName("STD Reader"); + reader.start(); + } +} diff --git a/src/me/topchetoeu/jscript/engine/Context.java b/src/me/topchetoeu/jscript/engine/Context.java index 0a0ecab..3872541 100644 --- a/src/me/topchetoeu/jscript/engine/Context.java +++ b/src/me/topchetoeu/jscript/engine/Context.java @@ -119,7 +119,6 @@ public class Context { } public Context(Engine engine, Environment env) { this(engine); - this.pushEnv(env); - + if (env != null) this.pushEnv(env); } } diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index 320585e..50ff0d3 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -141,13 +141,13 @@ public class Engine implements DebugController { return this.thread != null; } - public Awaitable pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) { - var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args, micro); + public Awaitable pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) { + var msg = new Task(new Context(this, env), func, thisArg, args, micro); tasks.add(msg); return msg.notifier; } - public Awaitable pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) { - return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args); + public Awaitable pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) { + return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args); } @Override diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/Environment.java index f55c165..da6a868 100644 --- a/src/me/topchetoeu/jscript/engine/Environment.java +++ b/src/me/topchetoeu/jscript/engine/Environment.java @@ -53,7 +53,6 @@ public class Environment implements PermissionsProvider { res.defineProperty(ctx, "function", target.func(env)); res.defineProperty(ctx, "mapChain", new ArrayValue()); - if (isDebug) { res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))); } @@ -128,4 +127,7 @@ public class Environment implements PermissionsProvider { this.wrappers = nativeConverter; this.global = global; } + public Environment() { + this(null, null, null); + } } diff --git a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java index 136096f..d2989b3 100644 --- a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java +++ b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java @@ -483,7 +483,7 @@ public class SimpleDebugger implements Debugger { env.global = new GlobalScope(codeFrame.local); var ctx = new Context(engine).pushEnv(env); - var awaiter = engine.pushMsg(false, ctx, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); + var awaiter = engine.pushMsg(false, ctx.environment(), new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); engine.run(true); diff --git a/src/me/topchetoeu/jscript/lib/Internals.java b/src/me/topchetoeu/jscript/lib/Internals.java index 5a199a1..2bc6a26 100644 --- a/src/me/topchetoeu/jscript/lib/Internals.java +++ b/src/me/topchetoeu/jscript/lib/Internals.java @@ -1,221 +1,221 @@ -package me.topchetoeu.jscript.lib; - -import java.io.IOException; -import java.util.HashMap; - -import me.topchetoeu.jscript.Buffer; -import me.topchetoeu.jscript.Reading; -import me.topchetoeu.jscript.engine.Context; -import me.topchetoeu.jscript.engine.DataKey; -import me.topchetoeu.jscript.engine.Environment; -import me.topchetoeu.jscript.engine.scope.GlobalScope; -import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.Values; -import me.topchetoeu.jscript.exceptions.EngineException; -import me.topchetoeu.jscript.interop.Native; -import me.topchetoeu.jscript.interop.NativeGetter; -import me.topchetoeu.jscript.parsing.Parsing; - -public class Internals { - private static final DataKey> THREADS = new DataKey<>(); - private static final DataKey I = new DataKey<>(); - - - @Native public static Object log(Context ctx, Object ...args) { - for (var arg : args) { - Values.printValue(ctx, arg); - System.out.print(" "); - } - System.out.println(); - - if (args.length == 0) return null; - else return args[0]; - } - @Native public static String readline(Context ctx) { - try { - return Reading.read(); - } - catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - @Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) { - var thread = new Thread(() -> { - var ms = (long)delay; - var ns = (int)((delay - ms) * 10000000); - - try { - Thread.sleep(ms, ns); - } - catch (InterruptedException e) { return; } - - ctx.engine.pushMsg(false, ctx, func, null, args); - }); - thread.start(); - - int i = ctx.environment().data.increase(I, 1, 0); - var threads = ctx.environment().data.get(THREADS, new HashMap<>()); - threads.put(++i, thread); - return i; - } - @Native public static int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) { - 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; } - - ctx.engine.pushMsg(false, ctx, func, null, args); - } - }); - thread.start(); - - int i = ctx.environment().data.increase(I, 1, 0); - var threads = ctx.environment().data.get(THREADS, new HashMap<>()); - threads.put(++i, thread); - return i; - } - - @Native public static void clearTimeout(Context ctx, int i) { - var threads = ctx.environment().data.get(THREADS, new HashMap<>()); - - var thread = threads.remove(i); - if (thread != null) thread.interrupt(); - } - @Native public static void clearInterval(Context ctx, int i) { - clearTimeout(ctx, i); - } - - @Native public static double parseInt(Context ctx, String val) { - return NumberLib.parseInt(ctx, val); - } - @Native public static double parseFloat(Context ctx, String val) { - return NumberLib.parseFloat(ctx, val); - } - - @Native public static boolean isNaN(Context ctx, double val) { - return NumberLib.isNaN(ctx, val); - } - @Native public static boolean isFinite(Context ctx, double val) { - return NumberLib.isFinite(ctx, val); - } - @Native public static boolean isInfinite(Context ctx, double val) { - return NumberLib.isInfinite(ctx, val); - } - - @NativeGetter public static double NaN(Context ctx) { - return Double.NaN; - } - @NativeGetter public static double Infinity(Context ctx) { - return Double.POSITIVE_INFINITY; - } - private static final String HEX = "0123456789ABCDEF"; - - private static String encodeUriAny(String str, String keepAlphabet) { - if (str == null) str = "undefined"; - - var bytes = str.getBytes(); - var sb = new StringBuilder(bytes.length); - - for (byte c : bytes) { - if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c); - else { - sb.append('%'); - sb.append(HEX.charAt(c / 16)); - sb.append(HEX.charAt(c % 16)); - } - } - - return sb.toString(); - } - private static String decodeUriAny(String str, String keepAlphabet) { - if (str == null) str = "undefined"; - - var res = new Buffer(); - var bytes = str.getBytes(); - - for (var i = 0; i < bytes.length; i++) { - var c = bytes[i]; - if (c == '%') { - if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed."); - var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]); - if (!Parsing.isAny((char)b, keepAlphabet)) { - i += 2; - res.append((byte)b); - continue; - } - } - res.append(c); - } - - return new String(res.data()); - } - - @Native public static String encodeURIComponent(String str) { - return encodeUriAny(str, ".-_!~*'()"); - } - @Native public static String decodeURIComponent(String str) { - return decodeUriAny(str, ""); - } - @Native public static String encodeURI(String str) { - return encodeUriAny(str, ";,/?:@&=+$#.-_!~*'()"); - } - @Native public static String decodeURI(String str) { - return decodeUriAny(str, ",/?:@&=+$#."); - } - - public static Environment apply(Environment env) { - var wp = env.wrappers; - var glob = env.global = 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(false, wp.getConstr(DateLib.class)); - glob.define(false, wp.getConstr(ObjectLib.class)); - glob.define(false, wp.getConstr(FunctionLib.class)); - glob.define(false, wp.getConstr(ArrayLib.class)); - - glob.define(false, wp.getConstr(BooleanLib.class)); - glob.define(false, wp.getConstr(NumberLib.class)); - glob.define(false, wp.getConstr(StringLib.class)); - glob.define(false, wp.getConstr(SymbolLib.class)); - - glob.define(false, wp.getConstr(PromiseLib.class)); - glob.define(false, wp.getConstr(RegExpLib.class)); - glob.define(false, wp.getConstr(MapLib.class)); - glob.define(false, wp.getConstr(SetLib.class)); - - glob.define(false, wp.getConstr(ErrorLib.class)); - glob.define(false, wp.getConstr(SyntaxErrorLib.class)); - glob.define(false, wp.getConstr(TypeErrorLib.class)); - glob.define(false, wp.getConstr(RangeErrorLib.class)); - - env.setProto("object", wp.getProto(ObjectLib.class)); - env.setProto("function", wp.getProto(FunctionLib.class)); - env.setProto("array", wp.getProto(ArrayLib.class)); - - env.setProto("bool", wp.getProto(BooleanLib.class)); - env.setProto("number", wp.getProto(NumberLib.class)); - env.setProto("string", wp.getProto(StringLib.class)); - env.setProto("symbol", wp.getProto(SymbolLib.class)); - - env.setProto("error", wp.getProto(ErrorLib.class)); - env.setProto("syntaxErr", wp.getProto(SyntaxErrorLib.class)); - env.setProto("typeErr", wp.getProto(TypeErrorLib.class)); - env.setProto("rangeErr", wp.getProto(RangeErrorLib.class)); - - wp.getProto(ObjectLib.class).setPrototype(null, null); - env.regexConstructor = wp.getConstr(RegExpLib.class); - - return env; - } -} +package me.topchetoeu.jscript.lib; + +import java.io.IOException; +import java.util.HashMap; + +import me.topchetoeu.jscript.Buffer; +import me.topchetoeu.jscript.Reading; +import me.topchetoeu.jscript.engine.Context; +import me.topchetoeu.jscript.engine.DataKey; +import me.topchetoeu.jscript.engine.Environment; +import me.topchetoeu.jscript.engine.scope.GlobalScope; +import me.topchetoeu.jscript.engine.values.FunctionValue; +import me.topchetoeu.jscript.engine.values.Values; +import me.topchetoeu.jscript.exceptions.EngineException; +import me.topchetoeu.jscript.interop.Native; +import me.topchetoeu.jscript.interop.NativeGetter; +import me.topchetoeu.jscript.parsing.Parsing; + +public class Internals { + private static final DataKey> THREADS = new DataKey<>(); + private static final DataKey I = new DataKey<>(); + + + @Native public static Object log(Context ctx, Object ...args) { + for (var arg : args) { + Values.printValue(ctx, arg); + System.out.print(" "); + } + System.out.println(); + + if (args.length == 0) return null; + else return args[0]; + } + @Native public static String readline(Context ctx) { + try { + return Reading.read(); + } + catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) { + var thread = new Thread(() -> { + var ms = (long)delay; + var ns = (int)((delay - ms) * 10000000); + + try { + Thread.sleep(ms, ns); + } + catch (InterruptedException e) { return; } + + ctx.engine.pushMsg(false, ctx.environment(), func, null, args); + }); + thread.start(); + + int i = ctx.environment().data.increase(I, 1, 0); + var threads = ctx.environment().data.get(THREADS, new HashMap<>()); + threads.put(++i, thread); + return i; + } + @Native public static int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) { + 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; } + + ctx.engine.pushMsg(false, ctx.environment(), func, null, args); + } + }); + thread.start(); + + int i = ctx.environment().data.increase(I, 1, 0); + var threads = ctx.environment().data.get(THREADS, new HashMap<>()); + threads.put(++i, thread); + return i; + } + + @Native public static void clearTimeout(Context ctx, int i) { + var threads = ctx.environment().data.get(THREADS, new HashMap<>()); + + var thread = threads.remove(i); + if (thread != null) thread.interrupt(); + } + @Native public static void clearInterval(Context ctx, int i) { + clearTimeout(ctx, i); + } + + @Native public static double parseInt(Context ctx, String val) { + return NumberLib.parseInt(ctx, val); + } + @Native public static double parseFloat(Context ctx, String val) { + return NumberLib.parseFloat(ctx, val); + } + + @Native public static boolean isNaN(Context ctx, double val) { + return NumberLib.isNaN(ctx, val); + } + @Native public static boolean isFinite(Context ctx, double val) { + return NumberLib.isFinite(ctx, val); + } + @Native public static boolean isInfinite(Context ctx, double val) { + return NumberLib.isInfinite(ctx, val); + } + + @NativeGetter public static double NaN(Context ctx) { + return Double.NaN; + } + @NativeGetter public static double Infinity(Context ctx) { + return Double.POSITIVE_INFINITY; + } + private static final String HEX = "0123456789ABCDEF"; + + private static String encodeUriAny(String str, String keepAlphabet) { + if (str == null) str = "undefined"; + + var bytes = str.getBytes(); + var sb = new StringBuilder(bytes.length); + + for (byte c : bytes) { + if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c); + else { + sb.append('%'); + sb.append(HEX.charAt(c / 16)); + sb.append(HEX.charAt(c % 16)); + } + } + + return sb.toString(); + } + private static String decodeUriAny(String str, String keepAlphabet) { + if (str == null) str = "undefined"; + + var res = new Buffer(); + var bytes = str.getBytes(); + + for (var i = 0; i < bytes.length; i++) { + var c = bytes[i]; + if (c == '%') { + if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed."); + var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]); + if (!Parsing.isAny((char)b, keepAlphabet)) { + i += 2; + res.append((byte)b); + continue; + } + } + res.append(c); + } + + return new String(res.data()); + } + + @Native public static String encodeURIComponent(String str) { + return encodeUriAny(str, ".-_!~*'()"); + } + @Native public static String decodeURIComponent(String str) { + return decodeUriAny(str, ""); + } + @Native public static String encodeURI(String str) { + return encodeUriAny(str, ";,/?:@&=+$#.-_!~*'()"); + } + @Native public static String decodeURI(String str) { + return decodeUriAny(str, ",/?:@&=+$#."); + } + + public static Environment apply(Environment env) { + var wp = env.wrappers; + var glob = env.global = 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(false, wp.getConstr(DateLib.class)); + glob.define(false, wp.getConstr(ObjectLib.class)); + glob.define(false, wp.getConstr(FunctionLib.class)); + glob.define(false, wp.getConstr(ArrayLib.class)); + + glob.define(false, wp.getConstr(BooleanLib.class)); + glob.define(false, wp.getConstr(NumberLib.class)); + glob.define(false, wp.getConstr(StringLib.class)); + glob.define(false, wp.getConstr(SymbolLib.class)); + + glob.define(false, wp.getConstr(PromiseLib.class)); + glob.define(false, wp.getConstr(RegExpLib.class)); + glob.define(false, wp.getConstr(MapLib.class)); + glob.define(false, wp.getConstr(SetLib.class)); + + glob.define(false, wp.getConstr(ErrorLib.class)); + glob.define(false, wp.getConstr(SyntaxErrorLib.class)); + glob.define(false, wp.getConstr(TypeErrorLib.class)); + glob.define(false, wp.getConstr(RangeErrorLib.class)); + + env.setProto("object", wp.getProto(ObjectLib.class)); + env.setProto("function", wp.getProto(FunctionLib.class)); + env.setProto("array", wp.getProto(ArrayLib.class)); + + env.setProto("bool", wp.getProto(BooleanLib.class)); + env.setProto("number", wp.getProto(NumberLib.class)); + env.setProto("string", wp.getProto(StringLib.class)); + env.setProto("symbol", wp.getProto(SymbolLib.class)); + + env.setProto("error", wp.getProto(ErrorLib.class)); + env.setProto("syntaxErr", wp.getProto(SyntaxErrorLib.class)); + env.setProto("typeErr", wp.getProto(TypeErrorLib.class)); + env.setProto("rangeErr", wp.getProto(RangeErrorLib.class)); + + wp.getProto(ObjectLib.class).setPrototype(null, null); + env.regexConstructor = wp.getConstr(RegExpLib.class); + + return env; + } +} diff --git a/src/me/topchetoeu/jscript/lib/PromiseLib.java b/src/me/topchetoeu/jscript/lib/PromiseLib.java index 6e76872..66f5191 100644 --- a/src/me/topchetoeu/jscript/lib/PromiseLib.java +++ b/src/me/topchetoeu/jscript/lib/PromiseLib.java @@ -253,7 +253,7 @@ import me.topchetoeu.jscript.interop.Native; this.val = val; this.state = STATE_FULFILLED; - ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> { + ctx.engine.pushMsg(true, ctx.environment(), new NativeFunction((_ctx, _thisArg, _args) -> { for (var handle : handles) { handle.fulfilled.call(handle.ctx, null, val); } @@ -288,7 +288,7 @@ import me.topchetoeu.jscript.interop.Native; this.val = val; this.state = STATE_REJECTED; - ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> { + ctx.engine.pushMsg(true, ctx.environment(), new NativeFunction((_ctx, _thisArg, _args) -> { for (var handle : handles) handle.rejected.call(handle.ctx, null, val); if (!handled) { Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)"); @@ -305,9 +305,9 @@ import me.topchetoeu.jscript.interop.Native; } private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) { - if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val); + if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx.environment(), fulfill, null, val); else if (state == STATE_REJECTED) { - ctx.engine.pushMsg(true, ctx, reject, null, val); + ctx.engine.pushMsg(true, ctx.environment(), reject, null, val); handled = true; } else handles.add(new Handle(ctx, fulfill, reject));