fix: clean up debugger API

This commit is contained in:
TopchetoEU 2023-12-27 20:02:45 +02:00
parent c0b895e00a
commit d5fd6e650e
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
13 changed files with 72 additions and 35 deletions

View File

@ -8,6 +8,7 @@ import java.nio.file.Path;
import me.topchetoeu.jscript.engine.Context; import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment; 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.DebugServer;
import me.topchetoeu.jscript.engine.debug.SimpleDebugger; import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
@ -46,7 +47,7 @@ public class Main {
} }
static Thread engineTask, debugTask; static Thread engineTask, debugTask;
static Engine engine = new Engine(true); static Engine engine = new Engine();
static DebugServer debugServer = new DebugServer(); static DebugServer debugServer = new DebugServer();
static Environment environment = new Environment(); static Environment environment = new Environment();
@ -133,7 +134,10 @@ public class Main {
environment.add(ModuleRepo.ENV_KEY, ModuleRepo.ofFilesystem(fs)); environment.add(ModuleRepo.ENV_KEY, ModuleRepo.ofFilesystem(fs));
} }
private static void initEngine() { 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(); engineTask = engine.start();
debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true); debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
} }

View File

@ -94,7 +94,7 @@ public class Context implements Extensions {
private Context curr = self; private Context curr = self;
private void update() { 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() { @Override public boolean hasNext() {

View File

@ -57,7 +57,6 @@ public class Engine {
public final Environment globalEnvironment = new Environment(); public final Environment globalEnvironment = new Environment();
public final int id = ++nextId; public final int id = ++nextId;
public final boolean debugging;
public int maxStackFrames = 10000; public int maxStackFrames = 10000;
private Thread thread; private Thread thread;
@ -111,7 +110,6 @@ public class Engine {
return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args); return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args);
} }
public Engine(boolean debugging) { public Engine() {
this.debugging = debugging;
} }
} }

View File

@ -50,7 +50,7 @@ public interface DebugController {
void onFramePop(Context ctx, CodeFrame frame); void onFramePop(Context ctx, CodeFrame frame);
public static DebugController empty() { public static DebugController empty() {
return new DebugController() { return new DebugController () {
@Override public void onFramePop(Context ctx, CodeFrame frame) { } @Override public void onFramePop(Context ctx, CodeFrame frame) { }
@Override public void onFramePush(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) { @Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {

View File

@ -25,9 +25,6 @@ public interface DebugHandler {
void getProperties(V8Message msg); void getProperties(V8Message msg);
void releaseObjectGroup(V8Message msg); void releaseObjectGroup(V8Message msg);
void releaseObject(V8Message msg); void releaseObject(V8Message msg);
/**
* This method might not execute the actual code for well-known requests
*/
void callFunctionOn(V8Message msg); void callFunctionOn(V8Message msg);
void runtimeEnable(V8Message msg); void runtimeEnable(V8Message msg);

View File

@ -50,8 +50,6 @@ public class DebugServer {
private void handle(WebSocket ws, Debugger debugger) { private void handle(WebSocket ws, Debugger debugger) {
WebSocketMessage raw; WebSocketMessage raw;
debugger.connect();
while ((raw = ws.receive()) != null) { while ((raw = ws.receive()) != null) {
if (raw.type != Type.Text) { if (raw.type != Type.Text) {
ws.send(new V8Error("Expected a text message.")); ws.send(new V8Error("Expected a text message."));
@ -72,8 +70,9 @@ public class DebugServer {
switch (msg.name) { switch (msg.name) {
case "Debugger.enable": case "Debugger.enable":
connNotifier.next(); connNotifier.next();
debugger.enable(msg); continue; debugger.enable(msg);
case "Debugger.disable": debugger.disable(msg); continue; continue;
case "Debugger.disable": debugger.close(); continue;
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue; case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(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) { private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
var key = req.headers.get("sec-websocket-key"); var key = req.headers.get("sec-websocket-key");
@ -151,7 +150,7 @@ public class DebugServer {
catch (RuntimeException e) { catch (RuntimeException e) {
ws.send(new V8Error(e.getMessage())); ws.send(new V8Error(e.getMessage()));
} }
finally { ws.close(); debugger.disconnect(); } finally { ws.close(); debugger.close(); }
}, "Debug Handler"); }, "Debug Handler");
} }

View File

@ -1,6 +1,5 @@
package me.topchetoeu.jscript.engine.debug; package me.topchetoeu.jscript.engine.debug;
public interface Debugger extends DebugHandler, DebugController { public interface Debugger extends DebugHandler, DebugController {
void connect(); void close();
void disconnect();
} }

View File

@ -174,7 +174,6 @@ public class SimpleDebugger implements Debugger {
public State state = State.RESUMED; public State state = State.RESUMED;
public final WebSocket ws; public final WebSocket ws;
public final Engine target;
private ObjectValue emptyObject = new ObjectValue(); private ObjectValue emptyObject = new ObjectValue();
@ -477,7 +476,7 @@ public class SimpleDebugger implements Debugger {
private RunResult run(Frame codeFrame, String code) { private RunResult run(Frame codeFrame, String code) {
if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); 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(); var env = codeFrame.func.environment.fork();
env.global = new GlobalScope(codeFrame.local); env.global = new GlobalScope(codeFrame.local);
@ -574,8 +573,36 @@ public class SimpleDebugger implements Debugger {
updateNotifier.next(); updateNotifier.next();
} }
@Override public synchronized void disable(V8Message msg) { @Override public synchronized void disable(V8Message msg) {
enabled = false; close();
ws.send(msg.respond()); 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(); updateNotifier.next();
} }
@ -952,19 +979,12 @@ public class SimpleDebugger implements Debugger {
} }
} }
@Override public synchronized void connect() { public SimpleDebugger attach(DebugContext ctx) {
if (!DebugContext.get(target.globalEnvironment).attachDebugger(this)) { ctx.attachDebugger(this);
ws.send(new V8Error("A debugger is already attached to this engine.")); return this;
}
}
@Override public synchronized void disconnect() {
DebugContext.get(target.globalEnvironment).detachDebugger();
enabled = false;
updateNotifier.next();
} }
public SimpleDebugger(WebSocket ws, Engine target) { public SimpleDebugger(WebSocket ws) {
this.ws = ws; this.ws = ws;
this.target = target;
} }
} }

View File

@ -303,6 +303,13 @@ public class CodeFrame {
return Runners.NO_RETURN; 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) { public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
this.args = args.clone(); this.args = args.clone();
this.scope = new LocalScope(func.localsN, func.captures); this.scope = new LocalScope(func.localsN, func.captures);

View File

@ -33,9 +33,16 @@ public class CodeFunction extends FunctionValue {
public Object call(Context ctx, Object thisArg, Object ...args) { public Object call(Context ctx, Object thisArg, Object ...args) {
var frame = new CodeFrame(ctx, thisArg, args, this); var frame = new CodeFrame(ctx, thisArg, args, this);
while (true) { frame.onPush();
var res = frame.next(Runners.NO_RETURN, Runners.NO_RETURN, null);
if (res != Runners.NO_RETURN) return res; try {
while (true) {
var res = frame.next(Runners.NO_RETURN, Runners.NO_RETURN, null);
if (res != Runners.NO_RETURN) return res;
}
}
finally {
frame.onPop();
} }
} }

View File

@ -21,6 +21,7 @@ import me.topchetoeu.jscript.interop.Native;
private void next(Context ctx, Object inducedValue, Object inducedError) { private void next(Context ctx, Object inducedValue, Object inducedError) {
Object res = null; Object res = null;
frame.onPush();
awaiting = false; awaiting = false;
while (!awaiting) { while (!awaiting) {
try { try {
@ -36,6 +37,7 @@ import me.topchetoeu.jscript.interop.Native;
break; break;
} }
} }
frame.onPop();
if (awaiting) { if (awaiting) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));

View File

@ -30,6 +30,7 @@ import me.topchetoeu.jscript.interop.Native;
Object res = null; Object res = null;
state = 0; state = 0;
frame.onPush();
while (state == 0) { while (state == 0) {
try { try {
res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError));
@ -47,6 +48,7 @@ import me.topchetoeu.jscript.interop.Native;
break; break;
} }
} }
frame.onPop();
if (state == 1) { if (state == 1) {
PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject)); PromiseLib.then(ctx, frame.pop(), new NativeFunction(this::fulfill), new NativeFunction(this::reject));

View File

@ -26,6 +26,7 @@ import me.topchetoeu.jscript.interop.Native;
Object res = null; Object res = null;
yielding = false; yielding = false;
frame.onPush();
while (!yielding) { while (!yielding) {
try { try {
res = frame.next(inducedValue, inducedReturn, inducedError == Runners.NO_RETURN ? null : new EngineException(inducedError)); 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; throw e;
} }
} }
frame.onPop();
if (done) frame = null; if (done) frame = null;
else res = frame.pop(); else res = frame.pop();