From a4c09b6cd608e0bf65356ef4fb583c709571e670 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 22 May 2025 14:19:53 +0300 Subject: [PATCH] various debugging-related fixes --- .../j2s/compilation/CompileResult.java | 336 +++++++++--------- .../j2s/compilation/CompoundNode.java | 226 ++++++------ .../me/topchetoeu/j2s/compilation/Node.java | 61 ++-- .../j2s/lib/debug/SimpleDebugger.java | 27 +- .../j2s/runtime/debug/DebugHandler.java | 2 +- .../values/objects/ArrayLikeValue.java | 1 - 6 files changed, 329 insertions(+), 324 deletions(-) diff --git a/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompileResult.java b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompileResult.java index b4f8f98..0644e1d 100644 --- a/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompileResult.java +++ b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompileResult.java @@ -1,168 +1,168 @@ -package me.topchetoeu.j2s.compilation; - -import java.util.List; -import java.util.Map; -import java.util.Stack; -import java.util.function.Function; - -import me.topchetoeu.j2s.common.Environment; -import me.topchetoeu.j2s.common.FunctionBody; -import me.topchetoeu.j2s.common.Instruction; -import me.topchetoeu.j2s.common.Key; -import me.topchetoeu.j2s.common.Location; -import me.topchetoeu.j2s.common.Instruction.BreakpointType; -import me.topchetoeu.j2s.compilation.CompilationFunctionMap.FunctionMapBuilder; -import me.topchetoeu.j2s.compilation.control.TryNode; -import me.topchetoeu.j2s.compilation.scope.FunctionScope; -import me.topchetoeu.j2s.compilation.scope.Variable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; - -public final class CompileResult { - public static final Key DEBUG_LOG = new Key<>(); - - private FunctionBody body; - public final List instructions; - public final List children; - public final Map childrenMap = new HashMap<>(); - public final Map childrenIndices = new HashMap<>(); - public final FunctionMapBuilder map; - public final Environment env; - public int length; - public final FunctionScope scope; - public final Map catchBindings = new HashMap<>(); - - public int temp() { - instructions.add(null); - return instructions.size() - 1; - } - - public CompileResult add(Instruction instr) { - instructions.add(instr); - return this; - } - public CompileResult set(int i, Instruction instr) { - instructions.set(i, instr); - return this; - } - - public int size() { return instructions.size(); } - - public void setDebug(Location loc, BreakpointType type) { - map.setDebug(loc, type); - } - public void setLocation(int i, Location loc) { - map.setLocation(i, loc); - } - public void setLocationAndDebug(int i, Location loc, BreakpointType type) { - map.setLocationAndDebug(i, loc, type); - } - public void setDebug(BreakpointType type) { - setDebug(map.last(), type); - } - public void setLocation(Location type) { - setLocation(instructions.size() - 1, type); - } - - public void setLocationAndDebug(Location loc, BreakpointType type) { - setLocationAndDebug(instructions.size() - 1, loc, type); - } - - public Iterable all() { - var stack = new Stack(); - stack.push(this); - - return () -> new Iterator() { - @Override public CompileResult next() { - if (stack.empty()) return null; - else { - var res = stack.pop(); - for (var child : res.children) { - stack.push(child); - } - return res; - } - } - @Override public boolean hasNext() { - return !stack.empty(); - } - }; - } - - public CompileResult addChild(FunctionNode node, CompileResult res) { - this.children.add(res); - this.childrenMap.put(node, res); - this.childrenIndices.put(node, this.children.size() - 1); - return res; - } - - public Instruction[] instructions() { - return instructions.toArray(new Instruction[0]); - } - - public CompilationFunctionMap map(Function mapper) { - return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames()); - } - public CompilationFunctionMap map() { - return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames()); - } - public FunctionBody body() { - if (body != null) return body; - - var builtChildren = new FunctionBody[children.size()]; - for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body(); - - var instrRes = instructions(); - - if (env.has(DEBUG_LOG)) { - System.out.println("================= BODY ================="); - System.out.println("LOCALS: " + scope.localsCount()); - System.out.println("CAPTURABLES: " + scope.capturablesCount()); - System.out.println("CAPTURES: " + scope.capturesCount()); - - for (var instr : instrRes) System.out.println(instr); - } - - return body = new FunctionBody( - scope.localsCount(), scope.capturablesCount(), scope.capturesCount(), - length, instrRes, builtChildren - ); - } - - public CompileResult subtarget() { - return new CompileResult(env, new FunctionScope(scope), this); - } - - public CompileResult setEnvironment(Environment env) { - return new CompileResult(env, scope, this); - } - /** - * Returns a compile result with a child of the environment that relates to the given key. - * In essence, this is used to create a compile result which is back at the root environment of the compilation - */ - public CompileResult rootEnvironment(Key env) { - return new CompileResult(this.env.get(env).child(), scope, this); - } - public CompileResult subEnvironment() { - return new CompileResult(env.child(), scope, this); - } - - public CompileResult(Environment env, FunctionScope scope, int length) { - this.scope = scope; - this.instructions = new ArrayList<>(); - this.children = new LinkedList<>(); - this.map = CompilationFunctionMap.builder(); - this.env = env; - this.length = length; - } - private CompileResult(Environment env, FunctionScope scope, CompileResult parent) { - this.scope = scope; - this.instructions = parent.instructions; - this.children = parent.children; - this.map = parent.map; - this.env = env; - } -} +package me.topchetoeu.j2s.compilation; + +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.function.Function; + +import me.topchetoeu.j2s.common.Environment; +import me.topchetoeu.j2s.common.FunctionBody; +import me.topchetoeu.j2s.common.Instruction; +import me.topchetoeu.j2s.common.Key; +import me.topchetoeu.j2s.common.Location; +import me.topchetoeu.j2s.common.Instruction.BreakpointType; +import me.topchetoeu.j2s.compilation.CompilationFunctionMap.FunctionMapBuilder; +import me.topchetoeu.j2s.compilation.control.TryNode; +import me.topchetoeu.j2s.compilation.scope.FunctionScope; +import me.topchetoeu.j2s.compilation.scope.Variable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; + +public final class CompileResult { + public static final Key DEBUG_LOG = new Key<>(); + + private FunctionBody body; + public final List instructions; + public final List children; + public final Map childrenMap = new HashMap<>(); + public final Map childrenIndices = new HashMap<>(); + public final FunctionMapBuilder map; + public final Environment env; + public int length; + public final FunctionScope scope; + public final Map catchBindings = new HashMap<>(); + + public int temp() { + instructions.add(null); + return instructions.size() - 1; + } + + public CompileResult add(Instruction instr) { + instructions.add(instr); + return this; + } + public CompileResult set(int i, Instruction instr) { + instructions.set(i, instr); + return this; + } + + public int size() { return instructions.size(); } + + public void setDebug(Location loc, BreakpointType type) { + map.setDebug(loc, type); + } + public void setLocation(int i, Location loc) { + map.setLocation(i, loc); + } + public void setLocationAndDebug(int i, Location loc, BreakpointType type) { + map.setLocationAndDebug(i, loc, type); + } + public void setDebug(BreakpointType type) { + setDebug(map.last(), type); + } + public void setLocation(Location type) { + setLocation(instructions.size() - 1, type); + } + + public void setLocationAndDebug(Location loc, BreakpointType type) { + setLocationAndDebug(instructions.size() - 1, loc, type); + } + + public Iterable all() { + var stack = new Stack(); + stack.push(this); + + return () -> new Iterator() { + @Override public CompileResult next() { + if (stack.empty()) return null; + else { + var res = stack.pop(); + for (var child : res.children) { + stack.push(child); + } + return res; + } + } + @Override public boolean hasNext() { + return !stack.empty(); + } + }; + } + + public CompileResult addChild(FunctionNode node, CompileResult res) { + this.children.add(res); + this.childrenMap.put(node, res); + this.childrenIndices.put(node, this.children.size() - 1); + return res; + } + + public Instruction[] instructions() { + return instructions.toArray(new Instruction[0]); + } + + public CompilationFunctionMap map(Function mapper) { + return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames()); + } + public CompilationFunctionMap map() { + return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames()); + } + public FunctionBody body() { + if (body != null) return body; + + var builtChildren = new FunctionBody[children.size()]; + for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body(); + + var instrRes = instructions(); + + if (env.has(DEBUG_LOG)) { + System.out.println("================= BODY ================="); + System.out.println("LOCALS: " + scope.localsCount()); + System.out.println("CAPTURABLES: " + scope.capturablesCount()); + System.out.println("CAPTURES: " + scope.capturesCount()); + + for (var instr : instrRes) System.out.println(instr); + } + + return body = new FunctionBody( + scope.localsCount(), scope.capturablesCount(), scope.capturesCount(), + length, instrRes, builtChildren + ); + } + + public CompileResult subtarget() { + return new CompileResult(env, new FunctionScope(scope), this); + } + + public CompileResult setEnvironment(Environment env) { + return new CompileResult(env, scope, this); + } + /** + * Returns a compile result with a child of the environment that relates to the given key. + * In essence, this is used to create a compile result which is back at the root environment of the compilation + */ + public CompileResult rootEnvironment(Key env) { + return new CompileResult(this.env.get(env).child(), scope, this); + } + public CompileResult subEnvironment() { + return new CompileResult(env.child(), scope, this); + } + + public CompileResult(Environment env, FunctionScope scope, int length) { + this.scope = scope; + this.instructions = new ArrayList<>(); + this.children = new LinkedList<>(); + this.map = CompilationFunctionMap.builder(); + this.env = env; + this.length = length; + } + private CompileResult(Environment env, FunctionScope scope, CompileResult parent) { + this.scope = scope; + this.instructions = parent.instructions; + this.children = parent.children; + this.map = parent.map; + this.env = env; + } +} diff --git a/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompoundNode.java b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompoundNode.java index 2a7c14b..be0dfcc 100644 --- a/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompoundNode.java +++ b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompoundNode.java @@ -1,112 +1,114 @@ -package me.topchetoeu.j2s.compilation; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import me.topchetoeu.j2s.common.Instruction; -import me.topchetoeu.j2s.common.Location; -import me.topchetoeu.j2s.common.Instruction.BreakpointType; -import me.topchetoeu.j2s.compilation.parsing.ParseRes; -import me.topchetoeu.j2s.compilation.parsing.Parsing; -import me.topchetoeu.j2s.compilation.parsing.Source; - - -public class CompoundNode extends Node { - public final Node[] statements; - public Location end; - - @Override public void resolve(CompileResult target) { - for (var stm : statements) stm.resolve(target); - } - @Override public void compileFunctions(CompileResult target) { - for (var stm : statements) stm.compileFunctions(target); - } - - public void compile(CompileResult target, boolean pollute, BreakpointType type) { - List statements = new ArrayList(); - - for (var stm : this.statements) { - if (stm instanceof FunctionStatementNode func) { - func.compile(target, false); - } - else statements.add(stm); - } - - var polluted = false; - - for (var i = 0; i < statements.size(); i++) { - var stm = statements.get(i); - - if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER); - else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER); - } - - if (!polluted && pollute) { - target.add(Instruction.pushUndefined()); - } - } - - public CompoundNode setEnd(Location loc) { - this.end = loc; - return this; - } - - public CompoundNode(Location loc, Node ...statements) { - super(loc); - this.statements = statements; - } - - public static ParseRes parseComma(Source src, int i, Node prev, int precedence) { - if (precedence > 1) return ParseRes.failed(); - - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!src.is(i + n, ",")) return ParseRes.failed(); - n++; - - var curr = JavaScript.parseExpression(src, i + n, 2); - if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma"); - n += curr.n; - - if (prev instanceof CompoundNode comp) { - var children = new ArrayList(); - children.addAll(Arrays.asList(comp.statements)); - children.add(curr.result); - - return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n); - } - else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n); - } - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!src.is(i + n, "{")) return ParseRes.failed(); - n++; - - var statements = new ArrayList(); - - while (true) { - n += Parsing.skipEmpty(src, i + n); - - if (src.is(i + n, "}")) { - n++; - break; - } - if (src.is(i + n, ";")) { - n++; - continue; - } - - var res = JavaScript.parseStatement(src, i + n); - if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement"); - n += res.n; - - statements.add(res.result); - } - - return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n); - } -} +package me.topchetoeu.j2s.compilation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import me.topchetoeu.j2s.common.Instruction; +import me.topchetoeu.j2s.common.Location; +import me.topchetoeu.j2s.common.Instruction.BreakpointType; +import me.topchetoeu.j2s.compilation.parsing.ParseRes; +import me.topchetoeu.j2s.compilation.parsing.Parsing; +import me.topchetoeu.j2s.compilation.parsing.Source; + + +public class CompoundNode extends Node { + public final Node[] statements; + public Location end; + + @Override public void resolve(CompileResult target) { + for (var stm : statements) stm.resolve(target); + } + @Override public void compileFunctions(CompileResult target) { + for (var stm : statements) stm.compileFunctions(target); + } + + public void compile(CompileResult target, boolean pollute, BreakpointType type) { + List statements = new ArrayList(); + + for (var stm : this.statements) { + if (stm instanceof FunctionStatementNode func) { + func.compile(target, false); + } + else statements.add(stm); + } + + var polluted = false; + + for (var i = 0; i < statements.size(); i++) { + var stm = statements.get(i); + + if (i != statements.size() - 1) stm.compileStatement(target, false, BreakpointType.STEP_OVER); + else stm.compileStatement(target, polluted = pollute, BreakpointType.STEP_OVER); + + target.setDebug(type); + } + + if (!polluted && pollute) { + target.add(Instruction.pushUndefined()); + } + } + + public CompoundNode setEnd(Location loc) { + this.end = loc; + return this; + } + + public CompoundNode(Location loc, Node ...statements) { + super(loc); + this.statements = statements; + } + + public static ParseRes parseComma(Source src, int i, Node prev, int precedence) { + if (precedence > 1) return ParseRes.failed(); + + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, ",")) return ParseRes.failed(); + n++; + + var curr = JavaScript.parseExpression(src, i + n, 2); + if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma"); + n += curr.n; + + if (prev instanceof CompoundNode comp) { + var children = new ArrayList(); + children.addAll(Arrays.asList(comp.statements)); + children.add(curr.result); + + return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n); + } + else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n); + } + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + if (!src.is(i + n, "{")) return ParseRes.failed(); + n++; + + var statements = new ArrayList(); + + while (true) { + n += Parsing.skipEmpty(src, i + n); + + if (src.is(i + n, "}")) { + n++; + break; + } + if (src.is(i + n, ";")) { + n++; + continue; + } + + var res = JavaScript.parseStatement(src, i + n); + if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement"); + n += res.n; + + statements.add(res.result); + } + + return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n); + } +} diff --git a/compilation/src/main/java/me/topchetoeu/j2s/compilation/Node.java b/compilation/src/main/java/me/topchetoeu/j2s/compilation/Node.java index 44c248e..ea2a75e 100644 --- a/compilation/src/main/java/me/topchetoeu/j2s/compilation/Node.java +++ b/compilation/src/main/java/me/topchetoeu/j2s/compilation/Node.java @@ -1,28 +1,33 @@ -package me.topchetoeu.j2s.compilation; - -import me.topchetoeu.j2s.common.Location; -import me.topchetoeu.j2s.common.Instruction.BreakpointType; - -public abstract class Node { - private Location loc; - - public void resolve(CompileResult target) {} - - public void compile(CompileResult target, boolean pollute, BreakpointType type) { - int start = target.size(); - compile(target, pollute); - if (target.size() != start) target.setLocationAndDebug(start, loc(), type); - } - public void compile(CompileResult target, boolean pollute) { - compile(target, pollute, BreakpointType.NONE); - } - - public abstract void compileFunctions(CompileResult target); - - public Location loc() { return loc; } - public void setLoc(Location loc) { this.loc = loc; } - - protected Node(Location loc) { - this.loc = loc; - } -} \ No newline at end of file +package me.topchetoeu.j2s.compilation; + +import me.topchetoeu.j2s.common.Location; +import me.topchetoeu.j2s.common.Instruction.BreakpointType; + +public abstract class Node { + private Location loc; + + public void resolve(CompileResult target) {} + + public void compile(CompileResult target, boolean pollute, BreakpointType type) { + compileStatement(target, pollute, type); + } + public void compileStatement(CompileResult target, boolean pollute, BreakpointType type) { + int start = target.size(); + compile(target, pollute); + if (target.size() != start) { + target.setLocationAndDebug(start, loc(), type); + } + } + public void compile(CompileResult target, boolean pollute) { + compile(target, pollute, BreakpointType.NONE); + } + + public abstract void compileFunctions(CompileResult target); + + public Location loc() { return loc; } + public void setLoc(Location loc) { this.loc = loc; } + + protected Node(Location loc) { + this.loc = loc; + } +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java index 9ec0656..7c4e64e 100644 --- a/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/debug/SimpleDebugger.java @@ -423,7 +423,7 @@ public class SimpleDebugger implements Debugger { desc.append("..."); break; } - + if (arr.has(i)) { try { var curr = arr.get(i); @@ -442,7 +442,7 @@ public class SimpleDebugger implements Debugger { desc.append(""); } } - + desc.append("]"); } @@ -814,7 +814,6 @@ public class SimpleDebugger implements Debugger { var cond = msg.params.string("condition", "").trim(); if (cond.equals("")) cond = null; - if (cond != null) cond = "(" + cond + ")"; Pattern regex; @@ -1064,7 +1063,7 @@ public class SimpleDebugger implements Debugger { } mappings.put(body, map); } - + private boolean instructionLock; @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) { @@ -1077,16 +1076,16 @@ public class SimpleDebugger implements Debugger { Location loc; DebugFrame frame; BreakpointType bptType; - + frame = getFrame(cf); - + var map = DebugHandler.get(env).getMapOrEmpty(env, frame.frame.function); - + frame.updateLoc(map.toLocation(frame.frame.codePtr)); loc = frame.location; bptType = map.getBreakpoint(frame.frame.codePtr); isBreakpointable = loc != null && (bptType.shouldStepIn()); - + if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { pauseException(env, error); } @@ -1114,19 +1113,19 @@ public class SimpleDebugger implements Debugger { instruction.params.length == 1 && instruction.get(0).equals("debug") ) pauseDebug(env, null); - + synchronized (this) { } - + while (enabled) { synchronized (this) { switch (state) { case PAUSED_EXCEPTION: case PAUSED_NORMAL: break; - + case STEPPING_OUT: case RESUMED: return false; - + case STEPPING_IN: case STEPPING_OVER: if (stepOutFrame.frame == frame.frame) { @@ -1135,7 +1134,7 @@ public class SimpleDebugger implements Debugger { continue; } else if (stepOutPtr != frame.frame.codePtr) { - + if (state == State.STEPPING_IN && bptType.shouldStepIn()) { pauseDebug(env, null); break; @@ -1149,7 +1148,7 @@ public class SimpleDebugger implements Debugger { return false; } } - + try { synchronized (updateNotifier) { updateNotifier.wait(); diff --git a/runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugHandler.java b/runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugHandler.java index bb677a7..6143ebc 100644 --- a/runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugHandler.java +++ b/runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugHandler.java @@ -93,7 +93,7 @@ public interface DebugHandler { } public default FunctionMap getMapOrEmpty(Environment env, FunctionValue func) { if (func instanceof CodeFunction codeFunc) return getMapOrEmpty(env, codeFunc.body); - else return null; + else return FunctionMap.EMPTY; } public static DebugHandler get(Environment exts) { diff --git a/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/objects/ArrayLikeValue.java b/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/objects/ArrayLikeValue.java index b6eea39..db75a9b 100644 --- a/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/objects/ArrayLikeValue.java +++ b/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/objects/ArrayLikeValue.java @@ -195,7 +195,6 @@ public abstract class ArrayLikeValue extends ObjectValue { res.add(" " + line); } } - res.set(res.size() - 1, res.getLast().substring(0, res.getLast().length() - 1)); res.add("]"); return res;