Debugging Lua scripts in VS Code using MoonSharp

Using MoonSharp as Lua interpreter in a .NET application is quite easy, but the docs lack of a good explanation on how to debug the Lua scripts.

MoonSharp provides a Visual Studio Code extension and a .NET package for a debug server.

I’m going to show how to implement the server using a simple .NET Core Console application.

I won’t explain much about Lua or MoonSharp itself, so I expect you have at least some knowledge on how to use MoonSharp.


You can find my demo application on GitHub.

Preparing the environment

For using MoonSharp, we need to install these NuGet packages:

  • MoonSharp
  • MoonSharp.Debugger.VsCode

In VS Code you have to install the MoonSharp Debug extension. Make sure to use the latest VS Code version – in an older version the current line in the debugger wasn’t highlighted.

In the launch.json, you have to put the debugServer object inside the configuration. The official documentation is wrong here.

Loading the script

For this demo, I added just one simple global function WriteToConsole which outputs the argument and returns the character count.

var script = new Script();
script.Globals["WriteToConsole"] = new Func(text => 
{ 
 Console.WriteLine(text); 
 return text.Length; 
});

Starting the server and waiting for the client to attach

After the script is loaded, we can start the debugger server, wait for the client to attach and then run the script’s main function. The code to actually wait for the client to attach is a method called AwaitDebuggerAttach.

MoonSharpVsCodeDebugServer server = new MoonSharpVsCodeDebugServer();
server.Start();
server.AttachToScript(script, "DebugScript");

// read script
string scriptData = File.ReadAllText(path);
script.DoString(scriptData, null, path);
            
// wait for debugger to attach
bool attached = AwaitDebuggerAttach(server);
if (!attached) 
{
 Console.WriteLine("VS Code debugger did not attach. Running the script.");
}

// run script's main function
script.Call(script.Globals["main"]);

Unfortunately the DebugServer doesn't support this natively, so we have to wait in a loop until a client has attached. Even the check if someone has attached isn't in the public api - we have to use reflection.

private bool AwaitDebuggerAttach(this MoonSharpVsCodeDebugServer server)
{
   // as soon as a client has attached, 'm_Client__' field of 'm_Current' isn't null anymore
   // 
   // we wait for ~60 seconds for a client to attach
   BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.NonPublic;
   FieldInfo field = server.GetType().GetField("m_Current", bindFlags);
   object current = field.GetValue(server);

   FieldInfo property = current.GetType().GetField("m_Client__", bindFlags);

   Stopwatch stopwatch = new Stopwatch();
   stopwatch.Start();
   Console.WriteLine("Waiting for VS Code debugger to attach");
   while (property.GetValue(current) == null)
   {
      Thread.Sleep(500);
      if (stopwatch.Elapsed.TotalSeconds > 60) return false;
   }
   stopwatch.Stop();
   Console.WriteLine("VS Code debugger attached");
   return true;
}

Running the application

When running the application, you have to input the path to the Lua file you want to debug. Next, the application will wait for the client to attach, so switch to VS Code where you have this Lua file open, add a breakpoint and start the MoonSharp Attach debugger. The debugger will stop at the breakpoint, and when stepping over the statement you can see the Hello World output in the console window and the local variable has the value 11 (the character count).

You have to have at least 2 lines of code inside the function for the debugger to break!


image


image

Comments

Popular posts from this blog

Testing the response body of middleware in ASP.NET Core

Creating a basic MVC 6 web application with Entity Framework 7 and xUnit - Part 2