Skip to content

Commit fb7d440

Browse files
committed
Add docs and final touches
1 parent 8398512 commit fb7d440

File tree

4 files changed

+245
-40
lines changed

4 files changed

+245
-40
lines changed

src/main/java/com/laytonsmith/core/Main.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,16 @@ public ArgumentParser getArgumentParser() {
11141114
.setDescription("Convenience shorthand that sets --debug-security NONE"
11151115
+ " and --debug-bind-address 0.0.0.0. For quick testing only.")
11161116
.asFlag()
1117-
.setName("debug-insecure"));
1117+
.setName("debug-insecure"))
1118+
.addArgument(new ArgumentBuilder()
1119+
.setDescription("The threading mode for the debug server."
1120+
+ " sync: blocks the executing thread at breakpoints."
1121+
+ " async: snapshots state without blocking."
1122+
+ " Defaults to sync.")
1123+
.setUsageName("mode")
1124+
.setOptional()
1125+
.setName("debug-threading-mode")
1126+
.setArgType(ArgumentBuilder.BuilderTypeNonFlag.STRING));
11181127
}
11191128

11201129
@Override
@@ -1130,6 +1139,7 @@ public void execute(ArgumentParser.ArgumentParserResults parsedArgs) throws Exce
11301139
boolean debugInsecure = parsedArgs.isFlagSet("debug-insecure");
11311140
String debugSecurityStr = parsedArgs.getStringArgument("debug-security");
11321141
String debugBindAddress = parsedArgs.getStringArgument("debug-bind-address");
1142+
String debugThreadingModeStr = parsedArgs.getStringArgument("debug-threading-mode");
11331143

11341144
// --debug-insecure changes defaults but explicit values take priority
11351145
String defaultSecurity = debugInsecure
@@ -1173,12 +1183,16 @@ public void execute(ArgumentParser.ArgumentParserResults parsedArgs) throws Exce
11731183
MSDebugServer debugServer = null;
11741184
if(debug) {
11751185
int port = debugPort == 0 ? MSDebugServer.DEFAULT_PORT : debugPort;
1186+
DebugContext.ThreadingMode threadingMode = DebugContext.ThreadingMode.SYNCHRONOUS;
1187+
if("async".equals(debugThreadingModeStr)) {
1188+
threadingMode = DebugContext.ThreadingMode.ASYNCHRONOUS;
1189+
}
11761190
debugServer = new MSDebugServer();
11771191
File authorizedKeysFile = debugSecurity == DebugSecurity.KEYPAIR
11781192
? MethodScriptFileLocations.getDefault().getAuthorizedDebugKeysFile()
11791193
: null;
11801194
env = debugServer.startListening(port, debugBindAddress, env,
1181-
debugSuspend, DebugContext.ThreadingMode.SYNCHRONOUS,
1195+
debugSuspend, threadingMode,
11821196
debugSecurity, authorizedKeysFile);
11831197
debugServer.awaitConfiguration();
11841198
}

src/main/java/com/laytonsmith/core/environments/DebugContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ public void awaitReconnect() {
292292
*/
293293
public void reconnect() {
294294
synchronized(reconnectMonitor) {
295+
disconnected = false;
295296
awaitingReconnect = false;
296297
awaitingInitialConnection = false;
297298
reconnectMonitor.notifyAll();

src/main/java/com/laytonsmith/tools/debugger/MSDebugServer.java

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.laytonsmith.core.environments.RuntimeMode;
1919
import com.laytonsmith.core.environments.StaticRuntimeEnv;
2020
import com.laytonsmith.core.exceptions.StackTraceFrame;
21+
import com.laytonsmith.core.constructs.CArray;
2122
import com.laytonsmith.core.natives.interfaces.Mixed;
2223
import com.laytonsmith.core.environments.Breakpoint;
2324
import org.eclipse.lsp4j.debug.Capabilities;
@@ -111,6 +112,7 @@ public class MSDebugServer implements IDebugProtocolServer {
111112
public static final int DEFAULT_PORT = 6732;
112113

113114
private static final int LOCALS_REF = 1;
115+
private static final int COMPOUND_REF_START = 100;
114116

115117
/** Default bind address for the DAP TCP listener. */
116118
public static final String DEFAULT_BIND_ADDRESS = "127.0.0.1";
@@ -137,6 +139,8 @@ public class MSDebugServer implements IDebugProtocolServer {
137139
private DebugSecurity securityMode = DebugSecurity.KEYPAIR;
138140
private DebugAuthenticator authenticator;
139141
private SSLContext sslContext;
142+
private final ConcurrentHashMap<Integer, Mixed> compoundVariableRefs = new ConcurrentHashMap<>();
143+
private int nextCompoundRef = COMPOUND_REF_START;
140144

141145
/**
142146
* Starts a DAP TCP listener on the given port with an explicit threading mode.
@@ -593,7 +597,10 @@ public CompletableFuture<Void> configurationDone(ConfigurationDoneArguments args
593597
@Override
594598
public CompletableFuture<Void> disconnect(DisconnectArguments args) {
595599
if(debugCtx != null) {
596-
if(suspendOnStart && !scriptCompleted) {
600+
if(managedExecution) {
601+
// Embedded mode: the server stays alive for reconnection.
602+
// Don't mark as disconnected, just release any paused threads.
603+
} else if(suspendOnStart && !scriptCompleted) {
597604
// Suspend mode: pause execution until a new debugger connects.
598605
// awaitReconnect() releases paused threads so they can block
599606
// in shouldPause() on the reconnect monitor.
@@ -866,27 +873,57 @@ public CompletableFuture<ScopesResponse> scopes(ScopesArguments args) {
866873
public CompletableFuture<VariablesResponse> variables(VariablesArguments args) {
867874
VariablesResponse response = new VariablesResponse();
868875
PausedState inspected = lastInspectedState;
869-
if(inspected == null || args.getVariablesReference() != LOCALS_REF) {
876+
int ref = args.getVariablesReference();
877+
878+
if(inspected == null) {
870879
response.setVariables(new Variable[0]);
871880
return CompletableFuture.completedFuture(response);
872881
}
873882

874-
Map<String, Mixed> vars = inspected.getVariables();
875-
List<Variable> result = new ArrayList<>();
876883
Environment stateEnv = inspected.getEnvironment();
877-
for(Map.Entry<String, Mixed> entry : vars.entrySet()) {
878-
Variable v = new Variable();
879-
v.setName(entry.getKey());
880-
v.setValue(entry.getValue().val());
881-
v.setType(entry.getValue().typeof(stateEnv).val());
882-
v.setVariablesReference(0);
883-
result.add(v);
884+
List<Variable> result = new ArrayList<>();
885+
886+
if(ref == LOCALS_REF) {
887+
compoundVariableRefs.clear();
888+
nextCompoundRef = COMPOUND_REF_START;
889+
Map<String, Mixed> vars = inspected.getVariables();
890+
for(Map.Entry<String, Mixed> entry : vars.entrySet()) {
891+
result.add(buildVariable(entry.getKey(), entry.getValue(), stateEnv));
892+
}
893+
} else if(compoundVariableRefs.containsKey(ref)) {
894+
Mixed compound = compoundVariableRefs.get(ref);
895+
if(compound instanceof CArray arr) {
896+
for(Mixed key : arr.keySet()) {
897+
Mixed value = arr.get(key, Target.UNKNOWN);
898+
result.add(buildVariable(key.val(), value, stateEnv));
899+
}
900+
}
884901
}
885902

886903
response.setVariables(result.toArray(new Variable[0]));
887904
return CompletableFuture.completedFuture(response);
888905
}
889906

907+
private Variable buildVariable(String name, Mixed value, Environment stateEnv) {
908+
Variable v = new Variable();
909+
v.setName(name);
910+
v.setType(value.typeof(stateEnv).val());
911+
if(value instanceof CArray arr) {
912+
int refId = nextCompoundRef++;
913+
compoundVariableRefs.put(refId, value);
914+
v.setVariablesReference(refId);
915+
if(arr.inAssociativeMode()) {
916+
v.setValue("array (associative, length: " + arr.size(stateEnv) + ")");
917+
} else {
918+
v.setValue("array (length: " + arr.size(stateEnv) + ")");
919+
}
920+
} else {
921+
v.setVariablesReference(0);
922+
v.setValue(value.val());
923+
}
924+
return v;
925+
}
926+
890927
@Override
891928
public CompletableFuture<EvaluateResponse> evaluate(EvaluateArguments args) {
892929
EvaluateResponse response = new EvaluateResponse();

0 commit comments

Comments
 (0)