move FunctionMap logic away from core

This commit is contained in:
2025-01-24 00:19:22 +02:00
parent eff076f6fe
commit 208444381e
12 changed files with 364 additions and 283 deletions

View File

@@ -1,113 +0,0 @@
package me.topchetoeu.j2s.runtime.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.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.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<DebugContext> KEY = new Key<>();
public static final Key<Void> IGNORE = new Key<>();
private HashMap<Filename, String> sources;
private WeakHashMap<FunctionBody, FunctionMap> maps;
private DebugHandler debugger;
public synchronized boolean attachDebugger(DebugHandler debugger) {
if (this.debugger != null) return false;
if (sources != null) {
for (var source : sources.entrySet()) debugger.onSourceLoad(source.getKey(), source.getValue());
}
if (maps != null) {
for (var map : maps.entrySet()) debugger.onFunctionLoad(map.getKey(), map.getValue());
}
this.debugger = debugger;
return true;
}
public boolean detachDebugger(DebugHandler debugger) {
if (this.debugger != debugger) return false;
return detachDebugger();
}
public boolean detachDebugger() {
this.debugger = null;
return true;
}
public DebugHandler debugger() {
return debugger;
}
public FunctionMap getMap(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);
}
public void onFramePush(Environment env, Frame frame) {
if (debugger != null) debugger.onFramePush(env, frame);
}
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) {
if (debugger != null) debugger.onSourceLoad(filename, source);
if (sources != null) sources.put(filename, source);
}
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) {
if (enabled) {
sources = new HashMap<>();
maps = new WeakHashMap<>();
}
}
public DebugContext() {
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);
}
}

View File

@@ -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<DebugHandler> KEY = new Key<>();
public static final Key<Void> 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);
}
}

View File

@@ -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)";

View File

@@ -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");

View File

@@ -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<String> toReadableLines(Environment env, HashSet<ObjectValue> 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);