From c8253795b2b73db7636b9a384a2f634e7a4e0dc2 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 2 Mar 2024 13:56:48 +0200 Subject: [PATCH] fix: make debugging work again --- .../topchetoeu/jscript/common/RefTracker.java | 23 ++ .../jscript/common/mapping/FunctionMap.java | 99 +++++--- .../jscript/compilation/parsing/Parsing.java | 2 +- .../me/topchetoeu/jscript/core/Compiler.java | 6 + .../me/topchetoeu/jscript/core/Context.java | 4 +- .../topchetoeu/jscript/core/Extensions.java | 5 + .../jscript/core/debug/DebugContext.java | 1 + .../topchetoeu/jscript/utils/JSCompiler.java | 2 + .../topchetoeu/jscript/utils/JScriptRepl.java | 11 +- .../jscript/utils/debug/DebugServer.java | 1 + ...mpleDebugger.java- => SimpleDebugger.java} | 218 +++++++++--------- .../jscript/utils/debug/WebSocket.java | 4 +- 12 files changed, 221 insertions(+), 155 deletions(-) create mode 100644 src/java/me/topchetoeu/jscript/common/RefTracker.java rename src/java/me/topchetoeu/jscript/utils/debug/{SimpleDebugger.java- => SimpleDebugger.java} (88%) diff --git a/src/java/me/topchetoeu/jscript/common/RefTracker.java b/src/java/me/topchetoeu/jscript/common/RefTracker.java new file mode 100644 index 0000000..249605f --- /dev/null +++ b/src/java/me/topchetoeu/jscript/common/RefTracker.java @@ -0,0 +1,23 @@ +package me.topchetoeu.jscript.common; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +public class RefTracker { + public static void onDestroy(Object obj, Runnable runnable) { + var queue = new ReferenceQueue<>(); + var ref = new WeakReference<>(obj, queue); + obj = null; + + var th = new Thread(() -> { + try { + queue.remove(); + ref.get(); + runnable.run(); + } + catch (InterruptedException e) { return; } + }); + th.setDaemon(true); + th.start(); + } +} diff --git a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index c9d63c5..768f88f 100644 --- a/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -1,13 +1,17 @@ package me.topchetoeu.jscript.common.mapping; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.SortedSet; +import java.util.NavigableSet; +import java.util.Objects; import java.util.TreeMap; import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.Location; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.core.scope.LocalScopeRecord; @@ -62,15 +66,15 @@ public class FunctionMap { public static final FunctionMap EMPTY = new FunctionMap(); - private final HashMap bps = new HashMap<>(); - private final TreeSet bpLocs = new TreeSet<>(); + private final HashMap bps = new HashMap<>(); + private final HashMap> bpLocs = new HashMap<>(); private final TreeMap pcToLoc = new TreeMap<>(); - private final HashMap locToPc = new HashMap<>(); public final String[] localNames, captureNames; public Location toLocation(int pc, boolean approxiamte) { + if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null; var res = pcToLoc.get(pc); if (!approxiamte || res != null) return res; var entry = pcToLoc.headMap(pc, true).lastEntry(); @@ -81,14 +85,39 @@ public class FunctionMap { return toLocation(pc, false); } - public BreakpointType getBreakpoint(Location loc) { - return bps.getOrDefault(loc, BreakpointType.NONE); + public BreakpointType getBreakpoint(int pc) { + return bps.getOrDefault(pc, BreakpointType.NONE); } public Location correctBreakpoint(Location loc) { - return bpLocs.ceiling(loc); + var set = bpLocs.get(loc.filename()); + if (set == null) return null; + else return set.ceiling(loc); } - public SortedSet breakpoints() { - return Collections.unmodifiableSortedSet(bpLocs); + public List correctBreakpoint(Pattern filename, int line, int column) { + var candidates = new HashMap>(); + + for (var name : bpLocs.keySet()) { + if (filename.matcher(name.toString()).matches()) { + candidates.put(name, bpLocs.get(name)); + } + } + + var res = new ArrayList(candidates.size()); + for (var candidate : candidates.entrySet()) { + res.add(candidate.getValue().ceiling(new Location(line, column, candidate.getKey()))); + } + + return res; + } + public List breakpoints(Location start, Location end) { + if (!Objects.equals(start.filename(), end.filename())) return List.of(); + NavigableSet set = bpLocs.get(start.filename()); + if (set == null) return List.of(); + + if (start != null) set = set.tailSet(start, true); + if (end != null) set = set.headSet(end, true); + + return set.stream().collect(Collectors.toList()); } public Location start() { @@ -100,49 +129,51 @@ public class FunctionMap { return pcToLoc.lastEntry().getValue(); } - public Integer toProgramCounter(Location loc) { - return locToPc.get(loc); - } - public FunctionMap apply(SourceMap map) { - var res = new FunctionMap(); + var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames); - for (var el : new ArrayList<>(pcToLoc.entrySet())) { + for (var el : pcToLoc.entrySet()) { res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue())); } - for (var el : new ArrayList<>(locToPc.entrySet())) { - var mapped = map.toOriginal(el.getKey()); - res.locToPc.put(mapped, el.getValue()); + + res.bps.putAll(bps); + + for (var el : bpLocs.entrySet()) { + for (var loc : el.getValue()) { + loc = map.toCompiled(loc); + if (loc == null) continue; + + if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>()); + res.bpLocs.get(loc.filename()).add(loc); + } } return res; } public FunctionMap clone() { - var res = new FunctionMap(); + var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames); res.pcToLoc.putAll(this.pcToLoc); - res.locToPc.putAll(this.locToPc); + res.bps.putAll(bps); + res.bpLocs.putAll(bpLocs); + res.pcToLoc.putAll(pcToLoc); return res; } public FunctionMap(Map map, Map breakpoints, String[] localNames, String[] captureNames) { + var locToPc = new HashMap(); + for (var el : map.entrySet()) { - var pc = el.getKey(); - var loc = el.getValue(); - - var a = pcToLoc.remove(pc); - var b = locToPc.remove(loc); - - if (b != null) pcToLoc.remove(b); - if (a != null) locToPc.remove(a); - - pcToLoc.put(pc, loc); - locToPc.put(loc, pc); + pcToLoc.put(el.getKey(), el.getValue()); + locToPc.putIfAbsent(el.getValue(), el.getKey()); } + for (var el : breakpoints.entrySet()) { if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue; - bps.put(el.getKey(), el.getValue()); - bpLocs.add(el.getKey()); + bps.put(locToPc.get(el.getKey()), el.getValue()); + + if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>()); + bpLocs.get(el.getKey().filename()).add(el.getKey()); } this.localNames = localNames; diff --git a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java index 5ba65b6..96ddeb7 100644 --- a/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java +++ b/src/java/me/topchetoeu/jscript/compilation/parsing/Parsing.java @@ -1330,7 +1330,7 @@ public class Parsing { var valRes = parseValue(filename, tokens, i, 0, true); if (!valRes.isSuccess()) return valRes.transform(); - valRes.result.setLoc(loc); + // valRes.result.setLoc(loc); var res = ParseRes.res(valRes.result, valRes.n); if (isStatementEnd(tokens, i + res.n)) { diff --git a/src/java/me/topchetoeu/jscript/core/Compiler.java b/src/java/me/topchetoeu/jscript/core/Compiler.java index 45fc51e..0b710ed 100644 --- a/src/java/me/topchetoeu/jscript/core/Compiler.java +++ b/src/java/me/topchetoeu/jscript/core/Compiler.java @@ -3,6 +3,8 @@ package me.topchetoeu.jscript.core; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.core.exceptions.EngineException; +import me.topchetoeu.jscript.core.scope.ValueVariable; +import me.topchetoeu.jscript.core.values.CodeFunction; public interface Compiler { public Key KEY = new Key<>(); @@ -14,4 +16,8 @@ public interface Compiler { throw EngineException.ofError("No compiler attached to engine."); }); } + + public static CodeFunction compile(Environment env, Filename filename, String raw) { + return new CodeFunction(env, filename.toString(), Compiler.get(env).compile(filename, raw), new ValueVariable[0]); + } } diff --git a/src/java/me/topchetoeu/jscript/core/Context.java b/src/java/me/topchetoeu/jscript/core/Context.java index 64ba6b3..07f7a02 100644 --- a/src/java/me/topchetoeu/jscript/core/Context.java +++ b/src/java/me/topchetoeu/jscript/core/Context.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.core; import java.util.Iterator; +import java.util.List; import me.topchetoeu.jscript.common.Filename; import me.topchetoeu.jscript.core.debug.DebugContext; @@ -41,7 +42,8 @@ public class Context implements Extensions { return res; } @Override public Iterable> keys() { - return environment.keys(); + if (environment == null) return List.of(); + else return environment.keys(); // if (engine == null && environment == null) return List.of(); // if (engine == null) return environment.keys(); diff --git a/src/java/me/topchetoeu/jscript/core/Extensions.java b/src/java/me/topchetoeu/jscript/core/Extensions.java index fcb5bc0..9d8a4f8 100644 --- a/src/java/me/topchetoeu/jscript/core/Extensions.java +++ b/src/java/me/topchetoeu/jscript/core/Extensions.java @@ -8,6 +8,10 @@ public interface Extensions { boolean has(Key key); boolean remove(Key key); + default void add(Key key) { + add(key, null); + } + default boolean hasNotNull(Key key) { return has(key) && get(key) != null; } @@ -26,6 +30,7 @@ public interface Extensions { } @SuppressWarnings("unchecked") default void addAll(Extensions source) { + if (source == null) return; for (var key : source.keys()) { add((Key)key, (Object)source.get(key)); } diff --git a/src/java/me/topchetoeu/jscript/core/debug/DebugContext.java b/src/java/me/topchetoeu/jscript/core/debug/DebugContext.java index b4cab7b..3f89b02 100644 --- a/src/java/me/topchetoeu/jscript/core/debug/DebugContext.java +++ b/src/java/me/topchetoeu/jscript/core/debug/DebugContext.java @@ -81,6 +81,7 @@ public class DebugContext { } public void onFunctionLoad(FunctionBody func, FunctionMap map) { if (maps != null) maps.put(func, map); + if (debugger != null) debugger.onFunctionLoad(func, map); } private DebugContext(boolean enabled) { diff --git a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java b/src/java/me/topchetoeu/jscript/utils/JSCompiler.java index f538ddb..c4e4c35 100644 --- a/src/java/me/topchetoeu/jscript/utils/JSCompiler.java +++ b/src/java/me/topchetoeu/jscript/utils/JSCompiler.java @@ -13,6 +13,7 @@ public class JSCompiler implements Compiler { private void registerFunc(FunctionBody body, CompileResult res) { var map = res.map(); + DebugContext.get(ext).onFunctionLoad(body, map); for (var i = 0; i < body.children.length; i++) { @@ -23,6 +24,7 @@ public class JSCompiler implements Compiler { @Override public FunctionBody compile(Filename filename, String source) { var res = Parsing.compile(filename, source); var func = res.body(); + DebugContext.get(ext).onSource(filename, source); registerFunc(func, res); return func; diff --git a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java index 3a280fa..512ea83 100644 --- a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java +++ b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java @@ -21,7 +21,7 @@ import me.topchetoeu.jscript.core.exceptions.InterruptException; import me.topchetoeu.jscript.core.exceptions.SyntaxException; import me.topchetoeu.jscript.lib.Internals; import me.topchetoeu.jscript.utils.debug.DebugServer; -// import me.topchetoeu.jscript.utils.debug.SimpleDebugger; +import me.topchetoeu.jscript.utils.debug.SimpleDebugger; import me.topchetoeu.jscript.utils.filesystem.Filesystem; import me.topchetoeu.jscript.utils.filesystem.MemoryFilesystem; import me.topchetoeu.jscript.utils.filesystem.Mode; @@ -62,11 +62,8 @@ public class JScriptRepl { var raw = Reading.readline(); if (raw == null) break; - var res = engine.pushMsg( - false, environment, - new Filename("jscript", "repl/" + i + ".js"), - raw, null - ).await(); + var func = Compiler.compile(environment, new Filename("jscript", "repl/" + i + ".js"), raw); + var res = engine.pushMsg(false, environment, func, null).await(); Values.printValue(null, res); System.out.println(); } @@ -126,7 +123,7 @@ public class JScriptRepl { var ctx = new DebugContext(); environment.add(DebugContext.KEY, ctx); - // debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws).attach(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/java/me/topchetoeu/jscript/utils/debug/DebugServer.java b/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java index f0f5a27..d27a039 100644 --- a/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java +++ b/src/java/me/topchetoeu/jscript/utils/debug/DebugServer.java @@ -140,6 +140,7 @@ public class DebugServer { try { handle(ws, debugger); } catch (RuntimeException | IOException e) { try { + e.printStackTrace(); ws.send(new V8Error(e.getMessage())); } catch (IOException e2) { /* Shit outta luck */ } diff --git a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java- b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java similarity index 88% rename from src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java- rename to src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java index 168c156..58ab504 100644 --- a/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java- +++ b/src/java/me/topchetoeu/jscript/utils/debug/SimpleDebugger.java @@ -6,30 +6,31 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.TreeSet; +import java.util.WeakHashMap; import java.util.regex.Pattern; import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Filename; +import me.topchetoeu.jscript.common.FunctionBody; +import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Location; +import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.Instruction.Type; import me.topchetoeu.jscript.common.events.Notifier; import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.common.json.JSONList; import me.topchetoeu.jscript.common.json.JSONMap; -import me.topchetoeu.jscript.compilation.Instruction; -import me.topchetoeu.jscript.compilation.Instruction.BreakpointType; -import me.topchetoeu.jscript.compilation.Instruction.Type; +import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.compilation.parsing.Parsing; import me.topchetoeu.jscript.core.Context; import me.topchetoeu.jscript.core.Engine; import me.topchetoeu.jscript.core.Environment; -import me.topchetoeu.jscript.core.Extensions; +import me.topchetoeu.jscript.core.EventLoop; import me.topchetoeu.jscript.core.Frame; import me.topchetoeu.jscript.core.debug.DebugContext; import me.topchetoeu.jscript.core.scope.GlobalScope; import me.topchetoeu.jscript.core.values.ArrayValue; -import me.topchetoeu.jscript.core.values.CodeFunction; import me.topchetoeu.jscript.core.values.FunctionValue; import me.topchetoeu.jscript.core.values.ObjectValue; import me.topchetoeu.jscript.core.values.Symbol; @@ -76,36 +77,48 @@ public class SimpleDebugger implements Debugger { this.source = source; } } - private static class Breakpoint { - public final int id; - public final Location location; - public final String condition; - public Breakpoint(int id, Location location, String condition) { - this.id = id; - this.location = location; - this.condition = condition; - } - } - private static class BreakpointCandidate { + private class Breakpoint { public final int id; public final String condition; public final Pattern pattern; public final int line, start; - public final HashSet resolvedBreakpoints = new HashSet<>(); + public final WeakHashMap> resolvedLocations = new WeakHashMap<>(); - public BreakpointCandidate(int id, Pattern pattern, int line, int start, String condition) { + public Breakpoint(int id, Pattern pattern, int line, int start, String condition) { this.id = id; + this.condition = condition; this.pattern = pattern; this.line = line; this.start = start; + if (condition != null && condition.trim().equals("")) condition = null; - this.condition = condition; + } + + // TODO: Figure out how to unload a breakpoint + public void addFunc(FunctionBody body, FunctionMap map) { + try { + for (var loc : map.correctBreakpoint(pattern, line, start)) { + if (!resolvedLocations.containsKey(body)) resolvedLocations.put(body, new HashSet<>()); + var set = resolvedLocations.get(body); + set.add(loc); + + ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap() + .set("breakpointId", id) + .set("location", serializeLocation(loc)) + )); + } + + updateBreakpoints(); + } + catch (IOException e) { + ws.close(); + close(); + } } } private class DebugFrame { public Frame frame; - public CodeFunction func; public int id; public ObjectValue local, capture, global, valstack; public JSONMap serialized; @@ -118,18 +131,17 @@ public class SimpleDebugger implements Debugger { public DebugFrame(Frame frame, int id) { this.frame = frame; - this.func = frame.function; this.id = id; this.global = frame.function.environment.global.obj; - this.local = frame.getLocalScope(true); - this.capture = frame.getCaptureScope(true); + this.local = frame.getLocalScope(); + this.capture = frame.getCaptureScope(); Values.makePrototypeChain(frame.ctx, global, capture, local); this.valstack = frame.getValStackScope(); this.serialized = new JSONMap() .set("callFrameId", id + "") - .set("functionName", func.name) + .set("functionName", frame.function.name) .set("scopeChain", new JSONList() .add(new JSONMap() .set("type", "local") @@ -190,11 +202,10 @@ public class SimpleDebugger implements Debugger { private ObjectValue emptyObject = new ObjectValue(); - private HashMap idToBptCand = new HashMap<>(); + private WeakHashMap mappings = new WeakHashMap<>(); + private WeakHashMap> bpLocs = new WeakHashMap<>(); private HashMap idToBreakpoint = new HashMap<>(); - private HashMap locToBreakpoint = new HashMap<>(); - private HashSet tmpBreakpts = new HashSet<>(); private HashMap filenameToId = new HashMap<>(); private HashMap idToSource = new HashMap<>(); @@ -276,17 +287,22 @@ public class SimpleDebugger implements Debugger { return res; } - private Location correctLocation(DebugSource source, Location loc) { - var set = source.breakpoints; + private void updateBreakpoints() { + bpLocs.clear(); - if (set.contains(loc)) return loc; + for (var bp : idToBreakpoint.values()) { + for (var el : bp.resolvedLocations.entrySet()) { + if (!bpLocs.containsKey(el.getKey())) bpLocs.put(el.getKey(), new HashMap<>()); + var map = bpLocs.get(el.getKey()); - var tail = set.tailSet(loc); - if (tail.isEmpty()) return null; - - return tail.first(); + for (var loc : el.getValue()) { + map.put(loc, bp); + } + } + } } - private Location deserializeLocation(JSONElement el, boolean correct) { + + private Location deserializeLocation(JSONElement el) { if (!el.isMap()) throw new RuntimeException("Expected location to be a map."); var id = Integer.parseInt(el.map().string("scriptId")); var line = (int)el.map().number("lineNumber") + 1; @@ -295,7 +311,6 @@ public class SimpleDebugger implements Debugger { if (!idToSource.containsKey(id)) throw new RuntimeException(String.format("The specified source %s doesn't exist.", id)); var res = new Location(line, column, idToSource.get(id).filename); - if (correct) res = correctLocation(idToSource.get(id), res); return res; } private JSONMap serializeLocation(Location loc) { @@ -318,8 +333,10 @@ public class SimpleDebugger implements Debugger { } private JSONMap serializeObj(Context ctx, Object val, boolean byValue) { val = Values.normalize(null, val); - ctx = new Context(ctx.engine.copy(), ctx.environment); - ctx.engine.add(DebugContext.IGNORE, true); + var newCtx = new Context(); + newCtx.addAll(ctx); + newCtx.add(DebugContext.IGNORE); + ctx = newCtx; if (val == Values.NULL) { return new JSONMap() @@ -507,30 +524,17 @@ public class SimpleDebugger implements Debugger { } } - private void addBreakpoint(Breakpoint bpt) { - try { - idToBreakpoint.put(bpt.id, bpt); - locToBreakpoint.put(bpt.location, bpt); - - ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap() - .set("breakpointId", bpt.id) - .set("location", serializeLocation(bpt.location)) - )); - } - catch (IOException e) { - ws.close(); - close(); - } - } - private RunResult run(DebugFrame codeFrame, String code) { if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!")); var engine = new Engine(); - var env = codeFrame.func.environment.copy(); + var env = codeFrame.frame.function.environment.copy(); env.global = new GlobalScope(codeFrame.local); + env.remove(EventLoop.KEY); + env.remove(DebugContext.KEY); + env.add(EventLoop.KEY, engine); - var ctx = new Context(engine, env); + var ctx = new Context(env); var awaiter = engine.pushMsg(false, ctx.environment, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args); engine.run(true); @@ -625,16 +629,16 @@ public class SimpleDebugger implements Debugger { close(); ws.send(msg.respond()); } - public synchronized void close() { + @Override public synchronized void close() { enabled = false; execptionType = CatchType.NONE; state = State.RESUMED; - idToBptCand.clear(); + // idToBptCand.clear(); idToBreakpoint.clear(); - locToBreakpoint.clear(); - tmpBreakpts.clear(); + // locToBreakpoint.clear(); + // tmpBreakpts.clear(); filenameToId.clear(); idToSource.clear(); @@ -660,15 +664,14 @@ public class SimpleDebugger implements Debugger { ws.send(msg.respond(new JSONMap().set("scriptSource", idToSource.get(id).source))); } @Override public synchronized void getPossibleBreakpoints(V8Message msg) throws IOException { - var src = idToSource.get(Integer.parseInt(msg.params.map("start").string("scriptId"))); - var start = deserializeLocation(msg.params.get("start"), false); - var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end"), false) : null; - + var start = deserializeLocation(msg.params.get("start")); + var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end")) : null; var res = new JSONList(); - for (var loc : src.breakpoints.tailSet(start, true)) { - if (end != null && loc.compareTo(end) > 0) break; - res.add(serializeLocation(loc)); + for (var el : mappings.values()) { + for (var bp : el.breakpoints(start, end)) { + res.add(serializeLocation(bp)); + } } ws.send(msg.respond(new JSONMap().set("locations", res))); @@ -694,54 +697,50 @@ public class SimpleDebugger implements Debugger { Pattern regex; if (msg.params.isString("url")) regex = Pattern.compile(Pattern.quote(msg.params.string("url"))); - else regex = Pattern.compile(msg.params.string("urlRegex")); + else if (msg.params.isString("urlRegex")) regex = Pattern.compile(msg.params.string("urlRegex")); + else { + ws.send(msg.respond(new JSONMap() + .set("breakpointId", "john-doe") + .set("locations", new JSONList()) + )); + return; + } - var bpcd = new BreakpointCandidate(nextId(), regex, line, col, cond); - idToBptCand.put(bpcd.id, bpcd); + var bpt = new Breakpoint(nextId(), regex, line, col, cond); + idToBreakpoint.put(bpt.id, bpt); var locs = new JSONList(); - for (var src : idToSource.values()) { - if (regex.matcher(src.filename.toString()).matches()) { - var loc = correctLocation(src, new Location(line, col, src.filename)); - if (loc == null) continue; - var bp = new Breakpoint(nextId(), loc, bpcd.condition); + for (var el : mappings.entrySet()) { + bpt.addFunc(el.getKey(), el.getValue()); + } - bpcd.resolvedBreakpoints.add(bp); + for (var el : bpt.resolvedLocations.values()) { + for (var loc : el) { locs.add(serializeLocation(loc)); - addBreakpoint(bp); } } ws.send(msg.respond(new JSONMap() - .set("breakpointId", bpcd.id + "") + .set("breakpointId", bpt.id + "") .set("locations", locs) )); } @Override public synchronized void removeBreakpoint(V8Message msg) throws IOException { var id = Integer.parseInt(msg.params.string("breakpointId")); - if (idToBptCand.containsKey(id)) { - var bpcd = idToBptCand.get(id); - for (var bp : bpcd.resolvedBreakpoints) { - idToBreakpoint.remove(bp.id); - locToBreakpoint.remove(bp.location); - } - idToBptCand.remove(id); - } - else if (idToBreakpoint.containsKey(id)) { - var bp = idToBreakpoint.remove(id); - locToBreakpoint.remove(bp.location); - } + idToBreakpoint.remove(id); ws.send(msg.respond()); } @Override public synchronized void continueToLocation(V8Message msg) throws IOException { - var loc = deserializeLocation(msg.params.get("location"), true); + // TODO: Figure out if we need this - tmpBreakpts.add(loc); + // var loc = correctLocation(deserializeLocation(msg.params.get("location"))); - resume(State.RESUMED); - ws.send(msg.respond()); + // tmpBreakpts.add(loc); + + // resume(State.RESUMED); + // ws.send(msg.respond()); } @Override public synchronized void setPauseOnExceptions(V8Message msg) throws IOException { @@ -915,25 +914,22 @@ public class SimpleDebugger implements Debugger { ws.send(msg.respond()); } - @Override public void onSource(Filename filename, String source) { + @Override public void onSourceLoad(Filename filename, String source) { int id = nextId(); var src = new DebugSource(id, filename, source); idToSource.put(id, src); filenameToId.put(filename, id); - for (var bpcd : idToBptCand.values()) { - if (!bpcd.pattern.matcher(filename.toString()).matches()) continue; - var loc = correctLocation(src, new Location(bpcd.line, bpcd.start, filename)); - var bp = new Breakpoint(nextId(), loc, bpcd.condition); - if (loc == null) continue; - bpcd.resolvedBreakpoints.add(bp); - addBreakpoint(bp); - } - if (!enabled) pendingSources.add(src); else sendSource(src); } + @Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { + for (var bpt : idToBreakpoint.values()) { + bpt.addFunc(body, map); + } + mappings.put(body, map); + } @Override public boolean onInstruction(Context ctx, Frame cf, Instruction instruction, Object returnVal, EngineException error, boolean caught) { if (!enabled) return false; @@ -945,11 +941,11 @@ public class SimpleDebugger implements Debugger { synchronized (this) { frame = getFrame(cf); - var map = DebugContext.get(ctx).getMap(frame.func); + var map = DebugContext.get(ctx).getMap(frame.frame.function); frame.updateLoc(map.toLocation(frame.frame.codePtr)); loc = frame.location; - bptType = DebugContext.get(ctx).getMap(frame.func).getBreakpoint(loc); + bptType = map.getBreakpoint(frame.frame.codePtr); isBreakpointable = loc != null && (bptType.shouldStepIn()); if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { @@ -962,12 +958,12 @@ public class SimpleDebugger implements Debugger { ) { pauseDebug(ctx, null); } - else if (isBreakpointable && locToBreakpoint.containsKey(loc)) { - var bp = locToBreakpoint.get(loc); + else if (isBreakpointable && bpLocs.getOrDefault(cf.function.body, new HashMap<>()).containsKey(loc)) { + var bp = bpLocs.get(cf.function.body).get(loc); var ok = bp.condition == null ? true : Values.toBoolean(run(currFrame, bp.condition).result); - if (ok) pauseDebug(ctx, locToBreakpoint.get(loc)); + if (ok) pauseDebug(ctx, bp); } - else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null); + // else if (isBreakpointable && tmpBreakpts.remove(loc)) pauseDebug(ctx, null); else if (isBreakpointable && pendingPause) { pauseDebug(ctx, null); pendingPause = false; diff --git a/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java b/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java index d79932a..bb80125 100644 --- a/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java +++ b/src/java/me/topchetoeu/jscript/utils/debug/WebSocket.java @@ -180,7 +180,9 @@ public class WebSocket implements AutoCloseable { if (!fin) continue; var raw = data.toByteArray(); - if (type == 1) return new WebSocketMessage(new String(raw)); + if (type == 1) { + return new WebSocketMessage(new String(raw)); + } else return new WebSocketMessage(raw); }