From 208444381eedcf252effadd50e5352460d8c6acd Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:19:22 +0200 Subject: [PATCH] move FunctionMap logic away from core --- .../me/topchetoeu/j2s/common/FunctionMap.java | 213 +++-------------- .../me/topchetoeu/j2s/common/Instruction.java | 8 +- .../j2s/common/TestFunctionMap.java | 18 -- .../compilation/CompilationFunctionMap.java | 219 ++++++++++++++++++ .../j2s/compilation/CompileResult.java | 9 +- .../me/topchetoeu/j2s/repl/SimpleRepl.java | 15 +- .../j2s/repl/debug/SimpleDebugHandler.java | 51 +--- .../j2s/repl/debug/SimpleDebugger.java | 34 ++- .../j2s/runtime/debug/DebugHandler.java | 67 +++++- .../runtime/exceptions/EngineException.java | 3 +- .../values/functions/CodeFunction.java | 4 +- .../values/functions/FunctionValue.java | 6 +- 12 files changed, 364 insertions(+), 283 deletions(-) delete mode 100644 common/src/test/java/me/topchetoeu/j2s/common/TestFunctionMap.java create mode 100644 compilation/src/main/java/me/topchetoeu/j2s/compilation/CompilationFunctionMap.java rename runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugContext.java => repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java (53%) diff --git a/common/src/main/java/me/topchetoeu/j2s/common/FunctionMap.java b/common/src/main/java/me/topchetoeu/j2s/common/FunctionMap.java index cb39a5c..6cc4c05 100644 --- a/common/src/main/java/me/topchetoeu/j2s/common/FunctionMap.java +++ b/common/src/main/java/me/topchetoeu/j2s/common/FunctionMap.java @@ -1,199 +1,62 @@ package me.topchetoeu.j2s.common; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.NavigableSet; -import java.util.Objects; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.function.Function; import java.util.regex.Pattern; -import java.util.stream.Collectors; import me.topchetoeu.j2s.common.Instruction.BreakpointType; -public class FunctionMap { - public static class FunctionMapBuilder { - private Location first, last; - private final TreeMap sourceMap = new TreeMap<>(); - private final HashMap breakpoints = new HashMap<>(); - - public Location toLocation(int pc) { - return sourceMap.headMap(pc, true).firstEntry().getValue(); +public interface FunctionMap { + public static final FunctionMap EMPTY = new FunctionMap() { + @Override public Location first() { + return null; + } + @Override public Location last() { + return null; } - public FunctionMapBuilder setDebug(Location loc, BreakpointType type) { - if (loc == null || type == null || type == BreakpointType.NONE) return this; - breakpoints.put(loc, type); - return this; - } - public FunctionMapBuilder setLocation(int i, Location loc) { - if (loc == null || i < 0) return this; - if (first == null || first.compareTo(loc) > 0) first = loc; - if (last == null || last.compareTo(loc) < 0) last = loc; - - sourceMap.put(i, loc); - return this; - } - public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) { - setDebug(loc, type); - setLocation(i, loc); - return this; + @Override public Location toLocation(int i, boolean approximate) { + return null; } - public Location first() { - return first; + @Override public BreakpointType getBreakpoint(int i) { + return BreakpointType.NONE; } - public Location last() { - return last; + @Override public Iterable breakpoints(Location start, Location end) { + return Arrays.asList(); } - public FunctionMapBuilder map(Function mapper) { - var newSourceMaps = new HashMap(); - var newBreakpoints = new HashMap(); - - for (var key : sourceMap.keySet()) { - var mapped = mapper.apply(sourceMap.get(key)); - if (mapped == null) continue; - newSourceMaps.put(key, mapped); - } - for (var key : breakpoints.keySet()) { - var mapped = mapper.apply(key); - if (mapped == null) continue; - newBreakpoints.put(mapped, breakpoints.get(key)); - } - - sourceMap.clear(); - sourceMap.putAll(newSourceMaps); - - breakpoints.clear(); - breakpoints.putAll(newBreakpoints); - - return this; + @Override public Location correctBreakpoint(Location i) { + return null; + } + @Override public Iterable correctBreakpoint(Pattern filename, int line, int column) { + return Arrays.asList(); } - public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) { - return new FunctionMap(sourceMap, breakpoints, first, last, localNames, capturableNames, captureNames); + @Override public String[] localNames() { + return null; } - public FunctionMap build() { - return new FunctionMap(sourceMap, breakpoints, first, last, new String[0], new String[0], new String[0]); + @Override public String[] capturableNames() { + return null; } - - private FunctionMapBuilder() { } - } - - public static final FunctionMap EMPTY = new FunctionMap(); - - private final HashMap bps = new HashMap<>(); - private final HashMap> bpLocs = new HashMap<>(); - - private final TreeMap pcToLoc = new TreeMap<>(); - - public final String[] localNames, capturableNames, captureNames; - public final Location first, last; - - public Location toLocation(int pc, boolean approximate) { - if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null; - var res = pcToLoc.get(pc); - if (!approximate || res != null) return res; - var entry = pcToLoc.headMap(pc, true).lastEntry(); - if (entry == null) return null; - else return entry.getValue(); - } - public Location toLocation(int pc) { - return toLocation(pc, false); - } - - public BreakpointType getBreakpoint(int pc) { - return bps.getOrDefault(pc, BreakpointType.NONE); - } - public Location correctBreakpoint(Location loc) { - var set = bpLocs.get(loc.filename()); - if (set == null) return null; - else return set.ceiling(loc); - } - 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)); - } + @Override public String[] captureNames() { + return null; } + }; - var res = new ArrayList(candidates.size()); - for (var candidate : candidates.entrySet()) { - var val = correctBreakpoint(Location.of(candidate.getKey(), line, column)); - if (val == null) continue; - res.add(val); - } + Location first(); + Location last(); - return res; - } - public List breakpoints(Location start, Location end) { - if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList(); - NavigableSet set = bpLocs.get(start.filename()); - if (set == null) return Arrays.asList(); - - if (start != null) set = set.tailSet(start, true); - if (end != null) set = set.headSet(end, true); - - return set.stream().collect(Collectors.toList()); + Location toLocation(int i, boolean approximate); + default Location toLocation(int i) { + return toLocation(i, false); } - public Location start() { - if (pcToLoc.size() == 0) return null; - return pcToLoc.firstEntry().getValue(); - } - public Location end() { - if (pcToLoc.size() == 0) return null; - return pcToLoc.lastEntry().getValue(); - } + BreakpointType getBreakpoint(int i); + Location correctBreakpoint(Location i); + Iterable correctBreakpoint(Pattern filename, int line, int column); + Iterable breakpoints(Location start, Location end); - public FunctionMap clone() { - var res = new FunctionMap(new HashMap<>(), new HashMap<>(), first, last, localNames, capturableNames, captureNames); - res.pcToLoc.putAll(this.pcToLoc); - res.bps.putAll(bps); - res.bpLocs.putAll(bpLocs); - res.pcToLoc.putAll(pcToLoc); - return res; - } - - public FunctionMap(Map map, Map breakpoints, Location first, Location last, String[] localNames, String[] capturableNames, String[] captureNames) { - var locToPc = new HashMap(); - - for (var el : map.entrySet()) { - 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(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; - this.captureNames = captureNames; - this.capturableNames = capturableNames; - - this.first = first; - this.last = last; - } - private FunctionMap() { - localNames = new String[0]; - captureNames = new String[0]; - capturableNames = new String[0]; - first = null; - last = null; - } - - public static FunctionMapBuilder builder() { - return new FunctionMapBuilder(); - } -} \ No newline at end of file + String[] localNames(); + String[] capturableNames(); + String[] captureNames(); +} diff --git a/common/src/main/java/me/topchetoeu/j2s/common/Instruction.java b/common/src/main/java/me/topchetoeu/j2s/common/Instruction.java index 73438ae..6135f50 100644 --- a/common/src/main/java/me/topchetoeu/j2s/common/Instruction.java +++ b/common/src/main/java/me/topchetoeu/j2s/common/Instruction.java @@ -51,11 +51,11 @@ public class Instruction { STORE_MEMBER_INT(0x4A), STORE_MEMBER_STR(0x4B), - OPERATION(0x50), + GLOB_GET(0x50), + GLOB_SET(0x51), + GLOB_DEF(0x52), - GLOB_GET(0x60), - GLOB_SET(0x61), - GLOB_DEF(0x62); + OPERATION(0x56); private static final HashMap types = new HashMap<>(); public final int numeric; diff --git a/common/src/test/java/me/topchetoeu/j2s/common/TestFunctionMap.java b/common/src/test/java/me/topchetoeu/j2s/common/TestFunctionMap.java deleted file mode 100644 index f3ac0e4..0000000 --- a/common/src/test/java/me/topchetoeu/j2s/common/TestFunctionMap.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.topchetoeu.j2s.common; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class TestFunctionMap { - @Test public void createEmpty() { - FunctionMap.builder().build(null, null, null); - FunctionMap.builder().build(); - } - @Test public void startOfEmpty() { - var empty = FunctionMap.EMPTY; - - assertEquals(null, empty.start()); - assertEquals(null, empty.end()); - } -} diff --git a/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompilationFunctionMap.java b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompilationFunctionMap.java new file mode 100644 index 0000000..3f502d4 --- /dev/null +++ b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompilationFunctionMap.java @@ -0,0 +1,219 @@ +package me.topchetoeu.j2s.compilation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Objects; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import me.topchetoeu.j2s.common.Filename; +import me.topchetoeu.j2s.common.FunctionMap; +import me.topchetoeu.j2s.common.Location; +import me.topchetoeu.j2s.common.Instruction.BreakpointType; + +public final class CompilationFunctionMap implements FunctionMap { + public static class FunctionMapBuilder { + private Location first, last; + private final TreeMap sourceMap = new TreeMap<>(); + private final HashMap breakpoints = new HashMap<>(); + + public Location toLocation(int pc) { + return sourceMap.headMap(pc, true).firstEntry().getValue(); + } + + public FunctionMapBuilder setDebug(Location loc, BreakpointType type) { + if (loc == null || type == null || type == BreakpointType.NONE) return this; + breakpoints.put(loc, type); + return this; + } + public FunctionMapBuilder setLocation(int i, Location loc) { + if (loc == null || i < 0) return this; + if (first == null || first.compareTo(loc) > 0) first = loc; + if (last == null || last.compareTo(loc) < 0) last = loc; + + sourceMap.put(i, loc); + return this; + } + public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) { + setDebug(loc, type); + setLocation(i, loc); + return this; + } + + public Location first() { + return first; + } + public Location last() { + return last; + } + + public FunctionMapBuilder map(Function mapper) { + var newSourceMaps = new HashMap(); + var newBreakpoints = new HashMap(); + + for (var key : sourceMap.keySet()) { + var mapped = mapper.apply(sourceMap.get(key)); + if (mapped == null) continue; + newSourceMaps.put(key, mapped); + } + for (var key : breakpoints.keySet()) { + var mapped = mapper.apply(key); + if (mapped == null) continue; + newBreakpoints.put(mapped, breakpoints.get(key)); + } + + sourceMap.clear(); + sourceMap.putAll(newSourceMaps); + + breakpoints.clear(); + breakpoints.putAll(newBreakpoints); + + return this; + } + + public CompilationFunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) { + return new CompilationFunctionMap(sourceMap, breakpoints, first, last, localNames, capturableNames, captureNames); + } + public CompilationFunctionMap build() { + return new CompilationFunctionMap(sourceMap, breakpoints, first, last, new String[0], new String[0], new String[0]); + } + + private FunctionMapBuilder() { } + } + + public static final CompilationFunctionMap EMPTY = new CompilationFunctionMap(); + + private final HashMap bps = new HashMap<>(); + private final HashMap> bpLocs = new HashMap<>(); + + private final TreeMap pcToLoc = new TreeMap<>(); + + public final String[] localNames, capturableNames, captureNames; + public final Location first, last; + + @Override public Location toLocation(int pc, boolean approximate) { + if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null; + var res = pcToLoc.get(pc); + if (!approximate || res != null) return res; + var entry = pcToLoc.headMap(pc, true).lastEntry(); + if (entry == null) return null; + else return entry.getValue(); + } + @Override public Location toLocation(int pc) { + return toLocation(pc, false); + } + + @Override public BreakpointType getBreakpoint(int pc) { + return bps.getOrDefault(pc, BreakpointType.NONE); + } + @Override public Location correctBreakpoint(Location loc) { + var set = bpLocs.get(loc.filename()); + if (set == null) return null; + else return set.ceiling(loc); + } + @Override 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()) { + var val = correctBreakpoint(Location.of(candidate.getKey(), line, column)); + if (val == null) continue; + res.add(val); + } + + return res; + } + @Override public List breakpoints(Location start, Location end) { + if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList(); + NavigableSet set = bpLocs.get(start.filename()); + if (set == null) return Arrays.asList(); + + 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() { + if (pcToLoc.size() == 0) return null; + return pcToLoc.firstEntry().getValue(); + } + public Location end() { + if (pcToLoc.size() == 0) return null; + return pcToLoc.lastEntry().getValue(); + } + + @Override public Location first() { + return first; + } + @Override public Location last() { + return last; + } + + @Override public String[] capturableNames() { + return capturableNames; + } + @Override public String[] captureNames() { + return captureNames; + } + @Override public String[] localNames() { + return localNames; + } + + public CompilationFunctionMap clone() { + var res = new CompilationFunctionMap(new HashMap<>(), new HashMap<>(), first, last, localNames, capturableNames, captureNames); + res.pcToLoc.putAll(this.pcToLoc); + res.bps.putAll(bps); + res.bpLocs.putAll(bpLocs); + res.pcToLoc.putAll(pcToLoc); + return res; + } + + public CompilationFunctionMap(Map map, Map breakpoints, Location first, Location last, String[] localNames, String[] capturableNames, String[] captureNames) { + var locToPc = new HashMap(); + + for (var el : map.entrySet()) { + 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(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; + this.captureNames = captureNames; + this.capturableNames = capturableNames; + + this.first = first; + this.last = last; + } + private CompilationFunctionMap() { + localNames = new String[0]; + captureNames = new String[0]; + capturableNames = new String[0]; + first = null; + last = null; + } + + public static FunctionMapBuilder builder() { + return new FunctionMapBuilder(); + } +} \ No newline at end of file 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 e55e956..b4f8f98 100644 --- a/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompileResult.java +++ b/compilation/src/main/java/me/topchetoeu/j2s/compilation/CompileResult.java @@ -7,12 +7,11 @@ import java.util.function.Function; import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.FunctionBody; -import me.topchetoeu.j2s.common.FunctionMap; import me.topchetoeu.j2s.common.Instruction; import me.topchetoeu.j2s.common.Key; import me.topchetoeu.j2s.common.Location; -import me.topchetoeu.j2s.common.FunctionMap.FunctionMapBuilder; 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; @@ -104,10 +103,10 @@ public final class CompileResult { return instructions.toArray(new Instruction[0]); } - public FunctionMap map(Function mapper) { + public CompilationFunctionMap map(Function mapper) { return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames()); } - public FunctionMap map() { + public CompilationFunctionMap map() { return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames()); } public FunctionBody body() { @@ -155,7 +154,7 @@ public final class CompileResult { this.scope = scope; this.instructions = new ArrayList<>(); this.children = new LinkedList<>(); - this.map = FunctionMap.builder(); + this.map = CompilationFunctionMap.builder(); this.env = env; this.length = length; } diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java b/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java index b002c50..0f76ad0 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java +++ b/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java @@ -30,6 +30,7 @@ import me.topchetoeu.j2s.repl.buffers.Int32ArrayValue; import me.topchetoeu.j2s.repl.buffers.Int8ArrayValue; import me.topchetoeu.j2s.repl.buffers.TypedArrayValue; import me.topchetoeu.j2s.repl.buffers.Uint8ArrayValue; +import me.topchetoeu.j2s.repl.debug.SimpleDebugHandler; import me.topchetoeu.j2s.repl.debug.DebugServer; import me.topchetoeu.j2s.repl.debug.Debugger; import me.topchetoeu.j2s.repl.debug.SimpleDebugger; @@ -39,7 +40,7 @@ import me.topchetoeu.j2s.runtime.Compiler; import me.topchetoeu.j2s.runtime.Engine; import me.topchetoeu.j2s.runtime.EventLoop; import me.topchetoeu.j2s.runtime.Frame; -import me.topchetoeu.j2s.runtime.debug.DebugContext; +import me.topchetoeu.j2s.runtime.debug.DebugHandler; import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.values.Value; import me.topchetoeu.j2s.runtime.values.functions.CodeFunction; @@ -61,9 +62,9 @@ public class SimpleRepl { var res = JavaScript.compile(env, filename, raw, true); var body = res.body(); - DebugContext.get(env).onSource(filename, raw); + DebugHandler.get(env).onSourceLoad(filename, raw); for (var el : res.all()) { - DebugContext.get(env).onFunctionLoad(el.body(), el.map(mapper)); + DebugHandler.get(env).onFunctionLoad(el.body(), el.map(mapper)); } return new CodeFunction(env, filename.toString(), body, new Value[0][]); @@ -98,8 +99,8 @@ public class SimpleRepl { server = new DebugServer(); debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true); server.targets.put("default", (socket, req) -> new SimpleDebugger(socket) - .attach(DebugContext.get(environment)) - .attach(DebugContext.get(tsEnvironment)) + .attach((SimpleDebugHandler)DebugHandler.get(environment)) + .attach((SimpleDebugHandler)DebugHandler.get(tsEnvironment)) ); try { @@ -846,7 +847,7 @@ public class SimpleRepl { private static Environment initEnv() { var env = new Environment(); env.add(EventLoop.KEY, engine); - env.add(DebugContext.KEY, new DebugContext()); + env.add(DebugHandler.KEY, new SimpleDebugHandler()); env.add(Compiler.KEY, DEFAULT_COMPILER); // env.add(CompileResult.DEBUG_LOG); @@ -905,7 +906,7 @@ public class SimpleRepl { tsGlob.defineOwnField(tsEnvironment, "registerSource", new NativeFunction(args -> { var filename = Filename.parse(args.get(0).toString(args.env)); var src = args.get(1).toString(args.env); - DebugContext.get(environment).onSource(filename, src); + DebugHandler.get(environment).onSourceLoad(filename, src); return Value.UNDEFINED; })); diff --git a/runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugContext.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java similarity index 53% rename from runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugContext.java rename to repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java index 1996e1e..4ffc5d3 100644 --- a/runtime/src/main/java/me/topchetoeu/j2s/runtime/debug/DebugContext.java +++ b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugHandler.java @@ -1,24 +1,19 @@ -package me.topchetoeu.j2s.runtime.debug; +package me.topchetoeu.j2s.repl.debug; import java.util.HashMap; import java.util.WeakHashMap; import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.Filename; +import me.topchetoeu.j2s.common.Instruction; import me.topchetoeu.j2s.common.FunctionBody; import me.topchetoeu.j2s.common.FunctionMap; -import me.topchetoeu.j2s.common.Instruction; -import me.topchetoeu.j2s.common.Key; import me.topchetoeu.j2s.runtime.Frame; +import me.topchetoeu.j2s.runtime.debug.DebugHandler; import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.values.Value; -import me.topchetoeu.j2s.runtime.values.functions.CodeFunction; -import me.topchetoeu.j2s.runtime.values.functions.FunctionValue; - -public class DebugContext { - public static final Key KEY = new Key<>(); - public static final Key IGNORE = new Key<>(); +public class SimpleDebugHandler implements DebugHandler { private HashMap sources; private WeakHashMap maps; private DebugHandler debugger; @@ -49,24 +44,10 @@ public class DebugContext { return debugger; } - public FunctionMap getMap(FunctionBody func) { + public FunctionMap getMap(Environment env, FunctionBody func) { if (maps == null) return null; return maps.get(func); } - public FunctionMap getMap(FunctionValue func) { - if (maps == null || !(func instanceof CodeFunction)) return null; - return getMap(((CodeFunction)func).body); - } - public FunctionMap getMapOrEmpty(FunctionBody func) { - if (maps == null) return FunctionMap.EMPTY; - var res = maps.get(func); - if (res == null) return FunctionMap.EMPTY; - else return res; - } - public FunctionMap getMapOrEmpty(FunctionValue func) { - if (maps == null || !(func instanceof CodeFunction)) return FunctionMap.EMPTY; - return getMapOrEmpty(((CodeFunction)func).body); - } public void onFramePop(Environment env, Frame frame) { if (debugger != null) debugger.onFramePop(env, frame); @@ -75,39 +56,27 @@ public class DebugContext { if (debugger != null) debugger.onFramePush(env, frame); } - public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught) { + @Override public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught) { if (debugger != null) return debugger.onInstruction(env, frame, instruction, returnVal, error, caught); else return false; } - public boolean onInstruction(Environment env, Frame frame, Instruction instruction) { - if (debugger != null) return debugger.onInstruction(env, frame, instruction, null, null, false); - else return false; - } - public void onSource(Filename filename, String source) { + @Override public void onSourceLoad(Filename filename, String source) { if (debugger != null) debugger.onSourceLoad(filename, source); if (sources != null) sources.put(filename, source); } - public void onFunctionLoad(FunctionBody func, FunctionMap map) { + @Override 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) { + private SimpleDebugHandler(boolean enabled) { if (enabled) { sources = new HashMap<>(); maps = new WeakHashMap<>(); } } - public DebugContext() { + public SimpleDebugHandler() { this(true); } - - public static boolean enabled(Environment exts) { - return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE); - } - public static DebugContext get(Environment exts) { - if (enabled(exts)) return exts.get(KEY); - else return new DebugContext(false); - } } diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java index d32e7c1..0ed2566 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java +++ b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java @@ -30,7 +30,7 @@ import me.topchetoeu.j2s.runtime.Compiler; import me.topchetoeu.j2s.runtime.Engine; import me.topchetoeu.j2s.runtime.EventLoop; import me.topchetoeu.j2s.runtime.Frame; -import me.topchetoeu.j2s.runtime.debug.DebugContext; +import me.topchetoeu.j2s.runtime.debug.DebugHandler; import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.values.Value; import me.topchetoeu.j2s.runtime.values.Member.FieldMember; @@ -201,11 +201,11 @@ public class SimpleDebugger implements Debugger { this.frame = frame; this.id = id; - var map = DebugContext.get(frame.env).getMapOrEmpty(frame.function); + var map = DebugHandler.get(frame.env).getMapOrEmpty(frame.env, frame.function); this.globals = Value.global(frame.env); - this.locals = ScopeObject.locals(frame, map.localNames); - this.capturables = ScopeObject.capturables(frame, map.capturableNames); - this.captures = ScopeObject.captures(frame, map.captureNames); + this.locals = ScopeObject.locals(frame, map.localNames()); + this.capturables = ScopeObject.capturables(frame, map.capturableNames()); + this.captures = ScopeObject.captures(frame, map.captureNames()); if (this.globals instanceof ObjectValue) { this.variables = ScopeObject.combine((ObjectValue)this.globals, locals, capturables, captures); } @@ -249,7 +249,7 @@ public class SimpleDebugger implements Debugger { private ObjectValue emptyObject = new ObjectValue(); - private WeakHashMap contexts = new WeakHashMap<>(); + private WeakHashMap contexts = new WeakHashMap<>(); private WeakHashMap mappings = new WeakHashMap<>(); private HashMap> bpLocs = new HashMap<>(); @@ -617,22 +617,12 @@ public class SimpleDebugger implements Debugger { } } - // private Environment sanitizeEnvironment(Environment env) { - // var res = env.child(); - - // res.remove(EventLoop.KEY); - // res.remove(DebugContext.KEY); - // res.add(DebugContext.IGNORE); - - // return res; - // } - private RunResult run(DebugFrame codeFrame, String code) { if (codeFrame == null) return new RunResult(null, null, EngineException.ofError("Invalid code frame!")); var engine = new Engine(); var env = codeFrame.frame.env.child(); - env.remove(DebugContext.KEY); + env.remove(DebugHandler.KEY); env.remove(EventLoop.KEY); env.remove(Value.GLOBAL); env.add(Compiler.KEY, SimpleRepl.DEFAULT_COMPILER); @@ -1076,7 +1066,7 @@ public class SimpleDebugger implements Debugger { } private boolean instructionLock; - + @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) { if (!enabled) return false; if (instructionLock) return false; @@ -1090,7 +1080,7 @@ public class SimpleDebugger implements Debugger { frame = getFrame(cf); - var map = DebugContext.get(env).getMap(frame.frame.function); + var map = DebugHandler.get(env).getMap(env, frame.frame.function); frame.updateLoc(map.toLocation(frame.frame.codePtr)); loc = frame.location; @@ -1197,7 +1187,11 @@ public class SimpleDebugger implements Debugger { } } - public SimpleDebugger attach(DebugContext ctx) { + @Override public FunctionMap getMap(Environment env, FunctionBody func) { + return mappings.get(func); + } + + public SimpleDebugger attach(SimpleDebugHandler ctx) { ctx.attachDebugger(this); contexts.put(ctx, ctx); return this; 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 e68fdd0..c756676 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 @@ -2,14 +2,31 @@ package me.topchetoeu.j2s.runtime.debug; import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.Filename; +import me.topchetoeu.j2s.common.Instruction; +import me.topchetoeu.j2s.common.Key; import me.topchetoeu.j2s.common.FunctionBody; import me.topchetoeu.j2s.common.FunctionMap; -import me.topchetoeu.j2s.common.Instruction; import me.topchetoeu.j2s.runtime.Frame; import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.values.Value; +import me.topchetoeu.j2s.runtime.values.functions.CodeFunction; +import me.topchetoeu.j2s.runtime.values.functions.FunctionValue; public interface DebugHandler { + public static final Key KEY = new Key<>(); + public static final Key IGNORE = new Key<>(); + + public static DebugHandler EMPTY = new DebugHandler() { + @Override public void onSourceLoad(Filename filename, String source) { } + @Override public void onFunctionLoad(FunctionBody body, FunctionMap map) { } + @Override public boolean onInstruction( + Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught + ) { return false; } + @Override public void onFramePush(Environment env, Frame frame) { } + @Override public void onFramePop(Environment env, Frame frame) { } + @Override public FunctionMap getMap(Environment env, FunctionBody body) { return null; } + }; + /** * Called when a script has been loaded * @param filename The name of the source @@ -17,17 +34,17 @@ public interface DebugHandler { * @param breakpoints A set of all the breakpointable locations in this source * @param map The source map associated with this file. null if this source map isn't mapped */ - void onSourceLoad(Filename filename, String source); + public void onSourceLoad(Filename filename, String source); /** * Called when a function body has been loaded * @param body The body loaded * @param map The map of the function */ - void onFunctionLoad(FunctionBody body, FunctionMap map); + public void onFunctionLoad(FunctionBody body, FunctionMap map); /** - * Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned. + * Called immediately before an instruction is executed, as well as after an instruction, if it has threw or returned. * This function might pause in order to await debugging commands. * @param env The context of execution * @param frame The frame in which execution is occuring @@ -37,7 +54,19 @@ public interface DebugHandler { * @param caught Whether or not the error has been caught * @return Whether or not the frame should restart (currently does nothing) */ - boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught); + public boolean onInstruction(Environment env, Frame frame, Instruction instruction, Value returnVal, EngineException error, boolean caught); + + /** + * Called immediately before an instruction is executed, as well as after an instruction, if it has threw or returned. + * This function might pause in order to await debugging commands. + * @param env The context of execution + * @param frame The frame in which execution is occuring + * @param instruction The instruction which was or will be executed + * @return Whether or not the frame should restart (currently does nothing) + */ + public default boolean onInstruction(Environment env, Frame frame, Instruction instruction) { + return onInstruction(env, frame, instruction, null, null, false); + } /** * Called immediatly before a frame has been pushed on the frame stack. @@ -45,12 +74,36 @@ public interface DebugHandler { * @param env The context of execution * @param frame The code frame which was pushed */ - void onFramePush(Environment env, Frame frame); + public void onFramePush(Environment env, Frame frame); /** * Called immediatly after a frame has been popped out of the frame stack. * This function might pause in order to await debugging commands. * @param env The context of execution * @param frame The code frame which was popped out */ - void onFramePop(Environment env, Frame frame); + public void onFramePop(Environment env, Frame frame); + + public FunctionMap getMap(Environment env, FunctionBody func); + public default FunctionMap getMap(Environment env, FunctionValue func) { + if (func instanceof CodeFunction codeFunc) return getMap(env, codeFunc.body); + else return null; + } + public default FunctionMap getMapOrEmpty(Environment env, FunctionBody func) { + var res = getMap(env, func); + if (res == null) return FunctionMap.EMPTY; + else return res; + } + public default FunctionMap getMapOrEmpty(Environment env, FunctionValue func) { + if (func instanceof CodeFunction codeFunc) return getMapOrEmpty(env, codeFunc.body); + else return null; + } + + public static DebugHandler get(Environment exts) { + if (enabled(exts)) return exts.get(KEY); + else return EMPTY; + } + + public static boolean enabled(Environment exts) { + return exts != null && exts.hasNotNull(KEY) && !exts.has(IGNORE); + } } diff --git a/runtime/src/main/java/me/topchetoeu/j2s/runtime/exceptions/EngineException.java b/runtime/src/main/java/me/topchetoeu/j2s/runtime/exceptions/EngineException.java index 978ed9e..36d7324 100644 --- a/runtime/src/main/java/me/topchetoeu/j2s/runtime/exceptions/EngineException.java +++ b/runtime/src/main/java/me/topchetoeu/j2s/runtime/exceptions/EngineException.java @@ -5,6 +5,7 @@ import java.util.List; import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.Location; +import me.topchetoeu.j2s.runtime.Frame; import me.topchetoeu.j2s.runtime.values.Value; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; import me.topchetoeu.j2s.runtime.values.objects.ObjectValue.PrototypeProvider; @@ -18,7 +19,7 @@ public class EngineException extends RuntimeException { public final Environment ext; public boolean visible() { - return ext == null || !ext.get(Value.HIDE_STACK, false); + return ext == null || !ext.get(Frame.HIDE_STACK, false); } public String toString() { if (name == null && location == null) return "(skipped)"; diff --git a/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/CodeFunction.java b/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/CodeFunction.java index 57e3ccf..8dbff7d 100644 --- a/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/CodeFunction.java +++ b/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/CodeFunction.java @@ -25,12 +25,12 @@ public final class CodeFunction extends FunctionValue { } } - @Override protected Value onApply(Environment ext, Value self, Value... args) { + @Override protected Value onApply(Environment env, Value self, Value... args) { var frame = new Frame(env, false, null, self, args, this); var res = onCall(frame); return res; } - @Override protected Value onConstruct(Environment ext, Value target, Value... args) { + @Override protected Value onConstruct(Environment env, Value target, Value... args) { var self = new ObjectValue(); var proto = target.getMember(env, "prototype"); diff --git a/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/FunctionValue.java b/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/FunctionValue.java index 8ed0ee6..1a1b073 100644 --- a/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/FunctionValue.java +++ b/runtime/src/main/java/me/topchetoeu/j2s/runtime/values/functions/FunctionValue.java @@ -6,7 +6,7 @@ import java.util.LinkedList; import java.util.List; import me.topchetoeu.j2s.common.Environment; -import me.topchetoeu.j2s.runtime.debug.DebugContext; +import me.topchetoeu.j2s.runtime.debug.DebugHandler; import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.values.KeyCache; import me.topchetoeu.j2s.runtime.values.Member; @@ -91,9 +91,9 @@ public abstract class FunctionValue extends ObjectValue { @Override public StringValue type() { return StringValue.of("function"); } @Override public List toReadableLines(Environment env, HashSet passed) { - var dbg = DebugContext.get(env); + var dbg = DebugHandler.get(env); var res = new StringBuilder(this.toString()); - var loc = dbg.getMapOrEmpty(this).start(); + var loc = dbg.getMapOrEmpty(env, this).first(); if (loc != null) res.append(" @ " + loc);