From 29813a599c2cbb1a7b393f708e28be0dac649516 Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Sun, 27 Sep 2020 12:13:20 +0200 Subject: [PATCH] Switch lua library to NLua and implement cancellation --- ChaosBot/ChaosBot.csproj | 2 +- .../Discord/Services/CustomCommandHandler.cs | 1 - .../IProgrammingLanguageInterpreter.cs | 4 +- .../LuaProgrammingLanguageInterpreter.cs | 68 ++++++++++--------- .../ProgrammingLanguageInterpreterFacade.cs | 13 ++-- 5 files changed, 46 insertions(+), 42 deletions(-) diff --git a/ChaosBot/ChaosBot.csproj b/ChaosBot/ChaosBot.csproj index c1612dd..82c074e 100644 --- a/ChaosBot/ChaosBot.csproj +++ b/ChaosBot/ChaosBot.csproj @@ -16,9 +16,9 @@ - + diff --git a/ChaosBot/Discord/Services/CustomCommandHandler.cs b/ChaosBot/Discord/Services/CustomCommandHandler.cs index b1e3bd3..da64597 100644 --- a/ChaosBot/Discord/Services/CustomCommandHandler.cs +++ b/ChaosBot/Discord/Services/CustomCommandHandler.cs @@ -6,7 +6,6 @@ using ChaosBot.Services; using ChaosBot.Services.ProgrammingLanguageInterpreter; using Discord; using Discord.Commands; -using Neo.IronLua; namespace ChaosBot.Discord.Services { diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs index f43d14c..ac8f6ff 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/IProgrammingLanguageInterpreter.cs @@ -1,10 +1,10 @@ +using System.Threading; using Discord.Commands; namespace ChaosBot.Services.ProgrammingLanguageInterpreter { public interface IProgrammingLanguageInterpreter { - string Interpret(SocketCommandContext context, string content, string command); - void StopExecution(); + string Interpret(CancellationToken ct, SocketCommandContext context, string content, string command); } } diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs index c87c056..bfdcb0a 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/LuaProgrammingLanguageInterpreter.cs @@ -1,48 +1,54 @@ -using System; using System.Linq; +using System.Reflection; using System.Text; -using System.Text.RegularExpressions; +using System.Threading; using Discord.Commands; -using Neo.IronLua; -using NLog; +using NLua; namespace ChaosBot.Services.ProgrammingLanguageInterpreter { internal class LuaProgrammingLanguageInterpreter : IProgrammingLanguageInterpreter { - private static readonly ILogger Logger = Program.GetLogger(); private readonly StringBuilder _outputBuilder = new StringBuilder(); - private Lua _lua; - - public string Interpret(SocketCommandContext context, string content, string command) + private CancellationToken _ct; + private Lua _state; + + public string Interpret(CancellationToken ct, SocketCommandContext context, string content, string command) { - using (_lua = new Lua()) + LoggingFacade.Debug($"Interpreting code for {command} using Lua"); + LoggingFacade.Trace($"Using CancellationToken: {ct}"); + _ct = ct; + LoggingFacade.Trace("Starting Lua interpreter"); + _state = new Lua(); + LoggingFacade.Trace("Setting Lua debug hook"); + _state.SetDebugHook(KeraLua.LuaHookMask.Line, 0); + _state.DebugHook += LuaDebugHook; + + MethodInfo printMethod = GetType().GetMethod("Print", BindingFlags.NonPublic | BindingFlags.Instance); + LoggingFacade.Info($"Overwriting print function with {printMethod}"); + _state["print"] = printMethod; + + LoggingFacade.Trace("Disabling Lua `import` function"); + _state.DoString ("\nimport = function () end\n"); + + LoggingFacade.Trace("Running user Lua code"); + _state.DoString(content, command); + + LoggingFacade.Trace("Returning Lua output"); + return _outputBuilder.ToString(); + } + + private void LuaDebugHook(object sender, NLua.Event.DebugHookEventArgs e) + { + if (_ct.IsCancellationRequested) { - // This needs to be dynamic if we want to call - // functions from within the lua environment - // This is a runtime type check - dynamic env = _lua.CreateEnvironment(); - - ParamsDelegate printDel = Print; - env.print = printDel; - - env.context = context; - - string code = content; - - env.dochunk(code, $"{command}.lua"); - - return _outputBuilder.ToString(); + Lua l = (Lua) sender; + l.State.Error("Timeout kill"); } } - public void StopExecution() - { - _lua.Dispose(); - } - - private delegate void ParamsDelegate(params object[] parameters); - + // This is being used in the `Interpret` function to register the print function + // ReSharper disable once UnusedMember.Local private void Print(params object[] parameters) { string str = string.Join(" ", from param in parameters select param == null ? string.Empty : param.ToString()); diff --git a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs index 94ede93..59f85ce 100644 --- a/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs +++ b/ChaosBot/Services/ProgrammingLanguageInterpreter/ProgrammingLanguageInterpreterFacade.cs @@ -21,21 +21,19 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter try { - Thread taskThread = null; + CancellationTokenSource tokenSource = new CancellationTokenSource(); Task task = Task.Run(() => { - taskThread = Thread.CurrentThread; Thread.CurrentThread.Priority = ThreadPriority.Lowest; - return interpreter.Interpret(context, customCommand.Content, customCommand.Command); - }); + return interpreter.Interpret(tokenSource.Token, context, customCommand.Content, customCommand.Command); + }, tokenSource.Token); - const int timeout = 1000; + const int timeout = 250; bool isTaskCompleted = task.Wait(TimeSpan.FromMilliseconds(timeout)); if (!isTaskCompleted) { - interpreter.StopExecution(); - taskThread.Abort(); + tokenSource.Cancel(); throw new TimeoutException($"Command '{customCommand.Command}' took more than {timeout}ms to run. It has been killed."); } @@ -49,6 +47,7 @@ namespace ChaosBot.Services.ProgrammingLanguageInterpreter } catch (Exception ex) { + LoggingFacade.Exception(ex); errorReason = $"There was an error with your code ({ex.GetType().Name}): {ex.Message}"; return false; }