Browse Source

refactor: clean up code style and improve Lua thread handling

Akeit0 7 months ago
parent
commit
abdb7f0b2d

+ 9 - 9
.editorconfig

@@ -104,7 +104,7 @@ csharp_style_conditional_delegate_call = true
 
 
 # Modifier preferences
 # Modifier preferences
 csharp_prefer_static_local_function = true
 csharp_prefer_static_local_function = true
-csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async
 csharp_style_prefer_readonly_struct = true
 csharp_style_prefer_readonly_struct = true
 csharp_style_prefer_readonly_struct_member = true
 csharp_style_prefer_readonly_struct_member = true
 
 
@@ -208,26 +208,26 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
 
 
 dotnet_naming_symbols.interface.applicable_kinds = interface
 dotnet_naming_symbols.interface.applicable_kinds = interface
 dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.interface.required_modifiers = 
+dotnet_naming_symbols.interface.required_modifiers =
 
 
 dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
 dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
 dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.types.required_modifiers = 
+dotnet_naming_symbols.types.required_modifiers =
 
 
 dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
 dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
 dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.non_field_members.required_modifiers = 
+dotnet_naming_symbols.non_field_members.required_modifiers =
 
 
 # Naming styles
 # Naming styles
 
 
-dotnet_naming_style.pascal_case.required_prefix = 
-dotnet_naming_style.pascal_case.required_suffix = 
-dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
 dotnet_naming_style.pascal_case.capitalization = pascal_case
 dotnet_naming_style.pascal_case.capitalization = pascal_case
 
 
 dotnet_naming_style.begins_with_i.required_prefix = I
 dotnet_naming_style.begins_with_i.required_prefix = I
-dotnet_naming_style.begins_with_i.required_suffix = 
-dotnet_naming_style.begins_with_i.word_separator = 
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
 dotnet_naming_style.begins_with_i.capitalization = pascal_case
 dotnet_naming_style.begins_with_i.capitalization = pascal_case
 
 
 
 

+ 2 - 2
sanbox/ConsoleApp2/ConsoleApp2.csproj

@@ -9,8 +9,8 @@
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>
-      <ProjectReference Include="..\..\src\Lua.SourceGenerator\Lua.SourceGenerator.csproj" />
-      <ProjectReference Include="..\..\src\Lua\Lua.csproj" />
+        <ProjectReference Include="..\..\src\Lua.SourceGenerator\Lua.SourceGenerator.csproj"/>
+        <ProjectReference Include="..\..\src\Lua\Lua.csproj"/>
     </ItemGroup>
     </ItemGroup>
 
 
 </Project>
 </Project>

+ 10 - 7
sanbox/ConsoleApp2/Program.cs

@@ -5,11 +5,12 @@ using Lua.Standard;
 var state = LuaState.Create();
 var state = LuaState.Create();
 state.OpenStandardLibraries();
 state.OpenStandardLibraries();
 {
 {
-    var closure = state.Compile("return function (a,b,...)  print('a : '..a..' b :'..'args : ',...) end", "simple");
+    var closure = state.Load("return function (a,b,...)  print('a : '..a..' b :'..'args : ',...) end", "simple");
     using var threadLease = state.MainThread.RentUseThread();
     using var threadLease = state.MainThread.RentUseThread();
     var thread = threadLease.Thread;
     var thread = threadLease.Thread;
     {
     {
-        var results = await thread.RunAsync(closure, 0);
+        var count = await thread.RunAsync(closure);
+        var results = thread.ReadReturnValues(count);
         for (int i = 0; i < results.Length; i++)
         for (int i = 0; i < results.Length; i++)
         {
         {
             Console.WriteLine(results[i]);
             Console.WriteLine(results[i]);
@@ -18,13 +19,14 @@ state.OpenStandardLibraries();
         var f = results[0].Read<LuaClosure>();
         var f = results[0].Read<LuaClosure>();
         results.Dispose();
         results.Dispose();
         thread.Push("hello", "world", 1, 2, 3);
         thread.Push("hello", "world", 1, 2, 3);
-        var result2 = await thread.RunAsync(f, 5);
-        for (int i = 0; i < result2.Length; i++)
+        count = await thread.RunAsync(f);
+        results = thread.ReadReturnValues(count);
+        for (int i = 0; i < results.Length; i++)
         {
         {
-            Console.WriteLine(result2[i]);
+            Console.WriteLine(results[i]);
         }
         }
 
 
-        result2.Dispose();
+        results.Dispose();
     }
     }
 }
 }
 
 
@@ -48,7 +50,8 @@ state.OpenStandardLibraries();
         for (int i = 0; coroutine.CanResume; i++)
         for (int i = 0; coroutine.CanResume; i++)
         {
         {
             if (i != 0) coroutine.Push($"from C# {i}");
             if (i != 0) coroutine.Push($"from C# {i}");
-            using var resumeResult = await coroutine.ResumeAsync();
+            var count = await coroutine.ResumeAsync();
+            using var resumeResult = coroutine.ReadReturnValues(count);
             Console.Write("To C#:\t");
             Console.Write("To C#:\t");
             for (int j = 0; j < resumeResult.Length; j++)
             for (int j = 0; j < resumeResult.Length; j++)
             {
             {

+ 4 - 5
sandbox/Benchmark/InterpreterSteps.cs

@@ -17,7 +17,7 @@ public class InterpreterSteps
         sourceText = File.ReadAllText(filePath);
         sourceText = File.ReadAllText(filePath);
         state = LuaState.Create();
         state = LuaState.Create();
         state.OpenStandardLibraries();
         state.OpenStandardLibraries();
-        closure = state.Compile(sourceText, sourceText);
+        closure = state.Load(sourceText, sourceText);
     }
     }
 
 
     [IterationSetup]
     [IterationSetup]
@@ -40,14 +40,13 @@ public class InterpreterSteps
     [Benchmark]
     [Benchmark]
     public LuaClosure Compile()
     public LuaClosure Compile()
     {
     {
-        return state.Compile(sourceText, sourceText);
+        return state.Load(sourceText, sourceText);
     }
     }
 
 
     [Benchmark]
     [Benchmark]
     public async ValueTask RunAsync()
     public async ValueTask RunAsync()
     {
     {
-        using (await state.RunAsync(closure))
-        {
-        }
+        await state.MainThread.RunAsync(closure);
+        state.MainThread.Stack.Clear();
     }
     }
 }
 }

+ 13 - 6
sandbox/ConsoleApp1/Program.cs

@@ -3,11 +3,17 @@ using Lua.CodeAnalysis.Compilation;
 using Lua.Runtime;
 using Lua.Runtime;
 using Lua;
 using Lua;
 using Lua.Standard;
 using Lua.Standard;
+using System.Text.RegularExpressions;
 
 
 var state = LuaState.Create();
 var state = LuaState.Create();
 state.OpenStandardLibraries();
 state.OpenStandardLibraries();
 
 
-state.Environment["vec3"] = new LVec3();
+state.Environment["escape"] = new LuaFunction("escape",
+    (c, _) =>
+    {
+        var arg = c.HasArgument(0) ? c.GetArgument<string>(0) : "";
+        return new(c.Return(Regex.Escape(arg)));
+    });
 
 
 try
 try
 {
 {
@@ -18,17 +24,17 @@ try
 
 
     Console.WriteLine(source);
     Console.WriteLine(source);
 
 
-    var closure = state.Compile(source, "test.lua");
+    var closure = state.Load(source, "test.lua");
 
 
     DebugChunk(closure.Proto, 0);
     DebugChunk(closure.Proto, 0);
 
 
     Console.WriteLine("Output " + new string('-', 50));
     Console.WriteLine("Output " + new string('-', 50));
 
 
-    using var results = await state.RunAsync(closure);
+    var count = await state.MainThread.RunAsync(closure);
 
 
     Console.WriteLine("Result " + new string('-', 50));
     Console.WriteLine("Result " + new string('-', 50));
-
-    for (int i = 0; i < results.Count; i++)
+    using var results = state.MainThread.ReadReturnValues(count);
+    for (int i = 0; i < count; i++)
     {
     {
         Console.WriteLine(results[i]);
         Console.WriteLine(results[i]);
     }
     }
@@ -40,6 +46,7 @@ catch (Exception ex)
     Console.WriteLine(ex);
     Console.WriteLine(ex);
     if (ex is LuaRuntimeException { InnerException: not null } luaEx)
     if (ex is LuaRuntimeException { InnerException: not null } luaEx)
     {
     {
+        Console.WriteLine("Inner Exception " + new string('-', 50));
         Console.WriteLine(luaEx.InnerException);
         Console.WriteLine(luaEx.InnerException);
     }
     }
 }
 }
@@ -74,7 +81,7 @@ static void DebugChunk(Prototype chunk, int id)
     index = 0;
     index = 0;
     foreach (var constant in chunk.Constants.ToArray())
     foreach (var constant in chunk.Constants.ToArray())
     {
     {
-        Console.WriteLine($"[{index}]\t{constant}");
+        Console.WriteLine($"[{index}]\t{Regex.Escape(constant.ToString())}");
         index++;
         index++;
     }
     }
 
 

+ 15 - 9
src/Lua/LuaCoroutine.cs

@@ -14,7 +14,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
     {
     {
         if (!pool.TryPop(out LuaCoroutine result))
         if (!pool.TryPop(out LuaCoroutine result))
         {
         {
-            result = new ();
+            result = new();
         }
         }
 
 
         result.Init(parent, function, isProtectedMode);
         result.Init(parent, function, isProtectedMode);
@@ -56,6 +56,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
         State = parent.State;
         State = parent.State;
         IsProtectedMode = isProtectedMode;
         IsProtectedMode = isProtectedMode;
         Function = function;
         Function = function;
+        IsRunning = false;
     }
     }
 
 
     public override LuaThreadStatus GetStatus() => (LuaThreadStatus)status;
     public override LuaThreadStatus GetStatus() => (LuaThreadStatus)status;
@@ -66,7 +67,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
     }
     }
 
 
     public bool IsProtectedMode { get; private set; }
     public bool IsProtectedMode { get; private set; }
-    public LuaFunction Function { get; private set; }
+    public  LuaFunction Function { get; private set; } = null!;
     internal Traceback? LuaTraceback => traceback;
     internal Traceback? LuaTraceback => traceback;
 
 
     public bool CanResume => status == (byte)LuaThreadStatus.Suspended;
     public bool CanResume => status == (byte)LuaThreadStatus.Suspended;
@@ -82,8 +83,14 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
         return stackForDead;
         return stackForDead;
     }
     }
 
 
-    public async ValueTask<LuaResult> ResumeAsync(CancellationToken cancellationToken = default)
+    public async ValueTask<int> ResumeAsync(CancellationToken cancellationToken = default)
     {
     {
+        if (isFirstCall)
+        {
+            ThrowIfRunning();
+            IsRunning = true;
+        }
+
         switch ((LuaThreadStatus)Volatile.Read(ref status))
         switch ((LuaThreadStatus)Volatile.Read(ref status))
         {
         {
             case LuaThreadStatus.Suspended:
             case LuaThreadStatus.Suspended:
@@ -107,7 +114,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 {
                 {
                     Stack.PopUntil(lastBase);
                     Stack.PopUntil(lastBase);
                     Stack.Push("cannot resume non-suspended coroutine");
                     Stack.Push("cannot resume non-suspended coroutine");
-                    return new LuaResult(Stack, lastBase);
+                    return 1;
                 }
                 }
                 else
                 else
                 {
                 {
@@ -118,7 +125,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 {
                 {
                     var stack = GetStackForDead(1);
                     var stack = GetStackForDead(1);
                     stack.Push("cannot resume dead coroutine");
                     stack.Push("cannot resume dead coroutine");
-                    return new LuaResult(stack, 0);
+                    return 1;
                 }
                 }
                 else
                 else
                 {
                 {
@@ -154,8 +161,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 var results = result0.Results;
                 var results = result0.Results;
                 Stack.PushRange(results.AsSpan());
                 Stack.PushRange(results.AsSpan());
                 lastBase = Stack.Count - results.Length;
                 lastBase = Stack.Count - results.Length;
-                return new LuaResult(Stack, Stack.Count - results.Length);
-                ;
+                return results.Length;
             }
             }
             else
             else
             {
             {
@@ -164,7 +170,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 var stack = GetStackForDead(Math.Max(1, results.Length));
                 var stack = GetStackForDead(Math.Max(1, results.Length));
                 stack.PushRange(results);
                 stack.PushRange(results);
                 ReleaseCore();
                 ReleaseCore();
-                return new LuaResult(stack, 0);
+                return results.Length;
             }
             }
         }
         }
         catch (Exception ex) when (ex is not OperationCanceledException)
         catch (Exception ex) when (ex is not OperationCanceledException)
@@ -176,7 +182,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 ReleaseCore();
                 ReleaseCore();
                 var stack = GetStackForDead(1);
                 var stack = GetStackForDead(1);
                 stack.Push(ex is LuaRuntimeException luaEx ? luaEx.ErrorObject : ex.Message);
                 stack.Push(ex is LuaRuntimeException luaEx ? luaEx.ErrorObject : ex.Message);
-                return new LuaResult(stack, 0);
+                return 1;
             }
             }
             else
             else
             {
             {

+ 26 - 22
src/Lua/LuaFunctionExtensions.cs

@@ -1,28 +1,32 @@
-using Lua.Internal;
+using Lua.Runtime;
 
 
 namespace Lua;
 namespace Lua;
 
 
 public static class LuaFunctionExtensions
 public static class LuaFunctionExtensions
 {
 {
-    // public static async ValueTask<LuaValue[]> InvokeAsync(this LuaFunction function, LuaState state, LuaValue[] arguments, CancellationToken cancellationToken = default)
-    // {
-    //     var thread = state.CurrentThread;
-    //     var frameBase = thread.Stack.Count;
-    //
-    //     for (int i = 0; i < arguments.Length; i++)
-    //     {
-    //         thread.Stack.Push(arguments[i]);
-    //     }
-    //
-    //     var resultCount = await function.InvokeAsync(new()
-    //     {
-    //         State = state,
-    //         Thread = thread,
-    //         ArgumentCount = arguments.Length,
-    //         ReturnFrameBase = frameBase,
-    //     }, cancellationToken);
-    //     var r = thread.Stack.GetBuffer()[frameBase..(frameBase + resultCount)].ToArray();
-    //     thread.Stack.PopUntil(frameBase);
-    //     return r;
-    // }
+    public static async ValueTask<int> InvokeAsync(this LuaFunction function, LuaThread thread, int argumentCount, CancellationToken cancellationToken = default)
+    {
+        var varArgumentCount = function.GetVariableArgumentCount(argumentCount);
+        if (varArgumentCount != 0)
+        {
+            LuaVirtualMachine.PrepareVariableArgument(thread.Stack, argumentCount, varArgumentCount);
+        }
+
+        LuaFunctionExecutionContext context = new() { Thread = thread, ArgumentCount = argumentCount - varArgumentCount, ReturnFrameBase = thread.Stack.Count, };
+        var frame = new CallStackFrame { Base = context.FrameBase, VariableArgumentCount = varArgumentCount, Function = function, ReturnBase = context.ReturnFrameBase };
+        context.Thread.PushCallStackFrame(frame);
+        try
+        {
+            if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
+            {
+                return await LuaVirtualMachine.ExecuteCallHook(context, cancellationToken);
+            }
+
+            return await function.Func(context, cancellationToken);
+        }
+        finally
+        {
+            context.Thread.PopCallStackFrame();
+        }
+    }
 }
 }

+ 3 - 3
src/Lua/Internal/LuaResult.cs → src/Lua/LuaReturnValuesReader.cs

@@ -1,13 +1,13 @@
 using Lua.Runtime;
 using Lua.Runtime;
 
 
-namespace Lua.Internal;
+namespace Lua;
 
 
-public readonly struct LuaResult : IDisposable
+public readonly struct LuaReturnValuesReader : IDisposable
 {
 {
     readonly LuaStack stack;
     readonly LuaStack stack;
     readonly int returnBase;
     readonly int returnBase;
 
 
-    internal LuaResult(LuaStack stack, int returnBase)
+    internal LuaReturnValuesReader(LuaStack stack, int returnBase)
     {
     {
         this.stack = stack;
         this.stack = stack;
         this.returnBase = returnBase;
         this.returnBase = returnBase;

+ 5 - 41
src/Lua/LuaState.cs

@@ -19,7 +19,7 @@ public sealed class LuaState
     readonly LuaTable environment;
     readonly LuaTable environment;
     readonly LuaTable registry = new();
     readonly LuaTable registry = new();
     readonly UpValue envUpValue;
     readonly UpValue envUpValue;
-    bool isRunning;
+
 
 
     FastStackCore<LuaDebug.LuaDebugBuffer> debugBufferPool;
     FastStackCore<LuaDebug.LuaDebugBuffer> debugBufferPool;
 
 
@@ -33,7 +33,7 @@ public sealed class LuaState
     public LuaTable Registry => registry;
     public LuaTable Registry => registry;
     public LuaTable LoadedModules => packages;
     public LuaTable LoadedModules => packages;
     public LuaMainThread MainThread => mainThread;
     public LuaMainThread MainThread => mainThread;
-    
+
 
 
     public ILuaModuleLoader ModuleLoader { get; set; } = FileModuleLoader.Instance;
     public ILuaModuleLoader ModuleLoader { get; set; } = FileModuleLoader.Instance;
 
 
@@ -57,26 +57,6 @@ public sealed class LuaState
         envUpValue = UpValue.Closed(environment);
         envUpValue = UpValue.Closed(environment);
     }
     }
 
 
-    public async ValueTask<LuaResult> RunAsync(LuaClosure closure, CancellationToken cancellationToken = default)
-    {
-        ThrowIfRunning();
-
-        isRunning = true;
-        try
-        {
-            await closure.InvokeAsync(new()
-            {
-                Thread = MainThread, ArgumentCount = 0, ReturnFrameBase = 0, SourceLine = null,
-            }, cancellationToken);
-
-            return new LuaResult(MainThread.Stack, 0);
-        }
-        finally
-        {
-            isRunning = false;
-        }
-    }
-
 
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result)
     internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result)
@@ -160,24 +140,8 @@ public sealed class LuaState
         }
         }
     }
     }
 
 
-    void ThrowIfResultNotDisposed()
-    {
-        if (MainThread.Stack.Count != 0)
-        {
-            throw new InvalidOperationException("LuaResult is not disposed");
-        }
-    }
-
-    void ThrowIfRunning()
-    {
-        if (Volatile.Read(ref isRunning))
-        {
-            throw new InvalidOperationException("the lua state is currently running");
-        }
-    }
-
 
 
-    public unsafe LuaClosure Compile(ReadOnlySpan<char> chunk, string chunkName, LuaTable? environment = null)
+    public unsafe LuaClosure Load(ReadOnlySpan<char> chunk, string chunkName, LuaTable? environment = null)
     {
     {
         Prototype prototype;
         Prototype prototype;
         fixed (char* ptr = chunk)
         fixed (char* ptr = chunk)
@@ -188,7 +152,7 @@ public sealed class LuaState
         return new LuaClosure(MainThread, prototype, environment);
         return new LuaClosure(MainThread, prototype, environment);
     }
     }
 
 
-    public LuaClosure Compile(ReadOnlySpan<byte> chunk, string chunkName, string mode = "bt", LuaTable? environment = null)
+    public LuaClosure Load(ReadOnlySpan<byte> chunk, string chunkName, string mode = "bt", LuaTable? environment = null)
     {
     {
         if (chunk.Length > 4)
         if (chunk.Length > 4)
         {
         {
@@ -204,7 +168,7 @@ public sealed class LuaState
         {
         {
             var chars = pooled.AsSpan(0, charCount);
             var chars = pooled.AsSpan(0, charCount);
             Encoding.UTF8.GetChars(chunk, chars);
             Encoding.UTF8.GetChars(chunk, chars);
-            return Compile(chars, chunkName, environment);
+            return Load(chars, chunkName, environment);
         }
         }
         finally
         finally
         {
         {

+ 8 - 25
src/Lua/LuaStateExtensions.cs

@@ -1,41 +1,24 @@
-using Lua.CodeAnalysis.Compilation;
-using Lua.CodeAnalysis;
-
 namespace Lua;
 namespace Lua;
 
 
 public static class LuaStateExtensions
 public static class LuaStateExtensions
 {
 {
-    public static async ValueTask<int> DoStringAsync(this LuaState state, string source, Memory<LuaValue> buffer, string? chunkName = null, CancellationToken cancellationToken = default)
+    public static ValueTask<int> DoStringAsync(this LuaState state, string source, Memory<LuaValue> buffer, string? chunkName = null, CancellationToken cancellationToken = default)
     {
     {
-        var closure = state.Compile(source, chunkName ?? source);
-        using var result = await state.RunAsync(closure, cancellationToken);
-        result.AsSpan()[..Math.Min(buffer.Length, result.Length)].CopyTo(buffer.Span);
-        return result.Count;
+        return state.MainThread.DoStringAsync(source, buffer, chunkName, cancellationToken);
     }
     }
 
 
-    public static async ValueTask<LuaValue[]> DoStringAsync(this LuaState state, string source, string? chunkName = null, CancellationToken cancellationToken = default)
+    public static ValueTask<LuaValue[]> DoStringAsync(this LuaState state, string source, string? chunkName = null, CancellationToken cancellationToken = default)
     {
     {
-        var chunk = state.Compile(source, chunkName ?? source);
-        using var result = await state.RunAsync(chunk, cancellationToken);
-        return result.AsSpan().ToArray();
+        return state.MainThread.DoStringAsync(source, chunkName, cancellationToken);
     }
     }
 
 
-    public static async ValueTask<int> DoFileAsync(this LuaState state, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public static ValueTask<int> DoFileAsync(this LuaState state, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
-        var bytes = await File.ReadAllBytesAsync(path, cancellationToken);
-        var fileName = "@" + Path.GetFileName(path);
-        var closure = state.Compile(bytes, fileName);
-        using var result = await state.RunAsync(closure, cancellationToken);
-        result.AsSpan()[..Math.Min(buffer.Length, result.Length)].CopyTo(buffer.Span);
-        return result.Count;
+        return state.MainThread.DoFileAsync(path, buffer, cancellationToken);
     }
     }
 
 
-    public static async ValueTask<LuaValue[]> DoFileAsync(this LuaState state, string path, CancellationToken cancellationToken = default)
+    public static ValueTask<LuaValue[]> DoFileAsync(this LuaState state, string path, CancellationToken cancellationToken = default)
     {
     {
-        var bytes = await File.ReadAllBytesAsync(path, cancellationToken);
-        var fileName = "@" + Path.GetFileName(path);
-        var closure = state.Compile(bytes, fileName);
-        using var result = await state.RunAsync(closure, cancellationToken);
-        return result.AsSpan().ToArray();
+        return state.MainThread.DoFileAsync(path, cancellationToken);
     }
     }
 }
 }

+ 24 - 85
src/Lua/LuaThread.cs

@@ -6,7 +6,7 @@ namespace Lua;
 
 
 public abstract class LuaThread
 public abstract class LuaThread
 {
 {
-    internal LuaThread() { }
+    protected LuaThread() { }
 
 
     public virtual LuaThreadStatus GetStatus()
     public virtual LuaThreadStatus GetStatus()
     {
     {
@@ -25,11 +25,12 @@ public abstract class LuaThread
         throw new LuaRuntimeException(context.Thread.GetTraceback(), "attempt to yield from outside a coroutine");
         throw new LuaRuntimeException(context.Thread.GetTraceback(), "attempt to yield from outside a coroutine");
     }
     }
 
 
-    internal class ThreadCoreData: IPoolNode<ThreadCoreData>
+    internal class ThreadCoreData : IPoolNode<ThreadCoreData>
     {
     {
         //internal  LuaCoroutineData? coroutineData;
         //internal  LuaCoroutineData? coroutineData;
         internal LuaStack Stack = new();
         internal LuaStack Stack = new();
         internal FastStackCore<CallStackFrame> CallStack;
         internal FastStackCore<CallStackFrame> CallStack;
+
         public void Clear()
         public void Clear()
         {
         {
             Stack.Clear();
             Stack.Clear();
@@ -39,7 +40,7 @@ public abstract class LuaThread
         static LinkedPool<ThreadCoreData> pool;
         static LinkedPool<ThreadCoreData> pool;
         ThreadCoreData? nextNode;
         ThreadCoreData? nextNode;
         public ref ThreadCoreData? NextNode => ref nextNode;
         public ref ThreadCoreData? NextNode => ref nextNode;
-        
+
         public static ThreadCoreData Create()
         public static ThreadCoreData Create()
         {
         {
             if (!pool.TryPop(out ThreadCoreData result))
             if (!pool.TryPop(out ThreadCoreData result))
@@ -56,19 +57,18 @@ public abstract class LuaThread
             pool.TryPush(this);
             pool.TryPush(this);
         }
         }
     }
     }
-    public LuaState State { get;protected set; }
 
 
+    public LuaState State { get; protected set; } = null!;
     internal ThreadCoreData? CoreData = new();
     internal ThreadCoreData? CoreData = new();
-   
     internal BitFlags2 LineAndCountHookMask;
     internal BitFlags2 LineAndCountHookMask;
     internal BitFlags2 CallOrReturnHookMask;
     internal BitFlags2 CallOrReturnHookMask;
     internal bool IsInHook;
     internal bool IsInHook;
     internal int HookCount;
     internal int HookCount;
     internal int BaseHookCount;
     internal int BaseHookCount;
     internal int LastPc;
     internal int LastPc;
+    public bool IsRunning { get; protected set; }
     internal LuaFunction? Hook { get; set; }
     internal LuaFunction? Hook { get; set; }
-   
-    internal LuaStack Stack => CoreData!.Stack;
+    public LuaStack Stack => CoreData!.Stack;
     internal ref FastStackCore<CallStackFrame> CallStack => ref CoreData!.CallStack;
     internal ref FastStackCore<CallStackFrame> CallStack => ref CoreData!.CallStack;
 
 
     internal bool IsLineHookEnabled
     internal bool IsLineHookEnabled
@@ -96,102 +96,41 @@ public abstract class LuaThread
         set => CallOrReturnHookMask.Flag1 = value;
         set => CallOrReturnHookMask.Flag1 = value;
     }
     }
 
 
- 
-
-
-    public void Push(LuaValue value)
-    {
-        CoreData.Stack.Push(value);
-    }
-
-    public void Push(params ReadOnlySpan<LuaValue> span)
-    {
-        CoreData.Stack.PushRange(span);
-    }
-
-    public void Pop(int count)
-    {
-        CoreData.Stack.Pop(count);
-    }
-
-    public LuaValue Pop()
-    {
-        return CoreData.Stack.Pop();
-    }
-
-
-    public ref readonly CallStackFrame GetCurrentFrame()
-    {
-        return ref CoreData.CallStack.PeekRef();
-    }
-
-    public ReadOnlySpan<LuaValue> GetStackValues()
-    {
-        return CoreData.Stack.AsSpan();
-    }
-
-    public ReadOnlySpan<CallStackFrame> GetCallStackFrames()
-    {
-        return CoreData.CallStack.AsSpan();
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PushCallStackFrame(in CallStackFrame frame)
+    public async ValueTask<int> RunAsync(LuaClosure closure, CancellationToken cancellationToken = default)
     {
     {
-        CoreData.CallStack.Push(frame);
-    }
+        ThrowIfRunning();
 
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameWithStackPop()
-    {
-        if (CoreData.CallStack.TryPop(out var frame))
+        IsRunning = true;
+        try
         {
         {
-            CoreData.Stack.PopUntil(frame.Base);
-        }
-        else
-        {
-            ThrowForEmptyStack();
-        }
-    }
+            await closure.InvokeAsync(new()
+            {
+                Thread = this, ArgumentCount = 0, ReturnFrameBase = 0, SourceLine = null,
+            }, cancellationToken);
 
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameWithStackPop(int frameBase)
-    {
-        if (CoreData.CallStack.TryPop())
-        {
-            CoreData.Stack.PopUntil(frameBase);
+            return Stack.Count;
         }
         }
-        else
+        finally
         {
         {
-            ThrowForEmptyStack();
+            IsRunning = false;
         }
         }
     }
     }
 
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrame()
-    {
-        if (!CoreData.CallStack.TryPop())
-        {
-            ThrowForEmptyStack();
-        }
-    }
 
 
     internal void DumpStackValues()
     internal void DumpStackValues()
     {
     {
-        var span = GetStackValues();
+        var span = this.GetStackValues();
         for (int i = 0; i < span.Length; i++)
         for (int i = 0; i < span.Length; i++)
         {
         {
             Console.WriteLine($"LuaStack [{i}]\t{span[i]}");
             Console.WriteLine($"LuaStack [{i}]\t{span[i]}");
         }
         }
     }
     }
 
 
-    public Traceback GetTraceback()
+    protected void ThrowIfRunning()
     {
     {
-        var frames = GetCallStackFrames();
-
-        return new(State) { RootFunc = frames[0].Function, StackFrames = GetCallStackFrames()[1..].ToArray() };
+        if (IsRunning)
+        {
+            throw new InvalidOperationException("the lua state is currently running");
+        }
     }
     }
-
-
-    static void ThrowForEmptyStack() => throw new InvalidOperationException("Empty stack");
 }
 }

+ 122 - 14
src/Lua/LuaThreadExtensions.cs

@@ -1,29 +1,137 @@
-using Lua.Internal;
 using Lua.Runtime;
 using Lua.Runtime;
+using System.Runtime.CompilerServices;
 
 
 namespace Lua;
 namespace Lua;
 
 
 public static class LuaThreadExtensions
 public static class LuaThreadExtensions
 {
 {
-    public static async ValueTask<LuaResult> RunAsync(this LuaThread thread, LuaClosure closure, int argumentCount, CancellationToken cancellationToken = default)
+    public static UseThreadLease RentUseThread(this LuaThread thread)
     {
     {
-        var top = thread.CoreData.Stack.Count;
-        await closure.InvokeAsync(new()
-        {
-            Thread = thread, ArgumentCount = argumentCount, ReturnFrameBase = top - argumentCount, SourceLine = null,
-        }, cancellationToken);
+        return new(LuaUserThread.Create(thread));
+    }
 
 
-        return new LuaResult(thread.Stack, top);
+    public static CoroutineLease RentCoroutine(this LuaThread thread, LuaFunction function, bool isProtectedMode = false)
+    {
+        return new(LuaCoroutine.Create(thread, function, isProtectedMode));
     }
     }
-    
-    
-    public static UseThreadLease RentUseThread(this LuaThread thread)
+
+
+    public static async ValueTask<int> DoStringAsync(this LuaThread thread, string source, Memory<LuaValue> buffer, string? chunkName = null, CancellationToken cancellationToken = default)
+    {
+        var closure = thread.State.Load(source, chunkName ?? source);
+        var count = await thread.RunAsync(closure, cancellationToken);
+        using var results = thread.ReadReturnValues(count);
+        results.AsSpan()[..Math.Min(buffer.Length, count)].CopyTo(buffer.Span);
+        return count;
+    }
+
+    public static async ValueTask<LuaValue[]> DoStringAsync(this LuaThread thread, string source, string? chunkName = null, CancellationToken cancellationToken = default)
+    {
+        var closure = thread.State.Load(source, chunkName ?? source);
+        var count = await thread.RunAsync(closure, cancellationToken);
+        using var results = thread.ReadReturnValues(count);
+        return results.AsSpan().ToArray();
+    }
+
+    public static async ValueTask<int> DoFileAsync(this LuaThread thread, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
-        return new UseThreadLease(LuaUserThread.Create(thread));
+        var bytes = await File.ReadAllBytesAsync(path, cancellationToken);
+        var fileName = "@" + Path.GetFileName(path);
+        var closure = thread.State.Load(bytes, fileName);
+        var count = await thread.RunAsync(closure, cancellationToken);
+        using var results = thread.ReadReturnValues(count);
+        results.AsSpan()[..Math.Min(buffer.Length, results.Length)].CopyTo(buffer.Span);
+        return results.Count;
     }
     }
 
 
-    public static CoroutineLease RentCoroutine(this LuaThread thread,LuaFunction function ,bool isProtectedMode = false)
+    public static async ValueTask<LuaValue[]> DoFileAsync(this LuaThread thread, string path, CancellationToken cancellationToken = default)
     {
     {
-        return new CoroutineLease(LuaCoroutine.Create(thread, function, isProtectedMode));
+        var bytes = await File.ReadAllBytesAsync(path, cancellationToken);
+        var fileName = "@" + Path.GetFileName(path);
+        var closure = thread.State.Load(bytes, fileName);
+        var count = await thread.RunAsync(closure, cancellationToken);
+        using var results = thread.ReadReturnValues(count);
+        return results.AsSpan().ToArray();
+    }
+
+
+    public static void Push(this LuaThread thread, LuaValue value)
+    {
+        thread.CoreData!.Stack.Push(value);
+    }
+
+    public static void Push(this LuaThread thread, params ReadOnlySpan<LuaValue> span)
+    {
+        thread.CoreData!.Stack.PushRange(span);
+    }
+
+    public static void Pop(this LuaThread thread, int count)
+    {
+        thread.CoreData!.Stack.Pop(count);
+    }
+
+    public static LuaValue Pop(this LuaThread thread)
+    {
+        return thread.CoreData!.Stack.Pop();
+    }
+
+    public static LuaReturnValuesReader ReadReturnValues(this LuaThread thread, int argumentCount)
+    {
+        var stack = thread.CoreData!.Stack;
+        return new LuaReturnValuesReader(stack, stack.Count - argumentCount);
+    }
+
+
+    public static ref readonly CallStackFrame GetCurrentFrame(this LuaThread thread)
+    {
+        return ref thread.CoreData!.CallStack.PeekRef();
+    }
+
+    public static ReadOnlySpan<LuaValue> GetStackValues(this LuaThread thread)
+    {
+        return thread.CoreData!.Stack.AsSpan();
+    }
+
+    public static ReadOnlySpan<CallStackFrame> GetCallStackFrames(this LuaThread thread)
+    {
+        return thread.CoreData!.CallStack.AsSpan();
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static void PushCallStackFrame(this LuaThread thread, in CallStackFrame frame)
+    {
+        thread.CoreData!.CallStack.Push(frame);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static void PopCallStackFrameWithStackPop(this LuaThread thread)
+    {
+        var coreData = thread.CoreData!;
+
+        coreData.Stack.PopUntil(coreData!.CallStack.Pop().Base);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static void PopCallStackFrameWithStackPop(this LuaThread thread, int frameBase)
+    {
+        var coreData = thread.CoreData!;
+        coreData!.CallStack.Pop();
+        {
+            coreData.Stack.PopUntil(frameBase);
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static void PopCallStackFrame(this LuaThread thread)
+    {
+        var coreData = thread.CoreData!;
+        coreData!.CallStack.Pop();
+    }
+    
+    public static Traceback GetTraceback(this LuaThread thread)
+    {
+        var frames = thread.GetCallStackFrames();
+
+        return new(thread.State) { RootFunc = frames[0].Function, StackFrames = thread.GetCallStackFrames()[1..].ToArray() };
     }
     }
 }
 }

+ 1 - 2
src/Lua/Runtime/LuaVirtualMachine.cs

@@ -1,7 +1,5 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
 using Lua.Internal;
 using Lua.Internal;
 
 
 // ReSharper disable InconsistentNaming
 // ReSharper disable InconsistentNaming
@@ -48,6 +46,7 @@ public static partial class LuaVirtualMachine
             LastHookPc = -1;
             LastHookPc = -1;
             Task = default;
             Task = default;
         }
         }
+
         public LuaState State => Thread.State;
         public LuaState State => Thread.State;
         public LuaStack Stack = default!;
         public LuaStack Stack = default!;
         public LuaClosure LuaClosure = default!;
         public LuaClosure LuaClosure = default!;