diff --git a/src/DevTools/PerformanceImpact-Async-Fork/App.config b/src/DevTools/PerformanceImpact-Async-Fork/App.config new file mode 100644 index 00000000..bae5d6d8 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Fork/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/DevTools/PerformanceImpact-Async-Fork/PerformanceImpact-Async-Fork.csproj b/src/DevTools/PerformanceImpact-Async-Fork/PerformanceImpact-Async-Fork.csproj new file mode 100644 index 00000000..708a3c26 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Fork/PerformanceImpact-Async-Fork.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {F7E4153A-B2DA-47D0-BE67-DABC54DB6670} + Exe + PerformanceImpact_Async_Fork + PerformanceImpact_Async_Fork + v4.6.1 + 512 + true + true + + + + + AnyCPU + true + full + false + bin\Debug\ + TRACE;DEBUG;FORK + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;FORK + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + {49f32476-fca0-45fd-8f89-0c7c0d15e409} + MoonSharp.Interpreter.portable40 + + + + \ No newline at end of file diff --git a/src/DevTools/PerformanceImpact-Async-Fork/Program.cs b/src/DevTools/PerformanceImpact-Async-Fork/Program.cs new file mode 100644 index 00000000..9ea4936f --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Fork/Program.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using MoonSharp.Interpreter; +using MoonSharp.Interpreter.Diagnostics; + +namespace PerformanceImpact_Async +{ + class Program + { + static void Main(string[] args) + { + var code = @" + function move(n, src, dst, via) + if n > 0 then + move(n - 1, src, via, dst) + --check(src, 'to', dst) + move(n - 1, via, dst, src) + end + end + + function run_test() + for i = 1, 15000 do + move(4, 1, 2, 3) + end + end + "; + + Script.WarmUp(); + + var S = new Script(); + + S.DoString(code); + + long totalTime = 0; + long i = 0; + + var run_testFunc = S.Globals.Get("run_test"); + +#if FORK + var ecToken = new ExecutionControlToken(); +#endif + + while (true) + { + S.PerformanceStats.Enabled = true; + +#if FORK + S.CallAsync(ecToken, run_testFunc).Wait(); +#else + S.CallAsync(run_testFunc).Wait(); +#endif + + // Get current average + totalTime += S.PerformanceStats.GetPerformanceCounterResult(PerformanceCounter.Execution).Counter; + + ++i; + + Console.WriteLine("Current average: {0}", totalTime / i); + + //Thread.Sleep(20); + + S.PerformanceStats.Enabled = false; + } + } + } +} diff --git a/src/DevTools/PerformanceImpact-Async-Fork/Properties/AssemblyInfo.cs b/src/DevTools/PerformanceImpact-Async-Fork/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b15694a2 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Fork/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PerformanceImpact-Async-Original")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PerformanceImpact-Async-Original")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a7a642d3-2efa-4eaa-9762-6dc9e0c883a1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/DevTools/PerformanceImpact-Async-Fork/packages.config b/src/DevTools/PerformanceImpact-Async-Fork/packages.config new file mode 100644 index 00000000..52a7d804 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Fork/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/DevTools/PerformanceImpact-Async-Original/App.config b/src/DevTools/PerformanceImpact-Async-Original/App.config new file mode 100644 index 00000000..bae5d6d8 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Original/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/DevTools/PerformanceImpact-Async-Original/PerformanceImpact-Async-Original.csproj b/src/DevTools/PerformanceImpact-Async-Original/PerformanceImpact-Async-Original.csproj new file mode 100644 index 00000000..4efbb193 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Original/PerformanceImpact-Async-Original.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {A7A642D3-2EFA-4EAA-9762-6DC9E0C883A1} + Exe + PerformanceImpact_Async_Original + PerformanceImpact_Async_Original + v4.6.1 + 512 + true + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + ..\..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DevTools/PerformanceImpact-Async-Original/Program.cs b/src/DevTools/PerformanceImpact-Async-Original/Program.cs new file mode 100644 index 00000000..9ea4936f --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Original/Program.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using MoonSharp.Interpreter; +using MoonSharp.Interpreter.Diagnostics; + +namespace PerformanceImpact_Async +{ + class Program + { + static void Main(string[] args) + { + var code = @" + function move(n, src, dst, via) + if n > 0 then + move(n - 1, src, via, dst) + --check(src, 'to', dst) + move(n - 1, via, dst, src) + end + end + + function run_test() + for i = 1, 15000 do + move(4, 1, 2, 3) + end + end + "; + + Script.WarmUp(); + + var S = new Script(); + + S.DoString(code); + + long totalTime = 0; + long i = 0; + + var run_testFunc = S.Globals.Get("run_test"); + +#if FORK + var ecToken = new ExecutionControlToken(); +#endif + + while (true) + { + S.PerformanceStats.Enabled = true; + +#if FORK + S.CallAsync(ecToken, run_testFunc).Wait(); +#else + S.CallAsync(run_testFunc).Wait(); +#endif + + // Get current average + totalTime += S.PerformanceStats.GetPerformanceCounterResult(PerformanceCounter.Execution).Counter; + + ++i; + + Console.WriteLine("Current average: {0}", totalTime / i); + + //Thread.Sleep(20); + + S.PerformanceStats.Enabled = false; + } + } + } +} diff --git a/src/DevTools/PerformanceImpact-Async-Original/Properties/AssemblyInfo.cs b/src/DevTools/PerformanceImpact-Async-Original/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..b15694a2 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Original/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PerformanceImpact-Async-Original")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PerformanceImpact-Async-Original")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a7a642d3-2efa-4eaa-9762-6dc9e0c883a1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/DevTools/PerformanceImpact-Async-Original/packages.config b/src/DevTools/PerformanceImpact-Async-Original/packages.config new file mode 100644 index 00000000..52a7d804 --- /dev/null +++ b/src/DevTools/PerformanceImpact-Async-Original/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/MoonSharp.Interpreter.Tests/EndToEnd/AsyncTests.cs b/src/MoonSharp.Interpreter.Tests/EndToEnd/AsyncTests.cs new file mode 100644 index 00000000..2e5a7f55 --- /dev/null +++ b/src/MoonSharp.Interpreter.Tests/EndToEnd/AsyncTests.cs @@ -0,0 +1,141 @@ +#if HASDYNAMIC +using System; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace MoonSharp.Interpreter.Tests.EndToEnd +{ + [TestFixture] + public class AsyncTests + { + [Test] + public void ThreadPausedIfRunAsync() + { + Script S = new Script(); + + S.Globals.Get("os").Table["sleep"] = (Action)((ctx, args) => + { + ctx.PauseExecution(TimeSpan.FromSeconds(2)); + }); + + string code = @" + local timeStarted = os.clock() + os.sleep() + local timeEnded = os.clock() + + return timeEnded - timeStarted > 2"; + + var ecToken = new ExecutionControlToken(); + Assert.IsTrue(S.DoStringAsync(ecToken, code).Result.CastToBool()); + } + + [Test] + public void ThreadPausedIfRunSync() + { + Script S = new Script(); + + S.Globals.Get("os").Table["sleep"] = (Action)((ctx, args) => + { + ctx.PauseExecution(TimeSpan.FromSeconds(2)); + }); + + string code = @" + local timeStarted = os.clock() + os.sleep() + local timeEnded = os.clock() + + return timeEnded - timeStarted > 2"; + + Assert.IsTrue(S.DoString(code).CastToBool()); + } + + [Test] + public void ClrFunctionExecutionCanBeCancelledIfPausedWhenRunAsync() + { + Script S = new Script(); + + S.Globals.Set("pause", DynValue.NewCallback((ctx, args) => + { + ctx.PauseExecution(TimeSpan.FromSeconds(20)); + return DynValue.NewNil(); + })); + + var ecToken = new ExecutionControlToken(); + + Task t = S.CallAsync(ecToken, S.Globals.Get("pause")); + + t.ContinueWith(_ => Assert.Pass(), TaskContinuationOptions.OnlyOnCanceled); + t.ContinueWith(t_ => { if (t_.Exception.InnerExceptions.Count != 1 || !(t_.Exception.InnerException.InnerException is ScriptTerminationRequestedException)) { Assert.Fail("task faulted"); } }, TaskContinuationOptions.OnlyOnFaulted); + t.ContinueWith(_ => Assert.Fail("task didn't abort"), TaskContinuationOptions.OnlyOnRanToCompletion); + + Thread.Sleep(500); + ecToken.Terminate(); + + while (t.Status != TaskStatus.Canceled && t.Status != TaskStatus.Faulted && t.Status != TaskStatus.RanToCompletion) { } + } + + [Test] + public void LuaCodeExecutionCanBeCancelledIfPausedWhenRunAsync() + { + Script S = new Script(); + + S.Globals.Set("pause", DynValue.NewCallback((ctx, args) => + { + ctx.PauseExecution(TimeSpan.FromSeconds(20)); + return DynValue.NewNil(); + })); + + var ecToken = new ExecutionControlToken(); + + Task t = S.DoStringAsync(ecToken, "pause()"); + + t.ContinueWith(_ => Assert.Pass(), TaskContinuationOptions.OnlyOnCanceled); + t.ContinueWith(t_ => { if (t_.Exception.InnerExceptions.Count != 1 || !(t_.Exception.InnerException.InnerException is ScriptTerminationRequestedException)) { Assert.Fail("task faulted"); } }, TaskContinuationOptions.OnlyOnFaulted); + t.ContinueWith(_ => Assert.Fail("task didn't abort"), TaskContinuationOptions.OnlyOnRanToCompletion); + + Thread.Sleep(500); + ecToken.Terminate(); + + while (t.Status != TaskStatus.Canceled && t.Status != TaskStatus.Faulted && t.Status != TaskStatus.RanToCompletion) { } + } + + [Test] + public void ExecutionControlTokenCanBeAssociatedWithMultipleScriptsSimultaneously() + { + var callback = DynValue.NewCallback((ctx, args) => + { + ctx.PauseExecution(TimeSpan.FromSeconds(20)); + return DynValue.NewNil(); + }); + + Script S1 = new Script(); + + S1.Globals.Set("pause", callback); + + Script S2 = new Script(); + + S2.Globals.Set("pause", callback); + + var ecToken = new ExecutionControlToken(); + + Task t1 = S1.DoStringAsync(ecToken, "pause()"); + Task t2 = S2.DoStringAsync(ecToken, "pause()"); + + t1.ContinueWith(t => { foreach (var e in t.Exception.InnerExceptions) { Console.WriteLine(e.InnerException.Message); } }, TaskContinuationOptions.NotOnRanToCompletion); + t1.ContinueWith(t => Assert.IsTrue(t1.IsCanceled, "t1 is canceled"), TaskContinuationOptions.OnlyOnCanceled); + t1.ContinueWith(t => Assert.Fail(), TaskContinuationOptions.OnlyOnRanToCompletion); + + t2.ContinueWith(t => { foreach (var e in t.Exception.InnerExceptions) { Console.WriteLine(e.InnerException.Message); } }, TaskContinuationOptions.NotOnRanToCompletion); + t2.ContinueWith(t => Assert.IsTrue(t2.IsCanceled, "t2 is canceled"), TaskContinuationOptions.OnlyOnCanceled); + t2.ContinueWith(t => Assert.Fail(), TaskContinuationOptions.OnlyOnRanToCompletion); + + Thread.Sleep(500); + ecToken.Terminate(); + + while ((t1.Status != TaskStatus.Canceled && t1.Status != TaskStatus.Faulted && t1.Status != TaskStatus.RanToCompletion) || + (t2.Status != TaskStatus.Canceled && t2.Status != TaskStatus.Faulted && t2.Status != TaskStatus.RanToCompletion)) { } + } + } +} +#endif \ No newline at end of file diff --git a/src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataMethodsTests.cs b/src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataMethodsTests.cs index 09d4ebfc..d4f81881 100755 --- a/src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataMethodsTests.cs +++ b/src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataMethodsTests.cs @@ -291,12 +291,12 @@ public Type Type get { return typeof(SomeOtherClassCustomDescriptor); } } - public DynValue Index(Script script, object obj, DynValue index, bool dummy) + public DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool dummy) { return DynValue.NewNumber(index.Number * 4); } - public bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool dummy) + public bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool dummy) { throw new NotImplementedException(); } @@ -306,7 +306,7 @@ public string AsString(object obj) return null; } - public DynValue MetaIndex(Script script, object obj, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { return null; } @@ -322,17 +322,17 @@ public bool IsTypeCompatible(Type type, object obj) public class SelfDescribingClass : IUserDataType { - public DynValue Index(Script script, DynValue index, bool isNameIndex) + public DynValue Index(ExecutionControlToken ecToken, Script script, DynValue index, bool isNameIndex) { return DynValue.NewNumber(index.Number * 3); } - public bool SetIndex(Script script, DynValue index, DynValue value, bool isNameIndex) + public bool SetIndex(ExecutionControlToken ecToken, Script script, DynValue index, DynValue value, bool isNameIndex) { throw new NotImplementedException(); } - public DynValue MetaIndex(Script script, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, string metaname) { throw new NotImplementedException(); } diff --git a/src/MoonSharp.Interpreter.Tests/EndToEnd/VtUserDataMethodsTests.cs b/src/MoonSharp.Interpreter.Tests/EndToEnd/VtUserDataMethodsTests.cs index 856e2d16..73280c82 100755 --- a/src/MoonSharp.Interpreter.Tests/EndToEnd/VtUserDataMethodsTests.cs +++ b/src/MoonSharp.Interpreter.Tests/EndToEnd/VtUserDataMethodsTests.cs @@ -290,12 +290,12 @@ public Type Type get { return typeof(SomeOtherClassCustomDescriptor); } } - public DynValue Index(Script script, object obj, DynValue index, bool dummy) + public DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool dummy) { return DynValue.NewNumber(index.Number * 4); } - public bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool dummy) + public bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool dummy) { throw new NotImplementedException(); } @@ -305,7 +305,7 @@ public string AsString(object obj) return null; } - public DynValue MetaIndex(Script script, object obj, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { return null; } @@ -321,17 +321,17 @@ public bool IsTypeCompatible(Type type, object obj) public struct SelfDescribingClass : IUserDataType { - public DynValue Index(Script script, DynValue index, bool isNameIndex) + public DynValue Index(ExecutionControlToken ecToken, Script script, DynValue index, bool isNameIndex) { return DynValue.NewNumber(index.Number * 3); } - public bool SetIndex(Script script, DynValue index, DynValue value, bool isNameIndex) + public bool SetIndex(ExecutionControlToken ecToken, Script script, DynValue index, DynValue value, bool isNameIndex) { throw new NotImplementedException(); } - public DynValue MetaIndex(Script script, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, string metaname) { throw new NotImplementedException(); } diff --git a/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.net40-client/MoonSharp.Interpreter.Tests.net40-client.csproj b/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.net40-client/MoonSharp.Interpreter.Tests.net40-client.csproj index 18489fdf..73153d2c 100644 --- a/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.net40-client/MoonSharp.Interpreter.Tests.net40-client.csproj +++ b/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.net40-client/MoonSharp.Interpreter.Tests.net40-client.csproj @@ -213,6 +213,7 @@ _Hardwired.cs + diff --git a/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.portable40/MoonSharp.Interpreter.Tests.portable40.csproj b/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.portable40/MoonSharp.Interpreter.Tests.portable40.csproj index 90d0a48d..3ff9c214 100644 --- a/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.portable40/MoonSharp.Interpreter.Tests.portable40.csproj +++ b/src/MoonSharp.Interpreter.Tests/_Projects/MoonSharp.Interpreter.Tests.portable40/MoonSharp.Interpreter.Tests.portable40.csproj @@ -213,6 +213,7 @@ _Hardwired.cs + diff --git a/src/MoonSharp.Interpreter/AsyncExtensions.cs b/src/MoonSharp.Interpreter/AsyncExtensions.cs deleted file mode 100755 index eb0055c5..00000000 --- a/src/MoonSharp.Interpreter/AsyncExtensions.cs +++ /dev/null @@ -1,421 +0,0 @@ -#if HASDYNAMIC -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MoonSharp.Interpreter.REPL; - -namespace MoonSharp.Interpreter -{ - /// - /// This class contains extension methods providing async wrappers of many methods. - /// Asynchronous execution is performed by scheduling the method on the thread pool (through a Task.Factory.StartNew). - /// - /// This type is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - public static class AsyncExtensions - { - private static Task ExecAsync(Func func) - { - return Task.Factory.StartNew(func); - } - - private static Task ExecAsyncVoid(Action func) - { - return Task.Factory.StartNew(func); - } - - - - /// - /// Asynchronously calls this function with the specified args - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The function. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Closure function) - { - return ExecAsync(() => function.Call()); - } - - /// - /// Asynchronously calls this function with the specified args - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The function. - /// The arguments to pass to the function. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Closure function, params object[] args) - { - return ExecAsync(() => function.Call(args)); - } - - /// - /// Asynchronously calls this function with the specified args - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The function. - /// The arguments to pass to the function. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Closure function, params DynValue[] args) - { - return ExecAsync(() => function.Call(args)); - } - - /// - /// Asynchronously loads and executes a string containing a Lua/MoonSharp script. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The code. - /// The global context. - /// Name of the code - used to report errors, etc. Also used by debuggers to locate the original source file. - /// - /// A DynValue containing the result of the processing of the loaded chunk. - /// - public static Task DoStringAsync(this Script script, string code, Table globalContext = null, string codeFriendlyName = null) - { - return ExecAsync(() => script.DoString(code, globalContext, codeFriendlyName)); - } - - - /// - /// Asynchronously loads and executes a stream containing a Lua/MoonSharp script. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The stream. - /// The global context. - /// Name of the code - used to report errors, etc. Also used by debuggers to locate the original source file. - /// - /// A DynValue containing the result of the processing of the loaded chunk. - /// - public static Task DoStreamAsync(this Script script, Stream stream, Table globalContext = null, string codeFriendlyName = null) - { - return ExecAsync(() => script.DoStream(stream, globalContext, codeFriendlyName)); - } - - - /// - /// Asynchronously loads and executes a file containing a Lua/MoonSharp script. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The filename. - /// The global context. - /// Name of the code - used to report errors, etc. Also used by debuggers to locate the original source file. - /// - /// A DynValue containing the result of the processing of the loaded chunk. - /// - public static Task DoFileAsync(this Script script, string filename, Table globalContext = null, string codeFriendlyName = null) - { - return ExecAsync(() => script.DoFile(filename, globalContext, codeFriendlyName)); - } - - /// - /// Asynchronously loads a string containing a Lua/MoonSharp function. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The code. - /// The global table to bind to this chunk. - /// Name of the function used to report errors, etc. - /// - /// A DynValue containing a function which will execute the loaded code. - /// - public static Task LoadFunctionAsync(this Script script, string code, Table globalTable = null, string funcFriendlyName = null) - { - return ExecAsync(() => script.LoadFunction(code, globalTable, funcFriendlyName)); - } - - - - /// - /// Asynchronously loads a string containing a Lua/MoonSharp script. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The code. - /// The global table to bind to this chunk. - /// Name of the code - used to report errors, etc. - /// - /// A DynValue containing a function which will execute the loaded code. - /// - public static Task LoadStringAsync(this Script script, string code, Table globalTable = null, string codeFriendlyName = null) - { - return ExecAsync(() => script.LoadString(code, globalTable, codeFriendlyName)); - } - - - - /// - /// Asynchronously loads a Lua/MoonSharp script from a System.IO.Stream. NOTE: This will *NOT* close the stream! - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The stream containing code. - /// The global table to bind to this chunk. - /// Name of the code - used to report errors, etc. - /// - /// A DynValue containing a function which will execute the loaded code. - /// - public static Task LoadStreamAsync(this Script script, Stream stream, Table globalTable = null, string codeFriendlyName = null) - { - return ExecAsync(() => script.LoadStream(stream, globalTable, codeFriendlyName)); - } - - - /// - /// Asynchronously dumps a function on the specified stream. - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The function. - /// The stream. - /// - /// function arg is not a function! - /// or - /// stream is readonly! - /// or - /// function arg has upvalues other than _ENV - public static Task DumpAsync(this Script script, DynValue function, Stream stream) - { - return ExecAsyncVoid(() => script.Dump(function, stream)); - } - - - - /// - /// Asynchronously loads a string containing a Lua/MoonSharp script. - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The code. - /// The global table to bind to this chunk. - /// The filename to be used in error messages. - /// - /// A DynValue containing a function which will execute the loaded code. - /// - public static Task LoadFileAsync(this Script script, string filename, Table globalContext = null, string friendlyFilename = null) - { - return ExecAsync(() => script.LoadFile(filename, globalContext, friendlyFilename)); - } - - - - /// - /// Calls the specified function. - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The Lua/MoonSharp function to be called - /// - /// The return value(s) of the function call. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Script script, DynValue function) - { - return ExecAsync(() => script.Call(function)); - } - - - /// - /// Asynchronously calls the specified function. - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The Lua/MoonSharp function to be called - /// The arguments to pass to the function. - /// - /// The return value(s) of the function call. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Script script, DynValue function, params DynValue[] args) - { - return ExecAsync(() => script.Call(function, args)); - } - - - - /// - /// Asynchronously calls the specified function. - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The Lua/MoonSharp function to be called - /// The arguments to pass to the function. - /// - /// The return value(s) of the function call. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Script script, DynValue function, params object[] args) - { - return ExecAsync(() => script.Call(function, args)); - } - - - - /// - /// Asynchronously calls the specified function. - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The Lua/MoonSharp function to be called - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Script script, object function) - { - return ExecAsync(() => script.Call(function)); - } - - - /// - /// Asynchronously calls the specified function. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The Lua/MoonSharp function to be called - /// The arguments to pass to the function. - /// - /// Thrown if function is not of DataType.Function - public static Task CallAsync(this Script script, object function, params object[] args) - { - return ExecAsync(() => script.Call(function, args)); - } - - /// - /// Asynchronously creates a new dynamic expression. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The script. - /// The code of the expression. - /// - public static Task CreateDynamicExpressionAsync(this Script script, string code) - { - return ExecAsync(() => script.CreateDynamicExpression(code)); - } - - /// - /// Asynchronously evaluates a REPL command. - /// This method returns the result of the computation, or null if more input is needed for having valid code. - /// In case of errors, exceptions are propagated to the caller. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The interpreter. - /// The input. - /// - /// This method returns the result of the computation, or null if more input is needed for a computation. - /// - public static Task EvaluateAsync(this ReplInterpreter interpreter, string input) - { - return ExecAsync(() => interpreter.Evaluate(input)); - } - - /// - /// Resumes the coroutine. - /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The coroutine - /// The arguments. - /// - /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead - public static Task ResumeAsync(this Coroutine cor, params DynValue[] args) - { - return ExecAsync(() => cor.Resume(args)); - } - - - /// - /// Resumes the coroutine. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The coroutine - /// The ScriptExecutionContext. - /// The arguments. - /// - public static Task ResumeAsync(this Coroutine cor, ScriptExecutionContext context, params DynValue[] args) - { - return ExecAsync(() => cor.Resume(context, args)); - } - - /// - /// Resumes the coroutine. - /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The coroutine - /// - /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead - public static Task ResumeAsync(this Coroutine cor) - { - return ExecAsync(() => cor.Resume()); - } - - - /// - /// Resumes the coroutine. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The coroutine - /// The ScriptExecutionContext. - /// - public static Task ResumeAsync(this Coroutine cor, ScriptExecutionContext context) - { - return ExecAsync(() => cor.Resume(context)); - } - - /// - /// Resumes the coroutine. - /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The coroutine - /// The arguments. - /// - /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. - public static Task ResumeAsync(this Coroutine cor, params object[] args) - { - return ExecAsync(() => cor.Resume(args)); - } - - - /// - /// Resumes the coroutine - /// - /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. - /// - /// The coroutine - /// The ScriptExecutionContext. - /// The arguments. - /// - public static Task ResumeAsync(this Coroutine cor, ScriptExecutionContext context, params object[] args) - { - return ExecAsync(() => cor.Resume(context, args)); - } - } -} -#endif diff --git a/src/MoonSharp.Interpreter/CoreLib/ErrorHandlingModule.cs b/src/MoonSharp.Interpreter/CoreLib/ErrorHandlingModule.cs index ffa21d03..b8d3a3fd 100644 --- a/src/MoonSharp.Interpreter/CoreLib/ErrorHandlingModule.cs +++ b/src/MoonSharp.Interpreter/CoreLib/ErrorHandlingModule.cs @@ -14,11 +14,12 @@ public class ErrorHandlingModule [MoonSharpModuleMethod] public static DynValue pcall(ScriptExecutionContext executionContext, CallbackArguments args) { - return SetErrorHandlerStrategy("pcall", executionContext, args, null); + return SetErrorHandlerStrategy(executionContext.m_EcToken, "pcall", executionContext, args, null); } - private static DynValue SetErrorHandlerStrategy(string funcName, + private static DynValue SetErrorHandlerStrategy(ExecutionControlToken ecToken, + string funcName, ScriptExecutionContext executionContext, CallbackArguments args, DynValue handlerBeforeUnwind) @@ -125,7 +126,7 @@ public static DynValue xpcall(ScriptExecutionContext executionContext, CallbackA args.AsType(1, "xpcall", DataType.Function, false); } - return SetErrorHandlerStrategy("xpcall", executionContext, new CallbackArguments(a, false), handler); + return SetErrorHandlerStrategy(executionContext.m_EcToken, "xpcall", executionContext, new CallbackArguments(a, false), handler); } } diff --git a/src/MoonSharp.Interpreter/DataTypes/Closure.cs b/src/MoonSharp.Interpreter/DataTypes/Closure.cs index 0ed8e705..d2884a6e 100644 --- a/src/MoonSharp.Interpreter/DataTypes/Closure.cs +++ b/src/MoonSharp.Interpreter/DataTypes/Closure.cs @@ -1,4 +1,7 @@ using System.Collections.Generic; +#if HASDYNAMIC +using System.Threading.Tasks; +#endif using MoonSharp.Interpreter.Execution; namespace MoonSharp.Interpreter @@ -103,6 +106,50 @@ public DynValue Call(params DynValue[] args) } +#if HASDYNAMIC + /// + /// Asynchronously calls this function with the specified args + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The function. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken) + { + return OwnerScript.CallAsync(ecToken, this); + } + + /// + /// Asynchronously calls this function with the specified args + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The function. + /// The arguments to pass to the function. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, params object[] args) + { + return OwnerScript.CallAsync(ecToken, this, args); + } + + /// + /// Asynchronously calls this function with the specified args + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The function. + /// The arguments to pass to the function. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, params DynValue[] args) + { + return OwnerScript.CallAsync(ecToken, this, args); + } +#endif + + /// /// Gets a delegate wrapping calls to this scripted function /// diff --git a/src/MoonSharp.Interpreter/DataTypes/Coroutine.cs b/src/MoonSharp.Interpreter/DataTypes/Coroutine.cs index 07132e04..3f9e5b82 100755 --- a/src/MoonSharp.Interpreter/DataTypes/Coroutine.cs +++ b/src/MoonSharp.Interpreter/DataTypes/Coroutine.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +#if HASDYNAMIC +using System.Threading.Tasks; +#endif using MoonSharp.Interpreter.Debugging; using MoonSharp.Interpreter.Execution.VM; @@ -237,6 +240,96 @@ public DynValue Resume(ScriptExecutionContext context, params object[] args) } +#if HASDYNAMIC + /// + /// Resumes the coroutine. + /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The coroutine + /// The arguments. + /// + /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead + public Task ResumeAsync(params DynValue[] args) + { + return Task.Factory.StartNew(() => Resume(args)); + } + + + /// + /// Resumes the coroutine. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The coroutine + /// The ScriptExecutionContext. + /// The arguments. + /// + public Task ResumeAsync(ScriptExecutionContext context, params DynValue[] args) + { + return Task.Factory.StartNew(() => Resume(context, args)); + } + + /// + /// Resumes the coroutine. + /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The coroutine + /// + /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead + public Task ResumeAsync() + { + return Task.Factory.StartNew(() => Resume()); + } + + + /// + /// Resumes the coroutine. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The coroutine + /// The ScriptExecutionContext. + /// + public Task ResumeAsync(ScriptExecutionContext context) + { + return Task.Factory.StartNew(() => Resume(context)); + } + + /// + /// Resumes the coroutine. + /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The coroutine + /// The arguments. + /// + /// Only non-CLR coroutines can be resumed with this overload of the Resume method. Use the overload accepting a ScriptExecutionContext instead. + public Task ResumeAsync(params object[] args) + { + return Task.Factory.StartNew(() => Resume(args)); + } + + + /// + /// Resumes the coroutine + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The coroutine + /// The ScriptExecutionContext. + /// The arguments. + /// + public Task ResumeAsync(ScriptExecutionContext context, params object[] args) + { + return Task.Factory.StartNew(() => Resume(context, args)); + } +#endif + /// diff --git a/src/MoonSharp.Interpreter/Errors/ScriptTerminationRequestedException.cs b/src/MoonSharp.Interpreter/Errors/ScriptTerminationRequestedException.cs new file mode 100644 index 00000000..17e748c2 --- /dev/null +++ b/src/MoonSharp.Interpreter/Errors/ScriptTerminationRequestedException.cs @@ -0,0 +1,22 @@ +using System; + +namespace MoonSharp.Interpreter +{ + /// + /// Exception thrown when an async script is requested to abort + /// +#if !(PCL || ((!UNITY_EDITOR) && (ENABLE_DOTNET)) || NETFX_CORE) + [Serializable] +#endif + public class ScriptTerminationRequestedException : InterpreterException + { + /// + /// Initializes a new instance of the class. + /// + internal ScriptTerminationRequestedException() + : base("script has been requested to abort") + { + DecoratedMessage = Message; + } + } +} diff --git a/src/MoonSharp.Interpreter/Execution/DynamicExpression.cs b/src/MoonSharp.Interpreter/Execution/DynamicExpression.cs index e53b92c6..32519e6e 100644 --- a/src/MoonSharp.Interpreter/Execution/DynamicExpression.cs +++ b/src/MoonSharp.Interpreter/Execution/DynamicExpression.cs @@ -36,7 +36,7 @@ internal DynamicExpression(Script S, string strExpr, DynValue constant) /// public DynValue Evaluate(ScriptExecutionContext context = null) { - context = context ?? OwnerScript.CreateDynamicExecutionContext(); + context = context ?? OwnerScript.CreateDynamicExecutionContext(ExecutionControlToken.Dummy); this.CheckScriptOwnership(context.GetScript()); diff --git a/src/MoonSharp.Interpreter/Execution/ScriptExecutionContext.cs b/src/MoonSharp.Interpreter/Execution/ScriptExecutionContext.cs index 0a42ccfb..84327841 100644 --- a/src/MoonSharp.Interpreter/Execution/ScriptExecutionContext.cs +++ b/src/MoonSharp.Interpreter/Execution/ScriptExecutionContext.cs @@ -12,12 +12,14 @@ public class ScriptExecutionContext : IScriptPrivateResource { Processor m_Processor; CallbackFunction m_Callback; + internal ExecutionControlToken m_EcToken; - internal ScriptExecutionContext(Processor p, CallbackFunction callBackFunction, SourceRef sourceRef, bool isDynamic = false) + internal ScriptExecutionContext(ExecutionControlToken ecToken, Processor p, CallbackFunction callBackFunction, SourceRef sourceRef, bool isDynamic = false) { IsDynamicExecution = isDynamic; m_Processor = p; m_Callback = callBackFunction; + m_EcToken = ecToken; CallingLocation = sourceRef; } @@ -74,7 +76,7 @@ public Table GetMetatable(DynValue value) /// public DynValue GetMetamethod(DynValue value, string metamethod) { - return m_Processor.GetMetamethod(value, metamethod); + return m_Processor.GetMetamethod(m_EcToken, value, metamethod); } /// @@ -92,7 +94,7 @@ public DynValue GetMetamethodTailCall(DynValue value, string metamethod, params /// public DynValue GetBinaryMetamethod(DynValue op1, DynValue op2, string eventName) { - return m_Processor.GetBinaryMetamethod(op1, op2, eventName); + return m_Processor.GetBinaryMetamethod(m_EcToken, op1, op2, eventName); } /// @@ -248,7 +250,7 @@ public Table CurrentGlobalEnv public void PerformMessageDecorationBeforeUnwind(DynValue messageHandler, ScriptRuntimeException exception) { if (messageHandler != null) - exception.DecoratedMessage = m_Processor.PerformMessageDecorationBeforeUnwind(messageHandler, exception.Message, CallingLocation); + exception.DecoratedMessage = m_Processor.PerformMessageDecorationBeforeUnwind(m_EcToken, messageHandler, exception.Message, CallingLocation); else exception.DecoratedMessage = exception.Message; } @@ -265,5 +267,24 @@ public Script OwnerScript get { return this.GetScript(); } } + + /// + /// Pauses the script thread for the specified amount of time. + /// + /// + /// Timeout. + /// + public void PauseExecution(TimeSpan timeout) + { + m_EcToken.Wait(timeout); + + // This is not strictly required, but why allow the code + // to go back to Processor::Processing_Loop if we can check right here if + // we should stop or not? + if (m_EcToken.IsAbortRequested) + { + throw new ScriptTerminationRequestedException(); + } + } } } diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs index 18d689ca..09beee63 100755 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using MoonSharp.Interpreter.DataStructs; @@ -24,7 +24,6 @@ sealed partial class Processor int m_SavedInstructionPtr = -1; DebugContext m_Debug; - public Processor(Script script, Table globalContext, ByteCode byteCode) { m_ValueStack = new FastStack(STACK_SIZE); @@ -65,12 +64,12 @@ internal Processor(Processor parentProcessor, Processor recycleProcessor) m_State = CoroutineState.NotStarted; } - public DynValue Call(DynValue function, DynValue[] args) + public DynValue Call(ExecutionControlToken ecToken, DynValue function, DynValue[] args) { List coroutinesStack = m_Parent != null ? m_Parent.m_CoroutinesStack : this.m_CoroutinesStack; if (coroutinesStack.Count > 0 && coroutinesStack[coroutinesStack.Count - 1] != this) - return coroutinesStack[coroutinesStack.Count - 1].Call(function, args); + return coroutinesStack[coroutinesStack.Count - 1].Call(ecToken, function, args); EnterProcessor(); @@ -83,7 +82,7 @@ public DynValue Call(DynValue function, DynValue[] args) try { int entrypoint = PushClrToScriptStackFrame(CallStackItemFlags.CallEntryPoint, function, args); - return Processing_Loop(entrypoint); + return Processing_Loop(ecToken, entrypoint); } finally { diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs index be736426..853e3eb1 100644 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs @@ -67,7 +67,7 @@ public DynValue Coroutine_Resume(DynValue[] args) } m_State = CoroutineState.Running; - DynValue retVal = Processing_Loop(entrypoint); + DynValue retVal = Processing_Loop(ExecutionControlToken.Dummy, entrypoint); if (retVal.Type == DataType.YieldRequest) { diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Debugger.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Debugger.cs index f7878780..649d3a05 100755 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Debugger.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Debugger.cs @@ -256,7 +256,7 @@ private bool ToggleBreakPoint(DebuggerAction action, bool? state) private void RefreshDebugger(bool hard, int instructionPtr) { SourceRef sref = GetCurrentSourceRef(instructionPtr); - ScriptExecutionContext context = new ScriptExecutionContext(this, null, sref); + ScriptExecutionContext context = new ScriptExecutionContext(ExecutionControlToken.Dummy, this, null, sref); List watchList = m_Debug.DebuggerAttached.GetWatchItems(); List callStack = Debugger_GetCallStack(sref); diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_IExecutionContext.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_IExecutionContext.cs index e826f240..99b41ffb 100644 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_IExecutionContext.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_IExecutionContext.cs @@ -19,7 +19,7 @@ internal Table GetMetatable(DynValue value) } } - internal DynValue GetBinaryMetamethod(DynValue op1, DynValue op2, string eventName) + internal DynValue GetBinaryMetamethod(ExecutionControlToken ecToken, DynValue op1, DynValue op2, string eventName) { var op1_MetaTable = GetMetatable(op1); if (op1_MetaTable != null) @@ -39,7 +39,7 @@ internal DynValue GetBinaryMetamethod(DynValue op1, DynValue op2, string eventNa if (op1.Type == DataType.UserData) { - DynValue meta = op1.UserData.Descriptor.MetaIndex(this.m_Script, + DynValue meta = op1.UserData.Descriptor.MetaIndex(ecToken, this.m_Script, op1.UserData.Object, eventName); if (meta != null) @@ -48,7 +48,7 @@ internal DynValue GetBinaryMetamethod(DynValue op1, DynValue op2, string eventNa if (op2.Type == DataType.UserData) { - DynValue meta = op2.UserData.Descriptor.MetaIndex(this.m_Script, + DynValue meta = op2.UserData.Descriptor.MetaIndex(ecToken, this.m_Script, op2.UserData.Object, eventName); if (meta != null) @@ -58,11 +58,11 @@ internal DynValue GetBinaryMetamethod(DynValue op1, DynValue op2, string eventNa return null; } - internal DynValue GetMetamethod(DynValue value, string metamethod) + internal DynValue GetMetamethod(ExecutionControlToken ecToken, DynValue value, string metamethod) { if (value.Type == DataType.UserData) { - DynValue v = value.UserData.Descriptor.MetaIndex(m_Script, value.UserData.Object, metamethod); + DynValue v = value.UserData.Descriptor.MetaIndex(ecToken, m_Script, value.UserData.Object, metamethod); if (v != null) return v; } diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs index 38c0f63e..8dbd7b5f 100644 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs @@ -13,7 +13,7 @@ sealed partial class Processor internal long AutoYieldCounter = 0; - private DynValue Processing_Loop(int instructionPtr) + private DynValue Processing_Loop(ExecutionControlToken ecToken, int instructionPtr) { // This is the main loop of the processor, has a weird control flow and needs to be as fast as possible. // This sentence is just a convoluted way to say "don't complain about gotos". @@ -42,6 +42,11 @@ private DynValue Processing_Loop(int instructionPtr) return DynValue.NewForcedYieldReq(); } + if (ecToken.IsAbortRequested) + { + throw new ScriptTerminationRequestedException(); + } + ++instructionPtr; switch (i.OpCode) @@ -63,56 +68,56 @@ private DynValue Processing_Loop(int instructionPtr) m_ValueStack.Push(i.Value); break; case OpCode.Add: - instructionPtr = ExecAdd(i, instructionPtr); + instructionPtr = ExecAdd(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Concat: - instructionPtr = ExecConcat(i, instructionPtr); + instructionPtr = ExecConcat(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Neg: - instructionPtr = ExecNeg(i, instructionPtr); + instructionPtr = ExecNeg(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Sub: - instructionPtr = ExecSub(i, instructionPtr); + instructionPtr = ExecSub(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Mul: - instructionPtr = ExecMul(i, instructionPtr); + instructionPtr = ExecMul(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Div: - instructionPtr = ExecDiv(i, instructionPtr); + instructionPtr = ExecDiv(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Mod: - instructionPtr = ExecMod(i, instructionPtr); + instructionPtr = ExecMod(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Power: - instructionPtr = ExecPower(i, instructionPtr); + instructionPtr = ExecPower(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Eq: - instructionPtr = ExecEq(i, instructionPtr); + instructionPtr = ExecEq(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.LessEq: - instructionPtr = ExecLessEq(i, instructionPtr); + instructionPtr = ExecLessEq(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Less: - instructionPtr = ExecLess(i, instructionPtr); + instructionPtr = ExecLess(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Len: - instructionPtr = ExecLen(i, instructionPtr); + instructionPtr = ExecLen(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Call: case OpCode.ThisCall: - instructionPtr = Internal_ExecCall(i.NumVal, instructionPtr, null, null, i.OpCode == OpCode.ThisCall, i.Name); + instructionPtr = Internal_ExecCall(ecToken, i.NumVal, instructionPtr, null, null, i.OpCode == OpCode.ThisCall, i.Name); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Scalar: @@ -165,7 +170,7 @@ private DynValue Processing_Loop(int instructionPtr) ExecArgs(i); break; case OpCode.Ret: - instructionPtr = ExecRet(i); + instructionPtr = ExecRet(ecToken, i); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; if (instructionPtr < 0) goto return_to_native_code; @@ -187,7 +192,7 @@ private DynValue Processing_Loop(int instructionPtr) m_ValueStack.Push(DynValue.NewPrimeTable()); break; case OpCode.IterPrep: - ExecIterPrep(i); + ExecIterPrep(ecToken, i); break; case OpCode.IterUpd: ExecIterUpd(i); @@ -218,19 +223,19 @@ private DynValue Processing_Loop(int instructionPtr) case OpCode.Index: case OpCode.IndexN: case OpCode.IndexL: - instructionPtr = ExecIndex(i, instructionPtr); + instructionPtr = ExecIndex(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.IndexSet: case OpCode.IndexSetN: case OpCode.IndexSetL: - instructionPtr = ExecIndexSet(i, instructionPtr); + instructionPtr = ExecIndexSet(ecToken, i, instructionPtr); if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine; break; case OpCode.Invalid: throw new NotImplementedException(string.Format("Invalid opcode : {0}", i.Name)); default: - throw new NotImplementedException(string.Format("Execution for {0} not implented yet!", i.OpCode)); + throw new NotImplementedException(string.Format("Execution for {0} not implemented yet!", i.OpCode)); } } @@ -272,7 +277,7 @@ private DynValue Processing_Loop(int instructionPtr) var c = m_ExecutionStack.Peek(i); if (c.ErrorHandlerBeforeUnwind != null) - ex.DecoratedMessage = PerformMessageDecorationBeforeUnwind(c.ErrorHandlerBeforeUnwind, ex.DecoratedMessage, GetCurrentSourceRef(instructionPtr)); + ex.DecoratedMessage = PerformMessageDecorationBeforeUnwind(ecToken, c.ErrorHandlerBeforeUnwind, ex.DecoratedMessage, GetCurrentSourceRef(instructionPtr)); } @@ -292,7 +297,7 @@ private DynValue Processing_Loop(int instructionPtr) var cbargs = new DynValue[] { DynValue.NewString(ex.DecoratedMessage) }; - DynValue handled = csi.ErrorHandler.Invoke(new ScriptExecutionContext(this, csi.ErrorHandler, GetCurrentSourceRef(instructionPtr)), cbargs); + DynValue handled = csi.ErrorHandler.Invoke(new ScriptExecutionContext(ecToken, this, csi.ErrorHandler, GetCurrentSourceRef(instructionPtr)), cbargs); m_ValueStack.Push(handled); @@ -316,7 +321,7 @@ private DynValue Processing_Loop(int instructionPtr) } - internal string PerformMessageDecorationBeforeUnwind(DynValue messageHandler, string decoratedMessage, SourceRef sourceRef) + internal string PerformMessageDecorationBeforeUnwind(ExecutionControlToken ecToken, DynValue messageHandler, string decoratedMessage, SourceRef sourceRef) { try { @@ -325,11 +330,11 @@ internal string PerformMessageDecorationBeforeUnwind(DynValue messageHandler, st if (messageHandler.Type == DataType.Function) { - ret = this.Call(messageHandler, args); + ret = this.Call(ecToken, messageHandler, args); } else if (messageHandler.Type == DataType.ClrFunction) { - ScriptExecutionContext ctx = new ScriptExecutionContext(this, messageHandler.Callback, sourceRef); + ScriptExecutionContext ctx = new ScriptExecutionContext(ecToken, this, messageHandler.Callback, sourceRef); ret = messageHandler.Callback.Invoke(ctx, args); } else @@ -473,7 +478,7 @@ private void ExecExpTuple(Instruction i) } - private void ExecIterPrep(Instruction i) + private void ExecIterPrep(ExecutionControlToken ecToken, Instruction i) { DynValue v = m_ValueStack.Pop(); @@ -492,7 +497,7 @@ private void ExecIterPrep(Instruction i) if (f.Type != DataType.Function && f.Type != DataType.ClrFunction) { - DynValue meta = this.GetMetamethod(f, "__iterator"); + DynValue meta = this.GetMetamethod(ecToken, f, "__iterator"); if (meta != null && !meta.IsNil()) { @@ -510,7 +515,7 @@ private void ExecIterPrep(Instruction i) } else if (f.Type == DataType.Table) { - DynValue callmeta = this.GetMetamethod(f, "__call"); + DynValue callmeta = this.GetMetamethod(ecToken, f, "__call"); if (callmeta == null || callmeta.IsNil()) { @@ -666,7 +671,7 @@ private void ExecArgs(Instruction I) - private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunction handler = null, + private int Internal_ExecCall(ExecutionControlToken ecToken, int argsCount, int instructionPtr, CallbackFunction handler = null, CallbackFunction continuation = null, bool thisCall = false, string debugText = null, DynValue unwindHandler = null) { DynValue fn = m_ValueStack.Peek(argsCount); @@ -722,13 +727,14 @@ private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunctio Flags = flags, }); - var ret = fn.Callback.Invoke(new ScriptExecutionContext(this, fn.Callback, sref), args, isMethodCall: thisCall); + + var ret = fn.Callback.Invoke(new ScriptExecutionContext(ecToken, this, fn.Callback, sref), args, isMethodCall: thisCall); m_ValueStack.RemoveLast(argsCount + 1); m_ValueStack.Push(ret); m_ExecutionStack.Pop(); - return Internal_CheckForTailRequests(null, instructionPtr); + return Internal_CheckForTailRequests(ecToken, null, instructionPtr); } else if (fn.Type == DataType.Function) { @@ -749,7 +755,7 @@ private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunctio } // fallback to __call metamethod - var m = GetMetamethod(fn, "__call"); + var m = GetMetamethod(ecToken, fn, "__call"); if (m != null && m.IsNotNil()) { @@ -762,7 +768,7 @@ private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunctio for (int i = argsCount; i >= 0; i--) m_ValueStack.Push(tmp[i]); - return Internal_ExecCall(argsCount + 1, instructionPtr, handler, continuation); + return Internal_ExecCall(ecToken, argsCount + 1, instructionPtr, handler, continuation); } throw ScriptRuntimeException.AttemptToCallNonFunc(fn.Type, debugText); @@ -792,7 +798,7 @@ private int PerformTCO(int instructionPtr, int argsCount) - private int ExecRet(Instruction i) + private int ExecRet(ExecutionControlToken ecToken, Instruction i) { CallStackItem csi; int retpoint = 0; @@ -813,7 +819,7 @@ private int ExecRet(Instruction i) var argscnt = (int)(m_ValueStack.Pop().Number); m_ValueStack.RemoveLast(argscnt + 1); m_ValueStack.Push(retval); - retpoint = Internal_CheckForTailRequests(i, retpoint); + retpoint = Internal_CheckForTailRequests(ecToken, i, retpoint); } else { @@ -821,7 +827,7 @@ private int ExecRet(Instruction i) } if (csi.Continuation != null) - m_ValueStack.Push(csi.Continuation.Invoke(new ScriptExecutionContext(this, csi.Continuation, i.SourceCodeRef), + m_ValueStack.Push(csi.Continuation.Invoke(new ScriptExecutionContext(ecToken, this, csi.Continuation, i.SourceCodeRef), new DynValue[1] { m_ValueStack.Pop() })); return retpoint; @@ -829,7 +835,7 @@ private int ExecRet(Instruction i) - private int Internal_CheckForTailRequests(Instruction i, int instructionPtr) + private int Internal_CheckForTailRequests(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue tail = m_ValueStack.Peek(0); @@ -844,7 +850,7 @@ private int Internal_CheckForTailRequests(Instruction i, int instructionPtr) for (int ii = 0; ii < tcd.Args.Length; ii++) m_ValueStack.Push(tcd.Args[ii]); - return Internal_ExecCall(tcd.Args.Length, instructionPtr, tcd.ErrorHandler, tcd.Continuation, false, null, tcd.ErrorHandlerBeforeUnwind); + return Internal_ExecCall(ecToken, tcd.Args.Length, instructionPtr, tcd.ErrorHandler, tcd.Continuation, false, null, tcd.ErrorHandlerBeforeUnwind); } else if (tail.Type == DataType.YieldRequest) { @@ -886,7 +892,7 @@ private int ExecShortCircuitingOperator(Instruction i, int instructionPtr) } - private int ExecAdd(Instruction i, int instructionPtr) + private int ExecAdd(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -901,13 +907,13 @@ private int ExecAdd(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__add", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__add", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(l, r); } } - private int ExecSub(Instruction i, int instructionPtr) + private int ExecSub(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -922,14 +928,14 @@ private int ExecSub(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__sub", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__sub", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(l, r); } } - private int ExecMul(Instruction i, int instructionPtr) + private int ExecMul(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -944,13 +950,13 @@ private int ExecMul(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__mul", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__mul", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(l, r); } } - private int ExecMod(Instruction i, int instructionPtr) + private int ExecMod(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -967,13 +973,13 @@ private int ExecMod(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__mod", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__mod", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(l, r); } } - private int ExecDiv(Instruction i, int instructionPtr) + private int ExecDiv(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -988,12 +994,12 @@ private int ExecDiv(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__div", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__div", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(l, r); } } - private int ExecPower(Instruction i, int instructionPtr) + private int ExecPower(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -1008,14 +1014,14 @@ private int ExecPower(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__pow", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__pow", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(l, r); } } - private int ExecNeg(Instruction i, int instructionPtr) + private int ExecNeg(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); double? rn = r.CastToNumber(); @@ -1027,14 +1033,14 @@ private int ExecNeg(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeUnaryMetaMethod(r, "__unm", instructionPtr); + int ip = Internal_InvokeUnaryMetaMethod(ecToken, r, "__unm", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ArithmeticOnNonNumber(r); } } - private int ExecEq(Instruction i, int instructionPtr) + private int ExecEq(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -1049,7 +1055,7 @@ private int ExecEq(Instruction i, int instructionPtr) // then if they are userdatas, attempt meta if (l.Type == DataType.UserData || r.Type == DataType.UserData) { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__eq", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__eq", instructionPtr); if (ip >= 0) return ip; } @@ -1067,7 +1073,7 @@ private int ExecEq(Instruction i, int instructionPtr) // then attempt metatables for tables if ((l.Type == DataType.Table) && (GetMetatable(l) != null) && (GetMetatable(l) == GetMetatable(r))) { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__eq", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__eq", instructionPtr); if (ip >= 0) return ip; } @@ -1076,7 +1082,7 @@ private int ExecEq(Instruction i, int instructionPtr) return instructionPtr; } - private int ExecLess(Instruction i, int instructionPtr) + private int ExecLess(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -1091,7 +1097,7 @@ private int ExecLess(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__lt", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__lt", instructionPtr); if (ip < 0) throw ScriptRuntimeException.CompareInvalidType(l, r); else @@ -1102,7 +1108,7 @@ private int ExecLess(Instruction i, int instructionPtr) } - private int ExecLessEq(Instruction i, int instructionPtr) + private int ExecLessEq(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -1119,10 +1125,10 @@ private int ExecLessEq(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__le", instructionPtr, DynValue.False); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__le", instructionPtr, DynValue.False); if (ip < 0) { - ip = Internal_InvokeBinaryMetaMethod(r, l, "__lt", instructionPtr, DynValue.True); + ip = Internal_InvokeBinaryMetaMethod(ecToken, r, l, "__lt", instructionPtr, DynValue.True); if (ip < 0) throw ScriptRuntimeException.CompareInvalidType(l, r); @@ -1136,7 +1142,7 @@ private int ExecLessEq(Instruction i, int instructionPtr) return instructionPtr; } - private int ExecLen(Instruction i, int instructionPtr) + private int ExecLen(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); @@ -1144,7 +1150,7 @@ private int ExecLen(Instruction i, int instructionPtr) m_ValueStack.Push(DynValue.NewNumber(r.String.Length)); else { - int ip = Internal_InvokeUnaryMetaMethod(r, "__len", instructionPtr); + int ip = Internal_InvokeUnaryMetaMethod(ecToken, r, "__len", instructionPtr); if (ip >= 0) return ip; else if (r.Type == DataType.Table) @@ -1157,7 +1163,7 @@ private int ExecLen(Instruction i, int instructionPtr) } - private int ExecConcat(Instruction i, int instructionPtr) + private int ExecConcat(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { DynValue r = m_ValueStack.Pop().ToScalar(); DynValue l = m_ValueStack.Pop().ToScalar(); @@ -1172,7 +1178,7 @@ private int ExecConcat(Instruction i, int instructionPtr) } else { - int ip = Internal_InvokeBinaryMetaMethod(l, r, "__concat", instructionPtr); + int ip = Internal_InvokeBinaryMetaMethod(ecToken, l, r, "__concat", instructionPtr); if (ip >= 0) return ip; else throw ScriptRuntimeException.ConcatOnNonString(l, r); } @@ -1205,7 +1211,7 @@ private void ExecTblInitN(Instruction i) tbl.Table.Set(key, val.ToScalar()); } - private int ExecIndexSet(Instruction i, int instructionPtr) + private int ExecIndexSet(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { int nestedMetaOps = 100; // sanity check, to avoid potential infinite loop here @@ -1249,7 +1255,7 @@ private int ExecIndexSet(Instruction i, int instructionPtr) { UserData ud = obj.UserData; - if (!ud.Descriptor.SetIndex(this.GetScript(), ud.Object, originalIdx, value, isNameIndex)) + if (!ud.Descriptor.SetIndex(ecToken, this.GetScript(), ud.Object, originalIdx, value, isNameIndex)) { throw ScriptRuntimeException.UserDataMissingField(ud.Descriptor.Name, idx.String); } @@ -1273,7 +1279,7 @@ private int ExecIndexSet(Instruction i, int instructionPtr) m_ValueStack.Push(obj); m_ValueStack.Push(idx); m_ValueStack.Push(value); - return Internal_ExecCall(3, instructionPtr); + return Internal_ExecCall(ecToken, 3, instructionPtr); } else { @@ -1284,7 +1290,7 @@ private int ExecIndexSet(Instruction i, int instructionPtr) throw ScriptRuntimeException.LoopInNewIndex(); } - private int ExecIndex(Instruction i, int instructionPtr) + private int ExecIndex(ExecutionControlToken ecToken, Instruction i, int instructionPtr) { int nestedMetaOps = 100; // sanity check, to avoid potential infinite loop here @@ -1331,7 +1337,7 @@ private int ExecIndex(Instruction i, int instructionPtr) { UserData ud = obj.UserData; - var v = ud.Descriptor.Index(this.GetScript(), ud.Object, originalIdx, isNameIndex); + var v = ud.Descriptor.Index(ecToken, this.GetScript(), ud.Object, originalIdx, isNameIndex); if (v == null) { @@ -1355,7 +1361,7 @@ private int ExecIndex(Instruction i, int instructionPtr) m_ValueStack.Push(h); m_ValueStack.Push(obj); m_ValueStack.Push(idx); - return Internal_ExecCall(2, instructionPtr); + return Internal_ExecCall(ecToken, 2, instructionPtr); } else { diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_UtilityFunctions.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_UtilityFunctions.cs index 3ad7253d..69aa8dee 100644 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_UtilityFunctions.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_UtilityFunctions.cs @@ -44,13 +44,13 @@ private DynValue[] Internal_AdjustTuple(IList values) - private int Internal_InvokeUnaryMetaMethod(DynValue op1, string eventName, int instructionPtr) + private int Internal_InvokeUnaryMetaMethod(ExecutionControlToken ecToken, DynValue op1, string eventName, int instructionPtr) { DynValue m = null; if (op1.Type == DataType.UserData) { - m = op1.UserData.Descriptor.MetaIndex(m_Script, op1.UserData.Object, eventName); + m = op1.UserData.Descriptor.MetaIndex(ecToken, m_Script, op1.UserData.Object, eventName); } if (m == null) @@ -69,16 +69,16 @@ private int Internal_InvokeUnaryMetaMethod(DynValue op1, string eventName, int i { m_ValueStack.Push(m); m_ValueStack.Push(op1); - return Internal_ExecCall(1, instructionPtr); + return Internal_ExecCall(ecToken, 1, instructionPtr); } else { return -1; } } - private int Internal_InvokeBinaryMetaMethod(DynValue l, DynValue r, string eventName, int instructionPtr, DynValue extraPush = null) + private int Internal_InvokeBinaryMetaMethod(ExecutionControlToken ecToken, DynValue l, DynValue r, string eventName, int instructionPtr, DynValue extraPush = null) { - var m = GetBinaryMetamethod(l, r, eventName); + var m = GetBinaryMetamethod(ecToken, l, r, eventName); if (m != null) { @@ -88,7 +88,7 @@ private int Internal_InvokeBinaryMetaMethod(DynValue l, DynValue r, string event m_ValueStack.Push(m); m_ValueStack.Push(l); m_ValueStack.Push(r); - return Internal_ExecCall(2, instructionPtr); + return Internal_ExecCall(ecToken, 2, instructionPtr); } else { diff --git a/src/MoonSharp.Interpreter/ExecutionControlToken.cs b/src/MoonSharp.Interpreter/ExecutionControlToken.cs new file mode 100644 index 00000000..51ab9d48 --- /dev/null +++ b/src/MoonSharp.Interpreter/ExecutionControlToken.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; + +namespace MoonSharp.Interpreter +{ + /// + /// This class provides an interface to control execution of Lua scripts ran asynchronously. + /// + /// This class is supported only on .NET 4.x and .NET 4.x PCL targets. + /// On other targets, it acts as a dummy. + /// + public class ExecutionControlToken + { + public static readonly ExecutionControlToken Dummy = new ExecutionControlToken() { m_IsDummy = true }; + +#if HASDYNAMIC + CancellationTokenSource m_CancellationTokenSource = new CancellationTokenSource(); +#endif + + bool m_IsDummy; + + /// + /// Creates an usable execution control token. + /// + /// + public ExecutionControlToken() + { + m_IsDummy = false; + } + + /// + /// Aborts the execution of the script that is associated with this token. + /// + public void Terminate() + { +#if HASDYNAMIC + if (!m_IsDummy) + { + m_CancellationTokenSource.Cancel(true); + } +#endif + } + + internal bool IsAbortRequested + { + get + { +#if HASDYNAMIC + return m_CancellationTokenSource.IsCancellationRequested; +#else + return false; +#endif + } + } + + internal void Wait(TimeSpan timeSpan) + { +#if HASDYNAMIC + m_CancellationTokenSource.Token.WaitHandle.WaitOne(timeSpan); +#else + Thread.Sleep(timeSpan); +#endif + + } + } +} diff --git a/src/MoonSharp.Interpreter/Interop/BasicDescriptors/DispatchingUserDataDescriptor.cs b/src/MoonSharp.Interpreter/Interop/BasicDescriptors/DispatchingUserDataDescriptor.cs index 8867be86..98f33782 100644 --- a/src/MoonSharp.Interpreter/Interop/BasicDescriptors/DispatchingUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/BasicDescriptors/DispatchingUserDataDescriptor.cs @@ -210,12 +210,13 @@ private void AddMemberTo(Dictionary members, string n /// /// Performs an "index" "get" operation. This tries to resolve minor variations of member names. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public virtual DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing) + public virtual DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool isDirectIndexing) { if (!isDirectIndexing) { @@ -224,7 +225,7 @@ public virtual DynValue Index(Script script, object obj, DynValue index, bool is .WithAccessOrNull(MemberDescriptorAccess.CanExecute); if (mdesc != null) - return ExecuteIndexer(mdesc, script, obj, index, null); + return ExecuteIndexer(ecToken, mdesc, script, obj, index, null); } index = index.ToScalar(); @@ -323,7 +324,7 @@ protected virtual DynValue TryIndex(Script script, object obj, string indexName) /// The value to be set /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public virtual bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) + public virtual bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) { if (!isDirectIndexing) { @@ -333,7 +334,7 @@ public virtual bool SetIndex(Script script, object obj, DynValue index, DynValue if (mdesc != null) { - ExecuteIndexer(mdesc, script, obj, index, value); + ExecuteIndexer(ecToken, mdesc, script, obj, index, value); return true; } } @@ -427,7 +428,7 @@ public virtual string AsString(object obj) /// The dynvalue to set on a setter, or null. /// /// - protected virtual DynValue ExecuteIndexer(IMemberDescriptor mdesc, Script script, object obj, DynValue index, DynValue value) + protected virtual DynValue ExecuteIndexer(ExecutionControlToken ecToken, IMemberDescriptor mdesc, Script script, object obj, DynValue index, DynValue value) { IList values; @@ -456,7 +457,7 @@ protected virtual DynValue ExecuteIndexer(IMemberDescriptor mdesc, Script script } CallbackArguments args = new CallbackArguments(values, false); - ScriptExecutionContext execCtx = script.CreateDynamicExecutionContext(); + ScriptExecutionContext execCtx = script.CreateDynamicExecutionContext(ecToken); DynValue v = mdesc.GetValue(script, obj); @@ -485,12 +486,13 @@ protected virtual DynValue ExecuteIndexer(IMemberDescriptor mdesc, Script script /// __tonumber is dispatched to implicit or explicit conversion operators to standard numeric types. /// __tobool is dispatched to an implicit or explicit conversion operator to bool. If that fails, operator true is used. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The name of the metamember. /// /// - public virtual DynValue MetaIndex(Script script, object obj, string metaname) + public virtual DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { IMemberDescriptor desc = m_MetaMembers.GetOrDefault(metaname); diff --git a/src/MoonSharp.Interpreter/Interop/IUserDataDescriptor.cs b/src/MoonSharp.Interpreter/Interop/IUserDataDescriptor.cs index b93c8092..0b80218a 100644 --- a/src/MoonSharp.Interpreter/Interop/IUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/IUserDataDescriptor.cs @@ -18,22 +18,24 @@ public interface IUserDataDescriptor /// /// Performs an "index" "get" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing); + DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool isDirectIndexing); /// /// Performs an "index" "set" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// The value to be set /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing); + bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing); /// /// Converts this userdata to string /// @@ -54,11 +56,12 @@ public interface IUserDataDescriptor /// __index, __newindex, __tostring /// /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The name of the metamember. /// - DynValue MetaIndex(Script script, object obj, string metaname); + DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname); /// /// Determines whether the specified object is compatible with the specified type. /// Unless a very specific behaviour is needed, the correct implementation is a diff --git a/src/MoonSharp.Interpreter/Interop/IUserDataType.cs b/src/MoonSharp.Interpreter/Interop/IUserDataType.cs index 9ae18bbe..98b6253f 100644 --- a/src/MoonSharp.Interpreter/Interop/IUserDataType.cs +++ b/src/MoonSharp.Interpreter/Interop/IUserDataType.cs @@ -11,20 +11,22 @@ public interface IUserDataType /// /// Performs an "index" "get" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The index. /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - DynValue Index(Script script, DynValue index, bool isDirectIndexing); + DynValue Index(ExecutionControlToken ecToken, Script script, DynValue index, bool isDirectIndexing); /// /// Performs an "index" "set" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The index. /// The value to be set /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - bool SetIndex(Script script, DynValue index, DynValue value, bool isDirectIndexing); + bool SetIndex(ExecutionControlToken ecToken, Script script, DynValue index, DynValue value, bool isDirectIndexing); /// /// /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, @@ -39,9 +41,10 @@ public interface IUserDataType /// __index, __newindex, __tostring /// /// + /// The execution control token of the script processing thread /// The script originating the request /// The name of the metamember. /// - DynValue MetaIndex(Script script, string metaname); + DynValue MetaIndex(ExecutionControlToken ecToken, Script script, string metaname); } } diff --git a/src/MoonSharp.Interpreter/Interop/PredefinedUserData/EnumerableWrapper.cs b/src/MoonSharp.Interpreter/Interop/PredefinedUserData/EnumerableWrapper.cs index 6accf2ad..f6e32bad 100644 --- a/src/MoonSharp.Interpreter/Interop/PredefinedUserData/EnumerableWrapper.cs +++ b/src/MoonSharp.Interpreter/Interop/PredefinedUserData/EnumerableWrapper.cs @@ -61,7 +61,7 @@ internal static DynValue ConvertTable(Table table) } - public DynValue Index(Script script, DynValue index, bool isDirectIndexing) + public DynValue Index(ExecutionControlToken ecToken, Script script, DynValue index, bool isDirectIndexing) { if (index.Type == DataType.String) { @@ -83,12 +83,12 @@ public DynValue Index(Script script, DynValue index, bool isDirectIndexing) return null; } - public bool SetIndex(Script script, DynValue index, DynValue value, bool isDirectIndexing) + public bool SetIndex(ExecutionControlToken ecToken, Script script, DynValue index, DynValue value, bool isDirectIndexing) { return false; } - public DynValue MetaIndex(Script script, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, string metaname) { if (metaname == "__call") return DynValue.NewCallback(LuaIteratorCallback); diff --git a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/AutoDescribingUserDataDescriptor.cs b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/AutoDescribingUserDataDescriptor.cs index ea7bb48d..8cf779d1 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/AutoDescribingUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/AutoDescribingUserDataDescriptor.cs @@ -42,17 +42,18 @@ public Type Type /// /// Performs an "index" "get" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing) + public DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool isDirectIndexing) { IUserDataType u = obj as IUserDataType; if (u != null) - return u.Index(script, index, isDirectIndexing); + return u.Index(ecToken, script, index, isDirectIndexing); return null; } @@ -60,18 +61,19 @@ public DynValue Index(Script script, object obj, DynValue index, bool isDirectIn /// /// Performs an "index" "set" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// The value to be set /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) + public bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) { IUserDataType u = obj as IUserDataType; if (u != null) - return u.SetIndex(script, index, value, isDirectIndexing); + return u.SetIndex(ecToken, script, index, value, isDirectIndexing); return false; } @@ -99,16 +101,17 @@ public string AsString(object obj) /// These standard metamethods are supported through other calls for efficiency: /// __index, __newindex, __tostring /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The name of the metamember. /// - public DynValue MetaIndex(Script script, object obj, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { IUserDataType u = obj as IUserDataType; if (u != null) - return u.MetaIndex(script, metaname); + return u.MetaIndex(ecToken, script, metaname); return null; } diff --git a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/CompositeUserDataDescriptor.cs b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/CompositeUserDataDescriptor.cs index a7e1a528..ec262210 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/CompositeUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/CompositeUserDataDescriptor.cs @@ -55,16 +55,17 @@ public Type Type /// /// Performs an "index" "get" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public DynValue Index(Script script, object obj, DynValue index, bool isNameIndex) + public DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool isNameIndex) { foreach (IUserDataDescriptor dd in m_Descriptors) { - DynValue v = dd.Index(script, obj, index, isNameIndex); + DynValue v = dd.Index(ecToken, script, obj, index, isNameIndex); if (v != null) return v; @@ -75,17 +76,18 @@ public DynValue Index(Script script, object obj, DynValue index, bool isNameInde /// /// Performs an "index" "set" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// The value to be set /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isNameIndex) + public bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isNameIndex) { foreach (IUserDataDescriptor dd in m_Descriptors) { - if (dd.SetIndex(script, obj, index, value, isNameIndex)) + if (dd.SetIndex(ecToken, script, obj, index, value, isNameIndex)) return true; } return false; @@ -112,15 +114,16 @@ public string AsString(object obj) /// These standard metamethods are supported through other calls for efficiency: /// __index, __newindex, __tostring /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The name of the metamember. /// - public DynValue MetaIndex(Script script, object obj, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { foreach (IUserDataDescriptor dd in m_Descriptors) { - DynValue v = dd.MetaIndex(script, obj, metaname); + DynValue v = dd.MetaIndex(ecToken, script, obj, metaname); if (v != null) return v; diff --git a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/EventFacade.cs b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/EventFacade.cs index 6b9d98b8..6f2043dc 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/EventFacade.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/EventFacade.cs @@ -25,7 +25,7 @@ public EventFacade(Func /// Performs an "index" "get" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing) + public DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool isDirectIndexing) { - return m_ProxyDescriptor.Index(script, Proxy(obj), index, isDirectIndexing); + return m_ProxyDescriptor.Index(ecToken, script, Proxy(obj), index, isDirectIndexing); } /// /// Performs an "index" "set" operation. /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The index. /// The value to be set /// If set to true, it's indexed with a name, if false it's indexed through brackets. /// - public bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) + public bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) { - return m_ProxyDescriptor.SetIndex(script, Proxy(obj), index, value, isDirectIndexing); + return m_ProxyDescriptor.SetIndex(ecToken, script, Proxy(obj), index, value, isDirectIndexing); } /// @@ -100,13 +102,14 @@ public string AsString(object obj) /// These standard metamethods are supported through other calls for efficiency: /// __index, __newindex, __tostring /// + /// The execution control token of the script processing thread /// The script originating the request /// The object (null if a static request is done) /// The name of the metamember. /// - public DynValue MetaIndex(Script script, object obj, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { - return m_ProxyDescriptor.MetaIndex(script, Proxy(obj), metaname); + return m_ProxyDescriptor.MetaIndex(ecToken, script, Proxy(obj), metaname); } /// diff --git a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardEnumUserDataDescriptor.cs b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardEnumUserDataDescriptor.cs index 9f8c2e20..394e4c89 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardEnumUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardEnumUserDataDescriptor.cs @@ -326,11 +326,12 @@ public override bool IsTypeCompatible(Type type, object obj) /// In this specific case, only the concat operator is supported, only on flags enums and it implements the /// 'or' operator. /// + /// The execution control token of the script processing thread /// /// /// /// - public override DynValue MetaIndex(Script script, object obj, string metaname) + public override DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { if (metaname == "__concat" && IsFlags) return DynValue.NewCallback(Callback_Or); diff --git a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardGenericsUserDataDescriptor.cs b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardGenericsUserDataDescriptor.cs index 112e406c..e6626d8f 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardGenericsUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardGenericsUserDataDescriptor.cs @@ -37,13 +37,13 @@ public StandardGenericsUserDataDescriptor(Type type, InteropAccessMode accessMod public Type Type { get; private set; } /// - public DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing) + public DynValue Index(ExecutionControlToken ecToken, Script script, object obj, DynValue index, bool isDirectIndexing) { return null; } /// - public bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) + public bool SetIndex(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing) { return false; } @@ -55,7 +55,7 @@ public string AsString(object obj) } /// - public DynValue MetaIndex(Script script, object obj, string metaname) + public DynValue MetaIndex(ExecutionControlToken ecToken, Script script, object obj, string metaname) { return null; } diff --git a/src/MoonSharp.Interpreter/MoonSharp.Interpreter.net35-client.csproj b/src/MoonSharp.Interpreter/MoonSharp.Interpreter.net35-client.csproj index ff2e9694..148ec0aa 100644 --- a/src/MoonSharp.Interpreter/MoonSharp.Interpreter.net35-client.csproj +++ b/src/MoonSharp.Interpreter/MoonSharp.Interpreter.net35-client.csproj @@ -117,7 +117,6 @@ - @@ -150,6 +149,8 @@ + + diff --git a/src/MoonSharp.Interpreter/REPL/ReplInterpreter.cs b/src/MoonSharp.Interpreter/REPL/ReplInterpreter.cs index 155abd0e..b7137fea 100644 --- a/src/MoonSharp.Interpreter/REPL/ReplInterpreter.cs +++ b/src/MoonSharp.Interpreter/REPL/ReplInterpreter.cs @@ -1,4 +1,7 @@ using System; +#if HASDYNAMIC +using System.Threading.Tasks; +#endif namespace MoonSharp.Interpreter.REPL { @@ -115,5 +118,24 @@ public virtual DynValue Evaluate(string input) throw; } } + +#if HASDYNAMIC + /// + /// Asynchronously evaluates a REPL command. + /// This method returns the result of the computation, or null if more input is needed for having valid code. + /// In case of errors, exceptions are propagated to the caller. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The interpreter. + /// The input. + /// + /// This method returns the result of the computation, or null if more input is needed for a computation. + /// + public Task EvaluateAsync(string input) + { + return Task.Factory.StartNew(() => Evaluate(input)); + } +#endif } } diff --git a/src/MoonSharp.Interpreter/Script.cs b/src/MoonSharp.Interpreter/Script.cs index 19dd5141..41c13432 100755 --- a/src/MoonSharp.Interpreter/Script.cs +++ b/src/MoonSharp.Interpreter/Script.cs @@ -3,6 +3,9 @@ using System.IO; using System.Linq; using System.Text; +#if HASDYNAMIC +using System.Threading.Tasks; +#endif using MoonSharp.Interpreter.CoreLib; using MoonSharp.Interpreter.Debugging; using MoonSharp.Interpreter.Diagnostics; @@ -363,7 +366,6 @@ public DynValue DoFile(string filename, Table globalContext = null, string codeF return Call(func); } - /// /// Runs the specified file with all possible defaults for quick experimenting. /// @@ -442,23 +444,14 @@ public DynValue Call(DynValue function) return Call(function, new DynValue[0]); } - /// - /// Calls the specified function. - /// - /// The Lua/MoonSharp function to be called - /// The arguments to pass to the function. - /// - /// The return value(s) of the function call. - /// - /// Thrown if function is not of DataType.Function - public DynValue Call(DynValue function, params DynValue[] args) + private DynValue Internal_Call(ExecutionControlToken ecToken, DynValue function, params DynValue[] args) { this.CheckScriptOwnership(function); this.CheckScriptOwnership(args); if (function.Type != DataType.Function && function.Type != DataType.ClrFunction) { - DynValue metafunction = m_MainProcessor.GetMetamethod(function, "__call"); + DynValue metafunction = m_MainProcessor.GetMetamethod(ecToken, function, "__call"); if (metafunction != null) { @@ -477,10 +470,24 @@ public DynValue Call(DynValue function, params DynValue[] args) } else if (function.Type == DataType.ClrFunction) { - return function.Callback.ClrCallback(this.CreateDynamicExecutionContext(function.Callback), new CallbackArguments(args, false)); + return function.Callback.ClrCallback(this.CreateDynamicExecutionContext(ecToken, function.Callback), new CallbackArguments(args, false)); } - return m_MainProcessor.Call(function, args); + return m_MainProcessor.Call(ecToken, function, args); + } + + /// + /// Calls the specified function. + /// + /// The Lua/MoonSharp function to be called + /// The arguments to pass to the function. + /// + /// The return value(s) of the function call. + /// + /// Thrown if function is not of DataType.Function + public DynValue Call(DynValue function, params DynValue[] args) + { + return Internal_Call(ExecutionControlToken.Dummy, function, args); } /// @@ -525,6 +532,249 @@ public DynValue Call(object function, params object[] args) return Call(DynValue.FromObject(this, function), args); } + +#if HASDYNAMIC + /// + /// Asynchronously loads and executes a string containing a Lua/MoonSharp script. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The code. + /// The global context. + /// Name of the code - used to report errors, etc. Also used by debuggers to locate the original source file. + /// + /// A DynValue containing the result of the processing of the loaded chunk. + /// + public Task DoStringAsync(ExecutionControlToken ecToken, string code, Table globalContext = null, string codeFriendlyName = null) + { + return LoadStringAsync(code, globalContext, codeFriendlyName) + .ContinueWith((Task prevTask) => CallAsync(ecToken, prevTask.Result).Result); + } + + + /// + /// Asynchronously loads and executes a stream containing a Lua/MoonSharp script. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The stream. + /// The global context. + /// Name of the code - used to report errors, etc. Also used by debuggers to locate the original source file. + /// + /// A DynValue containing the result of the processing of the loaded chunk. + /// + public Task DoStreamAsync(ExecutionControlToken ecToken, Stream stream, Table globalContext = null, string codeFriendlyName = null) + { + return LoadStreamAsync(stream, globalContext, codeFriendlyName) + .ContinueWith((Task prevTask) => CallAsync(ecToken, prevTask.Result).Result); + } + + + /// + /// Asynchronously loads and executes a file containing a Lua/MoonSharp script. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The filename. + /// The global context. + /// Name of the code - used to report errors, etc. Also used by debuggers to locate the original source file. + /// + /// A DynValue containing the result of the processing of the loaded chunk. + /// + public Task DoFileAsync(ExecutionControlToken ecToken, string filename, Table globalContext = null, string codeFriendlyName = null) + { + return LoadFileAsync(filename, globalContext, codeFriendlyName) + .ContinueWith((Task prevTask) => CallAsync(ecToken, prevTask.Result).Result); + } + + + /// + /// Asynchronously loads a string containing a Lua/MoonSharp script. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The code. + /// The global table to bind to this chunk. + /// Name of the code - used to report errors, etc. + /// + /// A DynValue containing a function which will execute the loaded code. + /// + public Task LoadStringAsync(string code, Table globalTable = null, string codeFriendlyName = null) + { + return Task.Factory.StartNew(() => LoadString(code, globalTable, codeFriendlyName)); + } + + + /// + /// Asynchronously loads a Lua/MoonSharp script from a System.IO.Stream. NOTE: This will *NOT* close the stream! + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The stream containing code. + /// The global table to bind to this chunk. + /// Name of the code - used to report errors, etc. + /// + /// A DynValue containing a function which will execute the loaded code. + /// + public Task LoadStreamAsync(Stream stream, Table globalTable = null, string codeFriendlyName = null) + { + return Task.Factory.StartNew(() => LoadStream(stream, globalTable, codeFriendlyName)); + } + + + /// + /// Asynchronously loads a string containing a Lua/MoonSharp script. + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The code. + /// The global table to bind to this chunk. + /// The filename to be used in error messages. + /// + /// A DynValue containing a function which will execute the loaded code. + /// + public Task LoadFileAsync(string filename, Table globalContext = null, string friendlyFilename = null) + { + return Task.Factory.StartNew(() => LoadFile(filename, globalContext, friendlyFilename)); + } + + + /// + /// Asynchronously loads a string containing a Lua/MoonSharp function. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The code. + /// The global table to bind to this chunk. + /// Name of the function used to report errors, etc. + /// + /// A DynValue containing a function which will execute the loaded code. + /// + public Task LoadFunctionAsync(string code, Table globalTable = null, string funcFriendlyName = null) + { + return Task.Factory.StartNew(() => LoadFunction(code, globalTable, funcFriendlyName)); + } + + + /// + /// Asynchronously dumps a function on the specified stream. + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The function. + /// The stream. + /// + /// function arg is not a function! + /// or + /// stream is readonly! + /// or + /// function arg has upvalues other than _ENV + public Task DumpAsync(DynValue function, Stream stream) + { + return Task.Factory.StartNew(() => Dump(function, stream)); + } + + + /// + /// Calls the specified function. + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The Lua/MoonSharp function to be called + /// + /// The return value(s) of the function call. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, DynValue function) + { + return CallAsync(ecToken, function, new DynValue[0]); + } + + + /// + /// Asynchronously calls the specified function. + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The Lua/MoonSharp function to be called + /// The arguments to pass to the function. + /// + /// The return value(s) of the function call. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, DynValue function, params DynValue[] args) + { + return Task.Factory.StartNew(() => Internal_Call(ecToken, function, args)); + } + + + /// + /// Asynchronously calls the specified function. + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The Lua/MoonSharp function to be called + /// The arguments to pass to the function. + /// + /// The return value(s) of the function call. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, DynValue function, params object[] args) + { + DynValue[] dargs = new DynValue[args.Length]; + + for (int i = 0; i < dargs.Length; i++) + dargs[i] = DynValue.FromObject(this, args[i]); + + return CallAsync(ecToken, function, dargs); + } + + + + /// + /// Asynchronously calls the specified function. + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The Lua/MoonSharp function to be called + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, object function) + { + return CallAsync(ecToken, DynValue.FromObject(this, function)); + } + + + /// + /// Asynchronously calls the specified function. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The execution control token to be associated with the execution of this function + /// The Lua/MoonSharp function to be called + /// The arguments to pass to the function. + /// + /// Thrown if function is not of DataType.Function + public Task CallAsync(ExecutionControlToken ecToken, object function, params object[] args) + { + return CallAsync(ecToken, DynValue.FromObject(this, function), args); + } + + /// + /// Asynchronously creates a new dynamic expression. + /// + /// This method is supported only on .NET 4.x and .NET 4.x PCL targets. + /// + /// The code of the expression. + /// + public Task CreateDynamicExpressionAsync(string code) + { + return Task.Factory.StartNew(() => CreateDynamicExpression(code)); + } +#endif + + /// /// Creates a coroutine pointing at the specified function. /// @@ -731,9 +981,9 @@ public DynamicExpression CreateConstantDynamicExpression(string code, DynValue c /// those cases where the execution engine is not really running - for example for dynamic expression /// or calls from CLR to CLR callbacks /// - internal ScriptExecutionContext CreateDynamicExecutionContext(CallbackFunction func = null) + internal ScriptExecutionContext CreateDynamicExecutionContext(ExecutionControlToken ecToken, CallbackFunction func = null) { - return new ScriptExecutionContext(m_MainProcessor, func, null, isDynamic: true); + return new ScriptExecutionContext(ecToken, m_MainProcessor, func, null, isDynamic: true); } /// diff --git a/src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/MoonSharp.Interpreter.net40-client.csproj b/src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/MoonSharp.Interpreter.net40-client.csproj index e5ea7555..d8144fd1 100755 --- a/src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/MoonSharp.Interpreter.net40-client.csproj +++ b/src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/MoonSharp.Interpreter.net40-client.csproj @@ -61,9 +61,6 @@ - - AsyncExtensions.cs - AstNode.cs @@ -817,6 +814,8 @@ WhileStatement.cs + + - - AsyncExtensions.cs - AstNode.cs @@ -814,6 +811,8 @@ WhileStatement.cs + +