Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@
<artifactId>org.eclipse.lsp4j</artifactId>
<version>0.22.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.lsp4j</groupId>
<artifactId>org.eclipse.lsp4j.debug</artifactId>
<version>0.22.0</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
Expand Down Expand Up @@ -569,6 +574,8 @@
<include>org.brotli:dec:jar:*</include>
<include>org.eclipse.lsp4j:org.eclipse.lsp4j:jar:*</include>
<include>org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:jar:*</include>
<include>org.eclipse.lsp4j:org.eclipse.lsp4j.debug:jar:*</include>
<include>org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc.debug:jar:*</include>
<!-- We don't currently actually depend on these 2, but lsp4j does, so we
need to shade it in. In the future, we may rip out org.json:json,
and replace it with this, however, in which case we only need to
Expand Down Expand Up @@ -842,6 +849,24 @@
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.eclipse.lsp4j:org.eclipse.lsp4j.debug:jar:*</artifact>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc.debug:jar:*</artifact>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>com.google.code.gson:gson:jar:*</artifact>
<includes>
Expand Down
80 changes: 70 additions & 10 deletions src/main/java/com/laytonsmith/PureUtilities/DaemonManager.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.laytonsmith.PureUtilities;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
Expand All @@ -10,24 +12,79 @@
*/
public class DaemonManager {

/**
* Listener interface for thread lifecycle events.
*/
public interface ThreadLifecycleListener {
/**
* Called when a thread is activated (registered as active).
* @param t The thread that was activated
* @param displayName A user-facing display name for the thread, or null
* if no explicit name was provided (in which case the thread's Java
* name should be used as a fallback).
*/
void onActivated(Thread t, String displayName);

/**
* Called when a thread is deactivated (no longer active).
* @param t The thread that was deactivated
*/
void onDeactivated(Thread t);
}

private final Object lock = new Object();
private final Set<Thread> threads = new HashSet<>();
private final List<ThreadLifecycleListener> listeners = new ArrayList<>();
private int count = 0;

/**
* Adds a listener that will be notified when threads are activated or deactivated.
* @param listener The listener to add
*/
public void addThreadLifecycleListener(ThreadLifecycleListener listener) {
synchronized(lock) {
listeners.add(listener);
}
}

/**
* Removes a previously added lifecycle listener.
* @param listener The listener to remove
*/
public void removeThreadLifecycleListener(ThreadLifecycleListener listener) {
synchronized(lock) {
listeners.remove(listener);
}
}

/**
* Sets a thread to "daemon" mode, meaning it is currently active. Null may be sent, in which case the current
* thread is used. You should always put a deactivateThread call for every activateThread call.
*
* @param t The thread to activate
*/
public void activateThread(Thread t) {
activateThread(t, null);
}

/**
* Sets a thread to "daemon" mode with an explicit display name. Null may be sent for the thread,
* in which case the current thread is used.
*
* @param t The thread to activate
* @param displayName A user-facing name for the thread, or null to use the Java thread name
*/
public void activateThread(Thread t, String displayName) {
Thread resolved;
List<ThreadLifecycleListener> snapshot;
synchronized(lock) {
if(t != null) {
threads.add(t);
} else {
threads.add(Thread.currentThread());
}
resolved = t != null ? t : Thread.currentThread();
threads.add(resolved);
++count;
snapshot = new ArrayList<>(listeners);
}
for(ThreadLifecycleListener listener : snapshot) {
listener.onActivated(resolved, displayName);
}
}

Expand All @@ -37,14 +94,17 @@ public void activateThread(Thread t) {
* @param t The thread to deactivate
*/
public void deactivateThread(Thread t) {
Thread resolved;
List<ThreadLifecycleListener> snapshot;
synchronized(lock) {
if(t != null) {
threads.remove(t);
} else {
threads.remove(Thread.currentThread());
}
resolved = t != null ? t : Thread.currentThread();
threads.remove(resolved);
--count;
lock.notify();
snapshot = new ArrayList<>(listeners);
}
for(ThreadLifecycleListener listener : snapshot) {
listener.onDeactivated(resolved);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/laytonsmith/core/CallbackYield.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private void cleanupCurrentStep(CallbackState state, Environment env) {
if(step != null) {
if(step.preparedEnv != null) {
// Pop the stack trace element that prepareExecution pushed
step.preparedEnv.getEnv(GlobalEnv.class).GetStackTraceManager().popStackTraceElement();
step.preparedEnv.getEnv(GlobalEnv.class).GetStackTraceManager().popStackTraceFrame();
step.preparedEnv = null;
}
if(step.cleanupAction != null) {
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/laytonsmith/core/FlowFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.laytonsmith.core.StepAction.StepResult;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import com.laytonsmith.core.natives.interfaces.Mixed;
import com.laytonsmith.core.environments.Environment;

Expand Down Expand Up @@ -94,4 +95,21 @@ default StepResult<S> childInterrupted(Target t, S state, StepAction.FlowControl
*/
default void cleanup(Target t, S state, Environment env) {
}

/**
* Returns whether the given exception would be caught by this function,
* given the current per-call state. The default returns false. Override in
* exception-catching functions (e.g. try/catch) to inspect the state and
* determine if the exception matches a catch clause.
*
* <p>This is used by the debugger to determine if an exception is "uncaught"
* by inspecting the eval stack without actually propagating the exception.</p>
*
* @param state The per-call state from the stack frame
* @param exception The exception to check
* @return true if this function would catch the exception in its current state
*/
default boolean wouldCatch(S state, ConfigRuntimeException exception) {
return false;
}
}
82 changes: 76 additions & 6 deletions src/main/java/com/laytonsmith/core/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.laytonsmith.tools.ProfilerSummary;
import com.laytonsmith.tools.SyntaxHighlighters;
import com.laytonsmith.tools.UILauncher;
import com.laytonsmith.tools.debugger.MSDebugServer;
import com.laytonsmith.tools.docgen.DocGen;
import com.laytonsmith.tools.docgen.DocGenExportTool;
import com.laytonsmith.tools.docgen.DocGenTemplates;
Expand Down Expand Up @@ -764,6 +765,9 @@ public void execute(ArgumentParser.ArgumentParserResults parsedArgs) throws Exce
modules = modules.replaceAll("(.*)\n", "--add-opens $1=ALL-UNNAMED ");
args += " " + modules;
}
if(JavaVersion.GetMajorVersion() >= 16) {
args += "--enable-native-access=ALL-UNNAMED ";
}
args += "-Xrs ";
StreamUtils.GetSystemOut().println(args.trim());
}
Expand Down Expand Up @@ -1072,21 +1076,58 @@ public ArgumentParser getArgumentParser() {
.addArgument(new ArgumentBuilder()
.setDescription("The code to run")
.setUsageName("methodscript code")
.setRequiredAndDefault());
.setRequiredAndDefault())
.addArgument(new ArgumentBuilder()
.setDescription("Enable the DAP debug server.")
.asFlag()
.setName("debug"))
.addArgument(new ArgumentBuilder()
.setDescription("The port for the debug server. Defaults to "
+ MSDebugServer.DEFAULT_PORT + ".")
.setUsageName("port")
.setOptional()
.setName("debug-port")
.setArgType(ArgumentBuilder.BuilderTypeNonFlag.NUMBER))
.addArgument(new ArgumentBuilder()
.setDescription("Wait for the debugger to connect before executing.")
.asFlag()
.setName("debug-suspend"));
}

@Override
public void execute(ArgumentParser.ArgumentParserResults parsedArgs) throws Exception {
ClassDiscovery.getDefaultInstance().addThisJar();

boolean debug = parsedArgs.isFlagSet("debug");
int debugPort = 0;
if(parsedArgs.getNumberArgument("debug-port") != null) {
debugPort = parsedArgs.getNumberArgument("debug-port").intValue();
}
boolean debugSuspend = parsedArgs.isFlagSet("debug-suspend");

String script = parsedArgs.getStringArgument();
File file = new File("Interpreter");
Environment env = Static.GenerateStandaloneEnvironment(true,
EnumSet.of(RuntimeMode.CMDLINE, RuntimeMode.INTERPRETER));
Set<Class<? extends Environment.EnvironmentImpl>> envs = Environment.getDefaultEnvClasses();
MethodScriptCompiler.execute(script, file, true, env, envs, (s) -> {
System.out.println(s);
}, null, null);

MSDebugServer debugServer = null;
if(debug) {
int port = debugPort == 0 ? MSDebugServer.DEFAULT_PORT : debugPort;
debugServer = new MSDebugServer();
env = debugServer.startListening(port, env, debugSuspend);
debugServer.awaitConfiguration();
}

try {
MethodScriptCompiler.execute(script, file, true, env, envs, (s) -> {
System.out.println(s);
}, null, null);
} finally {
if(debugServer != null) {
debugServer.shutdown();
}
}
}
}

Expand Down Expand Up @@ -1218,15 +1259,44 @@ public void execute(ArgumentParser.ArgumentParserResults parsedArgs) throws Exce
//We actually can't use the parsedArgs, because there may be cmdline switches in
//the arguments that we want to ignore here, but otherwise pass through. parsedArgs
//will prevent us from seeing those, however.
List<String> allArgs = parsedArgs.getRawArguments();
List<String> allArgs = new ArrayList<>(parsedArgs.getRawArguments());
if(allArgs.isEmpty()) {
StreamUtils.GetSystemErr().println("Usage: path/to/file.ms [arg1 arg2]");
System.exit(1);
}

boolean debug = false;
int debugPort = 0;
boolean debugSuspend = false;
String debugThreadingMode = null;
for(java.util.Iterator<String> it = allArgs.iterator(); it.hasNext();) {
String arg = it.next();
if("--debug".equals(arg)) {
debug = true;
it.remove();
} else if("--debug-port".equals(arg)) {
it.remove();
if(it.hasNext()) {
debugPort = Integer.parseInt(it.next());
it.remove();
}
} else if("--debug-suspend".equals(arg)) {
debugSuspend = true;
it.remove();
} else if("--debug-threading-mode".equals(arg)) {
it.remove();
if(it.hasNext()) {
debugThreadingMode = it.next();
it.remove();
}
}
}

String fileName = allArgs.get(0);
allArgs.remove(0);
try {
Interpreter.startWithTTY(fileName, allArgs);
Interpreter.startWithTTY(fileName, allArgs, true,
debug, debugPort, debugSuspend, debugThreadingMode);
} catch (Profiles.InvalidProfileException ex) {
StreamUtils.GetSystemErr().println("Invalid profile file at " + MethodScriptFileLocations.getDefault()
.getProfilesFile()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3064,7 +3064,7 @@ public static Mixed execute(ParseTree root, Environment env, MethodScriptComplet
}
result = script.eval(root, env);
}
if(done != null) {
if(done != null && !Script.isDebuggerPaused(result)) {
done.done(result.val().trim());
}
return result;
Expand Down
Loading
Loading