feat: improve vscode debugging compatibility

This commit is contained in:
TopchetoEU 2023-10-29 12:25:33 +02:00
parent 16a9e5d761
commit d20df66982
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
5 changed files with 83 additions and 27 deletions

View File

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

View File

@ -59,7 +59,6 @@ public class DebugServer {
try { try {
msg = new V8Message(raw.textData()); msg = new V8Message(raw.textData());
// System.out.println(msg.name + ": " + JSON.stringify(msg.params));
} }
catch (SyntaxException e) { catch (SyntaxException e) {
ws.send(new V8Error(e.getMessage())); ws.send(new V8Error(e.getMessage()));
@ -90,6 +89,7 @@ public class DebugServer {
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue; case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(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.getProperties": debugger.getProperties(msg); continue;
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue; case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue; // case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;

View File

@ -40,7 +40,13 @@ import me.topchetoeu.jscript.lib.GeneratorLib.Generator;
public class SimpleDebugger implements Debugger { 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<i;++e)t=t[n[e]];return t}"; 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<i;++e)t=t[n[e]];return t}";
public static final String VSCODE_STRINGIFY_VAL = "function(...runtimeArgs){\n let t = 1024; let e = null;\n if(e)try{let r=\"<<default preview>>\",i=e.call(this,r);if(i!==r)return String(i)}catch(r){return`<<indescribable>>${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}";
public static final String VSCODE_STRINGIFY_PROPS = "function(...runtimeArgs){\n let t = 1024; let e = null;\n let r={},i=\"<<default preview>>\";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]=`<<indescribable>>${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}";
public static final String VSCODE_SYMBOL_REQUEST = "function(){return[Symbol.for(\"debug.description\"),Symbol.for(\"nodejs.util.inspect.custom\")]\n}";
public static final String VSCODE_SHALLOW_COPY = "function(){let t={__proto__:this.__proto__\n},e=Object.getOwnPropertyNames(this);for(let r=0;r<e.length;++r){let i=e[r],n=i>>>0;if(String(n>>>0)===i&&n>>>0!==4294967295)continue;let s=Object.getOwnPropertyDescriptor(this,i);s&&Object.defineProperty(t,i,s)}return t}";
public static final String VSCODE_FLATTEN_ARRAY = "function(t,e){let r={\n},i=t===-1?0:t,n=e===-1?this.length:t+e;for(let s=i;s<n&&s<this.length;++s){let o=Object.getOwnPropertyDescriptor(this,s);o&&Object.defineProperty(r,s,o)}return r}";
public static final String VSCODE_CALL = "function(t){return t.call(this)\n}";
private static enum State { private static enum State {
RESUMED, RESUMED,
STEPPING_IN, STEPPING_IN,
@ -131,8 +137,7 @@ public class SimpleDebugger implements Debugger {
.add(new JSONMap().set("type", "local").set("name", "Local Scope").set("object", serializeObj(ctx, local))) .add(new JSONMap().set("type", "local").set("name", "Local Scope").set("object", serializeObj(ctx, local)))
.add(new JSONMap().set("type", "closure").set("name", "Closure").set("object", serializeObj(ctx, capture))) .add(new JSONMap().set("type", "closure").set("name", "Closure").set("object", serializeObj(ctx, capture)))
.add(new JSONMap().set("type", "global").set("name", "Global Scope").set("object", serializeObj(ctx, global))) .add(new JSONMap().set("type", "global").set("name", "Global Scope").set("object", serializeObj(ctx, global)))
) );
.setNull("this");
} }
} }
} }
@ -156,6 +161,8 @@ public class SimpleDebugger implements Debugger {
public final WebSocket ws; public final WebSocket ws;
public final Engine target; public final Engine target;
private ObjectValue emptyObject = new ObjectValue();
private HashMap<Integer, BreakpointCandidate> idToBptCand = new HashMap<>(); private HashMap<Integer, BreakpointCandidate> idToBptCand = new HashMap<>();
private HashMap<Integer, Breakpoint> idToBreakpoint = new HashMap<>(); private HashMap<Integer, Breakpoint> idToBreakpoint = new HashMap<>();
@ -417,7 +424,7 @@ public class SimpleDebugger implements Debugger {
env.global = new GlobalScope(local); env.global = new GlobalScope(local);
var ctx = new Context(engine).pushEnv(env); var ctx = new Context(engine).pushEnv(env);
var awaiter = engine.pushMsg(false, ctx, new Filename("temp", "exec"), code, codeFrame.frame.thisArg, codeFrame.frame.args); var awaiter = engine.pushMsg(false, ctx, new Filename("temp", "exec"), "(" + code + ")", codeFrame.frame.thisArg, codeFrame.frame.args);
engine.run(true); engine.run(true);
@ -587,14 +594,22 @@ public class SimpleDebugger implements Debugger {
releaseGroup(group); releaseGroup(group);
ws.send(msg.respond()); ws.send(msg.respond());
} }
@Override
public void releaseObject(V8Message msg) {
var id = Integer.parseInt(msg.params.string("objectId"));
var obj = idToObject.remove(id);
if (obj != null) objectToId.remove(obj);
ws.send(msg.respond());
}
@Override public void getProperties(V8Message msg) { @Override public void getProperties(V8Message msg) {
var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); var obj = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var own = msg.params.bool("ownProperties") || true; var own = msg.params.bool("ownProperties");
var accessorPropertiesOnly = msg.params.bool("accessorPropertiesOnly", false);
var currOwn = true; var currOwn = true;
var res = new JSONList(); var res = new JSONList();
while (obj != null) { while (obj != emptyObject && obj != null) {
var ctx = objectToCtx.get(obj); var ctx = objectToCtx.get(obj);
for (var key : obj.keys(true)) { for (var key : obj.keys(true)) {
@ -611,7 +626,7 @@ public class SimpleDebugger implements Debugger {
propDesc.set("isOwn", currOwn); propDesc.set("isOwn", currOwn);
res.add(propDesc); res.add(propDesc);
} }
else { else if (!accessorPropertiesOnly) {
propDesc.set("name", Values.toString(ctx, key)); propDesc.set("name", Values.toString(ctx, key));
propDesc.set("value", serializeObj(ctx, obj.getMember(ctx, key))); propDesc.set("value", serializeObj(ctx, obj.getMember(ctx, key)));
propDesc.set("writable", obj.memberWritable(key)); propDesc.set("writable", obj.memberWritable(key));
@ -624,14 +639,16 @@ public class SimpleDebugger implements Debugger {
obj = obj.getPrototype(ctx); obj = obj.getPrototype(ctx);
var protoDesc = new JSONMap(); if (currOwn) {
protoDesc.set("name", "__proto__"); var protoDesc = new JSONMap();
protoDesc.set("value", serializeObj(ctx, obj == null ? Values.NULL : obj)); protoDesc.set("name", "__proto__");
protoDesc.set("writable", true); protoDesc.set("value", serializeObj(ctx, obj == null ? Values.NULL : obj));
protoDesc.set("enumerable", false); protoDesc.set("writable", true);
protoDesc.set("configurable", false); protoDesc.set("enumerable", false);
protoDesc.set("isOwn", currOwn); protoDesc.set("configurable", false);
res.add(protoDesc); protoDesc.set("isOwn", currOwn);
res.add(protoDesc);
}
currOwn = false; currOwn = false;
if (own) break; if (own) break;
@ -641,10 +658,24 @@ public class SimpleDebugger implements Debugger {
} }
@Override public void callFunctionOn(V8Message msg) { @Override public void callFunctionOn(V8Message msg) {
var src = msg.params.string("functionDeclaration"); var src = msg.params.string("functionDeclaration");
var args = msg.params.list("arguments", new JSONList()).stream().map(v -> v.map()).map(this::deserializeArgument).collect(Collectors.toList()); var args = msg.params
.list("arguments", new JSONList())
.stream()
.map(v -> v.map())
.map(this::deserializeArgument)
.collect(Collectors.toList());
var thisArg = idToObject.get(Integer.parseInt(msg.params.string("objectId"))); var thisArg = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var ctx = objectToCtx.get(thisArg); var ctx = objectToCtx.get(thisArg);
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);
}
switch (src) { switch (src) {
case CHROME_GET_PROP_FUNC: { case CHROME_GET_PROP_FUNC: {
var path = JSON.parse(new Filename("tmp", "json"), (String)args.get(0)).list(); var path = JSON.parse(new Filename("tmp", "json"), (String)args.get(0)).list();
@ -653,6 +684,23 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res)))); ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, res))));
return; return;
} }
case VSCODE_STRINGIFY_VAL:
case VSCODE_STRINGIFY_PROPS:
case VSCODE_SHALLOW_COPY:
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, emptyObject))));
break;
case VSCODE_FLATTEN_ARRAY: {
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, thisArg))));
break;
}
case VSCODE_SYMBOL_REQUEST:
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, new ArrayValue(ctx)))));
break;
case VSCODE_CALL: {
var func = (FunctionValue)(args.size() < 1 ? null : args.get(0));
ws.send(msg.respond(new JSONMap().set("result", serializeObj(ctx, func.call(ctx, thisArg)))));
break;
}
default: default:
ws.send(new V8Error("A non well-known function was used with callFunctionOn.")); ws.send(new V8Error("A non well-known function was used with callFunctionOn."));
break; break;
@ -728,11 +776,7 @@ public class SimpleDebugger implements Debugger {
break; break;
case STEPPING_OVER: case STEPPING_OVER:
if (stepOutFrame.frame == frame.frame) { if (stepOutFrame.frame == frame.frame) {
if (returnVal != Runners.NO_RETURN) { if (isBreakpointable && (
state = State.STEPPING_OUT;
return false;
}
else if (isBreakpointable && (
!loc.filename().equals(prevLocation.filename()) || !loc.filename().equals(prevLocation.filename()) ||
loc.line() != prevLocation.line() loc.line() != prevLocation.line()
)) pauseDebug(ctx, null); )) pauseDebug(ctx, null);
@ -754,10 +798,13 @@ public class SimpleDebugger implements Debugger {
if (StackData.frames(ctx).size() == 0) resume(State.RESUMED); if (StackData.frames(ctx).size() == 0) resume(State.RESUMED);
else if (stepOutFrame != null && stepOutFrame.frame == frame && else if (stepOutFrame != null && stepOutFrame.frame == frame &&
(state == State.STEPPING_OUT || state == State.STEPPING_IN) (state == State.STEPPING_OUT || state == State.STEPPING_IN || state == State.STEPPING_OVER)
) { ) {
pauseDebug(ctx, null); // if (state == State.STEPPING_OVER) state = State.STEPPING_IN;
updateNotifier.await(); // else {
pauseDebug(ctx, null);
updateNotifier.await();
// }
} }
} }

View File

@ -109,6 +109,8 @@ public class WebSocket implements AutoCloseable {
else send(msg.textData()); else send(msg.textData());
} }
public void send(Object data) { public void send(Object data) {
// TODO: Remove
System.out.println("SEND: " + data);
if (closed) throw new IllegalStateException("Object is closed."); if (closed) throw new IllegalStateException("Object is closed.");
write(1, data.toString().getBytes()); write(1, data.toString().getBytes());
} }
@ -190,6 +192,10 @@ public class WebSocket implements AutoCloseable {
if (!fin) continue; if (!fin) continue;
var raw = data.toByteArray(); var raw = data.toByteArray();
// TODO: Remove
System.out.println("RECEIVED: " + new String(raw));
if (type == 1) return new WebSocketMessage(new String(raw)); if (type == 1) return new WebSocketMessage(new String(raw));
else return new WebSocketMessage(raw); else return new WebSocketMessage(raw);
} }

View File

@ -83,8 +83,11 @@ public class JSON {
else return res.transform(); else return res.transform();
} }
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) { public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
if (minus) i++;
var res = Parsing.parseNumber(filename, tokens, i); var res = Parsing.parseNumber(filename, tokens, i);
if (res.isSuccess()) return ParseRes.res((Double)res.result.value, res.n); if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
else return res.transform(); else return res.transform();
} }
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) { public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {