From d5fd6e650eed57fd8a7140bd7b24f10a1b3c7447 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:02:45 +0200 Subject: [PATCH] fix: clean up debugger API --- src/me/topchetoeu/jscript/Main.java | 8 +++- src/me/topchetoeu/jscript/engine/Context.java | 2 +- src/me/topchetoeu/jscript/engine/Engine.java | 4 +- .../jscript/engine/debug/DebugController.java | 2 +- .../jscript/engine/debug/DebugHandler.java | 3 -- .../jscript/engine/debug/DebugServer.java | 11 ++--- .../jscript/engine/debug/Debugger.java | 3 +- .../jscript/engine/debug/SimpleDebugger.java | 48 +++++++++++++------ .../jscript/engine/frame/CodeFrame.java | 7 +++ .../jscript/engine/values/CodeFunction.java | 13 +++-- .../jscript/lib/AsyncFunctionLib.java | 2 + .../jscript/lib/AsyncGeneratorLib.java | 2 + .../topchetoeu/jscript/lib/GeneratorLib.java | 2 + 13 files changed, 72 insertions(+), 35 deletions(-) diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 252114f..afbc58e 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -8,6 +8,7 @@ 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.DebugContext; import me.topchetoeu.jscript.engine.debug.DebugServer; import me.topchetoeu.jscript.engine.debug.SimpleDebugger; import me.topchetoeu.jscript.engine.values.ArrayValue; @@ -46,7 +47,7 @@ public class Main { } static Thread engineTask, debugTask; - static Engine engine = new Engine(true); + static Engine engine = new Engine(); static DebugServer debugServer = new DebugServer(); static Environment environment = new Environment(); @@ -133,7 +134,10 @@ public class Main { environment.add(ModuleRepo.ENV_KEY, ModuleRepo.ofFilesystem(fs)); } private static void initEngine() { - debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine)); + var ctx = new DebugContext(); + // engine.globalEnvironment.add(DebugContext.ENV_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); } diff --git a/src/me/topchetoeu/jscript/engine/Context.java b/src/me/topchetoeu/jscript/engine/Context.java index e5c6138..0b08d6d 100644 --- a/src/me/topchetoeu/jscript/engine/Context.java +++ b/src/me/topchetoeu/jscript/engine/Context.java @@ -94,7 +94,7 @@ public class Context implements Extensions { private Context curr = self; private void update() { - while (curr.frame == null && curr != null) curr = curr.parent; + while (curr != null && curr.frame == null) curr = curr.parent; } @Override public boolean hasNext() { diff --git a/src/me/topchetoeu/jscript/engine/Engine.java b/src/me/topchetoeu/jscript/engine/Engine.java index 2a02e2f..9ee6498 100644 --- a/src/me/topchetoeu/jscript/engine/Engine.java +++ b/src/me/topchetoeu/jscript/engine/Engine.java @@ -57,7 +57,6 @@ public class Engine { public final Environment globalEnvironment = new Environment(); public final int id = ++nextId; - public final boolean debugging; public int maxStackFrames = 10000; private Thread thread; @@ -111,7 +110,6 @@ public class Engine { return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args); } - public Engine(boolean debugging) { - this.debugging = debugging; + public Engine() { } } diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugController.java b/src/me/topchetoeu/jscript/engine/debug/DebugController.java index d03d4f6..f52a7b7 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugController.java +++ b/src/me/topchetoeu/jscript/engine/debug/DebugController.java @@ -50,7 +50,7 @@ public interface DebugController { void onFramePop(Context ctx, CodeFrame frame); public static DebugController empty() { - return new DebugController() { + return new DebugController () { @Override public void onFramePop(Context ctx, CodeFrame frame) { } @Override public void onFramePush(Context ctx, CodeFrame frame) { } @Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) { diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java b/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java index 1392e10..a2ed59f 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java +++ b/src/me/topchetoeu/jscript/engine/debug/DebugHandler.java @@ -25,9 +25,6 @@ public interface DebugHandler { void getProperties(V8Message msg); void releaseObjectGroup(V8Message msg); void releaseObject(V8Message msg); - /** - * This method might not execute the actual code for well-known requests - */ void callFunctionOn(V8Message msg); void runtimeEnable(V8Message msg); diff --git a/src/me/topchetoeu/jscript/engine/debug/DebugServer.java b/src/me/topchetoeu/jscript/engine/debug/DebugServer.java index 0884618..18f9899 100644 --- a/src/me/topchetoeu/jscript/engine/debug/DebugServer.java +++ b/src/me/topchetoeu/jscript/engine/debug/DebugServer.java @@ -50,8 +50,6 @@ public class DebugServer { private void handle(WebSocket ws, Debugger debugger) { WebSocketMessage raw; - debugger.connect(); - while ((raw = ws.receive()) != null) { if (raw.type != Type.Text) { ws.send(new V8Error("Expected a text message.")); @@ -72,8 +70,9 @@ public class DebugServer { switch (msg.name) { case "Debugger.enable": connNotifier.next(); - debugger.enable(msg); continue; - case "Debugger.disable": debugger.disable(msg); continue; + debugger.enable(msg); + continue; + case "Debugger.disable": debugger.close(); continue; case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue; case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue; @@ -116,7 +115,7 @@ public class DebugServer { } } - debugger.disconnect(); + debugger.close(); } private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) { var key = req.headers.get("sec-websocket-key"); @@ -151,7 +150,7 @@ public class DebugServer { catch (RuntimeException e) { ws.send(new V8Error(e.getMessage())); } - finally { ws.close(); debugger.disconnect(); } + finally { ws.close(); debugger.close(); } }, "Debug Handler"); } diff --git a/src/me/topchetoeu/jscript/engine/debug/Debugger.java b/src/me/topchetoeu/jscript/engine/debug/Debugger.java index a38e1f7..dcafc83 100644 --- a/src/me/topchetoeu/jscript/engine/debug/Debugger.java +++ b/src/me/topchetoeu/jscript/engine/debug/Debugger.java @@ -1,6 +1,5 @@ package me.topchetoeu.jscript.engine.debug; public interface Debugger extends DebugHandler, DebugController { - void connect(); - void disconnect(); + void close(); } diff --git a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java index eb16afd..d72de6d 100644 --- a/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java +++ b/src/me/topchetoeu/jscript/engine/debug/SimpleDebugger.java @@ -174,7 +174,6 @@ public class SimpleDebugger implements Debugger { public State state = State.RESUMED; public final WebSocket ws; - public final Engine target; private ObjectValue emptyObject = new ObjectValue(); @@ -477,7 +476,7 @@ public class SimpleDebugger implements Debugger { private RunResult run(Frame codeFrame, String code) { if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); - var engine = new Engine(false); + var engine = new Engine(); var env = codeFrame.func.environment.fork(); env.global = new GlobalScope(codeFrame.local); @@ -574,8 +573,36 @@ public class SimpleDebugger implements Debugger { updateNotifier.next(); } @Override public synchronized void disable(V8Message msg) { - enabled = false; + close(); ws.send(msg.respond()); + } + public synchronized void close() { + enabled = false; + execptionType = CatchType.NONE; + state = State.RESUMED; + + idToBptCand.clear(); + + idToBreakpoint.clear(); + locToBreakpoint.clear(); + tmpBreakpts.clear(); + + filenameToId.clear(); + idToSource.clear(); + pendingSources.clear(); + + idToFrame.clear(); + codeFrameToFrame.clear(); + + idToObject.clear(); + objectToId.clear(); + objectGroups.clear(); + + pendingPause = false; + + stepOutFrame = currFrame = null; + stepOutPtr = 0; + updateNotifier.next(); } @@ -952,19 +979,12 @@ public class SimpleDebugger implements Debugger { } } - @Override public synchronized void connect() { - if (!DebugContext.get(target.globalEnvironment).attachDebugger(this)) { - ws.send(new V8Error("A debugger is already attached to this engine.")); - } - } - @Override public synchronized void disconnect() { - DebugContext.get(target.globalEnvironment).detachDebugger(); - enabled = false; - updateNotifier.next(); + public SimpleDebugger attach(DebugContext ctx) { + ctx.attachDebugger(this); + return this; } - public SimpleDebugger(WebSocket ws, Engine target) { + public SimpleDebugger(WebSocket ws) { this.ws = ws; - this.target = target; } } diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 9cecef2..23671cf 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -303,6 +303,13 @@ public class CodeFrame { return Runners.NO_RETURN; } + public void onPush() { + DebugContext.get(ctx).onFramePush(ctx, this); + } + public void onPop() { + DebugContext.get(ctx.parent).onFramePop(ctx.parent, this); + } + public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) { this.args = args.clone(); this.scope = new LocalScope(func.localsN, func.captures); diff --git a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java index deaa0de..ac41137 100644 --- a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java +++ b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java @@ -33,9 +33,16 @@ public class CodeFunction extends FunctionValue { public Object call(Context ctx, Object thisArg, Object ...args) { var frame = new CodeFrame(ctx, thisArg, args, this); - while (true) { - var res = frame.next(Runners.NO_RETURN, Runners.NO_RETURN, null); - if (res != Runners.NO_RETURN) return res; + frame.onPush(); + + try { + while (true) { + var res = frame.next(Runners.NO_RETURN, Runners.NO_RETURN, null); + if (res != Runners.NO_RETURN) return res; + } + } + finally { + frame.onPop(); } } diff --git a/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java b/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java index e8b65cb..597c93d 100644 --- a/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java +++ b/src/me/topchetoeu/jscript/lib/AsyncFunctionLib.java @@ -21,6 +21,7 @@ import me.topchetoeu.jscript.interop.Native; private void next(Context ctx, Object inducedValue, Object inducedError) { Object res = null; + frame.onPush(); awaiting = false; while (!awaiting) { try { @@ -36,6 +37,7 @@ import me.topchetoeu.jscript.interop.Native; break; } } + frame.onPop(); if (awaiting) { PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); diff --git a/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java b/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java index 23688cc..53181ca 100644 --- a/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java +++ b/src/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java @@ -30,6 +30,7 @@ import me.topchetoeu.jscript.interop.Native; Object res = null; state = 0; + frame.onPush(); while (state == 0) { try { res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); @@ -47,6 +48,7 @@ import me.topchetoeu.jscript.interop.Native; break; } } + frame.onPop(); if (state == 1) { PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); diff --git a/src/me/topchetoeu/jscript/lib/GeneratorLib.java b/src/me/topchetoeu/jscript/lib/GeneratorLib.java index 101f10c..a21f828 100644 --- a/src/me/topchetoeu/jscript/lib/GeneratorLib.java +++ b/src/me/topchetoeu/jscript/lib/GeneratorLib.java @@ -26,6 +26,7 @@ import me.topchetoeu.jscript.interop.Native; Object res = null; yielding = false; + frame.onPush(); while (!yielding) { try { res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); @@ -40,6 +41,7 @@ import me.topchetoeu.jscript.interop.Native; throw e; } } + frame.onPop(); if (done) frame = null; else res = frame.pop();