diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index bf0f549..6dd6463 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -1,131 +1,150 @@ -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.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.exceptions.UncheckedException; -import me.topchetoeu.jscript.lib.Internals; - -public class Main { - static Thread engineTask, debugTask; - static Engine engine; - static Environment env; - static int j = 0; - - private static Observer valuePrinter = new Observer() { - public void next(Object data) { - Values.printValue(null, data); - System.out.println(); - } - - public void error(RuntimeException err) { - Values.printError(err, null); - } - - @Override - public void finish() { - engineTask.interrupt(); - } - }; - - public static void main(String args[]) { - System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR)); - engine = new Engine(); - - env = new Environment(null, null, null); - var exited = new boolean[1]; - var server = new DebugServer(); - server.targets.put("target", (ws, req) -> SimpleDebugger.get(ws, engine)); - - engineTask = engine.start(); - debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true); - // server.awaitConnection(); - - engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> { - new Internals().apply(env); - - env.global.define("exit", _ctx -> { - exited[0] = true; - throw new InterruptException(); - }); - env.global.define("go", _ctx -> { - 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"); - } - }); - - return null; - }), null).await(); - - try { - var ts = engine.pushMsg( - false, new Context(engine).pushEnv(env), - new Filename("file", "/mnt/data/repos/java-jscript/src/me/topchetoeu/jscript/js/ts.js"), - Reading.resourceToString("js/ts.js"), null - ).await(); - System.out.println("Loaded typescript!"); - engine.pushMsg( - false, new Context(engine).pushEnv(env.child()), - new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null, - ts, env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts")) - ).await(); - } - catch (EngineException e) { - Values.printError(e, "(while initializing TS)"); - System.out.println("engine reported stack trace:"); - for (var el : e.stackTrace) { - System.out.println(el); - } - } - - - var reader = new Thread(() -> { - try { - for (var i = 0; ; i++) { - try { - var raw = Reading.read(); - - if (raw == null) break; - valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), new Filename("jscript", "repl/" + i + ".js"), raw, null).await()); - } - catch (EngineException e) { Values.printError(e, ""); } - } - } - catch (IOException e) { return; } - catch (SyntaxException ex) { - if (exited[0]) return; - System.out.println("Syntax error:" + ex.msg); - } - catch (RuntimeException ex) { - if (!exited[0]) { - System.out.println("Internal error ocurred:"); - ex.printStackTrace(); - } - } - catch (Throwable e) { throw new UncheckedException(e); } - if (exited[0]) debugTask.interrupt(); - }); - 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.StackData; +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.lib.Internals; + +public class Main { + static Thread engineTask, debugTask; + static Engine engine; + static Environment env; + static int j = 0; + + private static Observer valuePrinter = new Observer() { + public void next(Object data) { + Values.printValue(null, data); + System.out.println(); + } + + public void error(RuntimeException err) { + Values.printError(err, null); + } + + @Override + public void finish() { + engineTask.interrupt(); + } + }; + + public static void main(String args[]) { + System.out.println(String.format("Running %s v%s by %s", Metadata.NAME, Metadata.VERSION, Metadata.AUTHOR)); + engine = new Engine(true); + + env = new Environment(null, null, null); + var exited = new boolean[1]; + var server = new DebugServer(); + server.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine)); + + engineTask = engine.start(); + debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true); + + engine.pushMsg(false, null, new NativeFunction((ctx, thisArg, _a) -> { + new Internals().apply(env); + + env.global.define("exit", _ctx -> { + exited[0] = true; + throw new InterruptException(); + }); + env.global.define("go", _ctx -> { + 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"); + } + }); + // TODO: make better API + env.global.define(true, new NativeFunction("include", (_ctx, th, __args) -> { + try { + var currFilename = StackData.peekFrame(_ctx).function.loc().filename(); + var loc = Path.of("").toAbsolutePath(); + if (currFilename.protocol.equals("file")) loc = Path.of(currFilename.path).getParent(); + var path = loc.resolve(Path.of(__args.length >= 1 ? Values.toString(_ctx, __args[0]) : "")); + var src = Files.readString(path); + var func = _ctx.compile(Filename.fromFile(path.toFile()), src); + var callArgs = new ArrayValue(); + if (__args.length >= 2 && __args[1] instanceof ArrayValue) callArgs = (ArrayValue)__args[1]; + return func.call(_ctx, null, callArgs); + } + catch (IOException e) { throw EngineException.ofError("IOError", "Couldn't open file."); } + })); + + return null; + }), null).await(); + + try { + var tsEnv = env.child(); + tsEnv.global.define(null, "module", false, new ObjectValue()); + engine.pushMsg( + false, new Context(engine).pushEnv(tsEnv), + new Filename("jscript", "ts.js"), + Reading.resourceToString("js/ts.js"), null + ).await(); + System.out.println("Loaded typescript!"); + engine.pushMsg( + false, new Context(engine).pushEnv(env.child()), + new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null, + tsEnv.global.obj, env, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts")) + ).await(); + } + catch (EngineException e) { + Values.printError(e, "(while initializing TS)"); + } + + var reader = new Thread(() -> { + try { + for (var arg : args) { + try { + var file = Path.of(arg); + var raw = Files.readString(file); + valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), Filename.fromFile(file.toFile()), raw, null).await()); + } + catch (EngineException e) { Values.printError(e, ""); } + } + for (var i = 0; ; i++) { + try { + var raw = Reading.read(); + + if (raw == null) break; + valuePrinter.next(engine.pushMsg(false, new Context(engine).pushEnv(env), new Filename("jscript", "repl/" + i + ".js"), raw, null).await()); + } + catch (EngineException e) { Values.printError(e, ""); } + } + } + catch (IOException e) { exited[0] = true; } + catch (SyntaxException ex) { + if (exited[0]) return; + System.out.println("Syntax error:" + ex.msg); + } + catch (RuntimeException ex) { + if (!exited[0]) { + System.out.println("Internal error ocurred:"); + ex.printStackTrace(); + } + } + if (exited[0]) debugTask.interrupt(); + }); + 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 40fa042..c287c3e 100644 --- a/src/me/topchetoeu/jscript/engine/Context.java +++ b/src/me/topchetoeu/jscript/engine/Context.java @@ -6,7 +6,6 @@ import java.util.TreeSet; import me.topchetoeu.jscript.Filename; import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.engine.values.FunctionValue; -import me.topchetoeu.jscript.engine.values.NativeFunction; import me.topchetoeu.jscript.engine.values.ObjectValue; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.parsing.Parsing; @@ -40,10 +39,9 @@ public class Context { } else source = Values.toString(this, transpiled); - var debugger = StackData.getDebugger(this); var breakpoints = new TreeSet(); FunctionValue res = Parsing.compile(engine.functions, breakpoints, environment(), filename, source); - if (debugger != null) debugger.onSource(filename, source, breakpoints); + engine.onSource(filename, source, breakpoints); if (runner != null) res = (FunctionValue)runner.call(this, null, res); diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index 6c8ed51..f24e885 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -1,16 +1,22 @@ package me.topchetoeu.jscript.engine; import java.util.HashMap; +import java.util.TreeSet; import java.util.concurrent.LinkedBlockingDeque; import me.topchetoeu.jscript.Filename; +import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.FunctionBody; +import me.topchetoeu.jscript.compilation.Instruction; +import me.topchetoeu.jscript.engine.debug.DebugController; +import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.events.Awaitable; import me.topchetoeu.jscript.events.DataNotifier; +import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.InterruptException; -public class Engine { +public class Engine implements DebugController { private class UncompiledFunction extends FunctionValue { public final Filename filename; public final String raw; @@ -51,8 +57,42 @@ public class Engine { private LinkedBlockingDeque microTasks = new LinkedBlockingDeque<>(); public final int id = ++nextId; + public final Data data = new Data().set(StackData.MAX_FRAMES, 200); public final HashMap functions = new HashMap<>(); - public final Data data = new Data().set(StackData.MAX_FRAMES, 10000); + public final boolean debugging; + private final HashMap sources = new HashMap<>(); + private final HashMap> bpts = new HashMap<>(); + private DebugController debugger; + + public boolean attachDebugger(DebugController debugger) { + if (!debugging || this.debugger != null) return false; + + for (var source : sources.entrySet()) { + debugger.onSource(source.getKey(), source.getValue(), bpts.get(source.getKey())); + } + + this.debugger = debugger; + return true; + } + public boolean detachDebugger() { + if (!debugging || this.debugger == null) return false; + this.debugger = null; + return true; + } + + @Override public void onFramePop(Context ctx, CodeFrame frame) { + if (debugging && debugger != null) debugger.onFramePop(ctx, frame); + } + @Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { + if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught); + else return false; + } + @Override public void onSource(Filename filename, String source, TreeSet breakpoints) { + if (!debugging) return; + if (debugger != null) debugger.onSource(filename, source, breakpoints); + sources.put(filename, source); + bpts.put(filename, breakpoints); + } private void runTask(Task task) { try { @@ -108,4 +148,8 @@ public class Engine { 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 Engine(boolean debugging) { + this.debugging = debugging; + } } diff --git a/src/me/topchetoeu/jscript/engine/Environment.java b/src/me/topchetoeu/jscript/engine/Environment.java index 6208325..5c2c4e2 100644 --- a/src/me/topchetoeu/jscript/engine/Environment.java +++ b/src/me/topchetoeu/jscript/engine/Environment.java @@ -40,8 +40,7 @@ public class Environment { } @Native public Symbol symbol(String name) { - if (symbols.containsKey(name)) - return symbols.get(name); + if (symbols.containsKey(name)) return symbols.get(name); else { var res = new Symbol(name); symbols.put(name, res); diff --git a/src/me/topchetoeu/jscript/engine/StackData.java b/src/me/topchetoeu/jscript/engine/StackData.java index 820016a..0a34d79 100644 --- a/src/me/topchetoeu/jscript/engine/StackData.java +++ b/src/me/topchetoeu/jscript/engine/StackData.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.engine.debug.Debugger; import me.topchetoeu.jscript.engine.frame.CodeFrame; import me.topchetoeu.jscript.exceptions.EngineException; @@ -25,8 +26,7 @@ public class StackData { if (frames.get(frames.size() - 1) != frame) return false; frames.remove(frames.size() - 1); ctx.popEnv(); - var dbg = getDebugger(ctx); - if (dbg != null) dbg.onFramePop(ctx, frame); + ctx.engine.onFramePop(ctx, frame); return true; } public static CodeFrame peekFrame(Context ctx) { @@ -45,7 +45,11 @@ public class StackData { for (var i = frames.size() - 1; i >= 0; i--) { var el = frames.get(i); var name = el.function.name; - var loc = el.function.loc(); + Location loc = null; + + for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location; + if (loc == null) loc = el.function.loc(); + var trace = ""; if (loc != null) trace += "at " + loc.toString() + " "; @@ -58,8 +62,4 @@ public class StackData { return res; } - - public static Debugger getDebugger(Context ctx) { - return ctx.data.get(DEBUGGER); - } } diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugController.java b/src/me/topchetoeu/jscript/engine/debug/DebugController.java index beec045..0b04c0f 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugController.java +++ b/src/me/topchetoeu/jscript/engine/debug/DebugController.java @@ -37,7 +37,4 @@ public interface DebugController { * @param frame The code frame which was popped out */ void onFramePop(Context ctx, CodeFrame frame); - - void connect(); - void disconnect(); } diff --git a/src/me/topchetoeu/jscript/engine/debug/Debugger.java b/src/me/topchetoeu/jscript/engine/debug/Debugger.java index 3c704af..a38e1f7 100644 --- a/src/me/topchetoeu/jscript/engine/debug/Debugger.java +++ b/src/me/topchetoeu/jscript/engine/debug/Debugger.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.engine.debug; public interface Debugger extends DebugHandler, DebugController { - + void connect(); + void disconnect(); } diff --git a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java index c11f802..e6371bf 100644 --- a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java +++ b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java @@ -31,12 +31,6 @@ import me.topchetoeu.jscript.json.JSON; import me.topchetoeu.jscript.json.JSONElement; import me.topchetoeu.jscript.json.JSONList; import me.topchetoeu.jscript.json.JSONMap; -import me.topchetoeu.jscript.lib.DateLib; -import me.topchetoeu.jscript.lib.MapLib; -import me.topchetoeu.jscript.lib.PromiseLib; -import me.topchetoeu.jscript.lib.RegExpLib; -import me.topchetoeu.jscript.lib.SetLib; -import me.topchetoeu.jscript.lib.GeneratorLib.Generator; public class SimpleDebugger implements Debugger { public static final String CHROME_GET_PROP_FUNC = "function s(e){let t=this;const n=JSON.parse(e);for(let e=0,i=n.length;e