From 64fb6cc9f347bce31a4d0a9781c5f80e217f79aa Mon Sep 17 00:00:00 2001 From: Pedro Fanha Date: Tue, 1 Jan 2019 03:08:42 +0000 Subject: [PATCH 1/6] Implement script async execution control --- .gitignore | 2 + .../PerformanceImpact-Async-Fork/App.config | 6 + .../PerformanceImpact-Async-Fork.csproj | 64 + .../PerformanceImpact-Async-Fork/Program.cs | 66 + .../Properties/AssemblyInfo.cs | 36 + .../packages.config | 4 + .../App.config | 6 + .../PerformanceImpact-Async-Original.csproj | 61 + .../Program.cs | 66 + .../Properties/AssemblyInfo.cs | 36 + .../packages.config | 4 + .../EndToEnd/AsyncTests.cs | 141 + .../EndToEnd/UserDataMethodsTests.cs | 12 +- .../EndToEnd/VtUserDataMethodsTests.cs | 12 +- ...harp.Interpreter.Tests.net40-client.csproj | 1 + ...nSharp.Interpreter.Tests.portable40.csproj | 1 + src/MoonSharp.Interpreter/AsyncExtensions.cs | 421 - .../CoreLib/ErrorHandlingModule.cs | 7 +- .../DataTypes/Closure.cs | 53 +- .../DataTypes/Coroutine.cs | 105 +- .../ScriptTerminationRequestedException.cs | 22 + .../Execution/DynamicExpression.cs | 2 +- .../Execution/ScriptExecutionContext.cs | 31 +- .../Execution/VM/Processor/Processor.cs | 73 +- .../VM/Processor/Processor_Coroutines.cs | 2 +- .../VM/Processor/Processor_Debugger.cs | 2 +- .../Processor/Processor_IExecutionContext.cs | 10 +- .../VM/Processor/Processor_InstructionLoop.cs | 148 +- .../Processor/Processor_UtilityFunctions.cs | 12 +- .../ExecutionControlToken.cs | 66 + .../DispatchingUserDataDescriptor.cs | 78 +- .../Interop/IUserDataDescriptor.cs | 79 +- .../Interop/IUserDataType.cs | 73 +- .../PredefinedUserData/EnumerableWrapper.cs | 6 +- .../AutoDescribingUserDataDescriptor.cs | 61 +- .../CompositeUserDataDescriptor.cs | 77 +- .../StandardDescriptors/EventFacade.cs | 6 +- .../ProxyUserDataDescriptor.cs | 77 +- .../StandardEnumUserDataDescriptor.cs | 21 +- .../StandardGenericsUserDataDescriptor.cs | 6 +- .../MoonSharp.Interpreter.net35-client.csproj | 3 +- .../REPL/ReplInterpreter.cs | 24 +- src/MoonSharp.Interpreter/Script.cs | 342 +- .../MoonSharp.Interpreter.net40-client.csproj | 5 +- .../ScriptTerminationRequestedException.cs | 22 + .../MoonSharp.Interpreter.netcore.csproj | 19 + .../src/AsyncExtensions.cs | 421 - .../MoonSharp.Interpreter.portable40.csproj | 5 +- .../ScriptTerminationRequestedException.cs | 22 + .../MoonSharp.VsCodeDebugger.netcore.csproj | 23 + .../DotNetCoreTestRunner.csproj | 27 + src/moonsharp.sln | 23 +- src/packages/MoonSharp.2.0.0.0/.signature.p7s | Bin 0 -> 9473 bytes .../MoonSharp.2.0.0.0/MoonSharp.2.0.0.0.nupkg | Bin 0 -> 2372229 bytes .../net35-client/MoonSharp.Interpreter.dll | Bin 0 -> 359424 bytes .../net35-client/MoonSharp.Interpreter.xml | 8037 +++++++++++++++++ .../net40-client/MoonSharp.Interpreter.dll | Bin 0 -> 366592 bytes .../net40-client/MoonSharp.Interpreter.xml | 8037 +++++++++++++++++ .../netcore/MoonSharp.Interpreter.deps.json | 1040 +++ .../lib/netcore/MoonSharp.Interpreter.dll | Bin 0 -> 357376 bytes .../lib/netcore/MoonSharp.Interpreter.xml | 8037 +++++++++++++++++ .../MoonSharp.Interpreter.deps.json | 1040 +++ .../netstandard1.6/MoonSharp.Interpreter.dll | Bin 0 -> 357376 bytes .../netstandard1.6/MoonSharp.Interpreter.xml | 8037 +++++++++++++++++ .../MoonSharp.Interpreter.dll | Bin 0 -> 365056 bytes .../MoonSharp.Interpreter.xml | 8037 +++++++++++++++++ 66 files changed, 43867 insertions(+), 1290 deletions(-) create mode 100644 src/DevTools/PerformanceImpact-Async-Fork/App.config create mode 100644 src/DevTools/PerformanceImpact-Async-Fork/PerformanceImpact-Async-Fork.csproj create mode 100644 src/DevTools/PerformanceImpact-Async-Fork/Program.cs create mode 100644 src/DevTools/PerformanceImpact-Async-Fork/Properties/AssemblyInfo.cs create mode 100644 src/DevTools/PerformanceImpact-Async-Fork/packages.config create mode 100644 src/DevTools/PerformanceImpact-Async-Original/App.config create mode 100644 src/DevTools/PerformanceImpact-Async-Original/PerformanceImpact-Async-Original.csproj create mode 100644 src/DevTools/PerformanceImpact-Async-Original/Program.cs create mode 100644 src/DevTools/PerformanceImpact-Async-Original/Properties/AssemblyInfo.cs create mode 100644 src/DevTools/PerformanceImpact-Async-Original/packages.config create mode 100644 src/MoonSharp.Interpreter.Tests/EndToEnd/AsyncTests.cs delete mode 100755 src/MoonSharp.Interpreter/AsyncExtensions.cs create mode 100644 src/MoonSharp.Interpreter/Errors/ScriptTerminationRequestedException.cs create mode 100644 src/MoonSharp.Interpreter/ExecutionControlToken.cs create mode 100644 src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/ScriptTerminationRequestedException.cs create mode 100644 src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.netcore/MoonSharp.Interpreter.netcore.csproj delete mode 100755 src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.netcore/src/AsyncExtensions.cs create mode 100644 src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.portable40/ScriptTerminationRequestedException.cs create mode 100644 src/MoonSharp.VsCodeDebugger/_Projects/MoonSharp.VsCodeDebugger.netcore/MoonSharp.VsCodeDebugger.netcore.csproj create mode 100644 src/TestRunners/DotNetCoreTestRunner/DotNetCoreTestRunner.csproj create mode 100644 src/packages/MoonSharp.2.0.0.0/.signature.p7s create mode 100644 src/packages/MoonSharp.2.0.0.0/MoonSharp.2.0.0.0.nupkg create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/net35-client/MoonSharp.Interpreter.dll create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/net35-client/MoonSharp.Interpreter.xml create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/net40-client/MoonSharp.Interpreter.dll create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/net40-client/MoonSharp.Interpreter.xml create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/netcore/MoonSharp.Interpreter.deps.json create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/netcore/MoonSharp.Interpreter.dll create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/netcore/MoonSharp.Interpreter.xml create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/netstandard1.6/MoonSharp.Interpreter.deps.json create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/netstandard1.6/MoonSharp.Interpreter.dll create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/netstandard1.6/MoonSharp.Interpreter.xml create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/portable-net4+sl5+wp8+win8/MoonSharp.Interpreter.dll create mode 100644 src/packages/MoonSharp.2.0.0.0/lib/portable-net4+sl5+wp8+win8/MoonSharp.Interpreter.xml diff --git a/.gitignore b/.gitignore index 79a83c4b..3584cdfa 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ src/MoonSharp.Documentation/Help/ # +/src/.vs/moonsharp + # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets !packages/*/build/ 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..ddbc1a88 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,11 +106,55 @@ public DynValue Call(params DynValue[] args) } - /// - /// Gets a delegate wrapping calls to this scripted function +#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. /// - public ScriptFunctionDelegate GetDelegate() + /// 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 + /// + /// + public ScriptFunctionDelegate GetDelegate() { return args => this.Call(args).ToObject(); } diff --git a/src/MoonSharp.Interpreter/DataTypes/Coroutine.cs b/src/MoonSharp.Interpreter/DataTypes/Coroutine.cs index c1b37b58..aeadf248 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; @@ -228,12 +231,102 @@ public DynValue Resume(ScriptExecutionContext context, params object[] args) } - - - /// - /// Gets the coroutine state. - /// - public CoroutineState State +#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 + + + + /// + /// Gets the coroutine state. + /// + public CoroutineState State { get { diff --git a/src/MoonSharp.Interpreter/Errors/ScriptTerminationRequestedException.cs b/src/MoonSharp.Interpreter/Errors/ScriptTerminationRequestedException.cs new file mode 100644 index 00000000..577ef9d8 --- /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..d6908d47 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 7e2a068f..af7cc5db 100755 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs @@ -45,44 +45,43 @@ private Processor(Processor parentProcessor) } - - 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); - - EnterProcessor(); - - try - { - var stopwatch = this.m_Script.PerformanceStats.StartStopwatch(Diagnostics.PerformanceCounter.Execution); - - m_CanYield = false; - - try - { - int entrypoint = PushClrToScriptStackFrame(CallStackItemFlags.CallEntryPoint, function, args); - return Processing_Loop(entrypoint); - } - finally - { - m_CanYield = true; - - if (stopwatch != null) - stopwatch.Dispose(); - } - } - finally - { - LeaveProcessor(); - } - } - - // pushes all what's required to perform a clr-to-script function call. function can be null if it's already - // at vstack top. - private int PushClrToScriptStackFrame(CallStackItemFlags flags, 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(ecToken, function, args); + + EnterProcessor(); + + try + { + var stopwatch = this.m_Script.PerformanceStats.StartStopwatch(Diagnostics.PerformanceCounter.Execution); + + m_CanYield = false; + + try + { + int entrypoint = PushClrToScriptStackFrame(CallStackItemFlags.CallEntryPoint, function, args); + return Processing_Loop(ecToken, entrypoint); + } + finally + { + m_CanYield = true; + + if (stopwatch != null) + stopwatch.Dispose(); + } + } + finally + { + LeaveProcessor(); + } + } + + // pushes all what's required to perform a clr-to-script function call. function can be null if it's already + // at vstack top. + private int PushClrToScriptStackFrame(CallStackItemFlags flags, DynValue function, DynValue[] args) { if (function == null) function = m_ValueStack.Peek(); diff --git a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs index a9664670..2f1791d8 100644 --- a/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs +++ b/src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs @@ -51,7 +51,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 ec6c05b1..c18fdafb 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,7 +42,12 @@ private DynValue Processing_Loop(int instructionPtr) return DynValue.NewForcedYieldReq(); } - ++instructionPtr; + 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()) { @@ -509,7 +514,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()) { @@ -664,7 +669,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); @@ -717,13 +722,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); - m_ValueStack.RemoveLast(argsCount + 1); + + 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) { @@ -744,7 +750,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()) { @@ -757,7 +763,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); @@ -787,7 +793,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; @@ -808,7 +814,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 { @@ -816,7 +822,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; @@ -824,7 +830,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); @@ -839,7 +845,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) { @@ -881,7 +887,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(); @@ -896,13 +902,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(); @@ -917,14 +923,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(); @@ -939,13 +945,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(); @@ -962,13 +968,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(); @@ -983,12 +989,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(); @@ -1003,14 +1009,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(); @@ -1022,14 +1028,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(); @@ -1044,7 +1050,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; } @@ -1062,7 +1068,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; } @@ -1071,7 +1077,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(); @@ -1086,7 +1092,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 @@ -1097,7 +1103,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(); @@ -1114,10 +1120,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); @@ -1131,7 +1137,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(); @@ -1139,7 +1145,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) @@ -1152,7 +1158,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(); @@ -1167,7 +1173,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); } @@ -1200,7 +1206,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 @@ -1244,7 +1250,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); } @@ -1268,7 +1274,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 { @@ -1279,7 +1285,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 @@ -1326,7 +1332,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) { @@ -1350,7 +1356,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..39bf82a2 --- /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 209ca2f0..7485b7d5 100644 --- a/src/MoonSharp.Interpreter/Interop/BasicDescriptors/DispatchingUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/BasicDescriptors/DispatchingUserDataDescriptor.cs @@ -207,15 +207,16 @@ private void AddMemberTo(Dictionary members, string n } } - /// - /// Performs an "index" "get" operation. This tries to resolve minor variations of member names. - /// - /// 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) + /// + /// 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(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); @@ -467,30 +468,31 @@ protected virtual DynValue ExecuteIndexer(IMemberDescriptor mdesc, Script script } - /// - /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, - /// it should return "null" (not a nil). - /// See for further details. - /// - /// If a method exists marked with for the specific - /// metamethod requested, that method is returned. - /// - /// If the above fails, the following dispatching occur: - /// - /// __add, __sub, __mul, __div, __mod and __unm are dispatched to C# operator overloads (if they exist) - /// __eq is dispatched to System.Object.Equals. - /// __lt and __le are dispatched IComparable.Compare, if the type implements IComparable or IComparable{object} - /// __len is dispatched to Length and Count properties, if those exist. - /// __iterator is handled if the object implements IEnumerable or IEnumerator. - /// __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 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) + /// + /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, + /// it should return "null" (not a nil). + /// See for further details. + /// + /// If a method exists marked with for the specific + /// metamethod requested, that method is returned. + /// + /// If the above fails, the following dispatching occur: + /// + /// __add, __sub, __mul, __div, __mod and __unm are dispatched to C# operator overloads (if they exist) + /// __eq is dispatched to System.Object.Equals. + /// __lt and __le are dispatched IComparable.Compare, if the type implements IComparable or IComparable{object} + /// __len is dispatched to Length and Count properties, if those exist. + /// __iterator is handled if the object implements IEnumerable or IEnumerator. + /// __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(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..9b13b1da 100644 --- a/src/MoonSharp.Interpreter/Interop/IUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/IUserDataDescriptor.cs @@ -15,50 +15,53 @@ public interface IUserDataDescriptor /// Gets the type this descriptor refers to /// Type Type { get; } - /// - /// Performs an "index" "get" operation. - /// - /// 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); - /// - /// Performs an "index" "set" operation. - /// - /// 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); + /// + /// 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(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(ExecutionControlToken ecToken, Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing); /// /// Converts this userdata to string /// /// The object. /// string AsString(object obj); - /// - /// - /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, - /// it should return "null" (not a nil). - /// - /// These standard metamethods can be supported (the return value should be a function accepting the - /// classic parameters of the corresponding metamethod): - /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, - /// __pairs, __ipairs, __iterator, __call - /// - /// These standard metamethods are supported through other calls for efficiency: - /// __index, __newindex, __tostring - /// - /// - /// 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); + /// + /// + /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, + /// it should return "null" (not a nil). + /// + /// These standard metamethods can be supported (the return value should be a function accepting the + /// classic parameters of the corresponding metamethod): + /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, + /// __pairs, __ipairs, __iterator, __call + /// + /// 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. + /// + 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..5a172fcc 100644 --- a/src/MoonSharp.Interpreter/Interop/IUserDataType.cs +++ b/src/MoonSharp.Interpreter/Interop/IUserDataType.cs @@ -8,40 +8,43 @@ namespace MoonSharp.Interpreter.Interop /// public interface IUserDataType { - /// - /// Performs an "index" "get" operation. - /// - /// 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); - /// - /// Performs an "index" "set" operation. - /// - /// 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); - /// - /// - /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, - /// it should return "null" (not a nil). - /// - /// These standard metamethods can be supported (the return value should be a function accepting the - /// classic parameters of the corresponding metamethod): - /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, - /// __pairs, __ipairs, __iterator, __call - /// - /// These standard metamethods are supported through other calls for efficiency: - /// __index, __newindex, __tostring - /// - /// - /// The script originating the request - /// The name of the metamember. - /// - DynValue MetaIndex(Script script, string metaname); + /// + /// 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(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(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, + /// it should return "null" (not a nil). + /// + /// These standard metamethods can be supported (the return value should be a function accepting the + /// classic parameters of the corresponding metamethod): + /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, + /// __pairs, __ipairs, __iterator, __call + /// + /// 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 name of the metamember. + /// + 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 c9392485..e4699cbb 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/AutoDescribingUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/AutoDescribingUserDataDescriptor.cs @@ -42,36 +42,38 @@ 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; } - /// - /// Performs an "index" "set" operation. - /// - /// 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) + /// + /// 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(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; } @@ -89,26 +91,27 @@ public string AsString(object obj) return null; } - /// - /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, - /// it should return "null" (not a nil). - /// These standard metamethods can be supported (the return value should be a function accepting the - /// classic parameters of the corresponding metamethod): - /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, - /// __pairs, __ipairs, __iterator, __call - /// These standard metamethods are supported through other calls for efficiency: - /// __index, __newindex, __tostring - /// - /// 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) + /// + /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, + /// it should return "null" (not a nil). + /// These standard metamethods can be supported (the return value should be a function accepting the + /// classic parameters of the corresponding metamethod): + /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, + /// __pairs, __ipairs, __iterator, __call + /// 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(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..65f48a22 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/CompositeUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/CompositeUserDataDescriptor.cs @@ -52,19 +52,20 @@ public Type Type get { return m_Type; } } - /// - /// Performs an "index" "get" operation. - /// - /// 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) + /// + /// 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(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; @@ -72,20 +73,21 @@ public DynValue Index(Script script, object obj, DynValue index, bool isNameInde return null; } - /// - /// Performs an "index" "set" operation. - /// - /// 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) + /// + /// 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(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; @@ -102,25 +104,26 @@ public string AsString(object obj) } - /// - /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, - /// it should return "null" (not a nil). - /// These standard metamethods can be supported (the return value should be a function accepting the - /// classic parameters of the corresponding metamethod): - /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, - /// __pairs, __ipairs, __iterator, __call - /// These standard metamethods are supported through other calls for efficiency: - /// __index, __newindex, __tostring - /// - /// 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) + /// + /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, + /// it should return "null" (not a nil). + /// These standard metamethods can be supported (the return value should be a function accepting the + /// classic parameters of the corresponding metamethod): + /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, + /// __pairs, __ipairs, __iterator, __call + /// 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(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 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) + /// + /// 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(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 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) + /// + /// 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(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); } /// @@ -90,23 +92,24 @@ public string AsString(object obj) return m_ProxyDescriptor.AsString(Proxy(obj)); } - /// - /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, - /// it should return "null" (not a nil). - /// These standard metamethods can be supported (the return value should be a function accepting the - /// classic parameters of the corresponding metamethod): - /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, - /// __pairs, __ipairs, __iterator, __call - /// These standard metamethods are supported through other calls for efficiency: - /// __index, __newindex, __tostring - /// - /// 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) + /// + /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality, + /// it should return "null" (not a nil). + /// These standard metamethods can be supported (the return value should be a function accepting the + /// classic parameters of the corresponding metamethod): + /// __add, __sub, __mul, __div, __div, __pow, __unm, __eq, __lt, __le, __lt, __len, __concat, + /// __pairs, __ipairs, __iterator, __call + /// 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(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..98b8c4f6 100644 --- a/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardEnumUserDataDescriptor.cs +++ b/src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardEnumUserDataDescriptor.cs @@ -321,16 +321,17 @@ public override bool IsTypeCompatible(Type type, object obj) return base.IsTypeCompatible(type, obj); } - /// - /// Gets a "meta" operation on this userdata. - /// In this specific case, only the concat operator is supported, only on flags enums and it implements the - /// 'or' operator. - /// - /// - /// - /// - /// - public override DynValue MetaIndex(Script script, object obj, string metaname) + /// + /// Gets a "meta" operation on this userdata. + /// 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(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 67f6046d..78ede5d0 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..a02a78da 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 6e3bb11a..e8fd3bf3 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,13 +366,12 @@ public DynValue DoFile(string filename, Table globalContext = null, string codeF return Call(func); } - - /// - /// Runs the specified file with all possible defaults for quick experimenting. - /// - /// The filename. - /// A DynValue containing the result of the processing of the executed script. - public static DynValue RunFile(string filename) + /// + /// Runs the specified file with all possible defaults for quick experimenting. + /// + /// The filename. + /// A DynValue containing the result of the processing of the executed script. + public static DynValue RunFile(string filename) { Script S = new Script(); return S.DoFile(filename); @@ -442,6 +444,38 @@ public DynValue Call(DynValue function) return Call(function, new DynValue[0]); } + 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(ecToken, function, "__call"); + + if (metafunction != null) + { + DynValue[] metaargs = new DynValue[args.Length + 1]; + metaargs[0] = function; + for (int i = 0; i < args.Length; i++) + metaargs[i + 1] = args[i]; + + function = metafunction; + args = metaargs; + } + else + { + throw new ArgumentException("function is not a function and has no __call metamethod."); + } + } + else if (function.Type == DataType.ClrFunction) + { + return function.Callback.ClrCallback(this.CreateDynamicExecutionContext(ecToken, function.Callback), new CallbackArguments(args, false)); + } + + return m_MainProcessor.Call(ecToken, function, args); + } + /// /// Calls the specified function. /// @@ -453,34 +487,7 @@ public DynValue Call(DynValue function) /// Thrown if function is not of DataType.Function public DynValue Call(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"); - - if (metafunction != null) - { - DynValue[] metaargs = new DynValue[args.Length + 1]; - metaargs[0] = function; - for (int i = 0; i < args.Length; i++) - metaargs[i + 1] = args[i]; - - function = metafunction; - args = metaargs; - } - else - { - throw new ArgumentException("function is not a function and has no __call metamethod."); - } - } - else if (function.Type == DataType.ClrFunction) - { - return function.Callback.ClrCallback(this.CreateDynamicExecutionContext(function.Callback), new CallbackArguments(args, false)); - } - - return m_MainProcessor.Call(function, args); + return Internal_Call(ExecutionControlToken.Dummy, function, args); } /// @@ -525,15 +532,258 @@ public DynValue Call(object function, params object[] args) return Call(DynValue.FromObject(this, function), args); } - /// - /// Creates a coroutine pointing at the specified function. - /// - /// The function. - /// - /// The coroutine handle. - /// - /// Thrown if function is not of DataType.Function or DataType.ClrFunction - public DynValue CreateCoroutine(DynValue function) + +#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. + /// + /// The function. + /// + /// The coroutine handle. + /// + /// Thrown if function is not of DataType.Function or DataType.ClrFunction + public DynValue CreateCoroutine(DynValue function) { this.CheckScriptOwnership(function); @@ -708,9 +958,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 0dc04fd8..d8c82ef2 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 @@ -814,6 +811,8 @@ WhileStatement.cs + + - - AsyncExtensions.cs - AstNode.cs @@ -811,6 +808,8 @@ WhileStatement.cs + + F@39?(IfSj_#K4w!6!HoBPX~Hf`D5yRBzixx0Jw=AN}12R5(W+;{i7yEpcg zx0W~cZQ8u9Z}YZI-Mzj2Ti0#c*i#W2@9y2YdCR79d0=DjmhSTA&3$DW^QPYJzJb2G``2}E-b%rq zjlG*TuidzDpm+1up59FZ1nwsZ+?5Xj_nJeamA>+w`^Lvc4^@5w`+ro}A0yaLeHiQ~ zC#EXHd&fun%0Gep|8=bI?f(hZ{~v(;CoOgJ!=sbq!}~_#qhpl=@s`oYhL4O_?mskn zq|!wj$k?I%tCjk^r5+kQFm^aTJUaD@qle?+@q-hGEAgR;c=E{9aW*Tp8OtHa=E4zDB7pSZX&{dvLUJU}7XD z-^eHx+c#Vp9a$59?(it@juXE)IyF8qJVNd6<8AlQ{`j#YhxTFrJuKZGjvv`KHAX!c zp4xxpAfXA>-eIX=>(2WJzqDoRrq6xmOAqdPL@AfIR>cFyH8`90bnpH{)bBBB(i*h$ z8^^~dj_xE7j7^S@T9j?W$II6C@v<$ZJ~Er~ES0{$GJ24Exx8MrtN>-hb-s_0ZNtaQ zmi`!x-pCwL;5{&WaP;sbA&|FkVrt?D&Ccj`OOrAqqkE6+U%52e899Dv_~6*Sj-?6q z*hJ^jxb!a^o;b9;=DlN;-k#-|4-QujEU$Lr@bydMvV!ExD`r}MkEQx|jgJl!CLWeb)<(ccbz=CPmu z=7}pkKX>xqT#bLUVdr01=TCg&bN>Ij{Kbb(_P+mTFTMDUfAL43{g*=YyJu+sSY_zo@u7s>96CI;Z|IuD zd1zwqFAP0A%FwxXjy01bd)5DIf5)E9B>rE8KfZRp*`|C-eQEQ=)Ryt_ox@{?)WO5F z5=KYYjEs+S+SS{rjsoC|3%$6&iyO1J>iTK=ei=FNr3_ao!&S;~l`>qV3|A?`RmyPn zD!vwdQ}lJ{+fLt`==*8g z_o)Sn{HC4XswDbtz1$A7+-O7y=d_P8uAH0G$nj^!H?qwftewADENQnbA& z(6vzMMarWiMdheO`l5Ab5{eS zOA6IZO1l#GZw(?Nc{Yio!sSH}Fu4m;Wqml9)SL;Ks}TSW$K&B@*09-k5ia^BRr|&I znddq63Y}?4L?Z+3sb9@Cmxv8^r3CWlK78J7wpE0B0rt57I^P8|6}@y7)!EV(%9CGn z^y#)*hJmP7fbCRkK)=i1_JH$CrUU6q&iG|=Z*FEJ5OEDsk;lIC7y}Cf42CXi80GS<@@ZMPV5S zGL+&-KE{-McjvD!J-xj8yDS2&r+8oJ4qh5e|z`!b|Q z{_z>|PKD^lc_jDiQzx7&*q5z_igxe=9A{pYK_L}?hNhz2_Hb-^&#MWTORUN~08`@x zkT!ut67=LuiUxpB6b-&g|X-Th2Gqq_wmssRX(e;`8 z<@a-SE}R*x>TQVbs&^)xQXlPym&lD6*omliPHx(|0E8vWM~6P{dioUf!?xC{l`@u} zodz*D1YhvgS`GKijmNU_)jc7)yfiCw_ZkluCFXuW-na=2?M`auD+QjGmlS%|h|DaDwPNu8pOp{kqc8F!Hu;mlsn z=lfMS_LBe`%U(b>S*@4x+IcjtH%zelcwJ(R%p|D<3rT`<%4`J)w&bMj%nwG!!QA zVGf$W>-Aq$9Ava7()8x%TNm)LD=6!Mi5~(OpL_OS!jP(vvF3#>jpRB_gQJz#!x7=C ziFtjO=ycjodhV}ejQbIlLpLo8ukB0{sJ?wbTjp1HtF_Brkc%U8yIT}`esl`oWqb2} zJo`KIJXwb^l0wZ;+*G*^mL-wBFH)q7bemt+m@C-+FP%tWx|1K#wl(FX4N{sMJE$Dh zQZGlO{R_DQfpoA;J$#>HlMu|i?qq>zHnB|1D`*&z$2>*lI&KdnV(eM_BJ-l5@o>q8 zzjSWe3%=ZsPS`8hSY4eSH`|8|U#f6`Ys)5%*;g zPQZx`-70!`@Bs%6f(_Oet0i4v2nNPD9qU&XW1;;Te-e{=+Uh_xN1IZF46HpYDpZUg zEe}>ERcm64utw4)9GVP_>txx46eYNZd9uCiY4-X=CsDV;<@gCQ5uSjD-^9cC_k=$kS>pW{3GV0 zjoGPS4%#r&9qG|m`PQT&AgdPh5A9*=P=sSFHv&v}lV{~>+j?u?1|qB@9iYv$nOsAS zQ=ro6f4plPABjxqPEJHvCgla2q}Ep!HcfdSDxpgeaCALgJKYz?ruVWV`MuHEd02Fz zGS#|#{bO4qR5jJ7EiVn0)t0*om~S=$W+&`Eub=%bMSBIRajlqZCJQc|`T!iL(Dhm2fB)Z4=LVfY!roZe~t_4Nj#gI zggGN;O7s3@xf}5;98kJ#3EtZ8%zV(FBJ5tLyHc0dpe~Qe|Cap{DGFLq2He>OxS4lV z(v-Q6T3mo;cXhmS;;?pa1VY7y)oH)|>vX91OmW&`t`|ASX-L8U$k91V=RzHMW@xqj zivs_JV#4E!z3#93=#Y@kM>`FW=e#*~uBwp(tfUrsL_MV}TaC9NRhRFk&UnmC0G>Oq z{K+KRRq8u*LR6wdLe}XA9GAp^hy)Kn+5JTIW2Hj{@s`&AhOJJKkK^~>SBsN3R-tyZ z@f}i==zEOMBG`ql!!UCs3Y1^Y8?FRym=Xy|Abp>Bhbb3oySLpTS^ z$gBQ#Y*yASOQl*a%| zn-Y(XNJVtOdF?&!$I+`cQNZ`gZs)Y4I^ zU$p)jxN%U4Mk&jl9I#!Z-ngwdC>Pn>x2R}}%9D=ww zwW8|?Fk*K2qmvnEO{jY=aCizMOr|+AQwE`z4_w~FYMYKD38(u74QGddyoMR4C9~!y z=wXl(V7qd6uL}Zr()h}GOj$=rX_m(}5j}Nm^jd=;4Tb2uw{;96cy-g|8CWwn8R>dc zY0A2!ReWyZ`(I+l8f_?uHO}UJXrBrHuap)Hj>;K7IMU6{Y0haosw7F|1qSk>>kt`C zY-&>w7Mzm0*01frGzRz}@)ACmgoa^_8DVJLSQiR^4X~Hj%g*xFs^uz(xdcM6QJMp? zRGh6EV3c;Z{(iAH_M823(-9$aju>{pu%ATcosqU8XvJwOFET-?;n&Wd^Koy0lgR5E z{FHU4KpjMXCpO1jEEr%;>iJToQieVNFMO({5n0ZL8~Y-wATRaRHe%1z5az#dKV0I@AIY8cU?y> z9OYhZ6NJ;?nrdO?k(5g1@e%@a((D=J>5W^Es39|OD&tCTy?Z^sncL}fGsg$FLpn4< z>Do*$yVBzLlHD?YyU8s+y& z!0Shm*S6+|4@hGf`7G4)dl4Z1{M^O=Vrpb(C?Bwdg(5v|y}A!HPV?`vX3cG)g#{U_ zjRVr4ef;JPH*;ot`ytqJ(n7Ht1059D^}PIe2h3THJUBCT-@3Y-0`NjumXK>0MwbXn z1k6O^vevPudQTV-o;O<}wU_F7iRL@RaeuXf40kIv-tJ1fL}*Fq@m0NSs0ErjbRgaa z<3<|!!>kl-Z&#Th9w1V1siG8dn`)w0wJZ%T*Pwn7BD=FSlP$QC)fIGQkM`>H1OBcy z;wm7NCX8SseGsxPy!H3XAGGBnnS_%MlbcE3%iWO!aVeL*da*Orfs075n__T58k`mH zIJ~IW`h3zGkOw>~mfBe?|8WUP#g6Dktn>lvvvV3uIyMPKt`4__r!VS@D@W0W!Jo8i zSzQ0lOkT5w3y=!Jr+GNwwOeZc79FGu4pXjQOPjvy+1RKHOmn*aQ)77v@IMGokcfk} zCATCh7eP^udgN#dPucxftY^{x=&^GNViHO{<2f4D*#JvlBZl(0B<2JRXVD;m;ovoW zyeA__SfsIbED>Iv8nc~BuQ=K-WXS?#-scfBa9MD6p?fxl->S|PIzf!4NB_x(FQctC zmyjXy2Dh@FD+#|}RC@dW%4HU5GUL23n^ZuaFHq`iG0(W=sh9x!0l(>Irmy=Md2;Iq zJ(e=&m*9cmiv2KHd`_tIzxEEr%8n%c9kEs=+&OqD3#?cK1`_qK8D{$DRmC|hIX&;f z1~hWo-@K(wbN0FYD*oc07n&VKTF}wZHoa0=S8=U;Ekrraboed zR10@w;2FO|wj*`n&QLRYVWFw?rWDCISSB+lujw=2Z@a?HtQ>v6eXIUVi9&{bapwpH zU5oQ2{?Np^x8a73r0!3#@Uvj*iLwgV zKriG0cw@T!!W^jlpQxoEr(auut*+T5iEwQaytlV1`13aC0@0l`-Suud5mQ~v#c)w( zaG37IetIYQFi|5M-8mLy5vh+J6wEp;Pb6GZkkXx4HrmD6BMt>Is<#_Grdqz4YB!Kp z?HRak*m1#WMO-S3HPGBaM%OddIk3aOQOuI86$V?VAU|x4oT7V&M(WeHw!d@_TIK4% z?vl~DYVw=k=TuI>fSYDGDZ%|OzuV6GYA-%Nt@j(~RfIT=x5_Tq{iX~-q6u%{nyMYR zcBrReCLmsV8LM8b(4m_SFqw{?J8b3~$upvIA*TiPCW|eggDAQ+Xp9HXcG%;yZ1bI7 z&49OS%USX*wC1bOiM|J9*W$8hrWzYrpDKdZKtG%KIWfIP*NRR%Fx6%^UvS*aOUy>BJ^$*+eiCdD?yS@1#k16wTy6|XSlkRXn>0!Kuh)gu?{|?GHEOO8dlY@B*C=gG znjWgydGfyt1IQD#dOA^p-2JKiM`QKY>|s6H>N3^Hog08)MIZLD%abl6e{5XT1Chi= z`5!n02hfF(*IVP9{~m5%w;--ChU?LV`M(9Jz=-)`ZR~hQqObRx9{I&WHeH3_?EkvLD!>b;9s7cn^3-yEs8W)fN&og0 zPYeQiOSrs#;p0{BSvk^S_p6NyM%lkE1@~XVz^GP*4+&!h^rcdr*q%(B>|~9&Ur#O9 z81sjqbBqk2aGBaw;Lv{IDhF9!`jD6by0@K;?#;lTiu8tgM}v`G8Nk`mEsoH!qr~I2 zcjz_adaOV`RoGxX&Js&$J748Uy|$9@wcr-IcoS9a{49`yWhegMwk%I}rB@~Ot4@8< z(0>ab#ud3@{J>wa)7zc&oyhaGz2%x-WSbdmyk0J7VRc9 zLOVG)+5kk>$=bKj58PIW{I?erG> zTm0blG(vG)-(7dZ5z)!<;d*sn20aW2Eu7{%4$xxO$z}uB)ij5~ifF7-meDb_B1klV zvfI^FEhQk5(@e<J+^xo~t555Lp^}8B)K_cMI)t-mJpvDgOdnn< zdkZ3l_D-b>X?{e$wpwB|M)spF`eu=3)jB%eB?s-&3R5CakBf%5KF~G9_Wz6689B|r z&^YIox1ax~h>tz#qal!?k)1gXEuz50PupDW)MW)-YfkM)SG$nul}6^Md9=?xxWUp# z!E$bzYfIN)sdo*RiHKvxZnWUgRdN&~zUB0ep1Yr3A{^sroL-FR7AD^;?G=|`=TKVP znh7d53-HH0$6-iW7`yJJr8zoT$ny%Nk>sVNQxSoK$=2)I?rqS|bF1Qi5bq#@zW4!Z z1l(CDG#YjS!tQ<;S`)c`hISfu`XT$QYAlYBUGCgrH*`7Z+T!Dk-hUArBUw07L!OvS z?SD>aFCg^fIV=NE7F6-}qA8%(jiii=0kCaO2l8A4g^qUfo2SQ_VPLo0>)@Rnj_SOQ zt?D)%?b)0gCEzfNwTahjxY+8oy!TKzn% z<<!Rrgh=6z*nHCGE^zYd7G`rM zQ%dnP(7%T!NMiwIUGCgZe*VJqlEcA^ST0z<>(YQYdbl?5s;qK0E-uS!#HH(UgX9wG z!3(>{M7s@|axl7FG&lqxYU(dqwjw1uvm$wj(47T? z0s1Z**n4*8n>^j;|Z=5=DL;S1uPu_shuAK1Oe($56zOhXjWb_31u81!R^rdmHJ2(g}DYZC|am zNms(IyV7s69B$(;mgrYkPz)&Eg% z8UZPb@%}D6yb8B2wr*ou?$hw$Rdq`IbPe0^}T6nCwOw=e6&o6cI%X_w#RXMEU0^q zb-HrB%1g(hQjfE)7Nzs_(zdT55Q*PS$Oo>DJA=HW{;{%qKv?X(93gpe_mAQ=gJzuB z<_Z+|Ow?4x39d+kz@wqun$RqQXflNI*C&OFUC{NAz&lO~;`yEN8N5lMS^l=xpG_>J zpFJe398u&Z;a+6cwFZxl-qS7TE0h=};kMF^j$Kc*9I=g7LtQT*OvDM$!J5DRzjyE1kwt%urbS)fcqEo**@j9k<*X=b zixiJgko5d8zF*vPC$_U`NR1U=)qLOO*cay7UaRX=L`#+a%$vtn-SG`gD+4`bOir6#!5Y?Cw=}X!If1O`aXKQfZlz6p@3SiN6N3#o&bw>uIrKU(Z~BKgE|yX z6<(z0@Prf}aldxZHi~VFcVpFZjeQ$kD1Y!V%jjF^_|SaMh^1DVc_3Je^p>@}ZMhYLU;0R&4}S_;~o3_qux~UD*|r_2ZV` zc7BbD7sT(+{5nQ)*D^ZY$wdAKgO>1&u-nm(1lsrw0UnC{R(+R*kXWm=)oLpdD6=++ z!UyOh32t72lx!ue=&|LLh!=eyyC+`Uvl5Bpd!Ysz4A}bJem6^Vt}{NYCyFM1MTOgH zQ#FlB<|~g$Vic4nv@^BtE+4x|7vnd(ef3y!QE=EC?R6`hK7-Q`rLKlB8*!G9w=?RJij|8Xn!D{ zRs1SAh$Gfd>|0Y?8GrX%%4xcRRGqq*&Xb@7o@nFAKPQ%A%J|0vQYMtY8GH1*EP*Y> z*OU>=#*t4=7|O`(WZMWp!Ba8oCVd(vZUL1qr_6dfa5@7j<@@a;ba1HxiV=*JWPJC- zHCj@0a6xAdZ?BAe%j;CTIjq=!yh>4DlM4P-&K0Zmd0L&FKSRm>6MNb6(9}Cu6L*`} zFNQ26xea%7mZ6b+O#a53N1f}^2@$4F0X!>txHC}@}qf2$t zdCYN#_`YHDW25*^id6;ii|Kd)x}NRWk;bmOsh-5-Yh$JEcye{`$cQW{FfiyDTp1q4Hxt3c1W4 z#4>#ru}yMdRKO}CNL9Przwu3z$a8aY^Kqf#pwXrSFx`k5qC1`~k<}Vc7#?M>_v}Hx ztWwIEiA+iGCIOgeI00Hn*CqI6-s459(yLI@<%6{3rSHjGwCUv;6wxbN%?^cNp%}3S z@40C1z04D2^;~|vs^w7vZOFbuK8s~V;KBCtF#G41ui=r2&qJBEI43vKB-@zz+EQhE zbV$d(fA%1U#H5w8Ha)E~Q+FFSC^a?keYKq%@BhkZ`03{=Nz0~Y-x23{2T3Bf{&yC% zJ#&`CDLHA~hJ9cU_6kuoiSdqPWbB8w_2Ql%kCp2vCS`Jzq5D6i`&`rC!&_gDXuZQX zWzu0@6!gQS5t+hGRTNzG^~02<-N69|=9dODL;PucxyPOw1}KQj1^0+Ru7Afmz-`(X z3F2uqOYJLRm5@dI-KE~DzPF4WvvYu2?lX=yugRDuFxTc%zrBZNKV;=ZhL*pXqE{%G zuaZ=5k)ku_Jud#y_O}GG^%-k_ixjQ6-{Vui-e^b|uA%leX0aRpIwKM-Le>VIdYsZM zs-f`pCoS#K`7yUSH+S7e8jsbCn;Vmv^-R!Yb@vMoT))8xDpgCLqVa=L6{R(7e23}o zCWzAz<-n;aqG&2bJr-zF379-)a^z#~1+6<4J$rMk2N!2b8dCo%7kx+BYkYi&krzgK zSI_4#+a4U6gmISx?a^!vb zIFm)xUF3NAgp7mo=jt^Zy}KVZN%>xq_Nbk0Yylx<$ zZ1$kbaE3>z*AG?$I7<&P0DEn1BLrK4wVu6d>gRZt#VhR#PF25WTpUsd(6J{JTSje( zq6#HtCz=+U(P>E%Lh-#d>V7W6&%V*jlKpx#;r1e>zSMba)3=0twL+u$wJF0gnyWnNS>^|mF&+qJ5(f5)UK;4sHe1vJg5rlHk0C!sXTY3Pvl}y`(#*A(pmD_ zKnU9=KDI*XlzYLLg7#bVZ=U%%GxNA3PAWS1@Aa@3R%Z= zneUHWZ09lC6rS6=l9_Uu3=I1S#_?%&Ft+q@eOr6_h9|hXBTt^Gp6h)&67*Uw)9sqy z{jIfJ{{?77t}WmGLmsWRL)M&MN{w>ljFBNzuwxZVdb+Z4^uTr#x2fu)ImT;wWxu{C zoa^kd&lPkedl_1TJb?)vDiRb^{r6ml8f@=Lmb^(Sw;g=v_9n^CHsP0TRIYq%_FqS0 z9yd9B4#{X6>L_V}Ix58)HyPUVr=K(*EWwRjwKmeM5-d_;OL*;2&jFo&_LPtE?kYesCNG&4 z>kfhzks%S4o&B80>dm=P6kzbVo4e8wyD5~N=5A2w_nbJ*F6Kcyxx+)%xD zNmfl9TJvk=o(dZM?hnWhxV%%3Y3xu7>zLEC^)F=gI>890WShO;kzB&Xty>lA;6LYg z!?jbgWv1EGisBTdunkv3jR?V$CpB)IZT3F~7dQ&AbwC+0Gp|NU*poULk@uYvJq+Nk z(I1l@a#16=)fBC>Z8D+_QtRf)jB-dd4}_RYk5s#=@{iu7Hw5vuB2@0a?ft5O)> z6zDH9=Oud%zNB^uS`k{f*Cgh2VSXl~;bJ>*a+>Hrx?R}Rb016zSPtOp6#wtX= zm10)>k(N(vV#Z6R#rQ6eTkc7L?8v6^W0mkLqaPeBl2Qwf%-nKh=C2n-sJXx6IvCdl zOXyQPdo62z(4B)*$lXnOS7F7npN_S9W#UwQ;0;@(evJtB~g4f$+wW5kJDJB0gt-2H8% z=sq30H$9C>Tv}TR9`Pn1pXF3HKl9yGy&YMHlkGkwW{n-&D|71nW51 z&E=wc*^n~w<8d2CL^qYhK5(wXKftntMjvBZBcwDKI7R9MX6o{UrRVKj4y9tI z>exVhR;QMS9+|N>kUQ~en?70GPjwJ^e&QNeedyILIC;Vwdv?}m4ny8#(Bds9>u@M7ZbV_*kfh*#kS?#M- z@U_~r6S(h-JK@d~!efY>^1t+0QW!a&lCzF-N zz9V^nml@OU`+ZyZ9ZNyT!|uzm!+Q&LW^c=qG#tkm%aX|K%}pZjQpiXr8dm|CYZD%M z1BGUFq<;uIWH{)t-W!`-9!e;P$aI!Y;cW*%w;f9a9y4jV{;%_2T(fh9MfeN(Tj45DF^ODiNq*Hg&IZwn zcRXpbHxZA*gyRqsKQane_G{F@i8B3DBJQuwrqGeF?>~~XXXJ_30Sn)-{Y=l$`?`O% z)8M_gI6GFN4>-k0TngL!L1X3LhpKs03~Rs_8Sjb2!$O`LSz|HaT)d^Ic#&a9B`&1F zWW+FkCg-o|YXbOLj>W))2@W)kk$$9V{*-|PV=J8gFZZ8R_*YCZY|V#VmW)>-dCwW7 zF|&!V+%WhbGd?Gpups+!8e?(dII8`Sn}m~W*^Q(aA+N$NG(a&FqKrlB19E)6{Ak{k zV-nS(`&d8Q2Df9wTYVOA?j;aR>`98hs5c9TZ4q%_Oe6L0JFR2Q7aI7FQ15C63I{$^ zZCloN<9{XAq0$8JdKC4(4{_xwDXtwfg>18UEcm30yf}^|9F{01Vz#O8P0c5k>RtXb z6+@JL&#}h#w;>2??u%#3@lYA3E0Z`j9>rY&kB}oPOQ87odT|+#-}rEhz_zz+mOtEfBe$&ccTNQPToB?nmy2Oo)pW6r5V^Gk(}bR7`x4!$~FLrqunCtIDpOwpps%ej*%03U8 zv!XG{CER}a)(B6^$vphg-tBXlPh1p%7%;(Tz=WSlHfc+lfkISg?wa}0hL^47alGxI zY~uKw#@;NPOeWb;^OAX&sn}4<%*Eubj^lk>1#{O%%<%@fj=vvO5I(I;BDGeJzN9J2 z+_acn6U@;vMZ1?BO0X1>sA~d?b4Z;jmwepQy z8%Q&0f)fHJDU+oRE8nZMG{4L=JJ}MJ}q?a?zOz=Z$I~+omjM_e zy)H0qMWB_K15#1sKVg{3`c!k4O*T37M4!APvd7ho76-65`>>jhRiqoY6=WEGOBl;f zFKvmZq~ZwRPJQ1i^jE-sgWm`jqnn!B?ARilc=l*nA9I3_4vNp)-1ieSc;fG=I;_Nx zyV#Y=2sJ!n^%n?k_FqY;cLxhAhYVeIWnlkKwM(GGuaa$EKgQuW!L{&8$N)}!~h z7~OnE(3GD5>{nPzp>sYliEl-$VnvW1D$2?j$WOmLzWJmLq&muC*EUb=+J8JT4;E`2 z;*h-XBAAPmV=T>v-LbZu#GD)s3F2F)--_Uw*PFhtdH+J(3z)@Uw>8eDoJEDVl=Z;J z!C>a`$VYeQhf~E-XG%U^AF&(o-OGa-Kz#G>TmjCQ`ikB9v@Y}3qh;pHKBcCcdBi{G z*mN$B+Y54O)K^|l&-y%Ic%=SdXx()*PnZs-46}#tE*DH8BMFs62MK)XdUUq;)u zwM?*~Aa5{=?Mr#RsHXGfsq)0|9S8-zNH>g0{0u;3|92>{b z;)L{#Nvk43>$F*EVS2WTr5hg>pTrM`xjUJfqRuF}J%eKbDyukm?R%gYYsQ5#>4u$c zeQdsLrEMa~s2zfub9N0%1xnQv+mc}WMz-`$dplTgC(|90@r>m>8pDvzGzofwrqt-&FvZr zZ!Gk22=06(0&9gr1&BV}xY`X$&0SIBU?} z?D^p5WK=M8N*?K}C#NV2@BU#EG<AAn5_+kF3}=1 zmcrZxb%N||=E1$@I%cOsE_j!Aw?6Oon1?_1K4U~R-S2{L@**g(T!v$?Jc-y~IDNM{yYCf~t;Qu^#7yNsjGTh+2e5{QE-;FoeVI9^sZ4Nu* zUlC#}+s_A6!DWIG!b((E-eQ6ol8vdic~M${5=mw1r$cj4QKEbZ-GTNpAtHk+wnDXP z=O%JQx-GM5G_C)NXSALd-kh3HJz;dy^Q%Q>Ez>Bc7?OUv#&0IEtQV^Rjk+K2_&+Md2b@o?5_FBjpVUyTa4bHePVlHPwXM+P2>qB7+go;PI< zcbPuKGan?st&2WP!JWSQvr#Aufj3V50w)Z|j)@AP5&-^q6V|m@#Yv?U2{vj(=EmZY z$Mi)o&&qo3H-s(9qS@u2Z75DZ=$g=ckS^zj!x!4t)}4rh@<=kT;#-S=t}*fk7yd5| zDQq*n1Ne8MZU1k^9erU_tI_^7#KOD1RT8a-4PuiES~v zRR>+^1!x+GI{cTH&)Z+EC@05N1a?B4;X_8dQqoy0(BwgR4ZQVw+;%Fs zyJGkh>ssJ8_ZK+XBD4bGVR?3X5-2n;(!M)SdF^KcvX_2;&!@{>N0q3n4p6{|s^O2L zchRj^wbqBd%2|j2=sGTn*Fgtt#@TvB?r_4PkDnaWZUE2jFh2$9@H{df9RF=%N9bk1 zCcIPHxnd69ZeJC6vc{=T_q@kV1I7{#t$LAuW3q6~c8X5Js6l?K1@B*AOiR%B*!KsV ziO2hIYsraHY1+j^lT}DRJi?5t&!BGZ?GyMbYvq;Ja!geApRh9BUy71Tyocfn*-dp0 z5_OnYl3rViq5@aB4II~QN@={7Ez==V|AzS;tY?o4b9OW7*$U}y>wil%`J|VOObF0+ z%yMHP@18?P@D3enOFkVr=xFk4L~DTCDL=lF_0PklQY(e6*YGfMPgaS|qO+7SnpK@| zE(Zla`+p3_T2U&Z3OWSI33(LYw$MZuP5zASLi1*l zOoW}(w7a5)1^-dfjf87fr=i}|OCGot|;B-DM66ma{3* ztzt+YHYGQ3T7?NX{kgeaeJ6my@rtV7lQcNRQ^5%bC>cYO)JDH!w`!TUwVe0+<=MyG zFo3et<+t-{7NPTx2j9G_wJJy80CONB3*wUk@OF?`YFsc@a_sZiDKGfaE2>?I^+)xTQuWKG573v7MI z5UeLQh0_Caob3dSXvKq0k~!nTsu3pM)qP5by`x{k&AX~KljJ`SK+K8Ws>^b~6kcxfL!8{X$8>`{L_$$sZOEj90=== z^Lw018MDvN{vNUA&qk7+M&8P|2fmeqj9&8>nP3mX8#0M7o>^Ulp}x#;qV<+|hnWe| zYuJoCL~l=f>|ygsG@@NeG{XH)oky3j-^K)j;E-z6A3 zT#5+nEJl_Jm_wx5ExF#*N3b(L>c@slr{;nrRaCjuFIU8^PJ$*UzyICMGhdF`Ja z<9vuPQx-axk-!0}oLynx8$mXC(x)jk(9f{8n)h>D9Fknm&C0FeI_Mgj>DJ7e#q`ILR3!mn9o?WXNnZZ$u7B(EpkjiNlb&j+S6VN$p~k* zLp@U4#KVuISHj@GPU#IjhCN7?(ay#)sXda6u!PZ|3*LB$WoR_yXX!llBmi0PFhk_e z)t#6WPx`9yufyyK8p&fRc|9CKc29ROn;UsJ1ym)hXVia*%rC>5y;zd@elPM06}EZ3 zLNR+joOpHUnoR$vXaG--%V>`|yVu)29Leixkw@lsu;?2cs)Bkx(uq!}eXZE)F%7eO zYW>zgbmUWCsgg|aCEJbI>@c2v33O(A|G@l^2dKiq1RQu~kkSL1W^$|xyJWxVYEwtc zh8up#5vF^8anDa3b;xL~f0WF3($^S-;*YEh zp8E8qOi&Y}TI-qgKjzBRN>d8$UMV*{BGqjeygo-s26-hIh#2vbgD8hiq3bsSvPpES8 z_$+9xx$=NxP&D{_1qEz9Lk|P!^`vw=5wKKn`9Zee^WAi4IT1(r$KevDq@>Tuel}kq z^y0!P7lnPCjqfFx0L_%mMf;ZJZfnspAd&(1gesM^T^Mq-yD{Df+yPw6M6l7oY#3mZLQ8;PZk zta{6*Oc}L|tL@M-!RXF0u};>;Z@R+pGweNE?rNw~?7{d3=i zDAGXYJAv{aXXa+_|Dhic^?8i>O7dy}ylm?GH1w0!K3O}~wmb~g@n zMpo|>k+|7Z{&l8cA}`LL$9O?vc*7cb_r#xcX-bRXNlM^}P=i`!8|A(mEX5JnR#@6cZr%Q}eLzkG-EQpFa(~C-xssytPl&Z_mj5MHrIt(K5!&7k~5dZzbSJE+Ykz`r;$ zxVS19_8=uS&h>t1-kRdf{K96Xu$$S!zM z*uT*d_3@z%*u=@qQ&nJqsQaCU%(};yq2VQ%L8mxdFUPP;vG?v&2V`xPrhj$H+$vExnx?&d zFFu>sV+L~ZsNGK7V>DWb4J$?Tq0{##slJoHR0TIQQ2U>^@vt9!so+^R z@!%E599Yh{pmc@t1i~=k5^;xW2X}JdkyoS z;Y)ewn}(_3%h=n`W##ro$dzW7>Fv+6>*+ZW#F*aeh+b7*!(BFfp-e$6cl<^0-TOi! z@7(0#gMM=enHei7IOsyd-^qFLikB9T{lLBjzu(C{5u|kSDfl4&c_bB`s0!!jy*j;VR+`WSzb*-oWmLqw$| zr#3l1E<>+N7m^_J2NH}_*7hiMswm||j+wSpV)O;bxKR}dQZ?ZGCMdUpJ}X<$`P!v2 zDo^r!>_`VcGC4b(~%7tj|Pdp3rx>_)Qm_guEhb1H56o(;?@v-cjz}C~- z?q@pDpYRPIpQWcqg6e^sKCjC%TM$QF^6fAN@MpS94O9E^Pxj*(8(pUgXX0>yGze0DHBus$E6QS=GID8e9XuE$wF#89}KOjvctmS~>N$K@)MFZb>zk0#%BI*jBN+>QHJ{XPN+ zc|*hCPYj|WboW?JH5tpB3(SLN`0Sc6v;)qsio)dNS~ghl5QAY-_4-NA^%Q4iEe8@m zEiQF|H}e|m#)dccY7T1ttU>G68~v!;@`E%5@!Na9VRlvTMz73ZAbVuNK_|qQ!ksTz zk`!C{D#)OZnwxxw$Bdx?x&vdGP(^ENnM(ByL4AWV;>UBCu9SPe2Vnq>z5HW{wD0rv zj}9?j`U8rT1EQAKYg`$%{lf`***dFF0bdCZKn%-kpX)cE!B^@L>|lHb0E3=3C|Cxl-7_xfK3zkS#N+gE2v_Wa+|AvfA)0_l=u#H? zM*k?!ZK%y;cx)l$t>GZ!=8+J7eOlUX*|l>8{5(;%Z@h8a+VJO+XKGC31TKy^1>*tt z7g>zCsu!~~ab*>cy0v>O36sFFj}Pu+Q3gklH|9M-zN?+VI=_1%Z!SOsIZYv3^>U@- zFPQ|aaUawmnvk6b9oVmjsk>SZOXr<*`loijh>N=UnxviC$WR#c^2s~>JuC&Z9gx6U zJ;lndISnzPal?xRW@LNV2PC_tPM*f*-+=MtRB4N z*ahEv;69{}<7-lBc)m~G%9^w<#Lc=cn?#D0m|l2zR1@|F&wa=yz;~5jx^|KhyJ!=f z%4B_tHK6NUky5|0FIQ5@&B=bPdd{?Mkq!HgbJd#&5 z-h?9KUZ?!{h-`DLrzfqp`bzTMMEfgE7Erf{b)_p%gB|j>@@Mf~mB(f;I4SV1`Kq29 zzUpvcD0dt$F5IG9~#zb8e9Xb|~y@wK7r(@(d0 z(qX2#ACez7ZN5ISiL@zRamEjT3LnUt3yM@uYrWUs_@$j_LO%e)w!-RK8 zx=bG%y~24NR2?!I?$PqvL@#RZI$a-~0P#&poqu_w3obJ2THScV^hn5B!WDku7%Y-9C~{*P_qc9Xi`hPs|rl z+*7wvtDm(vX};a?W45@8+(@gHn+itE{bjNhc(2LovbRCD?TeEiC}A2uM_j4E*mx@v z@~CI~E!mc`XbXk2;^z9#*8Rh%2$KS|#F6_+g$Z-3NYU@Z>v!DR!VAeT~v zy{nF`)||Ia<#d{PtKj3Ek8ntq@dh`_$ebxx&~0+f%7$0PgN@fFRQmXK+l{uTyG5;x{?)kXmhI^mY9&f8_ zHhuBvd;1B4IthS=<&JX{-H)=jv{FM%1kx~0u9MYWE`fCZM}j^ve;QK*V@=Za$I{6|ei7d_i zeR$t@vUXTu4OUv@I~l)x28Q_n6m3kX@cX64kHCimu;!o1aZ{&$9{LHURU)g*Ta70t z*5Q&V_SNHGko&jyiW4xQb zmXLn0cNtfz^?VYF8#d3Iq(NBZMWNDG+nk&_wQ=U^+gRTGiBz%sK+)?yDx_ zvkKz+mwBENPB%1K2*dbF)Z*N3aNSz8KT6?S)Kuzd82_a1%g1+S(~@6sk&K7sirfmC zZ{M&k{D&^Za4|4`f4X9qg+P8$?t%Lx2zB_(QF(58DpGa=AEI7}yDDOyZFh5$stMg_ zO=)(9rJN>QEYrT6%)#RMb`!j3CT5}OD<~vkX@;|SA^&Y2DF9blg7(Oyi>=wiQwI~f zHt!ZfyStBY*zeiK-~Lt${S7eox(Cc>0TdNwQnepmw1e86oTDVUQ;I5`Zo6!hhH-}L zH}{uy;-!lxh5021AX1DAwmQHAuRS}_+OdLqYNvXrH9OXVOIacFAgwMDNA(J;no;~) z!+TlBH?3#`AX>QLp!sJ35?NJh^ak03JA1dN%&J<5JNptSMk!6sewd5Ax8_3uAy^ZA zaB?sgPhlZdN7dy1)Th>LC7da`F!KwpYu_Aux|0G#jx>{7-|^( zf#J>#EosfhHA&Y~nso%AQeHeG)Z^ibrM>Z^X9G6IiaTJ>eccDQ-;VuhSVE6YAH>&D zO#WD+fSH+t6L2gkZKONPkE&nnEE5UyX`vVI$?H+dW#ADh7(VIldO&mWy_$C1lfaSe zOH4>7BX=l&-J6GYj}{hr2FGmR{4W~yfuBES`bCm≤2mi)~ZTk zr*%THL-~gx6jchyiXV&T5DEzC8aG8>ohwDiLyF=$3R%KQ@~-zh*LYp{TnNlK>6P(n#H^}NxNFq;!@`j7h#tm1)27H(C1ev1v376 z4&$v6z%%d7Hyi(OiWX9WA77$a^{QlQQ!N4uNDDrF#0vaKVeZ>tsTVl*A;q>yVHstE z1!%e;2S_WLct-v5GstH>CxhP99T!Ol7F#WhUGE;C<;Cf`KomCe&8%Ia6fB?KsjeY^F6n%`J6=q?zdwJ(&9<)BbmFP2*zp zhGdQRb!Qs|=4~sMj(km_T#W({MuB$xa~(mAaKE8ghXu(!Fb#V{9s+tcLTvsdJLnVTuDRT>~?aBaBhc77O?c{OKE3~e*bP3!V&6Zzr^%Tk^pk=OKwM|*Ukfz zM^Y0$DNRj3WRJZj$l$FgLMSQb6lao%P?UrT_tUbS*vFk z+QzuTj`9hL-}(``e|MCI`R=**)_%Q5_b`<~?Rcfvk5R#-Zty#3!0{CcS~0?sdzz~M z-3i!NoeDKE;aPVqW4+r<{!Di2@fvlpf~*bt20RIy#s^Q*IppQmD^u@rIdKd0+=!*` zMm%#4m=ZKDl>87k^Ua-&_yR4GzpVOqB%d#&*U8_S-`n+oySJk@__`2H4ttEbp}!lr zM6kNt7NPN%0xL^|)0*ZSw8zZBlS~A|oU&NPD_a2><;%5patxlVy?UhiOC{6TiMemC z!XYo+ir-c9@+%<(CKu$V>!d*}^5_>!`?deZ)C%b+>@vvua_J1j$m#~W0K=ceJ$xEd+_gQEErjEQ0{@qa*? zaQTrh!7+`QiSM8*vnbOW{BZ(tW>@-E-T!<2_h$-j%oJ}}TpzEpbjsqf;-v-;I$L(y zby{^s29e;+;T_?(6VTyhTY%(@<|YX>?->y^KRgy*W$vVSy(}426J-D2+$vlCXvf7y#yOna*hjAqfVZ)LaLtgGwMayx|YAcv*C_nJffY&W62Yg06?HQi7dziY1mtVg=+a_>ziA3a*ZYzU z<(^lgh}9O*9j^Rt55T!k-~7K<{&2OpYm(vk{?jKA}|c!v|!i?mKpq@|BI{CzPoVF<;%7X{Ou^TA}Y1h1l9Y#NkhRK>C>2 zD`huYC8SC;(Rc)Z38`hKlt;-|uL~O#!aaK+&U72qV=sa$r|yf(XbG6bfHVY3_T3W? z)Sc+KtMEiSsbonWp3Ua{Ak+t*DE6upaNcF!P>_(JLV!XGNRA?e{UydiOxwADlLe~( z7ro@^QFF0V#uq@!He-!#{V7f~Fow~UR%3jJDEioXFSFxwqh#>e*p~~&zE00ro}Z{e zUrlP+lPH8vKhT%u$JKw>Dt zBH#kYN~9unnEd7zsA`~x4y1c_m}voF{_!JED?;FvLoV=EnWzw?H-hH0zw*oYiM;cU zzl?|t#r#W&L&kdnxVg6-zSj@;24qj3Ud=qGy~=oY$R%J(i`a^58JHs2os9-Q+HEd> z#&?7r2=O#zMjbrB@g5HM)YtGGUC}+V5A>X#vR>TfJ7p%aM@wukDg1da%5bhvX7AHH z;3Hlrv;MP6!C*-|)$5=kv|c~qLM%rVmi*FdO`G#;cKXrNd@S3uu|E8dlA73hAs^r3 zQS&_cYW})@^muW#5HN0RV~TCV%HGsLb9BSn2ViiQqCofW!`R4tvFzD$&e_eDk?ekd zzdkT>12~|-V^M5YLTOeKP$Gl2wz@Bl5#%{g*Zq~yuaXDML`=MQSR=mPfx+>1+vz4f z7yvY7yfI>(N9Q#4S#e^uSx8b#s^g^Z0R=(Zd};B>=g1d_txELa!2#XDcgT+{H;_^V zExKWxRJenT+ZYk~en8KV^h)MC8NQSMM2~OBD;WWO_T9cKgCPD&wXgRfH)E@e36D*+ zhGUmRGDNtfLfsi-54({USc`XUVEogxA@&Im20d@)p-EpZR+nH=fh+lYSABT@u(lzM zT9TX3QJOt}Psplr^08U3Sn3nAa&4 z$S$-qHQ*5XtKyLDYx+hr^!%NHj&Su=O5(0y$oSYB3cIl#mS^q!bm+EK znBsE0Kvt@)nn7xF8W&zYN#U8wGNHjkuwD^*k|-hWQzdZ-+KtPQHPYcvz_M({ph(!i z+j4ct$-70_@92jc44g0y(#E2@Q5NOU*kYy%`q75T%Voky@Y8lJU#7$(DsZsM-C025 z+%qH2lq4^q#0$Vt__4+V@+bBhw8*d73YRP&+AW)8SJK!I3p(4Z+VCz4JM$$ z-3WhbVO**Pj6RLXaSH9g(5eAomi2})_tKf$RR_kp+K@+R26JeSg*^%cg{ zm33P~9*z4_qa_)RY*SrK<2)4F(R3Z7Xyl%h3~-QJCdnqHQ!MOIoFMb@Uh{!RSXoXo zJSr12BU<(Bd>OPfzfejXj#tJR#rEiMWwJ8V*J0@V#7<32PqPyFXh+ z{cO2E(n%lm9OrE7&w4dZ9mtRO4C1S~-(h#_GcV+}avR?MVrSI}%%|=m>~MIYWp)=A zs5ZMTJ%Y_^Z>^54?SHhObRlkuTiE=OB@q^*u9kHlT!L_f1W86+J@!e2$KR1~0??y$ z8vn4I&Hnz%k5?@w(flg==Ddb*pO}f(J=y3d_G4_7LaTITbQ;m1hdB1Z^DV@cLvGKJpXeDRor%T@GN^ZnD1!q?>o0uV*!eM2@51&Fz1l_=KHExy<=X6j3dq!jd7Gv7)@09dE>moygoz8+|$y z{o+pS$451ZjP5lzG@r{{;GI|;+jiC+1-9c+&A4Qi)*7d|d);m*sx$?c4FAQ&r?IVo z1!TpLf$gNbaZy(fcQ3DwxcFvQD&<;rEWp_!HW!gpOC5pF$@TWZf%x%f7BH=~zx}ks zMgwltzDCqrt`@7Vfk7nnm2WYD@92#R5nHr-7RE=Cg@LcPS*^oPVl7)?e6yR*a;-td z@({ksf9{EJctWL_PWZJWJk;*zKZaEe6}=GwC>i-FIaDvIM!-0FWs&c`!&$8YuP2M==aM#SV)hj)9y z)1RT2^{BqAa&A9>61i(>5_kHMg{do}rwh*>Y^5+5pjTjvw9#C&;!$=xtbRaM*pL8c z&Wl{V0#?I(Q}?=M%U6DIA5Hm!|R1U-w#$`v^GokocH zdCol<>j(1Il_ZQ2=B4+Xt);+7uxA+ExQ^wbP?5rLxijvu;bD;%IjqRsBEY!Utx<@0 zW%hwtedX-)D2o1uB;3OB<7aU`Z<+puxjb-dCt3d_8sHX(eqQ#qKqmLGX<@~yWlFFJ zBcvJjqOu5=bSI&nn`E)+7E4~0jKaYP%b-Ba;wOoZ-AablyHi)XZtI3TjF8V+m#KF* zeEgLJYfPL{yJi@invNrN=2gms-@hyS{pX>jy5>^z?o@x;rA!3ZixKbbH{T~tFFs0p z&?yaw2Y9)yJQ`zN72~U^K=xd(HiJVEf+v}2%{F2CUwn_q=W33@_(uJwCHtC+bh-sM zw|IVF#+sOa=s3WIn>r1wave6Hf8fLVN{t|?h&8E5Hpv<<$(k)mvD@msRjr(l>KNfh zHj}TZ0z*p0`^MV$4%8`5f)>C-E5TE5eI9EiJ22L6OJ(qPxtp70dfoZ-$opXOH|(pOt4a}>$gh{Ed_K1x;8Ho$*nb9}f}XI+w5 zejk(C^mkoiSuC<1ARM0Z42*p;CkA^pxnSR3KsdK|yp*;ho2>=Z`&RoGOM9&OEvLLa zN_@HQgnKxtsdxSAGXAG2$}*Ls^6lE<^dsF@%@-jKnYr?pl5R%9i;vdQC|I6Nf|&IR zugk$)yRf*AT)R>~e+1IZEw>V^FIoV(JlLi;@%)q(2*Y-|ANhRIljs@cUxzHZv)TP$ z@cI9#bVBKGQOO~-#Ey^Y$nEg&LUT`C8CM^68V23Z&-;VdNEAan{U0X1*UWNEu}aaJ}6fTj4j^d_*! zCS$67Bp3_pL7H0QYm5`5D)$_d#y-<;z5Vhsn?%llMGGxn_QKRiKOAw8!{;yd4UTtV zN1j?Y;$d_dsD!8SlF!4mVsOX0W!Ww9&N;R3Ig6P2O6>oWn6>FuokVW4&)28pA!pK( z88P(CJl8sYEL*+(>;7L%?z$f-5o$1vskPcNL9-CXAPatV;Imu-IiYD?=W{NF_|sLe zz;1^RNS9zt)C{hZagnmc1S#@LFCy_yBpZ{?BcQf#Gnsm}qL9j$8}0e|3;6RxS>)@( znYY%3K7Vg;JO^zX3og<^-vV53iD>9^vS0V*q**$ zp3k>6m!jWtSt|mU^s`c1bT^~gQ^US>@m+h{v#cWjzlxH}&=4%E0p)HSa}MhsbN}Rt zNf}L5 z;G86lsKJwRi#+vax36TYs+|=<{dXZ-q9E-dd{LtRf4%=BHK>A`@Xn%Qe-n@$m!Gjp zxk7J3ZF9R_y>2Wyj{3LkjkCJ%(m(uWp^<`dFbP|d0YUj zMZ%^8C>l*I7WB*kg`YI>su_o>CKYwTiIN>Aiw;Z`!QlJDlu)dXq}*Mw_c9x@&kT7l zC%Z-I5~C_Yr(v+PAeGqnyv3ssxgodtT6UMT*O2zmmWD8E^2P1}c342eC2-dtamBYV zbx&gWmnA$9@8;y@oDXpNT`spjJ?Baniii0UC<%S`kADFTMtJ>df}8G{p4aD|gA^RG zfL1H`SI$pqg%C;RH?bGb73mdm17NbeW? z1mAy7lv3*8o3PG}9|q=?+(A^hMESaCqyAjV^w`mmZ3U{@cxyfPP#?q#vE04z_hQa1 z@fS{oZ+YfCPZq%hK>52&@*x8GVv)tP~vNAYuje6sSz{NfB5(`ES*H?&4{`Oa@8y~*y5M@uf#u! zirX|aDTX$cYKqnshR%ul+BKnlftGp>_I1B%F}Gj@aMXXyA2B+U{-^DbPmg7684Ey2 zBeKu|(g=w$<*p&qkre1i3`wz;qN8HQ;);I-(al;&%HdCI=*an79&RmFgit7d_!He( z+q-_=(j;0H74jCxF0)@iHMfh^IYLqOdSP>`d}J0Q0Nj?d zwVE5r{EABzWLNi6f(0%P7UY+PJnb0O<3H+rrtxas>h7oC%b>}OeO15>P(XF{)4`P= zY{U&hC_=ERysM=-rkztxEz1u~{Te7U&G&qC0&{M(2Yn`w5*xdgtp-g?&ZU5%?u9;V ziYu*v(lORqL^5;Y*d|7EVZ$f(IEtSOHinWaj-E-sji$`Lq9U9~k(H?Sp)f8X`o*m! zHMakyN^UbsGB* zBfl{(G`?)o%SBEhZt19GE|L34;&ZH)#V_*P=$|{#Tgu(Gc^9{dz+8IT4z$PmFEoUW z@2V<7?dGKlJlfOC$SUHnw%>b&fp%eSwmAju-NC^pxkqkyL!1bXz!gJfOLo6C?^NFM z@wM)ASS-tY_f${Tu0Ezx;JjPz`i zdt%FDR7Rz(`j8j*G(YTho5=)U-uxa)D{|nMHUFb7)Kr>cFtlEfAnq?1la0L=i`lsb zIP_@2n_@263wiOK4D8f8n^X-6vch-U%Kv;mbD2EdfnN}ZNy=EOrk(D%Gb`IJ)Ff5c zdM<*5*O>>LSBHOb3{zAiwgj{Ehw+E;vx5$Ul=1xWiSeuO!ty7l@IFHlO43;Q4mJDt zXmI~zZ?nC>zvto6#UWeNemJ-VsWgu5t8*BZ`YIJCsOlmvbo;-qKb+|JY_|zBr1!sc z_t2QvLjHUSJwsC4VP^>zZv%1N=)e|8Aj08V9(hIWt5u-7jHPWDw8E+~B`qbDDb?6C z!SZazYTo``S?L7x_HXf?$J}f$kv*jklveEsuI9f!5q~sH(h<^na+xg2*dY3aE*g7F z9IJeL)4$ulvLj;}eOLeA()m_P`RSYtf9DPRI_ppld(5Rqao{v4HocBBmp>kJ?>DVI za64dA7QLZQW1+J?`hKZCp6>$lAi%&vF6r#?Oly9{U>M8pj9>HYnL@qL!4z8n?G3Yp zlip>vD7W;V+(Inv7*M8*N_#gW=TF#eBNUZJpNi2XHuV4f8?99^*xEnZ z)CgJ+_D*idRVE!fr=$S3g`sx^lw-$+;5 z+^}?-mOpi3cj{0oJBPvf;d8I=wnekP&95Q|%Xgc)UJdS-1jUSZQaVrZpq!#zXa4j^DUj5{aTN* znVY>jtYB>KtF5S)Fkw+)2}f9oh+TSVpkRSL)I}56wgLlLc#MsJ>yaDkkBf7KDZEXj zZ{h)HR#Mi7C6@c!S#`n0c)g8=

=5dWSLflH9gI_IXmqOmO*En>E}I-7AG6J;a}& zGY>u6DoC5TI zE{9*TSImf>8EsyhW=g$FoFQX3lfd~oA<+5Zdu<>?fr`sfD9m|-9f<-bA3RxooV zQRy!Zsjxad0=2#y`s!rsZ|B-K|WR=O` zpvd5b70Ev@dHUbC0qne?dy{KzG161OH>&WWTlKn7noAQ}j?YHjiCmv%-m%)^VS3_M{3&`4+#QWT+d+)LH5`W5bqd+h z;nZ`74Up0MUTUS?Z)LNsjK_4ZsJxWsY3m&IV?sX{p=^y3Jrr=Mxeg1?TIVRk*J%he zNlKql(ck6(otS~8GFe0OEWSQYuDkxzf_p=8kF6bQ=kBeUYz@zr3aP)}AxKHob{_Le zk?j5?{A=`$Wt2AYrZfAg2ZpI-GU6IawA&vua3X8@F7B3mGOn_U@dTt}h2(xv7qioK z>;f|;trL`wg!jywCwB-aX?e}x*525z`(EOp@^AsK*o9ZH$5}iehcTT$yiTxq_2a(y zowdSl5$OkW;2f8%>@Niuw~~cS9QzF;k0tpG8=&LLqO){c*X>$sGI(R)Y36O?j1KZQ z?waci?*heQEIt2F=E@mg5}W1G>_fwDoL?uk8)AW(7eY=CbKg9lyG^BRxq&jA*q+%I zq8VFc^DB?ft{Ye#x#9M*7^8iRGir- zbut#4ei?eZRh~XB-~;Kp&wvdH9Md*i7K0g{RDS)w8-6kf5nD5%48f9)HC4Sf#lDG~ z+??YyB|_Rjemh+UB}vjhXo3?V5l=ILbCiYM zbhIS9u7%K8ePej#jTjk;R8GBRdvg!|k`}mG(C*N}231P94ps%B;QrKIfBKM|0^2b{SENhJ6mw z>?G@C=>&A*cWTRVigpVGEd&(>*>s9_vIlYCWAT;g%pRoBt#P?upXuRFwxCkyO^Gm% ztXwJ~mQmhYQPpt(7`Un?rum%AnsL zW`~YYyEQy>jb*KKSFQVJA7&TrIzEaF()ck~I<8cIv$@x?4wWEMnYzNlQ@V5f0j{DL zVQ#5KH*XCnrDyjgytN+-2GZam4k=qFEk*8Z)8ZtkfEL3s{#O4mdzY2~0Z?{f>FBi? zMRl*wvNrYlh#wgRW>)2$ZIzfppt#~AB6eVZSzh7C^_KM`M!1@6f$ zg`NE+5npbSWkUVi*~oF)uO3-Ui$5OSKj~tMBsH%jFhsm{?RGT{hWRFw zjx$ovtHe8BI0l#kO=2kd5K9vmF1LUK!-mOQ?>MvZPx!icjde>$P8@23%j>!AtrJYAm= z)xR2kn+so>2BEzqQxqa}Y~H?gxOOo9BXLUccmd?;3Yy*8dRK>bl9ePS&BOK+82m76 zI0e#DUQpJO|Er_JdFr`q0%|aMIt3lyeGVR7YxNH7bjh@_$dM6PV=gQ9XqbbrNyYCn z2L4!S{K{-c(%y(8b~P|sGOGKLJ?Pz_)#!Izx)JewaPG%UBe@@{+|oO#g$iR9=N+%Z zG7*^SsOkJ+j@$g2=oYxY=S3O#IrRK5_aZSQ9izP(z^BK`GjNXY<%gzz4;iyY{S9}o zud70NUPRS;Td%*yUPN`rw%~;AgJY-Ejj!LO%95m>c=K8s#0hHNzXLo&zFE^L*moakLskSI&x{~7uTl&aoP#h(krv7nJ;KRPdUr^#bSa8!rFC^zi34m-X zix-nUDS4UPo5bobcLFQ%ZHsBB*?$6my3TKMZIxBo-XFRBbOyv?b(LJlqm}gG z&&R$$-iMaVR+U6+0chOESK;BuVtmaf?-$HE8-ryDAy<3TizMz=X1V5} zIu+|5$Wt!rPAdma^3fQq9+5~zdIT(gs(5q8`z}}yZ!)MN$T$cTL>Ocl^mpfoJ}5Lu z30``A*E(|e=FI#}8T%`u^s|O}%0=tVh67pI@9i7Luutgre9iD9=#9zogHug6VBLZ2 zVCIs9NKG4LB9I#Hwzj`16G2r~r+XeqB29{;|L}#4GXafrjW4 z>OtflDEgEU;&?_i0N*{e?fte6x(9r^E|n!FNH)!`4_H*%0ZaV-17}%XM(m|gkCl>m zNiop|gnPKhVI=f5O!v0?7HBFmk-FzeomcAKaNfo7KCdPU17(kK%u`UL4O#)eKC4|} zwqd!JvajJ{V>l+44&{{?yT3l1aQU>?Yd<}Z6>f^g+dDd~1SU}8=BZ*Y->n)yPIGJd zV=wlk+q#;vtyqAvWriBKCp-Oa;~wvzPT)Gm?R(|0&XFq>%1bY;#AqD@Im$Rq_K#Ze zu?=r_0uIUa4kdDR$u#M>tt?-ljWFpR7fDQJ8!^$c3QNC+J{0@rrmk)0^6N-{9?sJg za^mN-y4ig=BHWUNxlx`N{If{Me@B;us6<=Ol2wh2-RGVl%Yr10YjF9_rCqF!Zg*y-!v@J<>bJ+qIH2jzYTn?IVpZI$@Y+@rzc;TdVoHzhJ@FeoF;5 z$P1Fjm6AFU;_T0z^hZmXh|E9O=sIIAI zCyssLIxEgFylC`#@cT~tZ_hnxNr#}ig^Aq)BoD_?(-gSKD@^$hrFc;ZC$AFf^T*Ko!QEJF|N5)d5y=YwOpIhDb49S~aMRou;w^y2$QI80vJzMVKg=^|+Pn%QYm z^X%vi@z!5vYv3B`T?1wEBXMrVq02GmBw|_z&ACrAnJ&XdhQo+lQ^^S%ED zFW8qWkLiCx{CPrOzpup=SDS(H7T*IeMC0#T^B&q&X?aJtB&gk>-PokJPK#R+|1Lm# z*PSP457`2?8eN^D25#-bm5k8Wu~AZUj99Baf}&U$9RG&1JiL5mKY%&2b$h$0Vn2yD zBfeH`=x~(q(+bNB;^eHM@ueZ3gDLA`-sRY&0nMxnBT0zgt!cZAp9^9`jH3XNK~aC(RxZRnu$Z%1G|pPXrr)T0?t_tMWt z?}li31@xR|g@ds{1J@x6dh&oBdF%;MM_#fjcUO=L=DH)bgAe>fBzi!5jX9|A5mJii zY3j9r2VKA(RF%>#%dh?UH!HNwtR7CqJX_M7QyAwpShPiXdq|B&3v|+j<N11Z(`5Ybfz{+g0+{L(B;^N7pZ5e#K>U*ch&=i|Oz#Ao zh*76j3ecVKp+RNXo&A-1y=Qs`bBIiAD<#5pXzMsrEic(m{q4wx;59eFZ8x2x8kxoR zSU9|;>nrFg8=tZtfQ+{x<)lbUNk61#Zvia8t>=y^$%(mQ%Obg%r^@{4&<#4+PC1oA z?7qE5(wV(6xe=D5Hj~JETuog~a(ubFeTk@A?G}LzGL!8a)GYd&=vzJUhi;5!9A*kn zZ)Z4UeAF0aNw|I^oBnDy}FId_Q3Wm%UL3p#75jxhqA5!VWs+sH5SH=M0k0`7F2j7WLp-XFk*BK@R2 z$${}(d2JLx==Rj%8Vk@{+ov@;0j;0wOpYFq5^+gSL%VN+U|Xo8&m_BZ)I%Y&{+^Y1 zqugAt|C&B4x#GG|dX46HgwV#%-2wm-ww$qNr$r-t zVT9{{(K-iZ5T66E4u$WaWgWlI;J6!4Xq{NX`{ArTF5wL~k6qkx-B_QBTL}{!Eg_;z z9oH+8g?{p7PH@xz&9f_}$}=IHTUBLBL(;a1pzYR>O_Ys}kl$fb)8yHbl=Oq|LYCUN zZ@iKMLme2?NNK1m%5-)zXNBMoaZu}AvI@jhn$`FWur>po$k?x^mDXJJeByjOd&jVO z%%Ybuj$*67tvuX$d9}XuSCqp4y!_F>)&hW?F@TmQu>i7p+LFBZwwqV8UauR0HjZa2 zHUIc~oegQ)4p}Xm^m?3czd8=D&!^H__$)K{Ba-K(0_ob$I%+EB`i~@qX!L!QN)Olg zklE8geOO49Q^i+R^V>3)dWW2t5@8I&wQSSP_h=b6cw`Ab5&?lYoMZPV{m72@fFiDZ z*Kf_yfCZ0((#c4mXDXg8M2kbMzv!;?#2lL?*eKIrmB;h^D~VxSHcQTN$lC!ujGu3b zJz7)uieW6a-BQ-LvgxoT(L3xsv3e+iUnS8p=N`+Md}nV^rYj9ZIGK{=(Nz-LrpN2K z0F&EdqZE#3efe~i_UVZp4A!C2Y1%YO%zxEHwt%sSH>-;tbW2?xbl7c4nG%65sr>Sk z#w+Rg3DI0{4r8SQ%_FhrTA-@pwwQs>tfCfy^8U1#2gO>vm`9i8#qb2k!7t6%TSezp z183_V80M;xA&uLW2;(7s;UZ00(8KG$rH;Z0mzFq@q6%5-!kB`dQ#rv$Xfe*M(A9%Jp?$dbe{B~gK3R0)M^Ya&6YDoU|QXUx}h(#yf`wu0v76!m4 zw$A+xbB>%uw`sr;GN~2Z-3!fB<3wK^-T`E$Bluo##K%TwW#?Wxj|t{5d$(J!zW}eb z$qf5?|Fp~MsoLU91JDO#=h~hns~MG?DbQb@MHv>4f#@&Um8|y8J4SFRHOrTxEFzj| zVS=uEud`W4uk`Ar`;nI(IRdb|?T0>nj;|i~dA4;$u$m9a0$h61#QWK|V^ADC!lnM| z{#V+wkSJEu{x80T%fiq+VQ8T+^p`O7w=gtS@r;#SSTQGABWX*zNSN7lbDyP#C#M$@ zncpk&1N^+uMXg)#3)Iw(ek)^ajux9Zi_N>hL3h5~lu>53DzqwfPY z-AxW_e@k&nQ@->^->ZoH5ytLD%=nLqysADeNd3b$j#cFZQ10>&V|~>?*}@7^&Ewnl zX}!2Mnah%8NJLMB_q-_#d@G612~r-1sz_5TNn&F1aw#!UU&885*#MF|-0~PlYD_4& ziklQ<5Y3v{gtv!h#1DHke{Y`Dmv*%yh#a3qWV%Vq?B0uGT`Gp$Y_LN}-~;)aZnTp| zF4b~)4PX%~jLG?F=Gd;^#$N+{Ez8Vob*(o#j$|USB-G!y!YW{-ux{*&KkHY;`vJYNTb&iv%|h~UN)l=@J)?>} zx$T>EInR{<|88Zjd&K*tKpMu2JJv|jn#k;#*?qRh|2ZSR46YQeq;Kd*PX03@|4&Hd z8bKl=ilYo_xnq}v%^1zZ%ve|PR$rSP>#s86Z9&!+Psa8!m#0us+`py;iK}}_zWD=J zfqmw%?6{q?mNWn~-0JE&)m^CZ$qgMyc(nuEr!{!tew7`_-|v4UeNzcfh!4D#(m>z8 zy{@5ezgCkS=2dX7g%@P>uU*x`#iYV!15^IlB}NW)Fw|EC0GFJSIj+mG>2IF%PX$2A z550$Kj37MN$l+S6Ge8IL0Q$Gq7|U#FUfGaeif$G?r>y~*RFu>Q&FgAqSuKft$QLsI zIGFI`omyHNqd;Df;clLl(PnCkMiQcUM7vj8sM|YEXYwB?~6^~{yqakC~ zKTDU{-~ar=yxh+YC3ax?UUi7qnjJN-M;vFicJ2bMY1gN=2n#UtCST4x96I{!9)wW zIIEYZ`}_tvG-t!h%4OtVe6H0NK0R}v>2*0m2;7MK$1S^m6_e`}c=k2Nos%&=W@T}0 z2s~bwn<<1U*IK>__vcHyOM3;LMn3}%y*%O>c^59V=Q;*b^53J9HZ8Y2T^$|hVooDQ z6Og9~h6>(c$b5dCyjupcIUpB#KKZ9b&Z(UAy~opp`B28aZy$6&IBlt%$G?XLrP0?M zX4qT5hp4X+742@9@ar_2SPPunE8J}MeIU|t{@dV|l$xm4CBHSjjz!2Y_NsO>Rq4w5iSpEtR;B!#QC!k`sNxH z(pb$C+!}q5mhatBGzzbFHO|w7wKb5U!2#LQvSczStFp0y2SLjYbR|FhVm^ugVqw4H zf+?SjhM~2Mm5vJN{=}iR(R3~Ge&Yk6X>820vNr$S#9ZND{VJe=1uj|{v2r1h)u%GR z8k0u9y`;%h&6-Ez2A>Edm*OHiCY+6l<7N@~Z77Ogc!KUM0CrKC1CN}4LHc;E*POvK z;&e;G?#tsmN>>sy;{T^l^6XM2Zmw7{CtHEUD6w1)fROW(Gr*>=Mc?!E_|LDU!$Po~ zeE$%xAo_%=VZE78O^rn&DbByOyz1`%qb4c+0E|tN;F(4>^f@dE*B{A6q(9eaAKs0MDE&=j+W^byuUkR{ zgkA~;rFTjMrlD`XF7y-)K6VO-eu584xL_WffarI&%un3=3(VHoy?U}&$!4Gy-8v;n zEdH5=xAD>W$LV<9kXZ;*y$vBEipSuSoht2o_*C-y^Q|ZZf$e*mirxxFtTa*{Yg)8r zs*pvTwf`Kc*7F6Fzp&WPN_E;Typt7NCndX=&tFGvX@q;kl!jp@DYO6zoN zOPkm(h^b${G)Yz$SX$Pc%i>4{geBC(AgiNmwig~ z+@Z6*hqMrr_Xhj5+WYQFvqd`lweR=c>t?r{4AKnG)m1wf7D3AlWmB&!y5ChK{n-%5 zHf?N}O#5w}QoC32RtfdUT;%5fA>=y?Smd~p!=s1T&78+?lSI)VB+7UqwQri z?{60^6=7oNG~OnP zs{)U6<)9ZyOp37qMVz2{$itYa*&1z1@9t^vtiE>_rp>UAV3{d_)t}pQ5BD3@6+lBk zVd<5;X}5DK+O_>gx7AV0+NwFKtAR?t01F9y%70%b?S9R;J|cm@{e%R+8qMF;>r);o z%}qxhPQ)l~1l_@LK&ptaxMSV^NB(U4`CAi1Db+H)Gk={{l_yF|36qh~eI5edTN7#y z=-@fI>;lDhL@4Oq6Aw610m`Ov8+WgYK8=XQYxN}i?wMq4%?z*`b?_dBRhOIU)h_gO zb%{*e{)2eeu)|^yuB-BZDZM2%7xsl!og$8FY72*Q3+$TVB6s1T18bmVoi)o3BK~)R zTe^5LCl;K?F7UMikFUh;KUD3pb}c~_CK@`xn?J=`x-J|XV4{|nU1~KDQW_SRm0oJq zQriBo$i1u?iRsMuc;zJ3I)Qs-B@aCf7aoWXO)N}u8fX?Sx`z&W*(c2^f04VWLU?{6 zr}}eyIgRVK^BheQ%@JJzBhUG?g4i%Rs=P=)OY`4N+RHRHw9GV1PyhRha_YR<1Mb|Zq^QyMBmSXU#)cpRExQbSyTXzG8?|REBge;g@;BRXMDR!=IBlhBJ-oBU z6svC86`JqaAC}k4E?;bDrs-zoPtrj4I|pwNiSm34IC>0y;_mp7-i=#L)EsqZ(&kka zs~f2imyxrL=a=D`IIIz5@Id}k*nMsAgaq#jgP$h(Yb?LRQWKOGv~y$`c&rU%T*5GR zW|QAyu`V%8g$)L-p#Gvj8U?~*SL=o_UYp9J+n_HVpLG8f$efxH?(;-PDG5LdXYO+} zdrhntwQhqRrKip)DBA!dDVmGMU>htj_c=1CqN=WviWhm`Fr5c|o!Q-GPAjRd#tST} zth%fthpkp^@)8!T3uSY6gAZym!g9U|aTARVs>Vcq%Rab(ltas3f?uy^`^p^WcY|2G zoSoCkI(gX3R2#-qU;04)8m@HUgBK-&tWSD45M4Eeu12D<@qN+bM3NwRL|JPfW*3PL z-XIXT_Y}L^y2MZI<8N#kosNE37{A(0YFHAy z!I&VcCElr>U8K&AXTCRn+A#uwu>WxgGGyMBw5@tX5~s;?H`-&9o? z+1|Q;mMD!d?7e2xxPl85C3jrqcQ)YMJC9ErxFk&ZF_~t+#nfmsF^aEa+1aR)8%zct%iWR6K5@8qzLP*lZ@h7}2Yn@ubAS$cTb0%QtB(N`kW``D zau{kOvmUfhpH}`dXdtU8Eg$M|_j3Jwg2AVFSS$ctXLHpa{ke&la5cNUnsKsO$45c> za*U7?14q1sKdmmUEA?}YuX7?wliJ*oe7){O-w1qt)*CsENv!q_n*skVpwN7B86+>A zjiHQ@^8x;bCRAZm|2TE&Vj26$sW?1$jZqM3?d$Eq66P+yc>Gn*9D2x0sS_Sl%f)Z5 zQ&4Fho71mlssu>NKZ(NYA_VPFYc&IEZ|*jDNabLv?eZpWH)+4^D)9$yM5QgAP~J(n zjq4xX%uMgy3t>zb`UdYy!S(;e2{|;~vuCgy#G*AjNL&6R`D>w7c!Dpd=_3U#GU)WN zHI4xrCjXd#x79i|G33uUF5Q6YlNOm0HtK4@8zPZ~S9x!vIMX?6Sk}UtjA8zuyzf{6 zSnvkEjXDen<^0%m!l?+GmSzs*zxDpu{kg4D5!)6PYPx6aOYTv(u<_{O4=gAFqFz}K}q}i9+VHjrn5juzQSu=V+%ocrt8P} zpeq8wwUlse-Bwpkzt$gGjrQTQ_fwX?o@B~33Sqe(?;bnN6?I&Oy&nIx6X@|m&)we$ zl-g4!!Z=(?uS^-X?9oxvpCqqYqGo4Po;6NHXV@;{Q0x}zH8-JRBK#D%AvGDr#Fy6O%#MAPVKZF9u zZ!ECnmGX4NqYG^20E!m7r+FiV1ibhnPhh=@+VNLfWVm&WM~*(O7kOijArGyTQzlQOH$R&5#;E_mnSxulA*4R29G~eGYM^4 z&ULGmdN_uWp=~ASAKXM55nUZXtoupcoxT1;=Lm?JoD%Yg6!E+c12e!&HQH*{wAbb5 zAiI%ActoI#&vEQ2H-5($j05iP-*8+LpuBI8{ESnt>4N}ABUbxA*6pWs)Ta-BRlH*x z7%`0N2okK_U*Bh0TdcJ?{;(WglytvJlK#zW-LZ9QpGLOk1M6?w2Nl82s*$@-pxrBP z`atop7hF3eC*#u>8vE419mO6XaK*317qi=y5w-UbG4x z@B$*_@%G(0n2(AshsU28{BV*|SoTsL)UW#Q;ltLe1g{;P*Ldl@rq`k&XdO zTv4XHMDCF9;%usUCptyFutJe7RB|}q0PdLJ3D#(7qvbFCHD|D+dV4^8OGrsFwTE$u zv*do8N9CZnR28}F{b2wrOo0jZ2Wz11?5>uw7vUID!cuI0lGB`0T~=-oMm8LXAXj*0 zo(K&qo5Rf&@lKTqp!CePVC=|W|EbbUSnL9P&!Pku1Q`C|?dA!D-KUN(y<~%FYVz8c zD_BNgRG%S{u@S63yJDaIp3js;PE~uaYb?B&<})eLk0UEcDJ#4mZ0}I|^Den@jfrV< zj?sTKLc%BVbL_fCgzJtYPwI9f-!__@59nOnV{08dY#z`{k!^aO-*enb1CEip#-;)& zij|rtFWwZhZ#V6Uxo`hPq=;4Wv6W>g6te)yAbe_)MSpW=sSos=C~&*M=T47nUo1iVd+W_A zY8{BI{KbbDSIUEx2;=-4!b$n$v<%F#6S{Qr&;rHsfW6RbM@FEvVI=b^PT3@O&EP6^ zh$dGAxxeU{P^Q-+L+aksF1a%kU*2kLQ_b-BHU6M>A&fP3jOEts&0N>};Ib*WBX3^2 z=`sA)sy!&wJ2RthCPO#-gyaGJk<7}hVe=XEP@7V6Sq!;NN(11;?iaDCd1M7^Mf8n< zxd{8Q#_taz(=YBtzF}h7(SMX1B#B5m`=b*I`$f7G)j<`w)rf{GysUv9kTGcKa~D=9 z<=kdzGSp=ix-731QFM=PEK^l7ne?VmNp8~s4Ds|XP9ezg`b1Ch>%X3QP$TD}n=D5s zE=QKE)%~0C>|rFy{$~2?x9J+wIH7-lUxOY z0h3b=L>jGw51|q-omOq$xNkNE%!4dR`)!RZPeoE6H11c#y&tru^Om;-O|f3wslyo( z6V>oLb>Oe|TUXxG7Tc;x`^COJ?Aj7DGR*|EZ(!rY;C@XH|6I2uV^dk|%?AnnCOuj? z@oF$rSEaK!_nc3KLC-M=eq)9cp>)MFa|}nVIV5HhS?r~F7!Ya@sBvp07MCKm{ODNU z#?I$f(1*$XHLloTPoxY{XvHGvk|ByPsy5TxkS8R&*8G)Us+}?BL2xughK)WB?(<-r zUqF>*lS!xMEYLSU|6`g|pLELErA>1kdR`z;@ac&1WVX)BMn%TW)y^J=T_yRSzIv_V z>S2KKkDVjsRRij$sn!VxcfMI^N@&e~Jh=7^r*1KFbbdK^+pD~S z@V8INfan9iZwBX=OSfHz33}g58N7CsEOCVj?-de7b`;cpJwvsK7bM_{Xy_#wIRut3 z#V?ITJ>u|8=eRhe9X{+Rx7%_|Yvo~o|E|aO+m~kG^_g1zE@D@$t|WwLdnhVvlp=NZ zSoo-wX^#?kY(qPHsAHi0;FpTfo6l+TJ$bJk>n0f=Bn`=y$66!s7HxNvNza%4#8KtV zEz7gjm%7v*S>JLu?-*{A^n>?Pi<69{V_tBF*F}dsY2LDx#^ITDg!9GQ0lo8S^jw@5&QsfecNcFh-caYftm;owPLoe1Ua0C6C>DUiG#r4d zB_!ig`Sy>8kY$uMx)ZJDh_d;UdDR939K)&ze)E-uujyTMAB8ja$|me?W<*$dGEN0jkO|L^Ys7ppjg81WKB#IfyEkLwp7f^YF&;BcvQ9X~ zN3<&LE6;33@epB$eX!|L3CWh5uN>C`Bk3Y$S^C;0OY_s^4>I#(ysm|s9tULJMLP7l zfTb9RW&2EV69@Y({{5*BG(GqBE1y6Q#UZ6l&GLdr`CWlj6Ss@JQ1UaGDU@^)U&&F>BD2kaf91jGEEeUw=SIVde(YXx_tC&dM^)XXFu$j1^37%k5$o*|nra2tAsj57AL};k|B+Apsx5zgq$w9`d+fC3%DRH9PjE&*kxpfQ-g| zRtcn6={4!TScZ=Jk#Z`ATL`kKSW?)O0Cf$VN&A9;FCD2hY0j(NG?lf>!w(4CDr*IH zp=*1b%3uCneFfBs_Zx`^_VmVN1~=pP({$&bZgFQef5N9JqaGh_XHqNKcGYl)1BB_S zFuOCU%E)&;sQK`pE^913M_s{{rc{!`FreO=QZoOxecUySFV{{AdK47TwvA7avoY6j9bm731|Z|C1pGZP z0DA+||FUwWJIt$f@&g#__e~|ma9snWye(_ZEGq}ZY5-C&g2ee{yXWY$H1a40Aua%D z`u^z==cT}h_obX;el+8w3h< z^Pf1H-0YIxx+r%xfA7fZwlm`8cRbyJBpKwmE#WA3;kWE>aH+gmPMwC0-^jz0e9Vr( z>WVn6`0Ib&jT>Lse)9h0{>kb?<8%GR$Hcr#_Zk1EwonN}Iv#pvEo}qpYx55V`d6(N zh|Bk?QFPVj)$rY5RbI{Gvu6-$R)=k%yFZ8NJ>V%)I+<`gQ+`U-nMrIi*XwT z61XDFaiRTG60I-)kL^%gvRTo@AN&awjPGEiL;%bPiMLzjeSom(W<1Vr(GZZg_o_QW ztunsK_e_!KAeN*QlD3yrzZPE;{=Pc5jk;?(M#Zd!5TbrfGndf*pc$m~j1s%Ceq74R z4_(o%U0-^D|^+vZ#nYydz-YeCy9=0GhbT!ChwJkUa?0iUpz36K#SVAPeG$@V z@zJrOg`2o(sP9W-n36^{$Wy^aWA4c+NYKmvakl@u)2vC3HNPjf{YwCE)9H_FWqHkV zU_oPka_L#5s@unJd}#dE7%VnSg}cf3f~RWa@|3-LK^SvHLfC$}tb(umNw+(=p@0OXFxHmQ2ei>aU zUFRQfFz2*tZewS!kS#49H)}I8QX{8m$wHaQK_q4{q?&u5I)7gvwl?(pbnHQ7n-H=u znu?PNy9lS=`giYE@brtPTb~P%>(EgFSkV3XrWhXz!G1k|dC#>-Hs3``aw@C#bj(l8 z)@Zh2tmLbOQPtCU5txRN_|S@R#zTELE-kfP)2OHLfo52B2`fiRaKvLHRPgxC6@1RP zV1iVlw;M4prTIW@>HwI~2Z*fPt4*Q(pUKW``0{tQ-)z+vh4xPwh^1+^zJ*kMSDAgI9}gr&>d zN>mWtlC)3i%-U#uxGT2vuKG&bS(W?YiCee#qp;ExGvAeX1uT~fc}Vv4y%7FIJ5gos zWUq6g=S1ZWv_)$xCwAf>gvlbsm>reK=YwBHx-_HQxRFDfY6-HCy#lZf1h}!O&2n+a z%@H`#Ck|?x^;F_Rdv;;?4g*4(Iu<_cUN{VVc(W3EdFN?awI=sAVJi&(#S?$Mfv~0^ zqIEJM1BFCLXc;Jy%M9GD=L@h{1JDcXo0_q1X}Q3#kOkd*K0eK|3C&2iV4b=@I(a%< zYYq;DX`yG2Hk&)fkikX6`{x8AkEVNI(#xjFzSmk1hU_1jmg~jVPx9ru0(dnRzLX~% zJ{=8=8QKviI(z|)%*Ai4yk<57@$`KAlQa6RwvzAi&bSMPB%yWSqu@*}WA|(Xe3{-` z!BX1pzS@Y1|(+iWHdp!nX`tExOg^c<<5 z(V=P`Y`pFN&J$|4oGp~f^ZldwLU{g)F5drgooTH8ng8j;pWaLg%f#XSbKHq+vEWs7 zb#3`y=8o^h!ug5>{!x|7a5Vyqj~^cD;d{ueX00MQGZC?FWrafYuO$$~JAcKkQHL4pH20wV!m8ih_Rt&bIVi zMO=I%+*Zx4AB3Vjgsa&&L&YIW>6l$(vI@5$2!>q^`AaI*#i4MAL}x`GJ|D7CQt%J5 zG-G;oBGWFtIXQL67c+!E{@uJl8KRoTlHcKWKk%Oez^4$Dm{|V7-@!q1dlhKz`aEZ2 z{YML(65(st9haJ99+PTDp`yq~X4u0u%P9ZZ;CczgEA@$%FJPvxP>Tg^R1KYCg-qM5 z>%(E=w_;&u{z}+S{$bA@(bj2!Cq!=Xs!SjSn_N0E1uH5pvzhdJ%<$%D*U0<{2CT__ z&E*d+W6IR&@<~*3bl0K%I86Mcn3qV|aF#Z;iQZvLM9UnRZ5}##v_n)juK52P^#muSA zU&za~CXd!Uzf`!iJ**WbV?u|yt!=Mq-3qOF$Td~G;95a9;>?rKpz-Yr7Zp%@*&BL& zTqm$X{Acz1L#)2Ntj}d-F83UZFn4gCUXhV&Jl zxYoeB%E}j9byM#*MEIx}u(*%RY^q7Z1o53ch{!~>BszE{pz?mW1KY-d_O+0PtRb!m zG5)?5)oE}KI^SxT(oqnpsHc^P<}k18LZk-rN6;O0vpq&*bOR%OKjzSw?{nXINB~Hs$QM)j`?T;eNXBRfyvqnyS9&eQ|LTNYbUdF&$clEV3Mw%amXdMOKeP6zv z*2({!B+NKxy5$ch%(EGPZ%^E}dnO<(QNsFU zAQq_unIq9|;XRF&^PIEHB@z8F;ym7fuS8|d)yDmHsv_lh~xD;^CI)r|51av8I^}>1QA<7AE^#o=K_smOn|J`z8W%4QaMU z8oDIifpO&kQS>)IQ>gQ9O`5sHD4N~Xy|Y_oJMK8OwljEBc}HLhe`;oOdM z!^rdv2nZ(y9(|J+0rjGtN!1RJkCbQ7mH5QJnmO=28mOp$X*UeeMSjjVpTrS2v*ZKI zW+c4)M9R2A@Zp;`nA=ml?SX+@NABfDDyvoRM2tgWB73{yEclCRIm&2uS#)OYzHffB zI$jZ#x`H()liY~_;Z&y<%J&s4B>wnyQNsQ8+;lzvgl`^P_LDt+g%Fp4@0;NXD=EHQ z!sSr@O+vzSL`)>I=p%s|sStb6+gLF5zOzMc)GG0%Z)0J{k$U`8bXJX~2ESW8T;UoV z{@Ws4uxH0X1%7;qvJxD=OJZ*iZXHmF6ul6tNU>Q$*Gt%pw-A^FYOE5eoEhbku;U`E zF|hg(uhX1Toj84sGIQ4h?1oi&z|FNU`0!#pW4@g@w7dvITK``d|1&^=IoYb|iV=;(@A z`1cG|t3iPIAfN4^jmtT!8T_BxcS(KLM+#!$?HQ_%1`EDB8f+DM1Ot7a!=de*;m z)CP4UEg!AQ#c5Ur=8zi?nR8Jxof@4FyrQBQsRjxv-u|zY>in&6@ge)q>!W?Tul}Du zaWLx6LmB}ZX{uQ9Pl@=y*#b1$w8dtfgEam(v#7QUd;WcanXqZoaJU-%S@6DyKH_$d z`z{KTMYVXnBc5UtP#A?Zig!{U?H1Q0^8|p^PsKF2p#wn~IUW>|(vRdh=Y)5CbO3cp zM8$5iNP&*c50><*Sj{h{wonQq1$-C$gOmP{BC8ae-otdmn;zrZHS%TFZ%ZHRe}hEa z3(Ks(H_pg;BEGW*e8N4UqlO)eqdEl%BJGw+?_=d}fd>90IWKt|NAw6T+)Qoz1>WB> zPJ4%*4x4!2nxSWFnT1QwI`n)Ui5n9h89>eowu0uO!({{`+1SZ~5Ab_?+|LQ59B5w@ zAoU)_XHUk|$)+WgM*(wcfMUtDwYCsFz(<3iRQu^X0388cfFrOW( zH=$t1-uF}eL3@XBt-NA4Pk{~%oB7cBx*FApYTOXl;I8<_nq7IdV`S>oh>l{(&!AT2 zoSoSb?)IQaJ_S>X{#?ubcP4&u)%EPKcL$r2#j^cHhp!eR2FCW6pB}zTTuFDNT>1@y zW71vO2XR81C#g~esquXweJe?$c+CAl%!D&V;jm45p?^*?wyM?gKSP{MV9enT_oNJ)i~>C#}~!o5RbVMw9P#U1fY$cLcJgSG%!I5xV2)BtUC0V_&e;T*gX-Dh@_@s529 z;0kYRt9rnp+#T!adZx_;6-I`ybxpEYI0rPNyTA)KbSNrcg%4?Sx5K$?$#(2>)4D|! z72l_cud`|SoW3X0_OvTpJU!+3c90-5)dNdzzLJTpd$KjRCUXj=TfB}%7DEn&Rw#ge zk_|)F{2%W>ztOs5{s;8_TpO304s-c>29wFRFA%uZY#7$$BzalqGn4{Oo6Q;`cI|R} zstZrw*ZyhRYz}KHpe>W`lXa}S$}2TsEd}ydT)bQDl~y=rpbr)RS@I6B3Y)e9vDhOT zU^T0AFgNAEOCa(Us(7WqkGqOM*qX^Cf>gpgw6VG!fPw0VPHm6Z<>v>a#7vQz)*Nli z>=x`z`lLOnpe-wBw6FN?c+0V^K+rh<7$BnSgALyyxo_?T8pk8tyG41amW=V*+IhK4 z2v%ZObMEryQR|iiFd#qJ{;*W+Kg;j@D6nTCXx?I7)8ECLA}en6@*H7bv3(Q7NMZ0&_(`vi%?H+7dksOK(;3-kTE?C7pkl~;16S+$+3msG z;EF)S?=s!2?1Qb-#5?|&Rc~Q**Pcnt`C?isg{DlU&?FW^J5$crj>gjtF-+2Mwfuei z%O?p;B!-Gn)Pz0KUhUYl_ieRK^{*q3D5(s;Z-@gQ~EUfLoRN_<9)+_7~*EhQYLGEhy9tZ>>g~w zv^c5QYVTaMk9R;s@p!gh1@b;DUO;hc@A55`E4=QCgFeomHN&*3@dM2_F7O99h8(RQ zVDUOuJ_~r{XV29d`O0TiyJl@BW{8Loyi)2R?+7l?JJ4LZ_G5?x^DAsv_*bOYL#8-b za}ZLn!%O$@W2qD6q`tt|f?m}kH~QiIbjP;(FX>gm#K(hF#$EB?vN!#27j0eo$60in z9%8$IBQHh`-3JrkhQmsPPyleFnRL$F+kcKgkbVh~HmxV{Ow>c-h<@j9NpET=H$(PgCxD z48qM*`JH7Yv~pL6yg`dlj*I9l<3EA4&PT&P0?l{>S!oJD_xcM5GU%H?voSB7A=(|o zF9)rY z*cE0z7(7E&lYD0#ST6i-_yyDcHuB~$)OK}uR!ut|5#d!pBC9VjWXS8DEX-&{aHeEJ zzM?}>0Miqj$m(&7iUh=YGo6#Tz*$ZWf+`j+to)3ApJ9AJKJjdRL3(6&T5xmE`Sw-X zE{WBcMIb-D3cbQP1qP1yF@OF$W;?~Ac&W*1aGHP6X?$mNI0Ka}Hqq zAc<8#!km{HZep_mn&9p))q^?t@{?n)YZF&>t{jCQ{|ZYDc|h&0{sI0p0P%%AsR)PJ zC+VEdkC|B^q0rivj@qi0&_HW4@uP8iNBtI)o-_9EkMV<0gVm>9_Rs2m_r$tnAvHE@ zIM&g}VH-F4p)yG0tTb~sIa?V)y_jrgi|4U3QLwoh?<%?BW9+q4=rzjlB<~=`w5hud ziQ}2{)+vdMoN`GxZQYU+sIWZa8Xo!q&*Et(dbCQON&J9|vK>Cb9*3%}D;|w^#XkpS zG{_XoziMo3veT&lsgLX!Q5wF8t!EB&lX7kEem3zjtl{f>*VFN9Wpj#xt%>n3p%EWV z<+m|$x>gGrca1iF020unBYLjKcS`ZmsS)(9JSJRc*%v{n$E(~r_O@S1;q6h_LF(mO zuUk;4QpndXX>*z8s)@u+9}S-c|I#_j<=%6KrOF7#yq>79qDMZWTi|Z^gefm&`$!=- zFWV-(bM79ox2?%Jz9;lfRrs03P8ovZg$MlSaW1l86w=@|Il`)}W~RyJzk&k5|5kOx zOJg>gWj(6LvvT#UQ~g=M?5n<<|BZR82j7BKXz~ zSHTAJE4F^C!Qor>IpC;F7|#hBH&MNz(*%8o3ato;7gRmJvyv*a{uyRtOoOqH%tznK zS>lx6IEhyl##T_&NAaA&pMw#Y(1Vzd*pO>LejIa0W_F3Q3Tll-`YRG(vGqZEn^^$h z+)Jk)*YkU_T99ik$pimTfHtutsBwwg_9vk~ zcz?q|fASUt66_YrKUznMjqlP&Ij%zg{+VTMr?-#4iHc6ojfXQ~u)j+e;Ny^@;K~?U z*`?`Y2hj)ge|vkJJrFYhJ$Ae&VNBtZ}5~ z?Di-2dht`x*OIiA)1mPrSDW>*JdTl~mS+={J7+3NKV-ngG8CiU8n#vt8u# zUo&oU;}@g$mO|QJ4hCUc)c9i52b`?9GoCB}5rth_0|ky)5y=XnSX`iGJEp-k3 zGE@kKw~!iIzI@piUENIYx0u+p_qZh1FE6P(JmX2a>+{c2xfl;@e}BJk!K|o4yRvvW z1B8LKAtRS2UQC-v4vt*T# z2-XKx;RU_yr9FViI*_kZH|JDcJ)G_I`cBKSp|?TRwob97ym4cr$MeRbnwj^Q{rnclyjqIoWTjQ%-b#Kc{Wm@lC)i$^g;-%Dp)s~V zPd*RA!o>%f1#EG#0ei)&)%=qaIWgmZl+$&?ZPY%5*RY#@r=CS=5QAS_oT#!@;oyn| zY3U#hBu1EE%+|qgmjCZ~TC04L+5RdLqZp>R9xDBs=|7yEiu!hbD6d(k{Xxs}AL@$% zy~C|Q{`aG>@uMl0RmZGqJpk}$xJ1ZVCG;k)41RiB^$3Pt!BezC^?)NYZ`m8Z3(Hd|_0)>V zyB=lay}NK}EXmLBWoahmX*C%e@~GZjVhhu%7hd%^(%DFhVd$Xs zL-mG9c;GC1wwICUR;C8(krrYRkD!0ms9BH$-RU>NZ-;+gYbhg0!UsHgygO7q46Yo# z!wD>2Pfvf@>(G9cmyO6lny+S{)7 z0Se(0Q)j2e5H1M@2SvkK+kKv+jFAo1M2N)9%*~M#Xr*QMRnBf1RR{@t)E52MmVoMl z0nmJ59a0qm-iX}r+JM_ao1hcEr1cK(Ot6UOx=@TSF_CTaM5^rvM5FF<+_Cn4M{-ZH} zXyvcf9QS@bD1A0$x04X;^)XRM4V{xQ67M}N-@Gu->SB2puXF*)fHoLYPp3xB1z*)P zE>N+;UDijm(N~e`CAkWN8RsQjW+hD?mjt1|c%MkMNr)Pv=T~S&^nPmzEcU4(sSz5u zo3qEq`(IdtwFsRRq!Q(i^{U0u+%b<7A{AjaQ?OF^@DQQLFx|Qi679fpsL*4e^d!D0 zDf?5sYJFi5?hb0_q0Jp!P@m|cIr~b+D~>qNPQP75KyFFAr#jUOCu;tUbk}nW;M7&J z`%*xXxfp0-L6X@_O5F+G5*8ey3opu6PZE5QgU7A{x)F%);ph&o#KECR9Cg81kX9^q zlGa*rb*TCl1_S$@FJ3Zs%8iD&x1CG@V)AyW+CcrRRK$AnS%Q}=Cq(QyZY)jiLqf=i zq&DSA{Nc}EI`QGeIp?9+CM}|EWMbqO(Co?kGc3@6Ujg+sqG9p%X?Aa?!8RigE%W#8 zW7?tfqUYWntQKW68|E8JaO^gpgghUZ%^&7V=S*8Q8IUZ}O| zP>IgU#^S=Nj=~4owMcB`PT3~Y9=W9C=o`hJHrz?{&V<*6`RZ&qW(*-)Re580xKjS* zy#)4+6hVR;uOp=;Fu-eCExSz^2qs<^*sa&*cqxA^ci9&qSUsJpSUo0XwLlK^`gqk+ zS-zyHtVn5p&{EQiKvTfbZj~EK**(QXv@Wb4_FI2Hg$1nwC_BbfgMW4J?!B9e$aHMm z5m^+?^0p^o0Up>9+7uibzMAoq|IJUzX`3^Jxrf_oH+|B;ndix_>AXmJ0JG?XWM^}u zioGYI=9Y8Ao>s*0nCT7BA4!vevJ^bG>$5p204f6L<`@>4`z2V^D!8R>U!_OR5KJ)? zAd*bC@M}xcT;86Vj@$7M{UTFwXBJ_WAgB2oVo$>V={ot#-Ft9gcQ1r8@_wGMLsKI4 zs4dzG4^F4k`LosgZ=Ip;pU#tGaqPP;x7=g&?EO9tp&` zb6gQLaVJ-8lTUWPRp9e&zH3L+urmIhNz~GlYO&cggAIP4sFVCf4Ty^OF9Yklm$~c8 zHokXmxP=YvfNBI6P#dG4EPOYr1wU6ew7|(+#K|c+IAJzI>dJW4FbA*<5j3}@v9WEc z1cmMktA{*z)~obF*ntN=Y#XCUcR?bcbOIx%XfY9hKMai2rd!H`8uENYqR9 ztQG|&6N?GFMd*?6Tay$%+tcvuG4(8ri`X;kx#={0#K6?ENA zo_q}&>%Z=Hbe*`UXu6ADK`2`sWUp4%xK8f4#8GAXrXJ&C>IgyvHPX_gSyqq~>gevj zAwzD`#!vCgbFIu?Jm&Z>;9zbO4OGLOl9E{>%H{f9f+aB(ozj~~VkY0n3 z3UV4#X``#>CsmuGW?!WdQ2R~S0$C28(iO#V2Dx@QHr%0WJ$d?~iS}H3_){nA`~3r% z&k#+GG9Q3a@`72x7T^?J@Njrg%Jz}iP8U#brYy1S`6i{-!0#& z1SfpQM4x>|F13Mb9%x%LWn*F+3eK`a>8?P{unB+O1(x*;ZfzH$_M`gn(=`9mEjjWs z?U^n3vpUhz>WIN=q!wW})cZB1Ehyo|JwFUS4icxX;8=#N8bGjcAMn^BX|D%_e4#$> zDr>xdz57=LIMQLebPj9rxstc+M6UKB)mxh#%V5D9DQ)W-z>*8GSGC)Yi=pp^uRYN% z3O76lEC+7ntoCeQkkwF`u}sxJ|2y*(Q0_T9ipWcQh32laA4SJ9K1E459xl95ai_fL zYxgX7uddTJuw1{C7VMIIDD4PMpYmoxbxKMD=P2=wX!&%=xwkkmf9FDk46&_@S|A#| za_@D^Zl=2M8pa#>U-Trh6*FhThZo-2VoyKjnV?~%iU8K~sU7r}*h)v^{qH2J{cmS`eEzQX->{gIo& z*;&peCzUgXRSHpG!%TN;|A>tIx#PgWr*^#^_#Am}YyVp9lwYeyEWvLE_{!{u*OF?r z3oP%4e`6VcDA352rn)aGr{S2KrYIG(>mrg_qBnJg2Gn3AZ=WVyTVu0H!o`0`N(N7~ zDwrootQ33+e#+J$o?20ntHe*g!<#6w0=Li-r}$0Ot4i0Sq#NSx>IO!^1$8@DLUp|6 zF3urOQtA3WC0l z_6j;)KB(zeb!b5W$4dq0!eS~1g|{+&em(TqJVz*5a(1CDTxoq|@aFG+Np&Ij>o3haC}%; zyz`C!$+J1G9!Xo~gCv0t>7NlH$Ts>okY4U+Z}OSvEr5(#;(@dd1#pG?mjolC)EOzC zLPb$~qq}~eA=OV`L*oyabdngB8B=QjumJ)W8#wci-8JWP7!h~25UuCbY0U|7!@yV1 zm*Xow#q9tuZQ^y%(Fcq=XwKB*I|Kpp)$v&SWzznH4(0r2rkx#(kdW0?e`Yh(W3;98 z_5hdyyka?j%?#yXkk(Fz0_SW;Lh2VzX zdrrM#I`)X0Ud<_o=7=P_aDy&f%)=l9T4OgQ{!yp92$EP-97j^qhBUFUSYp=5L*7P>x5gUqH!;y1Kn9!lb!lXy$~)4On6U|6*%A$r z!QJw^Jd;bz-{OVN^p!K(G(t753yjgR zH)nJtp+lnI(ArAeC(mB=DjoxPgx8<2+iO$9Z|?%%;@A!d!Z{<%+cVHl7}7`eh>o+%=y6UrxcH(BHLxAK8xK)*R{3a?| zYUDh$!r@+@JntIB!@eEp!C~Ys)c}ldxZMD#c(Q)wtsm3As!^bxIY-}YfZi>rYxS$P zXG7q&Sm+Zp{4lygR{oq_IH+hgxr+HQ_`CQOw6&SUHrK)xouM+>=nY5}%Uhu9<7dzx zipk2xN31tIE&t|!va`HZ^9S`YjVQmPgHDjS@qu+aQT^bRJj2&mf3|UhTMKU|w}^MT z!|YM-51!fw^6yZm0UB0tubX^Br|iu<$^SS)&qzPXis5SjHguEo^n!({&fx|t)Y#hEF#y$&U3(C{`86w?zl9p1` zopt*bY_j~DjLkKc>jKbSL&JGiPgUe$ds3n=tDQs}2p!IdNM`7iSY;IAOC{RX4v z>g7lGd;gMOGbcQSuqHi)K(4Xz!cug~xTPKlD&4lXA1M{UptFAjH!;89+b_SPd_m(R z241cIu|J~c1l7aY#b*mDGx*fPkv$pwb=U5R8Olz~`%&WlNL=>ZX42wm*3guqWE?0o zT1;YT?@@N$#APvf`Vu5xJg%QZH0cTQlNmhdw;8tSdCs-d-R~wnpdX^0r1_V>*G6MN_s{pt@I^J1W{oN?{fim- zpWkX5*$P}+`@<=^XOt;1NLi8oh%F!~mAu>H zY-v;}51d__+I)U1FRT*L|4H#r*h`7cM~WD(*cFG8OlP$D%hx|z^7H0;UA-!+@qzr| z0<0GS42{Y31RQ<#vYs(@}|tG27n^5KHHdI;r^GsiDb z_~rDaLfJd9mxlvxosN!Kq~##TeWr2-N6WCJX>lO~^pASMkopn9-yf3^wHd8PdYo}! z+k-^mj$D;6>H9GFXj_Hwr`NYZGhb4dWRg(NZ>coj!K2!f8P1Q*LShi2w<4BUzO4~! z+MAWd{xZd}`;S>58ta=PH+WTrCg#9;a4A)y9D!U;SCL@EkA8G)za`*4ypm{K)FG$(oa0ks|9G7yN|ss(#9xjr|0qYqfgBz2cV3 zmZ@Nhi~rPqw&0sGp>~B`k<7Sa_erxw*wYtqgWa6O=9|(0mK3i&s_uQ#b5_|rJv1tm z^_O6O82{XSUjf%N7Iu@r2(BFpOG4mQuJ_!Jf5mZ)$ekKUZ7u@QXbwjbk9_^u(yCe6 z3O&3s{+(3#KDInC;=@p6ZNsl?YF^H_^F4R2d_2tGv!?td7T{SbS8 zG9!WhW|LSKNRoB;k42R-eMJ3}NDL|aQe-R7Po7bR=}XfW|6>Xte!0$wlu}oGtw_!3 zVfZhegoCQuoR_tY1_g1KzmjagTG6nn5hKVD0omAId*_&tA_5 zpn7z&LH?o!>B$}9&CW0VsC<`kck`w^TlMPP$2m2|UC}mur_+Sf`>ea~ZZ?VLO+a31 ze@fHa>1q#W{>!|00douSUaFI)(@&lI_1q;q`fRDa&9Ju@Pn>&!3pG_sojzf2h|9Ii zeQqk(v~s~=XD^~Dxc)oL;cy^5q8AEQ&sf_h^Z>y?WKvcfG2`m&qiI13nL6-}C$Dp! zG#A=(Is)VW)u#B@^lv0;8}6P@0jE8TyNzR(!p5|!MONY|Nh#jn1~Wgd`;&%EtVttB z`@D~#7sY{z$E;M-Nw{=@{!xLc>{O+vt6p*L*8gGatHYY?!?pp7-NiW@1Ey)-|w&Q&)vrzyBELv zIjpO zbQuaXK*j$N@36$WO#2lGv@=^?ugmc#hKp3fn2Oa3rWa;rYhn3oJu@GJKX2^02s4eK z<6ph(WsSFvucVY%=+Wo5zQtL@u~77GNobkGW;ymH{|>9SV+V~KwEm& zH8HIP9cI%)19Du!?5oB0%E<8A-{a*jZgjkU{P*PHXk`{#vR#G&j>hPmov#AaGY?)% znR)W!I=;%#BRG^ajbOrH&t^Z87TnIv5;%E0Snq0<7m@|$gql5cnLcbbi66PQRA5Tn z5Mmrr5%c5wRrCYCJ#Zg`3d0+I8ky)Rdhw^uPG)mCkj4qEu?AU9o4h{V&Sc*~6t*Pv(keiR~}+s7Ag$p<3&68!D(i_@@H7aT~%{6vhzDExpU34E0A-8 z3NAd^&c0)$t6s{FkdyOMNMC=R?0HHn0!I6*ekIp@9$K-D%=y!^C{u_Y%?B`JiY@)D z%!PMB!l){f!U2Y3-_5HlH?c>qFXuYsBgR|@?_*RtGA`xT-ki4<#CwtfoazXck>6*t z3o9)G-0wu0k~*+{U^Zw`Yd#qO5}p0DM>X0sKH3mMYYXP;As8>!m2+E-#JeUNY!f?z zq!w;VvD2Mfm}d8+KeiB2qG9;B)_mUv6W;v?F2cj(RXR+VLeE{RnOAqJf7yx?L25vnoSPId3W${YxOKZTa&Fpr_ zzz#L!sVitEpTN7a$ea5p@5+9l#2_*8^=C$YwBG^TpdBt=6eADP@ zasFttr#6kLCoK`NoA;HY!I7obeHyOVTb+dXbRxrKTq|2sU)OkiQyf9=*RmElkckFs z2ZqD|i7ccz8I)DGp%gckjcs126cvZr`RI2&O6I;(jDgxC$L(K+(ec$=5Usy^*J2$H# zPzxcuL|}FOtm895|CYG{lF@ICD5o#mZ>_1qXF%#dAs@_!s?v@})2t|ZsF70r!E{(* z?Dh_wlqE5GIW*Z@*k|_b@$=^r~K3-7g_Bq{*9&8IwF*STPI-w!TAa&&;J_Cqi-&~AKVsQHGQ$@F4vUG zH{Ku6RC|S(C)HFEuazl6RRlxRqgLhdbKNG zK=ZcOvBk(cjU%hCBK&DNtLpP%fWU=}eUXy>_4(wB8mGpBBxP54h&voIz$cr?{I(j| z&I7SkSB_t`%U~gz@DOvj8xN>6WDGdQ`f?RsMY0NAL-D5u*mAW&itSV!$DhfPm-PLn zQH~GADeVnIg`e?ZEuG%mlm4!%@EPT&o2t}dT)uEw{xu?w=zN&C*L}YaaNZU!6 z(x~QGG<&_s71@-v&_VGlSTsXO*J8D?b}_9QU7&T3%7jTY4{`W<)n3qi@1_wC5z9>+ zQ1(Ks$4Pi&8pA{G{A0buiVc*)E*!R)FG~Jgp?0i#8lwnzvEQYdXb#>MpPNt~`JBB{ zp5{?x8^JZ|@71ca-fN36>St0DLVjJ^J5)&+d!{=(g@wp>Uw z3MnIxt$2=dp}%S&=AYRz@Qyu5Yk;{J^L)Y*_*G#O)_n)(T!$Q-Oh&DL(;bmB?mvm; z_`7p&Io0yhj7_w*0E1Qj*9ols%f}c!;3!m9JKN0z{A9kjj<{Vafmp9&D~KZzu4g|p zj8wO*BZaul9TlUNPgESM`3rthm0BwWIApzWg21pGM0EN zj1*?r@s2)}7U_7?ajrv6_u?_(+nJ@S3M2A+3|x#yOq|T74D?-JF>~D${c--s#UC6$ z1ij9B33;)O