Browse Source

Merge pull request #203 from nuskey8/simplyfy-lua-state

Simplify LuaState and Platform
Akeit0 3 months ago
parent
commit
d2eef68426
55 changed files with 2172 additions and 2108 deletions
  1. 1 1
      README.md
  2. 1 1
      sandbox/Benchmark/InterpreterSteps.cs
  3. 30 14
      sandbox/ConsoleApp1/Program.cs
  4. 6 8
      sandbox/ConsoleApp2/Program.cs
  5. 1 0
      sandbox/JitTest/JitTest.csproj
  6. 10 14
      sandbox/JitTest/Program.cs
  7. 3 3
      src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs
  8. 5 23
      src/Lua.Unity/Assets/Lua.Unity/Runtime/LuaThreadAssetExtensions.cs
  9. 40 5
      src/Lua.Unity/Assets/Sandbox/Sandbox.cs
  10. 74 74
      src/Lua/Exceptions.cs
  11. 255 0
      src/Lua/Internal/CoroutineCore.cs
  12. 6 6
      src/Lua/Internal/LuaDebug.cs
  13. 116 0
      src/Lua/Internal/LuaGlobalState.cs
  14. 323 323
      src/Lua/LuaCoroutine.cs
  15. 23 25
      src/Lua/LuaFunctionExecutionContext.cs
  16. 0 43
      src/Lua/LuaFunctionExtensions.cs
  17. 0 15
      src/Lua/LuaMainThread.cs
  18. 413 113
      src/Lua/LuaState.cs
  19. 269 16
      src/Lua/LuaStateExtensions.cs
  20. 0 234
      src/Lua/LuaThread.cs
  21. 4 13
      src/Lua/LuaThreadExtensions.cs
  22. 0 36
      src/Lua/LuaUserThread.cs
  23. 9 9
      src/Lua/LuaValue.cs
  24. 8 12
      src/Lua/Platforms/LuaPlatform.cs
  25. 0 21
      src/Lua/Runtime/Lease.cs
  26. 10 10
      src/Lua/Runtime/LuaClosure.cs
  27. 0 148
      src/Lua/Runtime/LuaThreadAccess.cs
  28. 0 320
      src/Lua/Runtime/LuaThreadAccessExtensions.cs
  29. 2 2
      src/Lua/Runtime/LuaValueRuntimeExtensions.cs
  30. 44 44
      src/Lua/Runtime/LuaVirtualMachine.Debug.cs
  31. 159 160
      src/Lua/Runtime/LuaVirtualMachine.cs
  32. 8 6
      src/Lua/Runtime/Tracebacks.cs
  33. 5 5
      src/Lua/Runtime/UpValue.cs
  34. 33 34
      src/Lua/Standard/BasicLibrary.cs
  35. 28 28
      src/Lua/Standard/BitwiseLibrary.cs
  36. 18 18
      src/Lua/Standard/CoroutineLibrary.cs
  37. 48 51
      src/Lua/Standard/DebugLibrary.cs
  38. 3 3
      src/Lua/Standard/FileHandle.cs
  39. 18 18
      src/Lua/Standard/IOLibrary.cs
  40. 4 4
      src/Lua/Standard/Internal/Bit32Helper.cs
  41. 12 12
      src/Lua/Standard/Internal/DateTimeHelper.cs
  42. 15 15
      src/Lua/Standard/Internal/IOHelper.cs
  43. 9 9
      src/Lua/Standard/Internal/MatchState.cs
  44. 2 2
      src/Lua/Standard/MathematicsLibrary.cs
  45. 23 25
      src/Lua/Standard/ModuleLibrary.cs
  46. 39 27
      src/Lua/Standard/OpenLibsExtensions.cs
  47. 12 12
      src/Lua/Standard/OperatingSystemLibrary.cs
  48. 31 31
      src/Lua/Standard/StringLibrary.cs
  49. 14 14
      src/Lua/Standard/TableLibrary.cs
  50. 10 10
      tests/Lua.Tests/AbstractFileTests.cs
  51. 1 1
      tests/Lua.Tests/AsyncTests.cs
  52. 2 2
      tests/Lua.Tests/CancellationTest.cs
  53. 24 32
      tests/Lua.Tests/LuaApiTests.cs
  54. 1 1
      tests/Lua.Tests/LuaTests.cs
  55. 0 55
      tests/Lua.Tests/ValidationTests.cs

+ 1 - 1
README.md

@@ -552,4 +552,4 @@ While `collectgarbage()` is available, it simply calls the corresponding .NET ga
 
 
 ## License
 ## License
 
 
-Lua-CSharp is licensed under the [MIT License](LICENSE).
+Lua-CSharp is licensed under the [MIT License](LICENSE).

+ 1 - 1
sandbox/Benchmark/InterpreterSteps.cs

@@ -46,6 +46,6 @@ public class InterpreterSteps
     [Benchmark]
     [Benchmark]
     public async ValueTask RunAsync()
     public async ValueTask RunAsync()
     {
     {
-        await state.RootAccess.Call(closure, []);
+        await state.Call(closure, []);
     }
     }
 }
 }

+ 30 - 14
sandbox/ConsoleApp1/Program.cs

@@ -6,24 +6,24 @@ using System.Text.RegularExpressions;
 using System;
 using System;
 using System.IO;
 using System.IO;
 using System.Text;
 using System.Text;
+
 var state = LuaState.Create();
 var state = LuaState.Create();
 state.OpenStandardLibraries();
 state.OpenStandardLibraries();
-
 state.Environment["escape"] = new LuaFunction("escape",
 state.Environment["escape"] = new LuaFunction("escape",
     (c, _) =>
     (c, _) =>
     {
     {
         var arg = c.HasArgument(0) ? c.GetArgument<string>(0) : "";
         var arg = c.HasArgument(0) ? c.GetArgument<string>(0) : "";
         return new(c.Return(Regex.Escape(arg)));
         return new(c.Return(Regex.Escape(arg)));
     });
     });
-string source = "";
+var source = "";
 try
 try
 {
 {
     source = File.ReadAllText(GetAbsolutePath("test.lua"));
     source = File.ReadAllText(GetAbsolutePath("test.lua"));
 
 
 
 
-    Console.WriteLine("Source Code " + new string('-', 50));
+    //Console.WriteLine("Source Code " + new string('-', 50));
 
 
-    Console.WriteLine(source);
+    // Console.WriteLine(source);
 
 
     var closure = state.Load(source, "@test.lua");
     var closure = state.Load(source, "@test.lua");
 
 
@@ -31,15 +31,28 @@ try
 
 
     Console.WriteLine("Output " + new string('-', 50));
     Console.WriteLine("Output " + new string('-', 50));
 
 
-    var count = await state.RootAccess.RunAsync(closure);
-
-    Console.WriteLine("Result " + new string('-', 50));
-    using var results = state.RootAccess.ReadTopValues(count);
-    for (int i = 0; i < count; i++)
+    //Console.Read();
+    var timer = new System.Diagnostics.Stopwatch();
+    timer.Start();
+    for (var i = 0; i < 1000; i++)
     {
     {
-        Console.WriteLine(results[i]);
+        var count = await state.RunAsync(closure);
+        state.Pop(count);
+        if (i % 100 == 0)
+        {
+            Console.WriteLine($"Iteration {i} completed. Time elapsed: {timer.ElapsedMilliseconds} ms");
+            Thread.Sleep(100);
+        }
     }
     }
 
 
+
+    // Console.WriteLine("Result " + new string('-', 50));
+    // using var results = state.RootAccess.ReadTopValues(count);
+    // for (var i = 0; i < count; i++)
+    // {
+    //     Console.WriteLine(results[i]);
+    // }
+
     Console.WriteLine("End " + new string('-', 50));
     Console.WriteLine("End " + new string('-', 50));
 }
 }
 catch (Exception ex)
 catch (Exception ex)
@@ -47,7 +60,8 @@ catch (Exception ex)
     if (ex is LuaCompileException luaCompileException)
     if (ex is LuaCompileException luaCompileException)
     {
     {
         Console.WriteLine("CompileError " + new string('-', 50));
         Console.WriteLine("CompileError " + new string('-', 50));
-        Console.WriteLine(RustLikeExceptionHook.OnCatch(source, luaCompileException)); ;
+        Console.WriteLine(RustLikeExceptionHook.OnCatch(source, luaCompileException));
+        ;
         Console.WriteLine(new string('-', 55));
         Console.WriteLine(new string('-', 55));
     }
     }
 
 
@@ -124,19 +138,21 @@ class RustLikeExceptionHook //: ILuaCompileHook
         {
         {
             lineOffset = 0;
             lineOffset = 0;
         }
         }
+
         foreach (var c in source[lineOffset..])
         foreach (var c in source[lineOffset..])
         {
         {
             if (c is '\n' or '\r')
             if (c is '\n' or '\r')
             {
             {
                 break;
                 break;
             }
             }
-       
+
             length++;
             length++;
         }
         }
+
         var builder = new StringBuilder();
         var builder = new StringBuilder();
         builder.AppendLine();
         builder.AppendLine();
-        builder.AppendLine("[error]: "+exception.MessageWithNearToken);
-        builder.AppendLine("-->"+exception.ChunkName + ":" + exception.Position.Line + ":" + exception.Position.Column);
+        builder.AppendLine("[error]: " + exception.MessageWithNearToken);
+        builder.AppendLine("-->" + exception.ChunkName + ":" + exception.Position.Line + ":" + exception.Position.Column);
         var line = source.Slice(lineOffset, length).ToString();
         var line = source.Slice(lineOffset, length).ToString();
         var lineNumString = exception.Position.Line.ToString();
         var lineNumString = exception.Position.Line.ToString();
         builder.AppendLine(new string(' ', lineNumString.Length) + " |");
         builder.AppendLine(new string(' ', lineNumString.Length) + " |");

+ 6 - 8
sandbox/ConsoleApp2/Program.cs

@@ -7,12 +7,11 @@ var state = LuaState.Create();
 state.OpenStandardLibraries();
 state.OpenStandardLibraries();
 {
 {
     var closure = state.Load("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.RentUserThread();
-    var access = threadLease.Thread.RootAccess;
+    using var access = state.CreateThread();
     {
     {
         var count = await access.RunAsync(closure, 0);
         var count = await access.RunAsync(closure, 0);
         var results = access.ReadTopValues(count);
         var results = access.ReadTopValues(count);
-        for (int i = 0; i < results.Length; i++)
+        for (var i = 0; i < results.Length; i++)
         {
         {
             Console.WriteLine(results[i]);
             Console.WriteLine(results[i]);
         }
         }
@@ -22,7 +21,7 @@ state.OpenStandardLibraries();
         access.Push("hello", "world", 1, 2, 3);
         access.Push("hello", "world", 1, 2, 3);
         count = await access.RunAsync(f, 5);
         count = await access.RunAsync(f, 5);
         results = access.ReadTopValues(count);
         results = access.ReadTopValues(count);
-        for (int i = 0; i < results.Length; i++)
+        for (var i = 0; i < results.Length; i++)
         {
         {
             Console.WriteLine(results[i]);
             Console.WriteLine(results[i]);
         }
         }
@@ -43,13 +42,12 @@ state.OpenStandardLibraries();
         end
         end
         """, "coroutine");
         """, "coroutine");
     var f = results[0].Read<LuaClosure>();
     var f = results[0].Read<LuaClosure>();
-    using var coroutineLease = state.MainThread.RentCoroutine(f);
-    var coroutine = coroutineLease.Thread;
+    using var coroutine = state.CreateCoroutine(f);
     {
     {
         var stack = new LuaStack();
         var stack = new LuaStack();
         stack.PushRange("a", "b", "c", "d", "e");
         stack.PushRange("a", "b", "c", "d", "e");
 
 
-        for (int i = 0; coroutine.CanResume; i++)
+        for (var i = 0; coroutine.CanResume; i++)
         {
         {
             if (i != 0)
             if (i != 0)
             {
             {
@@ -59,7 +57,7 @@ state.OpenStandardLibraries();
 
 
             await coroutine.ResumeAsync(stack);
             await coroutine.ResumeAsync(stack);
             Console.Write("In C#:\t");
             Console.Write("In C#:\t");
-            for (int j = 1; j < stack.Count; j++)
+            for (var j = 1; j < stack.Count; j++)
             {
             {
                 Console.Write(stack[j]);
                 Console.Write(stack[j]);
                 Console.Write('\t');
                 Console.Write('\t');

+ 1 - 0
sandbox/JitTest/JitTest.csproj

@@ -6,6 +6,7 @@
         <LangVersion>13</LangVersion>
         <LangVersion>13</LangVersion>
         <ImplicitUsings>enable</ImplicitUsings>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
         <Nullable>enable</Nullable>
+        <AllowUnsafeBlocks >true</AllowUnsafeBlocks>
     </PropertyGroup>
     </PropertyGroup>
 
 
     <ItemGroup>
     <ItemGroup>

+ 10 - 14
sandbox/JitTest/Program.cs

@@ -11,29 +11,25 @@ using Lua.Standard;
 // dotnet run --configuration Release /p:DefineConstants="CASE_MARKER"
 // dotnet run --configuration Release /p:DefineConstants="CASE_MARKER"
 // to activate the CASE_MARKER
 // to activate the CASE_MARKER
 // JitInspect can be run in Windows and Linux (MacOS is not supported yet)
 // JitInspect can be run in Windows and Linux (MacOS is not supported yet)
-var  luaState = LuaState.Create();
+var luaState = LuaState.Create();
 luaState.OpenStandardLibraries();
 luaState.OpenStandardLibraries();
-{
-   await luaState.DoFileAsync((GetAbsolutePath("db.lua")));
-   await luaState.DoFileAsync((GetAbsolutePath("events.lua")));
-}
-
-var closure = luaState.Load(File.ReadAllBytes(GetAbsolutePath("test.lua")),"test.lua");
+var closure = luaState.Load(File.ReadAllBytes(GetAbsolutePath("test.lua")), "test.lua");
 
 
-for (int i = 0; i < 1000; i++)
+for (var i = 0; i < 1000; i++)
 {
 {
-   await luaState.RootAccess.RunAsync(closure);
-   luaState.MainThread.Stack.Clear();
+    await luaState.RunAsync(closure);
+    luaState.Stack.Clear();
 }
 }
 
 
 var savePath = GetAbsolutePath("history");
 var savePath = GetAbsolutePath("history");
 var thisDir = GetThisDirectoryName();
 var thisDir = GetThisDirectoryName();
 var newJIitPath = Path.Join(thisDir, $"jit_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt");
 var newJIitPath = Path.Join(thisDir, $"jit_{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt");
-var lastJitPaths = Directory.GetFiles(thisDir).Where(x=>x.Contains("jit_"));
+var lastJitPaths = Directory.GetFiles(thisDir).Where(x => x.Contains("jit_"));
 if (!Directory.Exists(savePath))
 if (!Directory.Exists(savePath))
 {
 {
     Directory.CreateDirectory(savePath);
     Directory.CreateDirectory(savePath);
 }
 }
+
 if (lastJitPaths.Any())
 if (lastJitPaths.Any())
 {
 {
     Console.WriteLine("Last:" + File.ReadAllLines(lastJitPaths.First())[^1]);
     Console.WriteLine("Last:" + File.ReadAllLines(lastJitPaths.First())[^1]);
@@ -43,13 +39,13 @@ if (lastJitPaths.Any())
         var dest = Path.Join(savePath, Path.GetFileName(jitPath));
         var dest = Path.Join(savePath, Path.GetFileName(jitPath));
         File.Move(last, dest);
         File.Move(last, dest);
     }
     }
-    
 }
 }
+
 var method = typeof(LuaVirtualMachine).GetMethod("MoveNext", BindingFlags.Static | BindingFlags.NonPublic)!;
 var method = typeof(LuaVirtualMachine).GetMethod("MoveNext", BindingFlags.Static | BindingFlags.NonPublic)!;
 using var disassembler = JitDisassembler.Create();
 using var disassembler = JitDisassembler.Create();
-var nextJitText = disassembler.Disassemble(method);
+var nextJitText = disassembler.Disassemble(method, new() { PrintInstructionAddresses = true });
 File.WriteAllText(newJIitPath, nextJitText);
 File.WriteAllText(newJIitPath, nextJitText);
-Console.WriteLine("New:" + nextJitText.Split("\n")[^1]);
+//Console.WriteLine("New:" + nextJitText.Split("\n")[^1]);
 
 
 
 
 static string GetThisDirectoryName([CallerFilePath] string callerFilePath = "")
 static string GetThisDirectoryName([CallerFilePath] string callerFilePath = "")

+ 3 - 3
src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs

@@ -333,7 +333,7 @@ partial class LuaObjectGenerator
                     {
                     {
                         if (propertyMetadata.IsReadOnly)
                         if (propertyMetadata.IsReadOnly)
                         {
                         {
-                            builder.AppendLine($@"throw new global::Lua.LuaRuntimeException(context.Thread, $""'{{key}}' cannot overwrite."");");
+                            builder.AppendLine($@"throw new global::Lua.LuaRuntimeException(context.State, $""'{{key}}' cannot overwrite."");");
                         }
                         }
                         else if (propertyMetadata.IsStatic)
                         else if (propertyMetadata.IsStatic)
                         {
                         {
@@ -371,7 +371,7 @@ partial class LuaObjectGenerator
 
 
                     using (builder.BeginIndentScope())
                     using (builder.BeginIndentScope())
                     {
                     {
-                        builder.AppendLine($@"throw new global::Lua.LuaRuntimeException(context.Thread, $""'{{key}}' cannot overwrite."");");
+                        builder.AppendLine($@"throw new global::Lua.LuaRuntimeException(context.State, $""'{{key}}' cannot overwrite."");");
                     }
                     }
                 }
                 }
 
 
@@ -379,7 +379,7 @@ partial class LuaObjectGenerator
 
 
                 using (builder.BeginIndentScope())
                 using (builder.BeginIndentScope())
                 {
                 {
-                    builder.AppendLine(@$"throw new global::Lua.LuaRuntimeException(context.Thread, $""'{{key}}' not found."");");
+                    builder.AppendLine(@$"throw new global::Lua.LuaRuntimeException(context.State, $""'{{key}}' not found."");");
                 }
                 }
             }
             }
 
 

+ 5 - 23
src/Lua.Unity/Assets/Lua.Unity/Runtime/LuaThreadAssetExtensions.cs

@@ -7,21 +7,8 @@ namespace Lua.Unity
 {
 {
     public static class LuaThreadAssetExtensions
     public static class LuaThreadAssetExtensions
     {
     {
-        public static ValueTask<LuaValue[]> ExecuteAsync(this LuaThreadAccess access, LuaAssetBase luaAssetBase, string name, CancellationToken cancellationToken = default)
-        {
-            if (luaAssetBase == null)
-            {
-                throw new ArgumentNullException(nameof(luaAssetBase));
-            }
-
-            var module = luaAssetBase.GetModule(name);
-            var closure = module.Type == LuaModuleType.Bytes
-                ? access.State.Load(module.ReadBytes(), module.Name)
-                : access.State.Load(module.ReadText(), module.Name);
-            return access.ExecuteAsync(closure, cancellationToken);
-        }
 
 
-        public static ValueTask<int> ExecuteAsync(this LuaThreadAccess access, LuaAssetBase luaAssetBase, string name, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+        public static ValueTask<int> ExecuteAsync(this LuaState state, LuaAssetBase luaAssetBase, string name, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
         {
         {
             if (luaAssetBase == null)
             if (luaAssetBase == null)
             {
             {
@@ -30,19 +17,14 @@ namespace Lua.Unity
 
 
             var module = luaAssetBase.GetModule(name);
             var module = luaAssetBase.GetModule(name);
             var closure = module.Type == LuaModuleType.Bytes
             var closure = module.Type == LuaModuleType.Bytes
-                ? access.State.Load(module.ReadBytes(), module.Name)
-                : access.State.Load(module.ReadText(), module.Name);
-            return access.ExecuteAsync(closure, buffer, cancellationToken);
+                ? state.Load(module.ReadBytes(), module.Name)
+                : state.Load(module.ReadText(), module.Name);
+            return state.ExecuteAsync(closure, buffer, cancellationToken);
         }
         }
 
 
         public static ValueTask<LuaValue[]> ExecuteAsync(this LuaState state, LuaAssetBase luaAssetBase, string name, CancellationToken cancellationToken = default)
         public static ValueTask<LuaValue[]> ExecuteAsync(this LuaState state, LuaAssetBase luaAssetBase, string name, CancellationToken cancellationToken = default)
         {
         {
-            return state.RootAccess.ExecuteAsync(luaAssetBase, name, cancellationToken);
-        }
-
-        public static ValueTask<int> ExecuteAsync(this LuaState state, LuaAssetBase luaAssetBase, string name, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
-        {
-            return state.RootAccess.ExecuteAsync(luaAssetBase, name, buffer, cancellationToken);
+            return state.ExecuteAsync(luaAssetBase, name, cancellationToken);
         }
         }
     }
     }
 }
 }

+ 40 - 5
src/Lua.Unity/Assets/Sandbox/Sandbox.cs

@@ -5,19 +5,22 @@ using Lua.Loaders;
 using Lua.Platforms;
 using Lua.Platforms;
 using Lua.Standard;
 using Lua.Standard;
 using Lua.Unity;
 using Lua.Unity;
+using System.Collections.Generic;
+using System.Linq;
 using UnityEngine;
 using UnityEngine;
 
 
 public class Sandbox : MonoBehaviour
 public class Sandbox : MonoBehaviour
 {
 {
     async void Start()
     async void Start()
     {
     {
+        
         var state = LuaState.Create( new LuaPlatform(
         var state = LuaState.Create( new LuaPlatform(
-            fileSystem: new FileSystem(Application.streamingAssetsPath),
-            osEnvironment: new UnityApplicationOsEnvironment(),
-            standardIO: new UnityStandardIO(),
-            timeProvider: TimeProvider.System
+            FileSystem: new FileSystem(Application.streamingAssetsPath),
+            OsEnvironment: new UnityApplicationOsEnvironment(),
+            StandardIO: new UnityStandardIO(),
+            TimeProvider: TimeProvider.System
         ));
         ));
-        state.ModuleLoader = CompositeModuleLoader.Create(new AddressablesModuleLoader(), new ResourcesModuleLoader());
+        state.GlobalState.ModuleLoader = CompositeModuleLoader.Create(new AddressablesModuleLoader(), new ResourcesModuleLoader());
         state.OpenStandardLibraries();
         state.OpenStandardLibraries();
         state.Environment["print"] = new LuaFunction("print", (context, ct) =>
         state.Environment["print"] = new LuaFunction("print", (context, ct) =>
         {
         {
@@ -45,4 +48,36 @@ s.f()
             Debug.LogException(ex);
             Debug.LogException(ex);
         }
         }
     }
     }
+    
+    MeshTopology[ ] topologies = Enum.GetValues(typeof(MeshTopology)) as MeshTopology[];
+    public bool ContainsTriangle;
+
+    void Update()
+    {
+        if (Input.GetKeyDown(KeyCode.Space))
+        {
+            ContainsTriangle=(topologies.Contains( MeshTopology.Points));
+            
+            Debug.Break();
+        }
+        
+        if (Input.GetKeyDown(KeyCode.A))
+        {
+            ContainsTriangle=(ContainsInArray(topologies, MeshTopology.Points));
+            
+            Debug.Break();
+        }
+    }
+    
+    bool ContainsInArray<T>(T[] array, T value)
+    {
+        foreach (var item in array)
+        {
+            if (EqualityComparer<T>.Default.Equals(item, value))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
 }
 }

+ 74 - 74
src/Lua/Exceptions.cs

@@ -88,28 +88,28 @@ interface ILuaTracebackBuildable
 
 
 public class LuaRuntimeException : Exception, ILuaTracebackBuildable
 public class LuaRuntimeException : Exception, ILuaTracebackBuildable
 {
 {
-    public LuaRuntimeException(LuaThread? thread, Exception innerException) : base(innerException.Message, innerException)
+    public LuaRuntimeException(LuaState? state, Exception innerException) : base(innerException.Message, innerException)
     {
     {
-        if (thread != null)
+        if (state != null)
         {
         {
-            thread.CurrentException?.BuildOrGet();
-            thread.ExceptionTrace.Clear();
-            thread.CurrentException = this;
+            state.CurrentException?.BuildOrGet();
+            state.ExceptionTrace.Clear();
+            state.CurrentException = this;
         }
         }
 
 
-        Thread = thread;
+        State = state;
     }
     }
 
 
-    public LuaRuntimeException(LuaThread? thread, LuaValue errorObject, int level = 1)
+    public LuaRuntimeException(LuaState? state, LuaValue errorObject, int level = 1)
     {
     {
-        if (thread != null)
+        if (state != null)
         {
         {
-            thread.CurrentException?.BuildOrGet();
-            thread.ExceptionTrace.Clear();
-            thread.CurrentException = this;
+            state.CurrentException?.BuildOrGet();
+            state.ExceptionTrace.Clear();
+            state.CurrentException = this;
         }
         }
 
 
-        Thread = thread;
+        State = state;
 
 
         ErrorObject = errorObject;
         ErrorObject = errorObject;
         this.level = level;
         this.level = level;
@@ -132,31 +132,31 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         }
         }
     }
     }
 
 
-    internal LuaThread? Thread { get; private set; } = default!;
+    internal LuaState? State { get; private set; } = default!;
     public LuaValue ErrorObject { get; }
     public LuaValue ErrorObject { get; }
 
 
-    public static void AttemptInvalidOperation(LuaThread? thread, string op, LuaValue a, LuaValue b)
+    public static void AttemptInvalidOperation(LuaState? state, string op, LuaValue a, LuaValue b)
     {
     {
         var typeA = a.TypeToString();
         var typeA = a.TypeToString();
         var typeB = b.TypeToString();
         var typeB = b.TypeToString();
         if (typeA == typeB)
         if (typeA == typeB)
         {
         {
-            throw new LuaRuntimeException(thread, $"attempt to {op} two {typeA} values");
+            throw new LuaRuntimeException(state, $"attempt to {op} two {typeA} values");
         }
         }
 
 
-        throw new LuaRuntimeException(thread, $"attempt to {op} a {typeA} value with a {typeB} value");
+        throw new LuaRuntimeException(state, $"attempt to {op} a {typeA} value with a {typeB} value");
     }
     }
 
 
-    public static void AttemptInvalidOperation(LuaThread? thread, string op, LuaValue a)
+    public static void AttemptInvalidOperation(LuaState? state, string op, LuaValue a)
     {
     {
-        throw new LuaRuntimeException(thread, $"attempt to {op} a {a.TypeToString()} value");
+        throw new LuaRuntimeException(state, $"attempt to {op} a {a.TypeToString()} value");
     }
     }
 
 
-    internal static void AttemptInvalidOperationOnLuaStack(LuaThread thread, string op, int lastPc, int regA, int regB)
+    internal static void AttemptInvalidOperationOnLuaStack(LuaState state, string op, int lastPc, int regA, int regB)
     {
     {
-        var caller = thread.GetCurrentFrame();
-        var luaValueA = regA < 255 ? thread.Stack[caller.Base + regA] : ((LuaClosure)caller.Function).Proto.Constants[regA - 256];
-        var luaValueB = regB < 255 ? thread.Stack[caller.Base + regB] : ((LuaClosure)caller.Function).Proto.Constants[regB - 256];
+        var caller = state.GetCurrentFrame();
+        var luaValueA = regA < 255 ? state.Stack[caller.Base + regA] : ((LuaClosure)caller.Function).Proto.Constants[regA - 256];
+        var luaValueB = regB < 255 ? state.Stack[caller.Base + regB] : ((LuaClosure)caller.Function).Proto.Constants[regB - 256];
         var function = caller.Function;
         var function = caller.Function;
         var tA = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, regA, out var nameA);
         var tA = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, regA, out var nameA);
         var tB = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, regB, out var nameB);
         var tB = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, regB, out var nameB);
@@ -181,13 +181,13 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
             builder.AddRange($" ({tB} '{nameB}')");
             builder.AddRange($" ({tB} '{nameB}')");
         }
         }
 
 
-        throw new LuaRuntimeException(thread, builder.AsSpan().ToString());
+        throw new LuaRuntimeException(state, builder.AsSpan().ToString());
     }
     }
 
 
-    internal static void AttemptInvalidOperationOnLuaStack(LuaThread thread, string op, int lastPc, int reg)
+    internal static void AttemptInvalidOperationOnLuaStack(LuaState state, string op, int lastPc, int reg)
     {
     {
-        var caller = thread.GetCurrentFrame();
-        var luaValue = reg < 255 ? thread.Stack[caller.Base + reg] : ((LuaClosure)caller.Function).Proto.Constants[reg - 256];
+        var caller = state.GetCurrentFrame();
+        var luaValue = reg < 255 ? state.Stack[caller.Base + reg] : ((LuaClosure)caller.Function).Proto.Constants[reg - 256];
         var function = caller.Function;
         var function = caller.Function;
         var t = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, reg, out var name);
         var t = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, reg, out var name);
 
 
@@ -203,12 +203,12 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
             builder.AddRange($" ({t} '{name}')");
             builder.AddRange($" ({t} '{name}')");
         }
         }
 
 
-        throw new LuaRuntimeException(thread, builder.AsSpan().ToString());
+        throw new LuaRuntimeException(state, builder.AsSpan().ToString());
     }
     }
 
 
-    internal static void AttemptInvalidOperationOnUpValues(LuaThread thread, string op, int reg)
+    internal static void AttemptInvalidOperationOnUpValues(LuaState state, string op, int reg)
     {
     {
-        var caller = thread.GetCurrentFrame();
+        var caller = state.GetCurrentFrame();
         var closure = (LuaClosure)caller.Function;
         var closure = (LuaClosure)caller.Function;
         var proto = closure.Proto;
         var proto = closure.Proto;
 
 
@@ -216,22 +216,22 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         var luaValue = closure.UpValues[upValue.Index].GetValue();
         var luaValue = closure.UpValues[upValue.Index].GetValue();
         var name = upValue.Name;
         var name = upValue.Name;
 
 
-        throw new LuaRuntimeException(thread, $"attempt to {op} a {luaValue.TypeToString()} value (upvalue '{name}')");
+        throw new LuaRuntimeException(state, $"attempt to {op} a {luaValue.TypeToString()} value (upvalue '{name}')");
     }
     }
 
 
-    internal static (string NameWhat, string Name) GetCurrentFunctionName(LuaThread thread)
+    internal static (string NameWhat, string Name) GetCurrentFunctionName(LuaState state)
     {
     {
-        var current = thread.GetCurrentFrame();
+        var current = state.GetCurrentFrame();
         var pc = current.CallerInstructionIndex;
         var pc = current.CallerInstructionIndex;
         LuaFunction callerFunction;
         LuaFunction callerFunction;
         if (current.IsTailCall)
         if (current.IsTailCall)
         {
         {
-            pc = thread.LastPc;
-            callerFunction = thread.LastCallerFunction!;
+            pc = state.LastPc;
+            callerFunction = state.LastCallerFunction!;
         }
         }
         else
         else
         {
         {
-            var caller = thread.GetCallStackFrames()[^2];
+            var caller = state.GetCallStackFrames()[^2];
             callerFunction = caller.Function;
             callerFunction = caller.Function;
         }
         }
 
 
@@ -243,64 +243,64 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         return (LuaDebug.GetFuncName(callerClosure.Proto, pc, out var name) ?? "", name ?? current.Function.Name);
         return (LuaDebug.GetFuncName(callerClosure.Proto, pc, out var name) ?? "", name ?? current.Function.Name);
     }
     }
 
 
-    public static void BadArgument(LuaThread thread, int argumentId)
+    public static void BadArgument(LuaState state, int argumentId)
     {
     {
-        BadArgument(thread, argumentId, "value expected");
+        BadArgument(state, argumentId, "value expected");
     }
     }
 
 
 
 
-    public static void BadArgument(LuaThread thread, int argumentId, LuaValueType expected, LuaValueType actual)
+    public static void BadArgument(LuaState state, int argumentId, LuaValueType expected, LuaValueType actual)
     {
     {
-        BadArgument(thread, argumentId, $"{LuaValue.ToString(expected)} expected, got {LuaValue.ToString(actual)})");
+        BadArgument(state, argumentId, $"{LuaValue.ToString(expected)} expected, got {LuaValue.ToString(actual)})");
     }
     }
 
 
-    public static void BadArgument(LuaThread thread, int argumentId, LuaValueType[] expected, LuaValueType actual)
+    public static void BadArgument(LuaState state, int argumentId, LuaValueType[] expected, LuaValueType actual)
     {
     {
-        BadArgument(thread, argumentId, $"({string.Join(" or ", expected.Select(LuaValue.ToString))} expected, got {LuaValue.ToString(actual)})");
+        BadArgument(state, argumentId, $"({string.Join(" or ", expected.Select(LuaValue.ToString))} expected, got {LuaValue.ToString(actual)})");
     }
     }
 
 
 
 
-    public static void BadArgument(LuaThread thread, int argumentId, string expected, string actual)
+    public static void BadArgument(LuaState state, int argumentId, string expected, string actual)
     {
     {
-        BadArgument(thread, argumentId, $"({expected} expected, got {actual})");
+        BadArgument(state, argumentId, $"({expected} expected, got {actual})");
     }
     }
 
 
-    public static void BadArgument(LuaThread thread, int argumentId, string[] expected, string actual)
+    public static void BadArgument(LuaState state, int argumentId, string[] expected, string actual)
     {
     {
         if (expected.Length == 0)
         if (expected.Length == 0)
         {
         {
             throw new ArgumentException("Expected array must not be empty", nameof(expected));
             throw new ArgumentException("Expected array must not be empty", nameof(expected));
         }
         }
 
 
-        BadArgument(thread, argumentId, $"({string.Join(" or ", expected)} expected, got {actual})");
+        BadArgument(state, argumentId, $"({string.Join(" or ", expected)} expected, got {actual})");
     }
     }
 
 
 
 
-    public static void BadArgument(LuaThread thread, int argumentId, string message)
+    public static void BadArgument(LuaState state, int argumentId, string message)
     {
     {
-        var (nameWhat, name) = GetCurrentFunctionName(thread);
+        var (nameWhat, name) = GetCurrentFunctionName(state);
         if (nameWhat == "method")
         if (nameWhat == "method")
         {
         {
             argumentId--;
             argumentId--;
             if (argumentId == 0)
             if (argumentId == 0)
             {
             {
-                throw new LuaRuntimeException(thread, $"calling '{name}' on bad self ({message})");
+                throw new LuaRuntimeException(state, $"calling '{name}' on bad self ({message})");
             }
             }
         }
         }
 
 
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{name}' ({message})");
+        throw new LuaRuntimeException(state, $"bad argument #{argumentId} to '{name}' ({message})");
     }
     }
 
 
-    public static void BadArgumentNumberIsNotInteger(LuaThread thread, int argumentId)
+    public static void BadArgumentNumberIsNotInteger(LuaState state, int argumentId)
     {
     {
-        BadArgument(thread, argumentId, "number has no integer representation");
+        BadArgument(state, argumentId, "number has no integer representation");
     }
     }
 
 
-    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaThread thread, int argumentId, double value)
+    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaState state, int argumentId, double value)
     {
     {
         if (!MathEx.IsInteger(value))
         if (!MathEx.IsInteger(value))
         {
         {
-            BadArgumentNumberIsNotInteger(thread, argumentId);
+            BadArgumentNumberIsNotInteger(state, argumentId);
         }
         }
     }
     }
 
 
@@ -330,17 +330,17 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
             return luaTraceback;
             return luaTraceback;
         }
         }
 
 
-        if (Thread != null)
+        if (State != null)
         {
         {
-            var callStack = Thread.ExceptionTrace.AsSpan();
+            var callStack = State.ExceptionTrace.AsSpan();
             if (callStack.IsEmpty)
             if (callStack.IsEmpty)
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            luaTraceback = new(Thread.State, callStack);
-            Thread.ExceptionTrace.Clear();
-            Thread = null;
+            luaTraceback = new(State, callStack);
+            State.ExceptionTrace.Clear();
+            State = null;
         }
         }
 
 
         return luaTraceback;
         return luaTraceback;
@@ -348,8 +348,8 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
 
 
     internal void Forget()
     internal void Forget()
     {
     {
-        Thread?.ExceptionTrace.Clear();
-        Thread = null;
+        State?.ExceptionTrace.Clear();
+        State = null;
     }
     }
 
 
     internal string MinimalMessage()
     internal string MinimalMessage()
@@ -362,9 +362,9 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
 
 
         if (luaTraceback == null)
         if (luaTraceback == null)
         {
         {
-            if (Thread != null)
+            if (State != null)
             {
             {
-                var callStack = Thread.ExceptionTrace.AsSpan();
+                var callStack = State.ExceptionTrace.AsSpan();
                 level = Math.Min(level, callStack.Length + 1);
                 level = Math.Min(level, callStack.Length + 1);
                 callStack = callStack[..^(level - 1)];
                 callStack = callStack[..^(level - 1)];
                 if (callStack.IsEmpty)
                 if (callStack.IsEmpty)
@@ -450,7 +450,7 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
     }
     }
 }
 }
 
 
-public class LuaAssertionException(LuaThread? traceback, string message) : LuaRuntimeException(traceback, message);
+public class LuaAssertionException(LuaState? traceback, string message) : LuaRuntimeException(traceback, message);
 
 
 public class LuaModuleNotFoundException(string moduleName) : Exception($"module '{moduleName}' not found");
 public class LuaModuleNotFoundException(string moduleName) : Exception($"module '{moduleName}' not found");
 
 
@@ -471,14 +471,14 @@ public sealed class LuaCanceledException : OperationCanceledException, ILuaTrace
         }
         }
     }
     }
 
 
-    internal LuaThread? Thread { get; private set; }
+    internal LuaState? State { get; private set; }
 
 
-    internal LuaCanceledException(LuaThread thread, CancellationToken cancellationToken, Exception? innerException = null) : base("The operation was cancelled during execution on Lua.", innerException, cancellationToken)
+    internal LuaCanceledException(LuaState state, CancellationToken cancellationToken, Exception? innerException = null) : base("The operation was cancelled during execution on Lua.", innerException, cancellationToken)
     {
     {
-        thread.CurrentException?.BuildOrGet();
-        thread.ExceptionTrace.Clear();
-        thread.CurrentException = this;
-        Thread = thread;
+        state.CurrentException?.BuildOrGet();
+        state.ExceptionTrace.Clear();
+        state.CurrentException = this;
+        State = state;
     }
     }
 
 
 
 
@@ -490,17 +490,17 @@ public sealed class LuaCanceledException : OperationCanceledException, ILuaTrace
             return luaTraceback;
             return luaTraceback;
         }
         }
 
 
-        if (Thread != null)
+        if (State != null)
         {
         {
-            var callStack = Thread.ExceptionTrace.AsSpan();
+            var callStack = State.ExceptionTrace.AsSpan();
             if (callStack.IsEmpty)
             if (callStack.IsEmpty)
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            luaTraceback = new(Thread.State, callStack);
-            Thread.ExceptionTrace.Clear();
-            Thread = null!;
+            luaTraceback = new(State, callStack);
+            State.ExceptionTrace.Clear();
+            State = null!;
         }
         }
 
 
         return luaTraceback;
         return luaTraceback;

+ 255 - 0
src/Lua/Internal/CoroutineCore.cs

@@ -0,0 +1,255 @@
+using Lua.Internal.CompilerServices;
+using Lua.Runtime;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks.Sources;
+
+namespace Lua.Internal;
+
+class CoroutineCore:IValueTaskSource<CoroutineCore.YieldContext>, IValueTaskSource<CoroutineCore.ResumeContext>
+{
+    
+    public CoroutineCore(LuaState state, LuaFunction function, bool isProtectedMode)
+    {
+        Thread = state;
+        Function = function;
+        IsProtectedMode = isProtectedMode;
+        status = (byte)LuaThreadStatus.Suspended;
+    }
+    readonly struct YieldContext(LuaStack stack, int argCount)
+    {
+        public ReadOnlySpan<LuaValue> Results => stack.AsSpan()[^argCount..];
+    }
+
+    readonly struct ResumeContext(LuaStack? stack, int argCount)
+    {
+        public ReadOnlySpan<LuaValue> Results => stack!.AsSpan()[^argCount..];
+
+        public bool IsDead => stack == null;
+    }
+    internal byte status ;
+    bool isFirstCall = true;
+    ValueTask<int> functionTask;
+
+    ManualResetValueTaskSourceCore<ResumeContext> resume;
+    ManualResetValueTaskSourceCore<YieldContext> yield;
+    Traceback? traceback;
+    
+    public bool IsProtectedMode { get; private set; }
+    public LuaFunction Function { get; private set; } = null!;
+    
+    public LuaState Thread { get; private set; } = null!;
+    
+    public Traceback? Traceback => traceback;
+    
+    YieldContext IValueTaskSource<YieldContext>.GetResult(short token)
+    {
+        return yield.GetResult(token);
+    }
+
+    ValueTaskSourceStatus IValueTaskSource<YieldContext>.GetStatus(short token)
+    {
+        return yield.GetStatus(token);
+    }
+
+    void IValueTaskSource<YieldContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
+    {
+        yield.OnCompleted(continuation, state, token, flags);
+    }
+
+    ResumeContext IValueTaskSource<ResumeContext>.GetResult(short token)
+    {
+        return resume.GetResult(token);
+    }
+
+    ValueTaskSourceStatus IValueTaskSource<ResumeContext>.GetStatus(short token)
+    {
+        return resume.GetStatus(token);
+    }
+
+    void IValueTaskSource<ResumeContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
+    {
+        resume.OnCompleted(continuation, state, token, flags);
+    }
+    
+     [AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
+    internal async ValueTask<int> ResumeAsyncCore(LuaStack stack, int argCount, int returnBase, LuaState? baseThread, CancellationToken cancellationToken = default)
+    {
+        if (baseThread != null)
+        {
+            baseThread.UnsafeSetStatus(LuaThreadStatus.Normal);
+
+            baseThread.GlobalState.ThreadStack.Push(Thread);
+        }
+
+        try
+        {
+            switch ((LuaThreadStatus)Volatile.Read(ref status))
+            {
+                case LuaThreadStatus.Suspended:
+                    Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
+
+                    if (!isFirstCall)
+                    {
+                        yield.SetResult(new(stack, argCount));
+                    }
+
+                    break;
+                case LuaThreadStatus.Normal:
+                case LuaThreadStatus.Running:
+                    if (IsProtectedMode)
+                    {
+                        stack.PopUntil(returnBase);
+                        stack.Push(false);
+                        stack.Push("cannot resume non-suspended coroutine");
+                        return 2;
+                    }
+                    else
+                    {
+                        throw new LuaRuntimeException(baseThread, "cannot resume non-suspended coroutine");
+                    }
+                case LuaThreadStatus.Dead:
+                    if (IsProtectedMode)
+                    {
+                        stack.PopUntil(returnBase);
+                        stack.Push(false);
+                        stack.Push("cannot resume dead coroutine");
+                        return 2;
+                    }
+                    else
+                    {
+                        throw new LuaRuntimeException(baseThread, "cannot resume dead coroutine");
+                    }
+            }
+
+            ValueTask<ResumeContext> resumeTask = new(this, resume.Version);
+
+            CancellationTokenRegistration registration = default;
+            if (cancellationToken.CanBeCanceled)
+            {
+                registration = cancellationToken.UnsafeRegister(static x =>
+                {
+                    var coroutine = (CoroutineCore)x!;
+                    coroutine.yield.SetException(new OperationCanceledException());
+                }, this);
+            }
+
+            try
+            {
+                if (isFirstCall)
+                {
+                    Thread.Stack.PushRange(stack.AsSpan()[^argCount..]);
+                    //functionTask = Function.InvokeAsync(new() { Access = this.CurrentAccess, ArgumentCount = Stack.Count, ReturnFrameBase = 0 }, cancellationToken);
+                    functionTask = Thread.RunAsync(Function, Thread.Stack.Count, cancellationToken);
+                    Volatile.Write(ref isFirstCall, false);
+                    if (!functionTask.IsCompleted)
+                    {
+                        functionTask.GetAwaiter().OnCompleted(() => resume.SetResult(default));
+                    }
+                }
+
+                ResumeContext result0;
+                if (functionTask.IsCompleted || (result0 = await resumeTask).IsDead)
+                {
+                    _ = functionTask.Result;
+                    Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
+                    stack.PopUntil(returnBase);
+                    stack.Push(true);
+                    stack.PushRange(Thread.Stack.AsSpan());
+                    Thread.Dispose();
+                    return stack.Count - returnBase;
+                }
+                else
+                {
+                    Volatile.Write(ref status, (byte)LuaThreadStatus.Suspended);
+                    var results = result0.Results;
+                    stack.PopUntil(returnBase);
+                    stack.Push(true);
+                    stack.PushRange(results);
+                    return results.Length + 1;
+                }
+            }
+            catch (Exception ex) when (ex is not OperationCanceledException)
+            {
+                if (IsProtectedMode)
+                {
+                    if (ex is ILuaTracebackBuildable tracebackBuildable)
+                    {
+                        traceback = tracebackBuildable.BuildOrGet();
+                    }
+
+                    Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
+                    Thread.Dispose();
+                    stack.PopUntil(returnBase);
+                    stack.Push(false);
+                    stack.Push(ex is LuaRuntimeException luaEx ? luaEx.ErrorObject : ex.Message);
+                    return 2;
+                }
+                else
+                {
+                    throw;
+                }
+            }
+            finally
+            {
+                registration.Dispose();
+                resume.Reset();
+            }
+        }
+        finally
+        {
+            if (baseThread != null)
+            {
+                baseThread.GlobalState.ThreadStack.Pop();
+                baseThread.UnsafeSetStatus(LuaThreadStatus.Running);
+            }
+        }
+    }
+    [AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
+    internal async ValueTask<int> YieldAsyncCore(LuaStack stack, int argCount, int returnBase, LuaState? baseThread, CancellationToken cancellationToken = default)
+    {
+        if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
+        {
+            throw new LuaRuntimeException(baseThread, "cannot yield from a non-running coroutine");
+        }
+
+        if (baseThread != null)
+        {
+            if (baseThread.GetCallStackFrames()[^2].Function is not LuaClosure)
+            {
+                throw new LuaRuntimeException(baseThread, "attempt to yield across a C#-call boundary");
+            }
+        }
+
+        resume.SetResult(new(stack, argCount));
+
+
+        CancellationTokenRegistration registration = default;
+        if (cancellationToken.CanBeCanceled)
+        {
+            registration = cancellationToken.UnsafeRegister(static x =>
+            {
+                var coroutine = (CoroutineCore)x!;
+                coroutine.yield.SetException(new OperationCanceledException());
+            }, this);
+        }
+
+    RETRY:
+        try
+        {
+            var result = await new ValueTask<YieldContext>(this, yield.Version);
+            stack.PopUntil(returnBase);
+            stack.PushRange(result.Results);
+            return result.Results.Length;
+        }
+        catch (Exception ex) when (ex is not OperationCanceledException)
+        {
+            yield.Reset();
+            goto RETRY;
+        }
+        finally
+        {
+            registration.Dispose();
+            yield.Reset();
+        }
+    }
+}

+ 6 - 6
src/Lua/Internal/LuaDebug.cs

@@ -124,11 +124,11 @@ readonly struct LuaDebug : IDisposable
         }
         }
     }
     }
 
 
-    public static LuaDebug Create(LuaState state, CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunction function, int pc, ReadOnlySpan<char> what, out bool isValid)
+    public static LuaDebug Create(LuaGlobalState globalState, CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunction function, int pc, ReadOnlySpan<char> what, out bool isValid)
     {
     {
-        if (!state.DebugBufferPool.TryPop(out var buffer))
+        if (!globalState.DebugBufferPool.TryPop(out var buffer))
         {
         {
-            buffer = new(state);
+            buffer = new(globalState);
         }
         }
 
 
         isValid = buffer.GetInfo(prevFrame, frame, function, pc, what);
         isValid = buffer.GetInfo(prevFrame, frame, function, pc, what);
@@ -160,10 +160,10 @@ readonly struct LuaDebug : IDisposable
     }
     }
 
 
 
 
-    internal class LuaDebugBuffer(LuaState state)
+    internal class LuaDebugBuffer(LuaGlobalState globalState)
     {
     {
         internal uint version;
         internal uint version;
-        LuaState state = state;
+        LuaGlobalState globalState = globalState;
         public string? Name;
         public string? Name;
         public string? NameWhat;
         public string? NameWhat;
         public string? What;
         public string? What;
@@ -200,7 +200,7 @@ readonly struct LuaDebug : IDisposable
             if (version < uint.MaxValue)
             if (version < uint.MaxValue)
             {
             {
                 this.version++;
                 this.version++;
-                state.DebugBufferPool.Push(this);
+                globalState.DebugBufferPool.Push(this);
             }
             }
         }
         }
 
 

+ 116 - 0
src/Lua/Internal/LuaGlobalState.cs

@@ -0,0 +1,116 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Lua.Internal;
+using Lua.Platforms;
+using Lua.Runtime;
+using Lua.Standard;
+
+namespace Lua;
+
+sealed class LuaGlobalState
+{
+    // states
+    readonly LuaState mainState;
+    FastStackCore<LuaState> stateStack;
+    readonly LuaTable environment;
+    readonly LuaTable registry = new();
+    readonly UpValue envUpValue;
+
+    FastStackCore<LuaDebug.LuaDebugBuffer> debugBufferPool;
+
+    internal UpValue EnvUpValue => envUpValue;
+
+    internal ref FastStackCore<LuaState> ThreadStack => ref stateStack;
+
+    internal ref FastStackCore<LuaDebug.LuaDebugBuffer> DebugBufferPool => ref debugBufferPool;
+
+    public LuaTable Environment => environment;
+
+    public LuaTable Registry => registry;
+
+    public LuaTable LoadedModules => registry[ModuleLibrary.LoadedKeyForRegistry].Read<LuaTable>();
+
+    public LuaTable PreloadModules => registry[ModuleLibrary.PreloadKeyForRegistry].Read<LuaTable>();
+
+    public LuaState MainThread => mainState;
+
+    public LuaPlatform Platform { get; set; }
+
+    public ILuaModuleLoader? ModuleLoader { get; set; }
+
+    // metatables
+    LuaTable? nilMetatable;
+    LuaTable? numberMetatable;
+    LuaTable? stringMetatable;
+    LuaTable? booleanMetatable;
+    LuaTable? functionMetatable;
+    LuaTable? stateMetatable;
+
+    public static LuaGlobalState Create(LuaPlatform? platform = null)
+    {
+        LuaGlobalState globalState = new(platform ?? LuaPlatform.Default);
+        return globalState;
+    }
+
+    LuaGlobalState(LuaPlatform platform)
+    {
+        mainState = new(this);
+        environment = new();
+        envUpValue = UpValue.Closed(environment);
+        registry[ModuleLibrary.LoadedKeyForRegistry] = new LuaTable(0, 8);
+        registry[ModuleLibrary.PreloadKeyForRegistry] = new LuaTable(0, 8);
+        Platform = platform;
+    }
+
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result)
+    {
+        result = value.Type switch
+        {
+            LuaValueType.Nil => nilMetatable,
+            LuaValueType.Boolean => booleanMetatable,
+            LuaValueType.String => stringMetatable,
+            LuaValueType.Number => numberMetatable,
+            LuaValueType.Function => functionMetatable,
+            LuaValueType.Thread => stateMetatable,
+            LuaValueType.UserData => value.UnsafeRead<ILuaUserData>().Metatable,
+            LuaValueType.Table => value.UnsafeRead<LuaTable>().Metatable,
+            _ => null
+        };
+
+        return result != null;
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal void SetMetatable(LuaValue value, LuaTable metatable)
+    {
+        switch (value.Type)
+        {
+            case LuaValueType.Nil:
+                nilMetatable = metatable;
+                break;
+            case LuaValueType.Boolean:
+                booleanMetatable = metatable;
+                break;
+            case LuaValueType.String:
+                stringMetatable = metatable;
+                break;
+            case LuaValueType.Number:
+                numberMetatable = metatable;
+                break;
+            case LuaValueType.Function:
+                functionMetatable = metatable;
+                break;
+            case LuaValueType.Thread:
+                stateMetatable = metatable;
+                break;
+            case LuaValueType.UserData:
+                value.UnsafeRead<ILuaUserData>().Metatable = metatable;
+                break;
+            case LuaValueType.Table:
+                value.UnsafeRead<LuaTable>().Metatable = metatable;
+                break;
+        }
+    }
+}

+ 323 - 323
src/Lua/LuaCoroutine.cs

@@ -1,323 +1,323 @@
-using System.Threading.Tasks.Sources;
-using Lua.Internal;
-using Lua.Internal.CompilerServices;
-using Lua.Runtime;
-using System.Runtime.CompilerServices;
-
-namespace Lua;
-
-public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.YieldContext>, IValueTaskSource<LuaCoroutine.ResumeContext>, IPoolNode<LuaCoroutine>
-{
-    static LinkedPool<LuaCoroutine> pool;
-    LuaCoroutine? nextNode;
-
-    ref LuaCoroutine? IPoolNode<LuaCoroutine>.NextNode => ref nextNode;
-
-    public static LuaCoroutine Create(LuaThread parent, LuaFunction function, bool isProtectedMode)
-    {
-        if (!pool.TryPop(out var result))
-        {
-            result = new();
-        }
-
-        result.Init(parent, function, isProtectedMode);
-        return result;
-    }
-
-    public void Release()
-    {
-        if (CoreData != null && CoreData.CallStack.Count != 0)
-        {
-            throw new InvalidOperationException("This thread is running! Call stack is not empty!!");
-        }
-
-        ReleaseCore();
-        pool.TryPush(this);
-    }
-
-    readonly struct YieldContext(LuaStack stack, int argCount)
-    {
-        public ReadOnlySpan<LuaValue> Results => stack.AsSpan()[^argCount..];
-    }
-
-    readonly struct ResumeContext(LuaStack? stack, int argCount)
-    {
-        public ReadOnlySpan<LuaValue> Results => stack!.AsSpan()[^argCount..];
-
-        public bool IsDead => stack == null;
-    }
-
-    byte status;
-    bool isFirstCall = true;
-    ValueTask<int> functionTask;
-
-    ManualResetValueTaskSourceCore<ResumeContext> resume;
-    ManualResetValueTaskSourceCore<YieldContext> yield;
-    Traceback? traceback;
-
-    internal void Init(LuaThread parent, LuaFunction function, bool isProtectedMode)
-    {
-        CoreData = ThreadCoreData.Create();
-        State = parent.State;
-        IsProtectedMode = isProtectedMode;
-        Function = function;
-    }
-
-    public override LuaThreadStatus GetStatus()
-    {
-        return (LuaThreadStatus)status;
-    }
-
-    public override void UnsafeSetStatus(LuaThreadStatus status)
-    {
-        this.status = (byte)status;
-    }
-
-    public bool IsProtectedMode { get; private set; }
-    public LuaFunction Function { get; private set; } = null!;
-
-    internal Traceback? LuaTraceback => traceback;
-
-    public bool CanResume => status == (byte)LuaThreadStatus.Suspended;
-
-    public ValueTask<int> ResumeAsync(LuaStack stack, CancellationToken cancellationToken = default)
-    {
-        return ResumeAsyncCore(stack, stack.Count, 0, null, cancellationToken);
-    }
-
-    public override ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
-    {
-        return ResumeAsyncCore(context.Thread.Stack, context.ArgumentCount, context.ReturnFrameBase, context.Thread, cancellationToken);
-    }
-
-
-    [AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
-    async ValueTask<int> ResumeAsyncCore(LuaStack stack, int argCount, int returnBase, LuaThread? baseThread, CancellationToken cancellationToken = default)
-    {
-        if (baseThread != null)
-        {
-            baseThread.UnsafeSetStatus(LuaThreadStatus.Normal);
-
-            baseThread.State.ThreadStack.Push(this);
-        }
-
-        try
-        {
-            switch ((LuaThreadStatus)Volatile.Read(ref status))
-            {
-                case LuaThreadStatus.Suspended:
-                    Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
-
-                    if (!isFirstCall)
-                    {
-                        yield.SetResult(new(stack, argCount));
-                    }
-
-                    break;
-                case LuaThreadStatus.Normal:
-                case LuaThreadStatus.Running:
-                    if (IsProtectedMode)
-                    {
-                        stack.PopUntil(returnBase);
-                        stack.Push(false);
-                        stack.Push("cannot resume non-suspended coroutine");
-                        return 2;
-                    }
-                    else
-                    {
-                        throw new LuaRuntimeException(baseThread, "cannot resume non-suspended coroutine");
-                    }
-                case LuaThreadStatus.Dead:
-                    if (IsProtectedMode)
-                    {
-                        stack.PopUntil(returnBase);
-                        stack.Push(false);
-                        stack.Push("cannot resume dead coroutine");
-                        return 2;
-                    }
-                    else
-                    {
-                        throw new LuaRuntimeException(baseThread, "cannot resume dead coroutine");
-                    }
-            }
-
-            ValueTask<ResumeContext> resumeTask = new(this, resume.Version);
-
-            CancellationTokenRegistration registration = default;
-            if (cancellationToken.CanBeCanceled)
-            {
-                registration = cancellationToken.UnsafeRegister(static x =>
-                {
-                    var coroutine = (LuaCoroutine)x!;
-                    coroutine.yield.SetException(new OperationCanceledException());
-                }, this);
-            }
-
-            try
-            {
-                if (isFirstCall)
-                {
-                    Stack.PushRange(stack.AsSpan()[^argCount..]);
-                    //functionTask = Function.InvokeAsync(new() { Access = this.CurrentAccess, ArgumentCount = Stack.Count, ReturnFrameBase = 0 }, cancellationToken);
-                    functionTask = CurrentAccess.RunAsync(Function, Stack.Count, cancellationToken);
-                    Volatile.Write(ref isFirstCall, false);
-                    if (!functionTask.IsCompleted)
-                    {
-                        functionTask.GetAwaiter().OnCompleted(() => resume.SetResult(default));
-                    }
-                }
-
-                ResumeContext result0;
-                if (functionTask.IsCompleted || (result0 = await resumeTask).IsDead)
-                {
-                    _ = functionTask.Result;
-                    Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
-                    stack.PopUntil(returnBase);
-                    stack.Push(true);
-                    stack.PushRange(Stack.AsSpan());
-                    ReleaseCore();
-                    return stack.Count - returnBase;
-                }
-                else
-                {
-                    Volatile.Write(ref status, (byte)LuaThreadStatus.Suspended);
-                    var results = result0.Results;
-                    stack.PopUntil(returnBase);
-                    stack.Push(true);
-                    stack.PushRange(results);
-                    return results.Length + 1;
-                }
-            }
-            catch (Exception ex) when (ex is not OperationCanceledException)
-            {
-                if (IsProtectedMode)
-                {
-                    if (ex is ILuaTracebackBuildable tracebackBuildable)
-                    {
-                        traceback = tracebackBuildable.BuildOrGet();
-                    }
-
-                    Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
-                    ReleaseCore();
-                    stack.PopUntil(returnBase);
-                    stack.Push(false);
-                    stack.Push(ex is LuaRuntimeException luaEx ? luaEx.ErrorObject : ex.Message);
-                    return 2;
-                }
-                else
-                {
-                    throw;
-                }
-            }
-            finally
-            {
-                registration.Dispose();
-                resume.Reset();
-            }
-        }
-        finally
-        {
-            if (baseThread != null)
-            {
-                baseThread.State.ThreadStack.Pop();
-                baseThread.UnsafeSetStatus(LuaThreadStatus.Running);
-            }
-        }
-    }
-
-    public ValueTask<int> YieldAsync(LuaStack stack, CancellationToken cancellationToken = default)
-    {
-        return YieldAsyncCore(stack, stack.Count, 0, null, cancellationToken);
-    }
-
-    public override ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
-    {
-        return YieldAsyncCore(context.Thread.Stack, context.ArgumentCount, context.ReturnFrameBase, context.Thread, cancellationToken);
-    }
-
-    [AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
-    async ValueTask<int> YieldAsyncCore(LuaStack stack, int argCount, int returnBase, LuaThread? baseThread, CancellationToken cancellationToken = default)
-    {
-        if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
-        {
-            throw new LuaRuntimeException(baseThread, "cannot yield from a non-running coroutine");
-        }
-
-        if (baseThread != null)
-        {
-            if (baseThread.GetCallStackFrames()[^2].Function is not LuaClosure)
-            {
-                throw new LuaRuntimeException(baseThread, "attempt to yield across a C#-call boundary");
-            }
-        }
-
-        resume.SetResult(new(stack, argCount));
-
-
-        CancellationTokenRegistration registration = default;
-        if (cancellationToken.CanBeCanceled)
-        {
-            registration = cancellationToken.UnsafeRegister(static x =>
-            {
-                var coroutine = (LuaCoroutine)x!;
-                coroutine.yield.SetException(new OperationCanceledException());
-            }, this);
-        }
-
-    RETRY:
-        try
-        {
-            var result = await new ValueTask<YieldContext>(this, yield.Version);
-            stack.PopUntil(returnBase);
-            stack.PushRange(result.Results);
-            return result.Results.Length;
-        }
-        catch (Exception ex) when (ex is not OperationCanceledException)
-        {
-            yield.Reset();
-            goto RETRY;
-        }
-        finally
-        {
-            registration.Dispose();
-            yield.Reset();
-        }
-    }
-
-    YieldContext IValueTaskSource<YieldContext>.GetResult(short token)
-    {
-        return yield.GetResult(token);
-    }
-
-    ValueTaskSourceStatus IValueTaskSource<YieldContext>.GetStatus(short token)
-    {
-        return yield.GetStatus(token);
-    }
-
-    void IValueTaskSource<YieldContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
-    {
-        yield.OnCompleted(continuation, state, token, flags);
-    }
-
-    ResumeContext IValueTaskSource<ResumeContext>.GetResult(short token)
-    {
-        return resume.GetResult(token);
-    }
-
-    ValueTaskSourceStatus IValueTaskSource<ResumeContext>.GetStatus(short token)
-    {
-        return resume.GetStatus(token);
-    }
-
-    void IValueTaskSource<ResumeContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
-    {
-        resume.OnCompleted(continuation, state, token, flags);
-    }
-
-    void ReleaseCore()
-    {
-        // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
-        CoreData?.Release();
-        CoreData = null!;
-    }
-}
+// using System.Threading.Tasks.Sources;
+// using Lua.Internal;
+// using Lua.Internal.CompilerServices;
+// using Lua.Runtime;
+// using System.Runtime.CompilerServices;
+//
+// namespace Lua;
+//
+// public sealed class LuaCoroutine : LuaState, IValueTaskSource<LuaCoroutine.YieldContext>, IValueTaskSource<LuaCoroutine.ResumeContext>, IPoolNode<LuaCoroutine>
+// {
+//     static LinkedPool<LuaCoroutine> pool;
+//     LuaCoroutine? nextNode;
+//
+//     ref LuaCoroutine? IPoolNode<LuaCoroutine>.NextNode => ref nextNode;
+//
+//     public static LuaCoroutine Create(LuaState parent, LuaFunction function, bool isProtectedMode)
+//     {
+//         if (!pool.TryPop(out var result))
+//         {
+//             result = new();
+//         }
+//
+//         result.Init(parent, function, isProtectedMode);
+//         return result;
+//     }
+//
+//     public void Release()
+//     {
+//         if (CoreData != null && CoreData.CallStack.Count != 0)
+//         {
+//             throw new InvalidOperationException("This state is running! Call stack is not empty!!");
+//         }
+//
+//         ReleaseCore();
+//         pool.TryPush(this);
+//     }
+//
+//     readonly struct YieldContext(LuaStack stack, int argCount)
+//     {
+//         public ReadOnlySpan<LuaValue> Results => stack.AsSpan()[^argCount..];
+//     }
+//
+//     readonly struct ResumeContext(LuaStack? stack, int argCount)
+//     {
+//         public ReadOnlySpan<LuaValue> Results => stack!.AsSpan()[^argCount..];
+//
+//         public bool IsDead => stack == null;
+//     }
+//
+//     byte status;
+//     bool isFirstCall = true;
+//     ValueTask<int> functionTask;
+//
+//     ManualResetValueTaskSourceCore<ResumeContext> resume;
+//     ManualResetValueTaskSourceCore<YieldContext> yield;
+//     Traceback? traceback;
+//
+//     internal void Init(LuaState parent, LuaFunction function, bool isProtectedMode)
+//     {
+//         CoreData = ThreadCoreData.Create();
+//         GlobalState = parent.GlobalState;
+//         IsProtectedMode = isProtectedMode;
+//         Function = function;
+//     }
+//
+//     public override LuaThreadStatus GetStatus()
+//     {
+//         return (LuaThreadStatus)status;
+//     }
+//
+//     public override void UnsafeSetStatus(LuaThreadStatus status)
+//     {
+//         this.status = (byte)status;
+//     }
+//
+//     public bool IsProtectedMode { get; private set; }
+//     public LuaFunction Function { get; private set; } = null!;
+//
+//     internal Traceback? LuaTraceback => traceback;
+//
+//     public bool CanResume => status == (byte)LuaThreadStatus.Suspended;
+//
+//     public ValueTask<int> ResumeAsync(LuaStack stack, CancellationToken cancellationToken = default)
+//     {
+//         return ResumeAsyncCore(stack, stack.Count, 0, null, cancellationToken);
+//     }
+//
+//     public override ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
+//     {
+//         return ResumeAsyncCore(context.State.Stack, context.ArgumentCount, context.ReturnFrameBase, context.State, cancellationToken);
+//     }
+//
+//
+//     [AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
+//     async ValueTask<int> ResumeAsyncCore(LuaStack stack, int argCount, int returnBase, LuaState? baseThread, CancellationToken cancellationToken = default)
+//     {
+//         if (baseThread != null)
+//         {
+//             baseThread.UnsafeSetStatus(LuaThreadStatus.Normal);
+//
+//             baseThread.GlobalState.ThreadStack.Push(this);
+//         }
+//
+//         try
+//         {
+//             switch ((LuaThreadStatus)Volatile.Read(ref status))
+//             {
+//                 case LuaThreadStatus.Suspended:
+//                     Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
+//
+//                     if (!isFirstCall)
+//                     {
+//                         yield.SetResult(new(stack, argCount));
+//                     }
+//
+//                     break;
+//                 case LuaThreadStatus.Normal:
+//                 case LuaThreadStatus.Running:
+//                     if (IsProtectedMode)
+//                     {
+//                         stack.PopUntil(returnBase);
+//                         stack.Push(false);
+//                         stack.Push("cannot resume non-suspended coroutine");
+//                         return 2;
+//                     }
+//                     else
+//                     {
+//                         throw new LuaRuntimeException(baseThread, "cannot resume non-suspended coroutine");
+//                     }
+//                 case LuaThreadStatus.Dead:
+//                     if (IsProtectedMode)
+//                     {
+//                         stack.PopUntil(returnBase);
+//                         stack.Push(false);
+//                         stack.Push("cannot resume dead coroutine");
+//                         return 2;
+//                     }
+//                     else
+//                     {
+//                         throw new LuaRuntimeException(baseThread, "cannot resume dead coroutine");
+//                     }
+//             }
+//
+//             ValueTask<ResumeContext> resumeTask = new(this, resume.Version);
+//
+//             CancellationTokenRegistration registration = default;
+//             if (cancellationToken.CanBeCanceled)
+//             {
+//                 registration = cancellationToken.UnsafeRegister(static x =>
+//                 {
+//                     var coroutine = (LuaCoroutine)x!;
+//                     coroutine.yield.SetException(new OperationCanceledException());
+//                 }, this);
+//             }
+//
+//             try
+//             {
+//                 if (isFirstCall)
+//                 {
+//                     Stack.PushRange(stack.AsSpan()[^argCount..]);
+//                     //functionTask = Function.InvokeAsync(new() { Access = this.CurrentAccess, ArgumentCount = Stack.Count, ReturnFrameBase = 0 }, cancellationToken);
+//                     functionTask = CurrentAccess.RunAsync(Function, Stack.Count, cancellationToken);
+//                     Volatile.Write(ref isFirstCall, false);
+//                     if (!functionTask.IsCompleted)
+//                     {
+//                         functionTask.GetAwaiter().OnCompleted(() => resume.SetResult(default));
+//                     }
+//                 }
+//
+//                 ResumeContext result0;
+//                 if (functionTask.IsCompleted || (result0 = await resumeTask).IsDead)
+//                 {
+//                     _ = functionTask.Result;
+//                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
+//                     stack.PopUntil(returnBase);
+//                     stack.Push(true);
+//                     stack.PushRange(Stack.AsSpan());
+//                     ReleaseCore();
+//                     return stack.Count - returnBase;
+//                 }
+//                 else
+//                 {
+//                     Volatile.Write(ref status, (byte)LuaThreadStatus.Suspended);
+//                     var results = result0.Results;
+//                     stack.PopUntil(returnBase);
+//                     stack.Push(true);
+//                     stack.PushRange(results);
+//                     return results.Length + 1;
+//                 }
+//             }
+//             catch (Exception ex) when (ex is not OperationCanceledException)
+//             {
+//                 if (IsProtectedMode)
+//                 {
+//                     if (ex is ILuaTracebackBuildable tracebackBuildable)
+//                     {
+//                         traceback = tracebackBuildable.BuildOrGet();
+//                     }
+//
+//                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
+//                     ReleaseCore();
+//                     stack.PopUntil(returnBase);
+//                     stack.Push(false);
+//                     stack.Push(ex is LuaRuntimeException luaEx ? luaEx.ErrorObject : ex.Message);
+//                     return 2;
+//                 }
+//                 else
+//                 {
+//                     throw;
+//                 }
+//             }
+//             finally
+//             {
+//                 registration.Dispose();
+//                 resume.Reset();
+//             }
+//         }
+//         finally
+//         {
+//             if (baseThread != null)
+//             {
+//                 baseThread.GlobalState.ThreadStack.Pop();
+//                 baseThread.UnsafeSetStatus(LuaThreadStatus.Running);
+//             }
+//         }
+//     }
+//
+//     public ValueTask<int> YieldAsync(LuaStack stack, CancellationToken cancellationToken = default)
+//     {
+//         return YieldAsyncCore(stack, stack.Count, 0, null, cancellationToken);
+//     }
+//
+//     public override ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
+//     {
+//         return YieldAsyncCore(context.State.Stack, context.ArgumentCount, context.ReturnFrameBase, context.State, cancellationToken);
+//     }
+//
+//     [AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
+//     async ValueTask<int> YieldAsyncCore(LuaStack stack, int argCount, int returnBase, LuaState? baseThread, CancellationToken cancellationToken = default)
+//     {
+//         if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
+//         {
+//             throw new LuaRuntimeException(baseThread, "cannot yield from a non-running coroutine");
+//         }
+//
+//         if (baseThread != null)
+//         {
+//             if (baseThread.GetCallStackFrames()[^2].Function is not LuaClosure)
+//             {
+//                 throw new LuaRuntimeException(baseThread, "attempt to yield across a C#-call boundary");
+//             }
+//         }
+//
+//         resume.SetResult(new(stack, argCount));
+//
+//
+//         CancellationTokenRegistration registration = default;
+//         if (cancellationToken.CanBeCanceled)
+//         {
+//             registration = cancellationToken.UnsafeRegister(static x =>
+//             {
+//                 var coroutine = (LuaCoroutine)x!;
+//                 coroutine.yield.SetException(new OperationCanceledException());
+//             }, this);
+//         }
+//
+//     RETRY:
+//         try
+//         {
+//             var result = await new ValueTask<YieldContext>(this, yield.Version);
+//             stack.PopUntil(returnBase);
+//             stack.PushRange(result.Results);
+//             return result.Results.Length;
+//         }
+//         catch (Exception ex) when (ex is not OperationCanceledException)
+//         {
+//             yield.Reset();
+//             goto RETRY;
+//         }
+//         finally
+//         {
+//             registration.Dispose();
+//             yield.Reset();
+//         }
+//     }
+//
+//     YieldContext IValueTaskSource<YieldContext>.GetResult(short token)
+//     {
+//         return yield.GetResult(token);
+//     }
+//
+//     ValueTaskSourceStatus IValueTaskSource<YieldContext>.GetStatus(short token)
+//     {
+//         return yield.GetStatus(token);
+//     }
+//
+//     void IValueTaskSource<YieldContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
+//     {
+//         yield.OnCompleted(continuation, state, token, flags);
+//     }
+//
+//     ResumeContext IValueTaskSource<ResumeContext>.GetResult(short token)
+//     {
+//         return resume.GetResult(token);
+//     }
+//
+//     ValueTaskSourceStatus IValueTaskSource<ResumeContext>.GetStatus(short token)
+//     {
+//         return resume.GetStatus(token);
+//     }
+//
+//     void IValueTaskSource<ResumeContext>.OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
+//     {
+//         resume.OnCompleted(continuation, state, token, flags);
+//     }
+//
+//     void ReleaseCore()
+//     {
+//         // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+//         CoreData?.Release();
+//         CoreData = null!;
+//     }
+// }

+ 23 - 25
src/Lua/LuaFunctionExecutionContext.cs

@@ -7,15 +7,13 @@ namespace Lua;
 [StructLayout(LayoutKind.Auto)]
 [StructLayout(LayoutKind.Auto)]
 public readonly record struct LuaFunctionExecutionContext
 public readonly record struct LuaFunctionExecutionContext
 {
 {
-    public LuaState State => Thread.State;
-
-    public required LuaThreadAccess Access { get; init; }
-
-    public LuaThread Thread => Access.Thread;
+    internal LuaGlobalState GlobalState => State.GlobalState;
+    
+    public LuaState State { get; init; }
 
 
     public required int ArgumentCount { get; init; }
     public required int ArgumentCount { get; init; }
 
 
-    public int FrameBase => Thread.Stack.Count - ArgumentCount;
+    public int FrameBase => State.Stack.Count - ArgumentCount;
 
 
     public required int ReturnFrameBase { get; init; }
     public required int ReturnFrameBase { get; init; }
     //public object? AdditionalContext { get; init; }
     //public object? AdditionalContext { get; init; }
@@ -24,7 +22,7 @@ public readonly record struct LuaFunctionExecutionContext
     {
     {
         get
         get
         {
         {
-            var stack = Thread.Stack.AsSpan();
+            var stack = State.Stack.AsSpan();
             return stack.Slice(stack.Length - ArgumentCount);
             return stack.Slice(stack.Length - ArgumentCount);
         }
         }
     }
     }
@@ -64,19 +62,19 @@ public readonly record struct LuaFunctionExecutionContext
             var t = typeof(T);
             var t = typeof(T);
             if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _))
             if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _))
             {
             {
-                LuaRuntimeException.BadArgumentNumberIsNotInteger(Thread, index + 1);
+                LuaRuntimeException.BadArgumentNumberIsNotInteger(State, index + 1);
             }
             }
             else if (LuaValue.TryGetLuaValueType(t, out var type))
             else if (LuaValue.TryGetLuaValueType(t, out var type))
             {
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, type, arg.Type);
+                LuaRuntimeException.BadArgument(State, index + 1, type, arg.Type);
             }
             }
             else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData)
             else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData)
             {
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
+                LuaRuntimeException.BadArgument(State, index + 1, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
             }
             }
             else
             else
             {
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.TypeToString());
+                LuaRuntimeException.BadArgument(State, index + 1, t.Name, arg.TypeToString());
             }
             }
         }
         }
 
 
@@ -103,19 +101,19 @@ public readonly record struct LuaFunctionExecutionContext
             var t = typeof(T);
             var t = typeof(T);
             if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _))
             if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _))
             {
             {
-                LuaRuntimeException.BadArgumentNumberIsNotInteger(Thread, index + 1);
+                LuaRuntimeException.BadArgumentNumberIsNotInteger(State, index + 1);
             }
             }
             else if (LuaValue.TryGetLuaValueType(t, out var type))
             else if (LuaValue.TryGetLuaValueType(t, out var type))
             {
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, type, arg.Type);
+                LuaRuntimeException.BadArgument(State, index + 1, type, arg.Type);
             }
             }
             else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData)
             else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData)
             {
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
+                LuaRuntimeException.BadArgument(State, index + 1, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
             }
             }
             else
             else
             {
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.TypeToString());
+                LuaRuntimeException.BadArgument(State, index + 1, t.Name, arg.TypeToString());
             }
             }
         }
         }
 
 
@@ -124,13 +122,13 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     public int Return()
     public int Return()
     {
     {
-        Thread.Stack.PopUntil(ReturnFrameBase);
+        State.Stack.PopUntil(ReturnFrameBase);
         return 0;
         return 0;
     }
     }
 
 
     public int Return(LuaValue result)
     public int Return(LuaValue result)
     {
     {
-        var stack = Thread.Stack;
+        var stack = State.Stack;
         stack.SetTop(ReturnFrameBase + 1);
         stack.SetTop(ReturnFrameBase + 1);
         stack.FastGet(ReturnFrameBase) = result;
         stack.FastGet(ReturnFrameBase) = result;
         return 1;
         return 1;
@@ -138,7 +136,7 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     public int Return(LuaValue result0, LuaValue result1)
     public int Return(LuaValue result0, LuaValue result1)
     {
     {
-        var stack = Thread.Stack;
+        var stack = State.Stack;
         stack.SetTop(ReturnFrameBase + 2);
         stack.SetTop(ReturnFrameBase + 2);
         stack.FastGet(ReturnFrameBase) = result0;
         stack.FastGet(ReturnFrameBase) = result0;
         stack.FastGet(ReturnFrameBase + 1) = result1;
         stack.FastGet(ReturnFrameBase + 1) = result1;
@@ -147,7 +145,7 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     public int Return(LuaValue result0, LuaValue result1, LuaValue result2)
     public int Return(LuaValue result0, LuaValue result1, LuaValue result2)
     {
     {
-        var stack = Thread.Stack;
+        var stack = State.Stack;
         stack.SetTop(ReturnFrameBase + 3);
         stack.SetTop(ReturnFrameBase + 3);
         stack.FastGet(ReturnFrameBase) = result0;
         stack.FastGet(ReturnFrameBase) = result0;
         stack.FastGet(ReturnFrameBase + 1) = result1;
         stack.FastGet(ReturnFrameBase + 1) = result1;
@@ -157,7 +155,7 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     public int Return(ReadOnlySpan<LuaValue> results)
     public int Return(ReadOnlySpan<LuaValue> results)
     {
     {
-        var stack = Thread.Stack;
+        var stack = State.Stack;
         stack.EnsureCapacity(ReturnFrameBase + results.Length);
         stack.EnsureCapacity(ReturnFrameBase + results.Length);
         results.CopyTo(stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + results.Length)]);
         results.CopyTo(stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + results.Length)]);
         stack.SetTop(ReturnFrameBase + results.Length);
         stack.SetTop(ReturnFrameBase + results.Length);
@@ -166,7 +164,7 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     internal int Return(LuaValue result0, ReadOnlySpan<LuaValue> results)
     internal int Return(LuaValue result0, ReadOnlySpan<LuaValue> results)
     {
     {
-        var stack = Thread.Stack;
+        var stack = State.Stack;
         stack.EnsureCapacity(ReturnFrameBase + results.Length);
         stack.EnsureCapacity(ReturnFrameBase + results.Length);
         stack.SetTop(ReturnFrameBase + results.Length + 1);
         stack.SetTop(ReturnFrameBase + results.Length + 1);
         var buffer = stack.GetBuffer();
         var buffer = stack.GetBuffer();
@@ -177,7 +175,7 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     public Span<LuaValue> GetReturnBuffer(int count)
     public Span<LuaValue> GetReturnBuffer(int count)
     {
     {
-        var stack = Thread.Stack;
+        var stack = State.Stack;
         stack.SetTop(ReturnFrameBase + count);
         stack.SetTop(ReturnFrameBase + count);
         var buffer = stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + count)];
         var buffer = stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + count)];
         return buffer;
         return buffer;
@@ -185,19 +183,19 @@ public readonly record struct LuaFunctionExecutionContext
 
 
     public CSharpClosure? GetCsClosure()
     public CSharpClosure? GetCsClosure()
     {
     {
-        return Thread.GetCurrentFrame().Function as CSharpClosure;
+        return State.GetCurrentFrame().Function as CSharpClosure;
     }
     }
 
 
     internal void ThrowBadArgument(int index, string message)
     internal void ThrowBadArgument(int index, string message)
     {
     {
-        LuaRuntimeException.BadArgument(Thread, index, Thread.GetCurrentFrame().Function.Name, message);
+        LuaRuntimeException.BadArgument(State, index, State.GetCurrentFrame().Function.Name, message);
     }
     }
 
 
     void ThrowIfArgumentNotExists(int index)
     void ThrowIfArgumentNotExists(int index)
     {
     {
         if (ArgumentCount <= index)
         if (ArgumentCount <= index)
         {
         {
-            LuaRuntimeException.BadArgument(Thread, index + 1);
+            LuaRuntimeException.BadArgument(State, index + 1);
         }
         }
     }
     }
 }
 }

+ 0 - 43
src/Lua/LuaFunctionExtensions.cs

@@ -1,43 +0,0 @@
-// using Lua.Runtime;
-//
-// namespace Lua;
-//
-// public static class LuaFunctionExtensions
-// {
-//     
-//     public static async ValueTask<int> InvokeAsync(this LuaFunction function, LuaThread thread, int argumentCount, CancellationToken cancellationToken = default)
-//     {
-//         var returnFrameBase = thread.Stack.Count-argumentCount;
-//         var varArgumentCount = function.GetVariableArgumentCount(argumentCount);
-//         if (varArgumentCount != 0)
-//         {
-//             if (varArgumentCount < 0)
-//             {
-//                 thread.Stack.SetTop(thread.Stack.Count - varArgumentCount);
-//                 argumentCount -= varArgumentCount;
-//                 varArgumentCount = 0;
-//             }
-//             else
-//             {
-//                 LuaVirtualMachine.PrepareVariableArgument(thread.Stack, argumentCount, varArgumentCount);
-//             }
-//         }
-//
-//         LuaFunctionExecutionContext context = new() { Thread = thread, ArgumentCount = argumentCount , ReturnFrameBase = returnFrameBase, };
-//         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();
-//         }
-//     }
-// }

+ 0 - 15
src/Lua/LuaMainThread.cs

@@ -1,15 +0,0 @@
-namespace Lua;
-
-public sealed class LuaMainThread : LuaThread
-{
-    internal LuaMainThread(LuaState state)
-    {
-        State = state;
-        CoreData = ThreadCoreData.Create();
-    }
-
-    public override LuaThreadStatus GetStatus()
-    {
-        return LuaThreadStatus.Running;
-    }
-}

+ 413 - 113
src/Lua/LuaState.cs

@@ -1,175 +1,433 @@
 using Lua.CodeAnalysis.Compilation;
 using Lua.CodeAnalysis.Compilation;
-using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using Lua.Internal;
 using Lua.Internal;
-using Lua.IO;
-using Lua.Loaders;
 using Lua.Platforms;
 using Lua.Platforms;
 using Lua.Runtime;
 using Lua.Runtime;
-using Lua.Standard;
 using System.Buffers;
 using System.Buffers;
-using System.Text;
 
 
 namespace Lua;
 namespace Lua;
 
 
-public sealed class LuaState
+public class LuaState : IDisposable
 {
 {
-    // states
-    readonly LuaMainThread mainThread;
-    FastListCore<UpValue> openUpValues;
-    FastStackCore<LuaThread> threadStack;
-    readonly LuaTable environment;
-    readonly LuaTable registry = new();
-    readonly UpValue envUpValue;
+    internal LuaState(LuaGlobalState globalState)
+    {
+        GlobalState = globalState;
+        CoreData = ThreadCoreData.Create();
+    }
+
+    internal LuaState(LuaGlobalState globalState, LuaFunction function, bool isProtectedMode)
+    {
+        GlobalState = globalState;
+        CoreData = ThreadCoreData.Create();
+        coroutine = new(this, function, isProtectedMode);
+    }
+
+    public static LuaState Create()
+    {
+        var globalState = LuaGlobalState.Create();
+        return globalState.MainThread;
+    }
 
 
+    public static LuaState Create(LuaPlatform platform)
+    {
+        return LuaGlobalState.Create(platform).MainThread;
+    }
 
 
-    FastStackCore<LuaDebug.LuaDebugBuffer> debugBufferPool;
+    internal static LuaState CreateCoroutine(LuaGlobalState globalState, LuaFunction function, bool isProtectedMode = false)
+    {
+        return new(globalState, function, isProtectedMode);
+    }
 
 
-    internal int CallCount;
+    public LuaState CreateThread()
+    {
+        return new(GlobalState);
+    }
+
+    public LuaState CreateCoroutine(LuaFunction function, bool isProtectedMode = false)
+    {
+        return new(GlobalState, function, isProtectedMode);
+    }
+
+
+    public LuaThreadStatus GetStatus()
+    {
+        if (coroutine is not null)
+        {
+            return (LuaThreadStatus)coroutine.status;
+        }
+
+        return LuaThreadStatus.Running;
+    }
+
+    public void UnsafeSetStatus(LuaThreadStatus status)
+    {
+        if (coroutine is null)
+        {
+            return;
+        }
+
+        coroutine.status = (byte)status;
+    }
+
+    public ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
+    {
+        if (coroutine is not null)
+        {
+            return coroutine.ResumeAsyncCore(context.State.Stack, context.ArgumentCount, context.ReturnFrameBase, context.State, cancellationToken);
+        }
+
+        return new(context.Return(false, "cannot resume non-suspended coroutine"));
+    }
+
+    public ValueTask<int> ResumeAsync(LuaStack stack, CancellationToken cancellationToken = default)
+    {
+        if (coroutine is not null)
+        {
+            return coroutine.ResumeAsyncCore(stack, stack.Count, 0, null, cancellationToken);
+        }
+
+        stack.Push(false);
+        stack.Push("cannot resume non-suspended coroutine");
+        return new(2);
+    }
+
+    public ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
+    {
+        if (coroutine is not null)
+        {
+            return coroutine.YieldAsyncCore(context.State.Stack, context.ArgumentCount, context.ReturnFrameBase, context.State, cancellationToken);
+        }
+
+        throw new LuaRuntimeException(context.State, "cannot yield from a non-running coroutine");
+    }
+
+    public ValueTask<int> YieldAsync(LuaStack stack, CancellationToken cancellationToken = default)
+    {
+        if (coroutine is not null)
+        {
+            return coroutine.YieldAsyncCore(stack, stack.Count, 0, null, cancellationToken);
+        }
+
+        throw new LuaRuntimeException(null, "cannot yield from a non-running coroutine");
+    }
+
+    class ThreadCoreData : IPoolNode<ThreadCoreData>
+    {
+        //internal  LuaCoroutineData? coroutineData;
+        internal readonly LuaStack Stack = new();
+        internal FastStackCore<CallStackFrame> CallStack;
+
+        void Clear()
+        {
+            Stack.Clear();
+            CallStack.Clear();
+        }
+
+        static LinkedPool<ThreadCoreData> pool;
+        ThreadCoreData? nextNode;
 
 
-    internal UpValue EnvUpValue => envUpValue;
+        public ref ThreadCoreData? NextNode => ref nextNode;
 
 
-    internal ref FastStackCore<LuaThread> ThreadStack => ref threadStack;
+        public static ThreadCoreData Create()
+        {
+            if (!pool.TryPop(out var result))
+            {
+                result = new();
+            }
+
+            return result;
+        }
+
+        public void Release()
+        {
+            Clear();
+            pool.TryPush(this);
+        }
+    }
+
+
+    FastListCore<UpValue> openUpValues;
+    internal int CallCount;
+    internal LuaGlobalState GlobalState { get; }
+    ThreadCoreData? CoreData;
+    CoroutineCore? coroutine;
+    internal bool IsLineHookEnabled;
+    internal BitFlags2 CallOrReturnHookMask;
+    internal bool IsInHook;
+    internal long HookCount;
+    internal int BaseHookCount;
+    internal int LastPc;
+
+    internal ILuaTracebackBuildable? CurrentException;
+    internal readonly ReversedStack<CallStackFrame> ExceptionTrace = new();
+    internal LuaFunction? LastCallerFunction;
 
 
     internal ref FastListCore<UpValue> OpenUpValues => ref openUpValues;
     internal ref FastListCore<UpValue> OpenUpValues => ref openUpValues;
+    public bool IsRunning => CallStackFrameCount != 0;
+    public bool IsCoroutine => coroutine != null;
+    internal LuaFunction? Hook { get; set; }
 
 
-    internal ref FastStackCore<LuaDebug.LuaDebugBuffer> DebugBufferPool => ref debugBufferPool;
+    public LuaFunction? CoroutineFunction => coroutine?.Function;
 
 
-    public LuaTable Environment => environment;
+    public bool CanResume => GetStatus() == LuaThreadStatus.Suspended;
 
 
-    public LuaTable Registry => registry;
+    public LuaStack Stack => CoreData!.Stack;
 
 
-    public LuaTable LoadedModules => registry[ModuleLibrary.LoadedKeyForRegistry].Read<LuaTable>();
+    internal Traceback? LuaTraceback => coroutine?.Traceback;
 
 
-    public LuaTable PreloadModules => registry[ModuleLibrary.PreloadKeyForRegistry].Read<LuaTable>();
+    public LuaTable Environment => GlobalState.Environment;
 
 
-    public LuaMainThread MainThread => mainThread;
+    public LuaTable Registry => GlobalState.Registry;
 
 
-    public LuaThreadAccess RootAccess => new(mainThread, 0);
+    public LuaTable LoadedModules => GlobalState.LoadedModules;
 
 
-    public LuaPlatform Platform { get; }
+    public LuaTable PreloadModules => GlobalState.PreloadModules;
 
 
-    public ILuaModuleLoader? ModuleLoader { get; set; }
+    public LuaState MainThread => GlobalState.MainThread;
 
 
-    public ILuaFileSystem FileSystem => Platform.FileSystem ?? throw new InvalidOperationException("FileSystem is not set. Please set it before access.");
+    public ILuaModuleLoader? ModuleLoader
+    {
+        get => GlobalState.ModuleLoader;
+        set => GlobalState.ModuleLoader = value;
+    }
 
 
-    public ILuaOsEnvironment OsEnvironment => Platform.OsEnvironment ?? throw new InvalidOperationException("OperatingSystem is not set. Please set it before access.");
+    public LuaPlatform Platform
+    {
+        get => GlobalState.Platform;
+        set => GlobalState.Platform = value;
+    }
 
 
+    internal bool IsCallHookEnabled
+    {
+        get => CallOrReturnHookMask.Flag0;
+        set => CallOrReturnHookMask.Flag0 = value;
+    }
 
 
-    public TimeProvider TimeProvider => Platform.TimeProvider ?? throw new InvalidOperationException("TimeProvider is not set. Please set it before access.");
+    internal bool IsReturnHookEnabled
+    {
+        get => CallOrReturnHookMask.Flag1;
+        set => CallOrReturnHookMask.Flag1 = value;
+    }
 
 
-    public ILuaStandardIO StandardIO => Platform.StandardIO;
+    public int CallStackFrameCount => CoreData == null ? 0 : CoreData!.CallStack.Count;
 
 
-    // metatables
-    LuaTable? nilMetatable;
-    LuaTable? numberMetatable;
-    LuaTable? stringMetatable;
-    LuaTable? booleanMetatable;
-    LuaTable? functionMetatable;
-    LuaTable? threadMetatable;
+    public ref readonly CallStackFrame GetCurrentFrame()
+    {
+        return ref CoreData!.CallStack.PeekRef();
+    }
 
 
-    public static LuaState Create(LuaPlatform? platform = null)
+    public ReadOnlySpan<LuaValue> GetStackValues()
     {
     {
-        LuaState state = new(platform ?? LuaPlatform.Default);
-        return state;
+        return CoreData == null ? default : CoreData!.Stack.AsSpan();
     }
     }
 
 
-    LuaState(LuaPlatform platform)
+    public ReadOnlySpan<CallStackFrame> GetCallStackFrames()
     {
     {
-        mainThread = new(this);
-        environment = new();
-        envUpValue = UpValue.Closed(environment);
-        registry[ModuleLibrary.LoadedKeyForRegistry] = new LuaTable(0, 8);
-        registry[ModuleLibrary.PreloadKeyForRegistry] = new LuaTable(0, 8);
-        Platform = platform;
+        return CoreData == null ? default : CoreData!.CallStack.AsSpan();
     }
     }
 
 
 
 
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result)
-    {
-        result = value.Type switch
-        {
-            LuaValueType.Nil => nilMetatable,
-            LuaValueType.Boolean => booleanMetatable,
-            LuaValueType.String => stringMetatable,
-            LuaValueType.Number => numberMetatable,
-            LuaValueType.Function => functionMetatable,
-            LuaValueType.Thread => threadMetatable,
-            LuaValueType.UserData => value.UnsafeRead<ILuaUserData>().Metatable,
-            LuaValueType.Table => value.UnsafeRead<LuaTable>().Metatable,
-            _ => null
+    internal CallStackFrame CreateCallStackFrame(LuaFunction function, int argumentCount, int returnBase, int callerInstructionIndex)
+    {
+        var state = this;
+        var varArgumentCount = function.GetVariableArgumentCount(argumentCount);
+        if (varArgumentCount != 0)
+        {
+            if (varArgumentCount < 0)
+            {
+                state.Stack.SetTop(state.Stack.Count - varArgumentCount);
+                argumentCount -= varArgumentCount;
+                varArgumentCount = 0;
+            }
+            else
+            {
+                LuaVirtualMachine.PrepareVariableArgument(state.Stack, argumentCount, varArgumentCount);
+            }
+        }
+
+        CallStackFrame frame = new()
+        {
+            Base = state.Stack.Count - argumentCount,
+            VariableArgumentCount = varArgumentCount,
+            Function = function,
+            ReturnBase = returnBase,
+            CallerInstructionIndex = callerInstructionIndex
         };
         };
 
 
-        return result != null;
+        if (state.IsInHook)
+        {
+            frame.Flags |= CallStackFrameFlags.InHook;
+        }
+
+        return frame;
     }
     }
 
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void SetMetatable(LuaValue value, LuaTable metatable)
-    {
-        switch (value.Type)
-        {
-            case LuaValueType.Nil:
-                nilMetatable = metatable;
-                break;
-            case LuaValueType.Boolean:
-                booleanMetatable = metatable;
-                break;
-            case LuaValueType.String:
-                stringMetatable = metatable;
-                break;
-            case LuaValueType.Number:
-                numberMetatable = metatable;
-                break;
-            case LuaValueType.Function:
-                functionMetatable = metatable;
-                break;
-            case LuaValueType.Thread:
-                threadMetatable = metatable;
-                break;
-            case LuaValueType.UserData:
-                value.UnsafeRead<ILuaUserData>().Metatable = metatable;
-                break;
-            case LuaValueType.Table:
-                value.UnsafeRead<LuaTable>().Metatable = metatable;
-                break;
-        }
-    }
-
-    internal UpValue GetOrAddUpValue(LuaThread thread, int registerIndex)
+    internal void PushCallStackFrame(in CallStackFrame frame)
     {
     {
-        foreach (var upValue in openUpValues.AsSpan())
+        CurrentException?.BuildOrGet();
+        CurrentException = null;
+        ref var callStack = ref CoreData!.CallStack;
+        callStack.Push(frame);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal void PopCallStackFrameWithStackPop()
+    {
+        var coreData = CoreData!;
+        ref var callStack = ref coreData.CallStack;
+        var popFrame = callStack.Pop();
+        if (CurrentException != null)
         {
         {
-            if (upValue.RegisterIndex == registerIndex && upValue.Thread == thread)
-            {
-                return upValue;
-            }
+            ExceptionTrace.Push(popFrame);
         }
         }
 
 
-        var newUpValue = UpValue.Open(thread, registerIndex);
-        openUpValues.Add(newUpValue);
-        return newUpValue;
+        coreData.Stack.PopUntil(popFrame.ReturnBase);
     }
     }
 
 
-    internal void CloseUpValues(LuaThread thread, int frameBase)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal void PopCallStackFrameWithStackPop(int frameBase)
     {
     {
-        for (var i = 0; i < openUpValues.Length; i++)
+        var coreData = CoreData!;
+        ref var callStack = ref coreData.CallStack;
+        var popFrame = callStack.Pop();
+        if (CurrentException != null)
         {
         {
-            var upValue = openUpValues[i];
-            if (upValue.Thread != thread)
+            ExceptionTrace.Push(popFrame);
+        }
+
+        {
+            coreData.Stack.PopUntil(frameBase);
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal void PopCallStackFrame()
+    {
+        var coreData = CoreData!;
+        ref var callStack = ref coreData.CallStack;
+        var popFrame = callStack.Pop();
+        if (CurrentException != null)
+        {
+            ExceptionTrace.Push(popFrame);
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal void PopCallStackFrameUntil(int top)
+    {
+        var coreData = CoreData!;
+        ref var callStack = ref coreData.CallStack;
+        if (CurrentException != null)
+        {
+            ExceptionTrace.Push(callStack.AsSpan()[top..]);
+        }
+
+        callStack.PopUntil(top);
+    }
+
+    public void SetHook(LuaFunction? hook, string mask, int count = 0)
+    {
+        if (hook is null)
+        {
+            HookCount = 0;
+            BaseHookCount = 0;
+            Hook = null;
+            IsLineHookEnabled = false;
+            IsCallHookEnabled = false;
+            IsReturnHookEnabled = false;
+            return;
+        }
+
+        HookCount = count > 0 ? count + 1 : 0;
+        BaseHookCount = count;
+
+        IsLineHookEnabled = mask.Contains('l');
+        IsCallHookEnabled = mask.Contains('c');
+        IsReturnHookEnabled = mask.Contains('r');
+
+        if (IsLineHookEnabled)
+        {
+            LastPc = CallStackFrameCount > 0 ? GetCurrentFrame().CallerInstructionIndex : -1;
+        }
+
+        Hook = hook;
+    }
+
+    internal void DumpStackValues()
+    {
+        var span = GetStackValues();
+        for (var i = 0; i < span.Length; i++)
+        {
+            Console.WriteLine($"LuaStack [{i}]\t{span[i]}");
+        }
+    }
+
+    public Traceback GetTraceback()
+    {
+        return new(this, GetCallStackFrames());
+    }
+
+    public ValueTask<int> RunAsync(LuaFunction function, CancellationToken cancellationToken = default)
+    {
+        return RunAsync(function, 0, Stack.Count, cancellationToken);
+    }
+
+    public ValueTask<int> RunAsync(LuaFunction function, int argumentCount, CancellationToken cancellationToken = default)
+    {
+        return RunAsync(function, argumentCount, Stack.Count - argumentCount, cancellationToken);
+    }
+
+    public async ValueTask<int> RunAsync(LuaFunction function, int argumentCount, int returnBase, CancellationToken cancellationToken = default)
+    {
+        if (function == null)
+        {
+            throw new ArgumentNullException(nameof(function));
+        }
+
+        this.ThrowIfCancellationRequested(cancellationToken);
+        var state = this;
+        var varArgumentCount = function.GetVariableArgumentCount(argumentCount);
+        if (varArgumentCount != 0)
+        {
+            if (varArgumentCount < 0)
+            {
+                state.Stack.SetTop(state.Stack.Count - varArgumentCount);
+                varArgumentCount = 0;
+            }
+            else
             {
             {
-                continue;
+                LuaVirtualMachine.PrepareVariableArgument(state.Stack, argumentCount, varArgumentCount);
             }
             }
 
 
-            if (upValue.RegisterIndex >= frameBase)
+            argumentCount -= varArgumentCount;
+        }
+
+        CallStackFrame frame = new() { Base = state.Stack.Count - argumentCount, VariableArgumentCount = varArgumentCount, Function = function, ReturnBase = returnBase };
+        if (state.IsInHook)
+        {
+            frame.Flags |= CallStackFrameFlags.InHook;
+        }
+
+        state.PushCallStackFrame(frame);
+        LuaFunctionExecutionContext context = new() { State = state, ArgumentCount = argumentCount, ReturnFrameBase = returnBase };
+        var callStackTop = state.CallStackFrameCount;
+        try
+        {
+            if (CallOrReturnHookMask.Value != 0 && !IsInHook)
             {
             {
-                upValue.Close();
-                openUpValues.RemoveAtSwapBack(i);
-                i--;
+                return await LuaVirtualMachine.ExecuteCallHook(context, cancellationToken);
             }
             }
+
+            return await function.Func(context, cancellationToken);
+        }
+        finally
+        {
+            PopCallStackFrameUntil(callStackTop - 1);
         }
         }
     }
     }
 
 
+
     public unsafe LuaClosure Load(ReadOnlySpan<char> chunk, string chunkName, LuaTable? environment = null)
     public unsafe LuaClosure Load(ReadOnlySpan<char> chunk, string chunkName, LuaTable? environment = null)
     {
     {
         Prototype prototype;
         Prototype prototype;
@@ -178,7 +436,7 @@ public sealed class LuaState
             prototype = Parser.Parse(this, new(ptr, chunk.Length), chunkName);
             prototype = Parser.Parse(this, new(ptr, chunk.Length), chunkName);
         }
         }
 
 
-        return new(MainThread, prototype, environment);
+        return new(this, prototype, environment);
     }
     }
 
 
     public LuaClosure Load(ReadOnlySpan<byte> chunk, string? chunkName = null, string mode = "bt", LuaTable? environment = null)
     public LuaClosure Load(ReadOnlySpan<byte> chunk, string? chunkName = null, string mode = "bt", LuaTable? environment = null)
@@ -187,7 +445,7 @@ public sealed class LuaState
         {
         {
             if (chunk[0] == '\e')
             if (chunk[0] == '\e')
             {
             {
-                return new(MainThread, Parser.UnDump(chunk, chunkName), environment);
+                return new(this, Parser.UnDump(chunk, chunkName), environment);
             }
             }
         }
         }
 
 
@@ -208,4 +466,46 @@ public sealed class LuaState
             ArrayPool<char>.Shared.Return(pooled);
             ArrayPool<char>.Shared.Return(pooled);
         }
         }
     }
     }
+
+
+    internal UpValue GetOrAddUpValue(int registerIndex)
+    {
+        foreach (var upValue in openUpValues.AsSpan())
+        {
+            if (upValue.RegisterIndex == registerIndex)
+            {
+                return upValue;
+            }
+        }
+
+        var newUpValue = UpValue.Open(this, registerIndex);
+        openUpValues.Add(newUpValue);
+        return newUpValue;
+    }
+
+    internal void CloseUpValues(int frameBase)
+    {
+        for (var i = 0; i < openUpValues.Length; i++)
+        {
+            var upValue = openUpValues[i];
+
+            if (upValue.RegisterIndex >= frameBase)
+            {
+                upValue.Close();
+                openUpValues.RemoveAtSwapBack(i);
+                i--;
+            }
+        }
+    }
+
+    public void Dispose()
+    {
+        if (CoreData!.CallStack.Count != 0)
+        {
+            throw new InvalidOperationException("This state is running! Call stack is not empty!!");
+        }
+
+        CoreData.Release();
+        CoreData = null!;
+    }
 }
 }

+ 269 - 16
src/Lua/LuaStateExtensions.cs

@@ -1,47 +1,300 @@
 using Lua.IO;
 using Lua.IO;
 using Lua.Runtime;
 using Lua.Runtime;
+using System.Runtime.CompilerServices;
+
+// ReSharper disable MethodHasAsyncOverloadWithCancellation
 
 
 namespace Lua;
 namespace Lua;
 
 
 public static class LuaStateExtensions
 public static class LuaStateExtensions
 {
 {
-    public static ValueTask<int> DoStringAsync(this LuaState state, string source, Memory<LuaValue> buffer, string? chunkName = null, CancellationToken cancellationToken = default)
+    public static async ValueTask<LuaClosure> LoadFileAsync(this LuaState state, string fileName, string mode, LuaTable? environment, CancellationToken cancellationToken)
     {
     {
-        return state.RootAccess.DoStringAsync(source, buffer, chunkName, cancellationToken);
+        var name = "@" + fileName;
+        using var stream = await state.GlobalState.Platform.FileSystem.Open(fileName, LuaFileOpenMode.Read, cancellationToken);
+        var source = await stream.ReadAllAsync(cancellationToken);
+        var closure = state.Load(source, name, environment);
+
+        return closure;
+    }
+
+    public static ValueTask<int> DoStringAsync(this LuaState state, string source, Memory<LuaValue> results, string? chunkName = null, CancellationToken cancellationToken = default)
+    {
+        var closure = state.Load(source, chunkName ?? source);
+        return ExecuteAsync(state, closure, results, cancellationToken);
     }
     }
 
 
     public static 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)
     {
     {
-        return state.RootAccess.DoStringAsync(source, chunkName, cancellationToken);
+        var closure = state.Load(source, chunkName ?? source);
+        return ExecuteAsync(state, closure, cancellationToken);
     }
     }
 
 
-    public static ValueTask<int> ExecuteAsync(this LuaState state, ReadOnlySpan<byte> source, Memory<LuaValue> buffer, string chunkName, CancellationToken cancellationToken = default)
+    public static ValueTask<int> ExecuteAsync(this LuaState state, ReadOnlySpan<byte> source, Memory<LuaValue> results, string chunkName, CancellationToken cancellationToken = default)
     {
     {
-        return state.RootAccess.ExecuteAsync(source, buffer, chunkName, cancellationToken);
+        var closure = state.Load(source, chunkName);
+        return ExecuteAsync(state, closure, results, cancellationToken);
     }
     }
 
 
     public static ValueTask<LuaValue[]> ExecuteAsync(this LuaState state, ReadOnlySpan<byte> source, string chunkName, CancellationToken cancellationToken = default)
     public static ValueTask<LuaValue[]> ExecuteAsync(this LuaState state, ReadOnlySpan<byte> source, string chunkName, CancellationToken cancellationToken = default)
     {
     {
-        return state.RootAccess.ExecuteAsync(source, chunkName, cancellationToken);
+        var closure = state.Load(source, chunkName);
+        return ExecuteAsync(state, closure, cancellationToken);
     }
     }
 
 
-    public static ValueTask<int> DoFileAsync(this LuaState state, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public static async ValueTask<int> DoFileAsync(this LuaState state, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
-        return state.RootAccess.DoFileAsync(path, buffer, cancellationToken);
+        var closure = await state.LoadFileAsync(path, "bt", null, cancellationToken);
+        var count = await state.RunAsync(closure, 0, cancellationToken);
+        using var results = state.ReadTopValues(count);
+        results.AsSpan()[..Math.Min(buffer.Length, results.Length)].CopyTo(buffer.Span);
+        return results.Count;
     }
     }
 
 
-    public static ValueTask<LuaValue[]> DoFileAsync(this LuaState state, string path, CancellationToken cancellationToken = default)
+    public static async ValueTask<LuaValue[]> DoFileAsync(this LuaState state, string path, CancellationToken cancellationToken = default)
     {
     {
-        return state.RootAccess.DoFileAsync(path, cancellationToken);
+        var closure = await state.LoadFileAsync(path, "bt", null, cancellationToken);
+        var count = await state.RunAsync(closure, 0, cancellationToken);
+        using var results = state.ReadTopValues(count);
+        return results.AsSpan().ToArray();
     }
     }
 
 
-    public static async ValueTask<LuaClosure> LoadFileAsync(this LuaState state, string fileName, string mode, LuaTable? environment, CancellationToken cancellationToken)
+    public static async ValueTask<int> ExecuteAsync(this LuaState state, LuaClosure closure, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
-        var name = "@" + fileName;
-        using var stream = await state.FileSystem.Open(fileName, LuaFileOpenMode.Read, cancellationToken);
-        var source = await stream.ReadAllAsync(cancellationToken);
-        var closure = state.Load(source, name, environment);
+        var count = await state.RunAsync(closure, 0, cancellationToken);
+        using var results = state.ReadTopValues(count);
+        results.AsSpan()[..Math.Min(buffer.Length, results.Length)].CopyTo(buffer.Span);
+        return results.Count;
+    }
 
 
-        return closure;
+    public static async ValueTask<LuaValue[]> ExecuteAsync(this LuaState state, LuaClosure closure, CancellationToken cancellationToken = default)
+    {
+        var count = await state.RunAsync(closure, 0, cancellationToken);
+        using var results = state.ReadTopValues(count);
+        return results.AsSpan().ToArray();
+    }
+
+    public static void Push(this LuaState state, LuaValue value)
+    {
+        state.Stack.Push(value);
+    }
+
+    public static void Push(this LuaState state, params ReadOnlySpan<LuaValue> span)
+    {
+        state.Stack.PushRange(span);
+    }
+
+    public static void Pop(this LuaState state, int count)
+    {
+        state.Stack.Pop(count);
+    }
+
+    public static LuaValue Pop(this LuaState state)
+    {
+        return state.Stack.Pop();
+    }
+
+    public static LuaTopValuesReader ReadTopValues(this LuaState state, int argumentCount)
+    {
+        var stack = state.Stack;
+        return new(stack, stack.Count - argumentCount);
+    }
+
+    public static ValueTask<LuaValue> Add(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
+        {
+            return new(numX + numY);
+        }
+
+        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(state, x, y, OpCode.Add, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Sub(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
+        {
+            return new(numX - numY);
+        }
+
+        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(state, x, y, OpCode.Sub, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Mul(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
+        {
+            return new(numX * numY);
+        }
+
+        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(state, x, y, OpCode.Mul, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Div(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
+        {
+            return new(numX / numY);
+        }
+
+        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(state, x, y, OpCode.Div, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Mod(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
+        {
+            return new(LuaVirtualMachine.Mod(numX, numY));
+        }
+
+        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(state, x, y, OpCode.Mod, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Pow(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
+        {
+            return new(Math.Pow(numX, numY));
+        }
+
+        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(state, x, y, OpCode.Pow, cancellationToken);
+    }
+
+
+    public static ValueTask<LuaValue> Unm(this LuaState state, LuaValue value, CancellationToken cancellationToken = default)
+    {
+        if (value.TryReadDouble(out var numB))
+        {
+            return new(-numB);
+        }
+
+        return LuaVirtualMachine.ExecuteUnaryOperationMetaMethod(state, value, OpCode.Unm, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Len(this LuaState state, LuaValue value, CancellationToken cancellationToken = default)
+    {
+        if (value.TryReadString(out var str))
+        {
+            return new(str.Length);
+        }
+
+        if (value.TryReadTable(out var table))
+        {
+            return new(table.ArrayLength);
+        }
+
+        return LuaVirtualMachine.ExecuteUnaryOperationMetaMethod(state, value, OpCode.Len, cancellationToken);
+    }
+
+
+    public static ValueTask<bool> LessThan(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadNumber(out var numX) && y.TryReadNumber(out var numY))
+        {
+            return new(numX < numY);
+        }
+
+        if (x.TryReadString(out var strX) && y.TryReadString(out var strY))
+        {
+            var c = StringComparer.Ordinal.Compare(strX, strY);
+            return new(c < 0);
+        }
+
+        return LuaVirtualMachine.ExecuteCompareOperationMetaMethod(state, x, y, OpCode.Lt, cancellationToken);
+    }
+
+    public static ValueTask<bool> LessThanOrEquals(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x.TryReadNumber(out var numX) && y.TryReadNumber(out var numY))
+        {
+            return new(numX <= numY);
+        }
+
+        if (x.TryReadString(out var strX) && y.TryReadString(out var strY))
+        {
+            var c = StringComparer.Ordinal.Compare(strX, strY);
+            return new(c <= 0);
+        }
+
+        return LuaVirtualMachine.ExecuteCompareOperationMetaMethod(state, x, y, OpCode.Le, cancellationToken);
+    }
+
+    public static ValueTask<bool> Equals(this LuaState state, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
+    {
+        if (x == y)
+        {
+            return new(true);
+        }
+
+        return LuaVirtualMachine.ExecuteCompareOperationMetaMethod(state, x, y, OpCode.Eq, cancellationToken);
+    }
+
+    public static async ValueTask<LuaValue> GetTable(this LuaState state, LuaValue table, LuaValue key, CancellationToken cancellationToken = default)
+    {
+        if (table.TryReadTable(out var luaTable))
+        {
+            if (luaTable.TryGetValue(key, out var value))
+            {
+                return value;
+            }
+        }
+
+        return await LuaVirtualMachine.ExecuteGetTableSlowPath(state, table, key, cancellationToken);
+    }
+
+    public static ValueTask SetTable(this LuaState state, LuaValue table, LuaValue key, LuaValue value, CancellationToken cancellationToken = default)
+    {
+        if (key.TryReadNumber(out var numB))
+        {
+            if (double.IsNaN(numB))
+            {
+                throw new LuaRuntimeException(state, "table index is NaN");
+            }
+        }
+
+
+        if (table.TryReadTable(out var luaTable))
+        {
+            ref var valueRef = ref luaTable.FindValue(key);
+            if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
+            {
+                valueRef = value;
+                return default;
+            }
+        }
+
+        return LuaVirtualMachine.ExecuteSetTableSlowPath(state, table, key, value, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Concat(this LuaState state, ReadOnlySpan<LuaValue> values, CancellationToken cancellationToken = default)
+    {
+        state.Stack.PushRange(values);
+        return Concat(state, values.Length, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue> Concat(this LuaState state, int concatCount, CancellationToken cancellationToken = default)
+    {
+        return LuaVirtualMachine.Concat(state, concatCount, cancellationToken);
+    }
+
+    public static ValueTask<int> Call(this LuaState state, int funcIndex, int returnBase, CancellationToken cancellationToken = default)
+    {
+        return LuaVirtualMachine.Call(state, funcIndex, returnBase, cancellationToken);
+    }
+
+    public static ValueTask<LuaValue[]> Call(this LuaState state, LuaValue function, ReadOnlySpan<LuaValue> arguments, CancellationToken cancellationToken = default)
+    {
+        var funcIndex = state.Stack.Count;
+        state.Stack.Push(function);
+        state.Stack.PushRange(arguments);
+        return Impl(state, funcIndex, cancellationToken);
+
+        static async ValueTask<LuaValue[]> Impl(LuaState state, int funcIndex, CancellationToken cancellationToken)
+        {
+            await LuaVirtualMachine.Call(state, funcIndex, funcIndex, cancellationToken);
+            var count = state.Stack.Count - funcIndex;
+            using var results = state.ReadTopValues(count);
+            return results.AsSpan().ToArray();
+        }
     }
     }
 }
 }

+ 0 - 234
src/Lua/LuaThread.cs

@@ -1,234 +0,0 @@
-using System.Runtime.CompilerServices;
-using Lua.Internal;
-using Lua.Runtime;
-
-namespace Lua;
-
-public abstract class LuaThread
-{
-    protected LuaThread() { }
-
-    public virtual LuaThreadStatus GetStatus()
-    {
-        return LuaThreadStatus.Running;
-    }
-
-    public virtual void UnsafeSetStatus(LuaThreadStatus status) { }
-
-    public virtual ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
-    {
-        return new(context.Return(false, "cannot resume non-suspended coroutine"));
-    }
-
-    public virtual ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
-    {
-        throw new LuaRuntimeException(context.Thread, "attempt to yield from outside a coroutine");
-    }
-
-    protected class ThreadCoreData : IPoolNode<ThreadCoreData>
-    {
-        //internal  LuaCoroutineData? coroutineData;
-        internal readonly LuaStack Stack = new();
-        internal FastStackCore<CallStackFrame> CallStack;
-
-        public void Clear()
-        {
-            Stack.Clear();
-            CallStack.Clear();
-        }
-
-        static LinkedPool<ThreadCoreData> pool;
-        ThreadCoreData? nextNode;
-
-        public ref ThreadCoreData? NextNode => ref nextNode;
-
-        public static ThreadCoreData Create()
-        {
-            if (!pool.TryPop(out var result))
-            {
-                result = new();
-            }
-
-            return result;
-        }
-
-        public void Release()
-        {
-            Clear();
-            pool.TryPush(this);
-        }
-    }
-
-    public LuaState State { get; protected set; } = null!;
-    protected ThreadCoreData? CoreData = new();
-    internal bool IsLineHookEnabled;
-    internal BitFlags2 CallOrReturnHookMask;
-    internal bool IsInHook;
-    internal long HookCount;
-    internal int BaseHookCount;
-    internal int LastPc;
-
-    internal int LastVersion;
-    internal int CurrentVersion;
-
-    internal ILuaTracebackBuildable? CurrentException;
-    internal readonly ReversedStack<CallStackFrame> ExceptionTrace = new();
-    internal LuaFunction? LastCallerFunction;
-
-    public bool IsRunning => CallStackFrameCount != 0;
-
-    internal LuaFunction? Hook { get; set; }
-
-    public LuaStack Stack => CoreData!.Stack;
-
-    internal bool IsCallHookEnabled
-    {
-        get => CallOrReturnHookMask.Flag0;
-        set => CallOrReturnHookMask.Flag0 = value;
-    }
-
-    internal bool IsReturnHookEnabled
-    {
-        get => CallOrReturnHookMask.Flag1;
-        set => CallOrReturnHookMask.Flag1 = value;
-    }
-
-    public int CallStackFrameCount => CoreData == null ? 0 : CoreData!.CallStack.Count;
-
-    internal LuaThreadAccess CurrentAccess => new(this, CurrentVersion);
-
-    public LuaThreadAccess RootAccess => new(this, 0);
-
-    public ref readonly CallStackFrame GetCurrentFrame()
-    {
-        return ref CoreData!.CallStack.PeekRef();
-    }
-
-    public ReadOnlySpan<LuaValue> GetStackValues()
-    {
-        return CoreData == null ? default : CoreData!.Stack.AsSpan();
-    }
-
-    public ReadOnlySpan<CallStackFrame> GetCallStackFrames()
-    {
-        return CoreData == null ? default : CoreData!.CallStack.AsSpan();
-    }
-
-    void UpdateCurrentVersion(ref FastStackCore<CallStackFrame> callStack)
-    {
-        CurrentVersion = callStack.Count == 0 ? 0 : callStack.PeekRef().Version;
-    }
-
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal LuaThreadAccess PushCallStackFrame(in CallStackFrame frame)
-    {
-        CurrentException?.BuildOrGet();
-        CurrentException = null;
-        ref var callStack = ref CoreData!.CallStack;
-        callStack.Push(frame);
-        callStack.PeekRef().Version = CurrentVersion = ++LastVersion;
-        return new(this, CurrentVersion);
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameWithStackPop()
-    {
-        var coreData = CoreData!;
-        ref var callStack = ref coreData.CallStack;
-        var popFrame = callStack.Pop();
-        UpdateCurrentVersion(ref callStack);
-        if (CurrentException != null)
-        {
-            ExceptionTrace.Push(popFrame);
-        }
-
-        coreData.Stack.PopUntil(popFrame.ReturnBase);
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameWithStackPop(int frameBase)
-    {
-        var coreData = CoreData!;
-        ref var callStack = ref coreData.CallStack;
-        var popFrame = callStack.Pop();
-        UpdateCurrentVersion(ref callStack);
-        if (CurrentException != null)
-        {
-            ExceptionTrace.Push(popFrame);
-        }
-
-        {
-            coreData.Stack.PopUntil(frameBase);
-        }
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrame()
-    {
-        var coreData = CoreData!;
-        ref var callStack = ref coreData.CallStack;
-        var popFrame = callStack.Pop();
-        UpdateCurrentVersion(ref callStack);
-        if (CurrentException != null)
-        {
-            ExceptionTrace.Push(popFrame);
-        }
-    }
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameUntil(int top)
-    {
-        var coreData = CoreData!;
-        ref var callStack = ref coreData.CallStack;
-        if (CurrentException != null)
-        {
-            ExceptionTrace.Push(callStack.AsSpan()[top..]);
-        }
-
-        callStack.PopUntil(top);
-        UpdateCurrentVersion(ref callStack);
-    }
-
-    public void SetHook(LuaFunction? hook, string mask, int count = 0)
-    {
-        if (hook is null)
-        {
-            HookCount = 0;
-            BaseHookCount = 0;
-            Hook = null;
-            IsLineHookEnabled = false;
-            IsCallHookEnabled = false;
-            IsReturnHookEnabled = false;
-            return;
-        }
-
-        HookCount = count > 0 ? count + 1 : 0;
-        BaseHookCount = count;
-
-        IsLineHookEnabled = mask.Contains('l');
-        IsCallHookEnabled = mask.Contains('c');
-        IsReturnHookEnabled = mask.Contains('r');
-
-        if (IsLineHookEnabled)
-        {
-            LastPc = CallStackFrameCount > 0 ? GetCurrentFrame().CallerInstructionIndex : -1;
-        }
-
-        Hook = hook;
-    }
-
-    internal void DumpStackValues()
-    {
-        var span = GetStackValues();
-        for (var i = 0; i < span.Length; i++)
-        {
-            Console.WriteLine($"LuaStack [{i}]\t{span[i]}");
-        }
-    }
-
-    public Traceback GetTraceback()
-    {
-        return new(State, GetCallStackFrames());
-    }
-}

+ 4 - 13
src/Lua/LuaThreadExtensions.cs

@@ -4,28 +4,19 @@ namespace Lua;
 
 
 public static class LuaThreadExtensions
 public static class LuaThreadExtensions
 {
 {
-    public static UserThreadLease RentUserThread(this LuaThread thread)
-    {
-        return new(LuaUserThread.Create(thread));
-    }
-
-    public static CoroutineLease RentCoroutine(this LuaThread thread, LuaFunction function, bool isProtectedMode = false)
-    {
-        return new(LuaCoroutine.Create(thread, function, isProtectedMode));
-    }
 
 
-    internal static void ThrowIfCancellationRequested(this LuaThread thread, CancellationToken cancellationToken)
+    internal static void ThrowIfCancellationRequested(this LuaState state, CancellationToken cancellationToken)
     {
     {
         if (cancellationToken.IsCancellationRequested)
         if (cancellationToken.IsCancellationRequested)
         {
         {
-            Throw(thread, cancellationToken);
+            Throw(state, cancellationToken);
         }
         }
 
 
         return;
         return;
 
 
-        static void Throw(LuaThread thread, CancellationToken cancellationToken)
+        static void Throw(LuaState state, CancellationToken cancellationToken)
         {
         {
-            throw new LuaCanceledException(thread, cancellationToken);
+            throw new LuaCanceledException(state, cancellationToken);
         }
         }
     }
     }
 }
 }

+ 0 - 36
src/Lua/LuaUserThread.cs

@@ -1,36 +0,0 @@
-using Lua.Internal;
-
-namespace Lua;
-
-public sealed class LuaUserThread : LuaThread, IPoolNode<LuaUserThread>
-{
-    static LinkedPool<LuaUserThread> pool;
-    LuaUserThread? nextNode;
-
-    ref LuaUserThread? IPoolNode<LuaUserThread>.NextNode => ref nextNode;
-
-    public static LuaUserThread Create(LuaThread parent)
-    {
-        if (!pool.TryPop(out var result))
-        {
-            result = new();
-        }
-
-        result.State = parent.State;
-        result.CoreData = ThreadCoreData.Create();
-
-        return result;
-    }
-
-    public void Release()
-    {
-        if (CoreData!.CallStack.Count != 0)
-        {
-            throw new InvalidOperationException("This thread is running! Call stack is not empty!!");
-        }
-
-        CoreData.Release();
-        CoreData = null!;
-        pool.TryPush(this);
-    }
-}

+ 9 - 9
src/Lua/LuaValue.cs

@@ -139,7 +139,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
                     break;
                     break;
                 }
                 }
             case LuaValueType.Thread:
             case LuaValueType.Thread:
-                if (t == typeof(LuaThread))
+                if (t == typeof(LuaState))
                 {
                 {
                     var v = referenceValue!;
                     var v = referenceValue!;
                     result = Unsafe.As<object, T>(ref v);
                     result = Unsafe.As<object, T>(ref v);
@@ -440,7 +440,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             string stringValue => stringValue,
             string stringValue => stringValue,
             LuaFunction luaFunction => luaFunction,
             LuaFunction luaFunction => luaFunction,
             LuaTable luaTable => luaTable,
             LuaTable luaTable => luaTable,
-            LuaThread luaThread => luaThread,
+            LuaState luaThread => luaThread,
             ILuaUserData userData => FromUserData(userData),
             ILuaUserData userData => FromUserData(userData),
             int intValue => intValue,
             int intValue => intValue,
             long longValue => longValue,
             long longValue => longValue,
@@ -502,7 +502,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
     }
     }
 
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public LuaValue(LuaThread value)
+    public LuaValue(LuaState value)
     {
     {
         Type = LuaValueType.Thread;
         Type = LuaValueType.Thread;
         referenceValue = value;
         referenceValue = value;
@@ -546,7 +546,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
     }
     }
 
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static implicit operator LuaValue(LuaThread value)
+    public static implicit operator LuaValue(LuaState value)
     {
     {
         return new(value);
         return new(value);
     }
     }
@@ -674,7 +674,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             result = LuaValueType.Table;
             result = LuaValueType.Table;
             return true;
             return true;
         }
         }
-        else if (type == typeof(LuaThread))
+        else if (type == typeof(LuaState))
         {
         {
             result = LuaValueType.Thread;
             result = LuaValueType.Thread;
             return true;
             return true;
@@ -691,16 +691,16 @@ public readonly struct LuaValue : IEquatable<LuaValue>
 
 
     internal ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     internal ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        if (this.TryGetMetamethod(context.State, Metamethods.ToString, out var metamethod))
+        if (this.TryGetMetamethod(context.GlobalState, Metamethods.ToString, out var metamethod))
         {
         {
-            var stack = context.Thread.Stack;
+            var stack = context.State.Stack;
             stack.Push(metamethod);
             stack.Push(metamethod);
             stack.Push(this);
             stack.Push(this);
-            return LuaVirtualMachine.Call(context.Thread, stack.Count - 2, stack.Count - 2, cancellationToken);
+            return LuaVirtualMachine.Call(context.State, stack.Count - 2, stack.Count - 2, cancellationToken);
         }
         }
         else
         else
         {
         {
-            context.Thread.Stack.Push(ToString());
+            context.State.Stack.Push(ToString());
             return default;
             return default;
         }
         }
     }
     }

+ 8 - 12
src/Lua/Platforms/LuaPlatform.cs

@@ -6,23 +6,19 @@ namespace Lua.Platforms;
 /// <summary>
 /// <summary>
 ///  Platform abstraction for Lua.
 ///  Platform abstraction for Lua.
 /// </summary>
 /// </summary>
-/// <param name="fileSystem"></param>
-/// <param name="osEnvironment"></param>
-/// <param name="standardIO"></param>
-public sealed class LuaPlatform(ILuaFileSystem fileSystem, ILuaOsEnvironment osEnvironment, ILuaStandardIO standardIO, TimeProvider timeProvider)
+/// <param name="FileSystem"></param>
+/// <param name="OsEnvironment"></param>
+/// <param name="StandardIO"></param>
+public record LuaPlatform(ILuaFileSystem FileSystem, ILuaOsEnvironment OsEnvironment, ILuaStandardIO StandardIO, TimeProvider TimeProvider)
 {
 {
     /// <summary>
     /// <summary>
     /// Standard console platform implementation.
     /// Standard console platform implementation.
     /// Uses real file system, console I/O, and system operations.
     /// Uses real file system, console I/O, and system operations.
     /// </summary>
     /// </summary>
     public static LuaPlatform Default =>
     public static LuaPlatform Default =>
-        new(fileSystem: new FileSystem(),
-            osEnvironment: new SystemOsEnvironment(),
-            standardIO: new ConsoleStandardIO(),
-            timeProvider: TimeProvider.System);
+        new(FileSystem: new FileSystem(),
+            OsEnvironment: new SystemOsEnvironment(),
+            StandardIO: new ConsoleStandardIO(),
+            TimeProvider: TimeProvider.System);
 
 
-    public ILuaFileSystem FileSystem { get; set; } = fileSystem;
-    public ILuaOsEnvironment OsEnvironment { get; set; } = osEnvironment;
-    public ILuaStandardIO StandardIO { get; set; } = standardIO;
-    public TimeProvider TimeProvider { get; set; } = timeProvider;
 }
 }

+ 0 - 21
src/Lua/Runtime/Lease.cs

@@ -1,21 +0,0 @@
-namespace Lua.Runtime;
-
-public readonly struct UserThreadLease(LuaUserThread thread) : IDisposable
-{
-    public LuaUserThread Thread { get; } = thread;
-
-    public void Dispose()
-    {
-        Thread.Release();
-    }
-}
-
-public readonly struct CoroutineLease(LuaCoroutine thread) : IDisposable
-{
-    public LuaCoroutine Thread { get; } = thread;
-
-    public void Dispose()
-    {
-        Thread.Release();
-    }
-}

+ 10 - 10
src/Lua/Runtime/LuaClosure.cs

@@ -8,8 +8,8 @@ public sealed class LuaClosure : LuaFunction
 {
 {
     FastListCore<UpValue> upValues;
     FastListCore<UpValue> upValues;
 
 
-    public LuaClosure(LuaThread thread, Prototype proto, LuaTable? environment = null)
-        : base(proto.ChunkName, static (context, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.Thread, ct))
+    public LuaClosure(LuaState state, Prototype proto, LuaTable? environment = null)
+        : base(proto.ChunkName, static (context, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, ct))
     {
     {
         Proto = proto;
         Proto = proto;
         if (environment != null)
         if (environment != null)
@@ -18,19 +18,19 @@ public sealed class LuaClosure : LuaFunction
             return;
             return;
         }
         }
 
 
-        if (thread.CallStackFrameCount == 0)
+        if (state.CallStackFrameCount == 0)
         {
         {
-            upValues.Add(thread.State.EnvUpValue);
+            upValues.Add(state.GlobalState.EnvUpValue);
             return;
             return;
         }
         }
 
 
-        var baseIndex = thread.GetCallStackFrames()[^1].Base;
+        var baseIndex = state.GetCallStackFrames()[^1].Base;
 
 
         // add upvalues
         // add upvalues
         for (var i = 0; i < proto.UpValues.Length; i++)
         for (var i = 0; i < proto.UpValues.Length; i++)
         {
         {
             var description = proto.UpValues[i];
             var description = proto.UpValues[i];
-            var upValue = GetUpValueFromDescription(thread.State, thread, description, baseIndex);
+            var upValue = GetUpValueFromDescription(state.GlobalState, state, description, baseIndex);
             upValues.Add(upValue);
             upValues.Add(upValue);
         }
         }
     }
     }
@@ -62,20 +62,20 @@ public sealed class LuaClosure : LuaFunction
         upValues[index].SetValue(value);
         upValues[index].SetValue(value);
     }
     }
 
 
-    static UpValue GetUpValueFromDescription(LuaState state, LuaThread thread, UpValueDesc description, int baseIndex = 0)
+    static UpValue GetUpValueFromDescription(LuaGlobalState globalState, LuaState state, UpValueDesc description, int baseIndex = 0)
     {
     {
         if (description.IsLocal)
         if (description.IsLocal)
         {
         {
             if (description is { Index: 0, Name: "_ENV" })
             if (description is { Index: 0, Name: "_ENV" })
             {
             {
-                return state.EnvUpValue;
+                return globalState.EnvUpValue;
             }
             }
 
 
-            return state.GetOrAddUpValue(thread, baseIndex + description.Index);
+            return state.GetOrAddUpValue(baseIndex + description.Index);
         }
         }
 
 
 
 
-        if (thread.GetCurrentFrame().Function is LuaClosure parentClosure)
+        if (state.GetCurrentFrame().Function is LuaClosure parentClosure)
         {
         {
             return parentClosure.UpValues[description.Index];
             return parentClosure.UpValues[description.Index];
         }
         }

+ 0 - 148
src/Lua/Runtime/LuaThreadAccess.cs

@@ -1,148 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace Lua.Runtime;
-
-public readonly struct LuaThreadAccess
-{
-    internal LuaThreadAccess(LuaThread thread, int version)
-    {
-        Thread = thread;
-        Version = version;
-    }
-
-    public readonly LuaThread Thread;
-    public readonly int Version;
-
-    public bool IsValid => Version == Thread.CurrentVersion;
-
-    public LuaState State
-    {
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        get
-        {
-            ThrowIfInvalid();
-            return Thread.State;
-        }
-    }
-
-    public LuaStack Stack
-    {
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        get
-        {
-            ThrowIfInvalid();
-            return Thread.Stack;
-        }
-    }
-
-    public ValueTask<int> RunAsync(LuaFunction function, CancellationToken cancellationToken = default)
-    {
-        return RunAsync(function, 0, Thread.Stack.Count, cancellationToken);
-    }
-
-    public ValueTask<int> RunAsync(LuaFunction function, int argumentCount, CancellationToken cancellationToken = default)
-    {
-        return RunAsync(function, argumentCount, Thread.Stack.Count - argumentCount, cancellationToken);
-    }
-
-    public async ValueTask<int> RunAsync(LuaFunction function, int argumentCount, int returnBase, CancellationToken cancellationToken = default)
-    {
-        ThrowIfInvalid();
-        if (function == null)
-        {
-            throw new ArgumentNullException(nameof(function));
-        }
-
-        Thread.ThrowIfCancellationRequested(cancellationToken);
-        var thread = Thread;
-        var varArgumentCount = function.GetVariableArgumentCount(argumentCount);
-        if (varArgumentCount != 0)
-        {
-            if (varArgumentCount < 0)
-            {
-                thread.Stack.SetTop(thread.Stack.Count - varArgumentCount);
-                varArgumentCount = 0;
-            }
-            else
-            {
-                LuaVirtualMachine.PrepareVariableArgument(thread.Stack, argumentCount, varArgumentCount);
-            }
-
-            argumentCount -= varArgumentCount;
-        }
-
-        CallStackFrame frame = new() { Base = thread.Stack.Count - argumentCount, VariableArgumentCount = varArgumentCount, Function = function, ReturnBase = returnBase };
-        if (thread.IsInHook)
-        {
-            frame.Flags |= CallStackFrameFlags.InHook;
-        }
-
-        var access = thread.PushCallStackFrame(frame);
-        LuaFunctionExecutionContext context = new() { Access = access, ArgumentCount = argumentCount, ReturnFrameBase = returnBase };
-        var callStackTop = thread.CallStackFrameCount;
-        try
-        {
-            if (Thread.CallOrReturnHookMask.Value != 0 && !Thread.IsInHook)
-            {
-                return await LuaVirtualMachine.ExecuteCallHook(context, cancellationToken);
-            }
-
-            return await function.Func(context, cancellationToken);
-        }
-        finally
-        {
-            Thread.PopCallStackFrameUntil(callStackTop - 1);
-        }
-    }
-
-
-    internal CallStackFrame CreateCallStackFrame(LuaFunction function, int argumentCount, int returnBase, int callerInstructionIndex)
-    {
-        var thread = Thread;
-        var varArgumentCount = function.GetVariableArgumentCount(argumentCount);
-        if (varArgumentCount != 0)
-        {
-            if (varArgumentCount < 0)
-            {
-                thread.Stack.SetTop(thread.Stack.Count - varArgumentCount);
-                argumentCount -= varArgumentCount;
-                varArgumentCount = 0;
-            }
-            else
-            {
-                LuaVirtualMachine.PrepareVariableArgument(thread.Stack, argumentCount, varArgumentCount);
-            }
-        }
-
-        CallStackFrame frame = new()
-        {
-            Base = thread.Stack.Count - argumentCount,
-            VariableArgumentCount = varArgumentCount,
-            Function = function,
-            ReturnBase = returnBase,
-            CallerInstructionIndex = callerInstructionIndex
-        };
-
-        if (thread.IsInHook)
-        {
-            frame.Flags |= CallStackFrameFlags.InHook;
-        }
-
-        return frame;
-    }
-
-
-    [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void ThrowIfInvalid()
-    {
-        if (Version != Thread.CurrentVersion)
-        {
-            ThrowInvalid();
-        }
-    }
-
-    void ThrowInvalid()
-    {
-        throw new InvalidOperationException("Thread access is invalid.");
-    }
-}

+ 0 - 320
src/Lua/Runtime/LuaThreadAccessExtensions.cs

@@ -1,320 +0,0 @@
-using System.Runtime.CompilerServices;
-
-// ReSharper disable MethodHasAsyncOverloadWithCancellation
-
-namespace Lua.Runtime;
-
-public static class LuaThreadAccessAccessExtensions
-{
-    public static ValueTask<int> DoStringAsync(this LuaThreadAccess access, string source, Memory<LuaValue> results, string? chunkName = null, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var closure = access.State.Load(source, chunkName ?? source);
-        return ExecuteAsync(access, closure, results, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue[]> DoStringAsync(this LuaThreadAccess access, string source, string? chunkName = null, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var closure = access.State.Load(source, chunkName ?? source);
-        return ExecuteAsync(access, closure, cancellationToken);
-    }
-
-    public static ValueTask<int> ExecuteAsync(this LuaThreadAccess access, ReadOnlySpan<byte> source, Memory<LuaValue> results, string chunkName, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var closure = access.State.Load(source, chunkName);
-        return ExecuteAsync(access, closure, results, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue[]> ExecuteAsync(this LuaThreadAccess access, ReadOnlySpan<byte> source, string chunkName, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var closure = access.State.Load(source, chunkName);
-        return ExecuteAsync(access, closure, cancellationToken);
-    }
-
-    public static async ValueTask<int> DoFileAsync(this LuaThreadAccess access, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var closure = await access.State.LoadFileAsync(path, "bt", null, cancellationToken);
-        var count = await access.RunAsync(closure, 0, cancellationToken);
-        using var results = access.ReadTopValues(count);
-        results.AsSpan()[..Math.Min(buffer.Length, results.Length)].CopyTo(buffer.Span);
-        return results.Count;
-    }
-
-    public static async ValueTask<LuaValue[]> DoFileAsync(this LuaThreadAccess access, string path, CancellationToken cancellationToken = default)
-    {
-        var closure = await access.State.LoadFileAsync(path, "bt", null, cancellationToken);
-        var count = await access.RunAsync(closure, 0, cancellationToken);
-        using var results = access.ReadTopValues(count);
-        return results.AsSpan().ToArray();
-    }
-
-    public static async ValueTask<int> ExecuteAsync(this LuaThreadAccess access, LuaClosure closure, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var count = await access.RunAsync(closure, 0, cancellationToken);
-        using var results = access.ReadTopValues(count);
-        results.AsSpan()[..Math.Min(buffer.Length, results.Length)].CopyTo(buffer.Span);
-        return results.Count;
-    }
-
-    public static async ValueTask<LuaValue[]> ExecuteAsync(this LuaThreadAccess access, LuaClosure closure, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var count = await access.RunAsync(closure, 0, cancellationToken);
-        using var results = access.ReadTopValues(count);
-        return results.AsSpan().ToArray();
-    }
-
-    public static void Push(this LuaThreadAccess access, LuaValue value)
-    {
-        access.ThrowIfInvalid();
-        access.Stack.Push(value);
-    }
-
-    public static void Push(this LuaThreadAccess access, params ReadOnlySpan<LuaValue> span)
-    {
-        access.ThrowIfInvalid();
-        access.Stack.PushRange(span);
-    }
-
-    public static void Pop(this LuaThreadAccess access, int count)
-    {
-        access.ThrowIfInvalid();
-        access.Stack.Pop(count);
-    }
-
-    public static LuaValue Pop(this LuaThreadAccess access)
-    {
-        access.ThrowIfInvalid();
-        return access.Stack.Pop();
-    }
-
-    public static LuaTopValuesReader ReadTopValues(this LuaThreadAccess access, int argumentCount)
-    {
-        access.ThrowIfInvalid();
-        var stack = access.Stack;
-        return new(stack, stack.Count - argumentCount);
-    }
-
-    public static ValueTask<LuaValue> Add(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
-        {
-            return new(numX + numY);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(access.Thread, x, y, OpCode.Add, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Sub(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
-        {
-            return new(numX - numY);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(access.Thread, x, y, OpCode.Sub, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Mul(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
-        {
-            return new(numX * numY);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(access.Thread, x, y, OpCode.Mul, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Div(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
-        {
-            return new(numX / numY);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(access.Thread, x, y, OpCode.Div, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Mod(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
-        {
-            return new(LuaVirtualMachine.Mod(numX, numY));
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(access.Thread, x, y, OpCode.Mod, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Pow(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadDouble(out var numX) && y.TryReadDouble(out var numY))
-        {
-            return new(Math.Pow(numX, numY));
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteBinaryOperationMetaMethod(access.Thread, x, y, OpCode.Pow, cancellationToken);
-    }
-
-
-    public static ValueTask<LuaValue> Unm(this LuaThreadAccess access, LuaValue value, CancellationToken cancellationToken = default)
-    {
-        if (value.TryReadDouble(out var numB))
-        {
-            return new(-numB);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteUnaryOperationMetaMethod(access.Thread, value, OpCode.Unm, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Len(this LuaThreadAccess access, LuaValue value, CancellationToken cancellationToken = default)
-    {
-        if (value.TryReadString(out var str))
-        {
-            return new(str.Length);
-        }
-
-        if (value.TryReadTable(out var table))
-        {
-            return new(table.ArrayLength);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteUnaryOperationMetaMethod(access.Thread, value, OpCode.Len, cancellationToken);
-    }
-
-
-    public static ValueTask<bool> LessThan(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadNumber(out var numX) && y.TryReadNumber(out var numY))
-        {
-            return new(numX < numY);
-        }
-
-        if (x.TryReadString(out var strX) && y.TryReadString(out var strY))
-        {
-            var c = StringComparer.Ordinal.Compare(strX, strY);
-            return new(c < 0);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteCompareOperationMetaMethod(access.Thread, x, y, OpCode.Lt, cancellationToken);
-    }
-
-    public static ValueTask<bool> LessThanOrEquals(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x.TryReadNumber(out var numX) && y.TryReadNumber(out var numY))
-        {
-            return new(numX <= numY);
-        }
-
-        if (x.TryReadString(out var strX) && y.TryReadString(out var strY))
-        {
-            var c = StringComparer.Ordinal.Compare(strX, strY);
-            return new(c <= 0);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteCompareOperationMetaMethod(access.Thread, x, y, OpCode.Le, cancellationToken);
-    }
-
-    public static ValueTask<bool> Equals(this LuaThreadAccess access, LuaValue x, LuaValue y, CancellationToken cancellationToken = default)
-    {
-        if (x == y)
-        {
-            return new(true);
-        }
-
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.ExecuteCompareOperationMetaMethod(access.Thread, x, y, OpCode.Eq, cancellationToken);
-    }
-
-
-    public static async ValueTask<LuaValue> GetTable(this LuaThreadAccess access, LuaValue table, LuaValue key, CancellationToken cancellationToken = default)
-    {
-        if (table.TryReadTable(out var luaTable))
-        {
-            if (luaTable.TryGetValue(key, out var value))
-            {
-                return value;
-            }
-        }
-
-        access.ThrowIfInvalid();
-        return await LuaVirtualMachine.ExecuteGetTableSlowPath(access.Thread, table, key, cancellationToken);
-    }
-
-    public static ValueTask SetTable(this LuaThreadAccess access, LuaValue table, LuaValue key, LuaValue value, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-
-        if (key.TryReadNumber(out var numB))
-        {
-            if (double.IsNaN(numB))
-            {
-                throw new LuaRuntimeException(access.Thread, "table index is NaN");
-            }
-        }
-
-
-        if (table.TryReadTable(out var luaTable))
-        {
-            ref var valueRef = ref luaTable.FindValue(key);
-            if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
-            {
-                valueRef = value;
-                return default;
-            }
-        }
-
-        return LuaVirtualMachine.ExecuteSetTableSlowPath(access.Thread, table, key, value, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Concat(this LuaThreadAccess access, ReadOnlySpan<LuaValue> values, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        access.Stack.PushRange(values);
-        return Concat(access, values.Length, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue> Concat(this LuaThreadAccess access, int concatCount, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.Concat(access.Thread, concatCount, cancellationToken);
-    }
-
-    public static ValueTask<int> Call(this LuaThreadAccess access, int funcIndex, int returnBase, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        return LuaVirtualMachine.Call(access.Thread, funcIndex, returnBase, cancellationToken);
-    }
-
-    public static ValueTask<LuaValue[]> Call(this LuaThreadAccess access, LuaValue function, ReadOnlySpan<LuaValue> arguments, CancellationToken cancellationToken = default)
-    {
-        access.ThrowIfInvalid();
-        var thread = access.Thread;
-        var funcIndex = thread.Stack.Count;
-        thread.Stack.Push(function);
-        thread.Stack.PushRange(arguments);
-        return Impl(access, funcIndex, cancellationToken);
-
-        static async ValueTask<LuaValue[]> Impl(LuaThreadAccess access, int funcIndex, CancellationToken cancellationToken)
-        {
-            await LuaVirtualMachine.Call(access.Thread, funcIndex, funcIndex, cancellationToken);
-            var count = access.Stack.Count - funcIndex;
-            using var results = access.ReadTopValues(count);
-            return results.AsSpan().ToArray();
-        }
-    }
-}

+ 2 - 2
src/Lua/Runtime/LuaValueRuntimeExtensions.cs

@@ -5,10 +5,10 @@ namespace Lua.Runtime;
 static class LuaRuntimeExtensions
 static class LuaRuntimeExtensions
 {
 {
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public static bool TryGetMetamethod(this LuaValue value, LuaState state, string methodName, out LuaValue result)
+    public static bool TryGetMetamethod(this LuaValue value, LuaGlobalState globalState, string methodName, out LuaValue result)
     {
     {
         result = default;
         result = default;
-        return state.TryGetMetatable(value, out var metatable) &&
+        return globalState.TryGetMetatable(value, out var metatable) &&
                metatable.TryGetValue(methodName, out result);
                metatable.TryGetValue(methodName, out result);
     }
     }
 
 

+ 44 - 44
src/Lua/Runtime/LuaVirtualMachine.Debug.cs

@@ -12,7 +12,7 @@ public static partial class LuaVirtualMachine
         {
         {
             if (r.Result == 0)
             if (r.Result == 0)
             {
             {
-                context.Thread.PopCallStackFrameWithStackPop();
+                context.State.PopCallStackFrameWithStackPop();
             }
             }
 
 
             return false;
             return false;
@@ -27,63 +27,63 @@ public static partial class LuaVirtualMachine
             var countHookIsDone = false;
             var countHookIsDone = false;
             var pc = context.Pc;
             var pc = context.Pc;
             var prototype = context.Prototype;
             var prototype = context.Prototype;
-            if (context.Thread.HookCount == 0)
+            if (context.State.HookCount == 0)
             {
             {
-                context.Thread.HookCount = context.Thread.BaseHookCount + 1;
+                context.State.HookCount = context.State.BaseHookCount + 1;
 
 
-                var hook = context.Thread.Hook!;
+                var hook = context.State.Hook!;
 
 
-                var stack = context.Thread.Stack;
+                var stack = context.State.Stack;
                 var top = stack.Count;
                 var top = stack.Count;
                 stack.Push("count");
                 stack.Push("count");
                 stack.Push(LuaValue.Nil);
                 stack.Push(LuaValue.Nil);
-                context.Thread.IsInHook = true;
-                var frame = context.Thread.CurrentAccess.CreateCallStackFrame(hook, 2, top, pc);
-                var access = context.Thread.PushCallStackFrame(frame);
-                LuaFunctionExecutionContext funcContext = new() { Access = access, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
+                context.State.IsInHook = true;
+                var frame = context.State.CreateCallStackFrame(hook, 2, top, pc);
+                context.State.PushCallStackFrame(frame);
+                LuaFunctionExecutionContext funcContext = new() { State = context.State, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
                 await hook.Func(funcContext, context.CancellationToken);
                 await hook.Func(funcContext, context.CancellationToken);
-                context.Thread.IsInHook = false;
+                context.State.IsInHook = false;
 
 
                 countHookIsDone = true;
                 countHookIsDone = true;
             }
             }
 
 
             context.ThrowIfCancellationRequested();
             context.ThrowIfCancellationRequested();
-            if (context.Thread.IsLineHookEnabled)
+            if (context.State.IsLineHookEnabled)
             {
             {
                 var sourcePositions = prototype.LineInfo;
                 var sourcePositions = prototype.LineInfo;
                 var line = sourcePositions[pc];
                 var line = sourcePositions[pc];
 
 
-                if (countHookIsDone || pc == 0 || context.Thread.LastPc < 0 || pc <= context.Thread.LastPc || sourcePositions[context.Thread.LastPc] != line)
+                if (countHookIsDone || pc == 0 || context.State.LastPc < 0 || pc <= context.State.LastPc || sourcePositions[context.State.LastPc] != line)
                 {
                 {
                     if (countHookIsDone)
                     if (countHookIsDone)
                     {
                     {
-                        context.Thread.PopCallStackFrameWithStackPop();
+                        context.State.PopCallStackFrameWithStackPop();
                     }
                     }
 
 
 
 
-                    var hook = context.Thread.Hook!;
-                    var stack = context.Thread.Stack;
+                    var hook = context.State.Hook!;
+                    var stack = context.State.Stack;
                     var top = stack.Count;
                     var top = stack.Count;
                     stack.Push("line");
                     stack.Push("line");
                     stack.Push(line);
                     stack.Push(line);
-                    context.Thread.IsInHook = true;
-                    var frame = context.Thread.CurrentAccess.CreateCallStackFrame(hook, 2, top, pc);
-                    var access = context.Thread.PushCallStackFrame(frame);
-                    LuaFunctionExecutionContext funcContext = new() { Access = access, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
+                    context.State.IsInHook = true;
+                    var frame = context.State.CreateCallStackFrame(hook, 2, top, pc);
+                    context.State.PushCallStackFrame(frame);
+                    LuaFunctionExecutionContext funcContext = new() { State = context.State, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
                     try
                     try
                     {
                     {
                         await hook.Func(funcContext, context.CancellationToken);
                         await hook.Func(funcContext, context.CancellationToken);
                     }
                     }
                     finally
                     finally
                     {
                     {
-                        context.Thread.IsInHook = false;
+                        context.State.IsInHook = false;
                     }
                     }
 
 
-                    context.Thread.LastPc = pc;
+                    context.State.LastPc = pc;
                     return 0;
                     return 0;
                 }
                 }
 
 
-                context.Thread.LastPc = pc;
+                context.State.LastPc = pc;
             }
             }
 
 
             if (countHookIsDone)
             if (countHookIsDone)
@@ -98,63 +98,63 @@ public static partial class LuaVirtualMachine
     [MethodImpl(MethodImplOptions.NoInlining)]
     [MethodImpl(MethodImplOptions.NoInlining)]
     static ValueTask<int> ExecuteCallHook(VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments, bool isTailCall = false)
     static ValueTask<int> ExecuteCallHook(VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments, bool isTailCall = false)
     {
     {
-        return ExecuteCallHook(new() { Access = context.Thread.CurrentAccess, ArgumentCount = arguments, ReturnFrameBase = frame.ReturnBase }, context.CancellationToken, isTailCall);
+        return ExecuteCallHook(new() { State = context.State, ArgumentCount = arguments, ReturnFrameBase = frame.ReturnBase }, context.CancellationToken, isTailCall);
     }
     }
 
 
     internal static async ValueTask<int> ExecuteCallHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken, bool isTailCall = false)
     internal static async ValueTask<int> ExecuteCallHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken, bool isTailCall = false)
     {
     {
         var argCount = context.ArgumentCount;
         var argCount = context.ArgumentCount;
-        var hook = context.Thread.Hook!;
-        var stack = context.Thread.Stack;
-        if (context.Thread.IsCallHookEnabled)
+        var hook = context.State.Hook!;
+        var stack = context.State.Stack;
+        if (context.State.IsCallHookEnabled)
         {
         {
             var top = stack.Count;
             var top = stack.Count;
             stack.Push(isTailCall ? "tail call" : "call");
             stack.Push(isTailCall ? "tail call" : "call");
 
 
             stack.Push(LuaValue.Nil);
             stack.Push(LuaValue.Nil);
-            context.Thread.IsInHook = true;
-            var frame = context.Thread.CurrentAccess.CreateCallStackFrame(hook, 2, top, 0);
-            var access = context.Thread.PushCallStackFrame(frame);
-            LuaFunctionExecutionContext funcContext = new() { Access = access, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
+            context.State.IsInHook = true;
+            var frame = context.State.CreateCallStackFrame(hook, 2, top, 0);
+            context.State.PushCallStackFrame(frame);
+            LuaFunctionExecutionContext funcContext = new() { State = context.State, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
             try
             try
             {
             {
                 await hook.Func(funcContext, cancellationToken);
                 await hook.Func(funcContext, cancellationToken);
             }
             }
             finally
             finally
             {
             {
-                context.Thread.IsInHook = false;
-                context.Thread.PopCallStackFrameWithStackPop();
+                context.State.IsInHook = false;
+                context.State.PopCallStackFrameWithStackPop();
             }
             }
         }
         }
 
 
-        context.Thread.ThrowIfCancellationRequested(cancellationToken);
+        context.State.ThrowIfCancellationRequested(cancellationToken);
 
 
         {
         {
-            var frame = context.Thread.GetCurrentFrame();
-            var task = frame.Function.Func(new() { Access = context.Thread.CurrentAccess, ArgumentCount = argCount, ReturnFrameBase = frame.ReturnBase }, cancellationToken);
+            var frame = context.State.GetCurrentFrame();
+            var task = frame.Function.Func(new() { State = context.State, ArgumentCount = argCount, ReturnFrameBase = frame.ReturnBase }, cancellationToken);
             var r = await task;
             var r = await task;
-            if (isTailCall || !context.Thread.IsReturnHookEnabled)
+            if (isTailCall || !context.State.IsReturnHookEnabled)
             {
             {
                 return r;
                 return r;
             }
             }
 
 
-            context.Thread.ThrowIfCancellationRequested(cancellationToken);
+            context.State.ThrowIfCancellationRequested(cancellationToken);
             var top = stack.Count;
             var top = stack.Count;
             stack.Push("return");
             stack.Push("return");
             stack.Push(LuaValue.Nil);
             stack.Push(LuaValue.Nil);
-            context.Thread.IsInHook = true;
-            frame = context.Thread.CurrentAccess.CreateCallStackFrame(hook, 2, top, 0);
-            var access = context.Thread.PushCallStackFrame(frame);
-            LuaFunctionExecutionContext funcContext = new() { Access = access, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
+            context.State.IsInHook = true;
+            frame = context.State.CreateCallStackFrame(hook, 2, top, 0);
+            context.State.PushCallStackFrame(frame);
+            LuaFunctionExecutionContext funcContext = new() { State = context.State, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
             try
             try
             {
             {
-                context.Thread.IsInHook = true;
+                context.State.IsInHook = true;
                 await hook.Func(funcContext, cancellationToken);
                 await hook.Func(funcContext, cancellationToken);
             }
             }
             finally
             finally
             {
             {
-                context.Thread.IsInHook = false;
-                context.Thread.PopCallStackFrameWithStackPop();
+                context.State.IsInHook = false;
+                context.State.PopCallStackFrameWithStackPop();
             }
             }
 
 
             return r;
             return r;

File diff suppressed because it is too large
+ 159 - 160
src/Lua/Runtime/LuaVirtualMachine.cs


+ 8 - 6
src/Lua/Runtime/Tracebacks.cs

@@ -5,6 +5,8 @@ namespace Lua.Runtime;
 
 
 public class Traceback(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames)
 public class Traceback(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames)
 {
 {
+    internal LuaGlobalState GlobalState => state.GlobalState;
+
     public LuaState State => state;
     public LuaState State => state;
 
 
     public LuaFunction RootFunc => StackFrames[0].Function;
     public LuaFunction RootFunc => StackFrames[0].Function;
@@ -108,7 +110,7 @@ public class Traceback(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames)
 
 
     public override string ToString()
     public override string ToString()
     {
     {
-        return CreateTracebackMessage(State, StackFrames, LuaValue.Nil);
+        return CreateTracebackMessage(GlobalState, StackFrames, LuaValue.Nil);
     }
     }
 
 
     public string ToString(int skipFrames)
     public string ToString(int skipFrames)
@@ -118,15 +120,15 @@ public class Traceback(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames)
             return "stack traceback:\n";
             return "stack traceback:\n";
         }
         }
 
 
-        return CreateTracebackMessage(State, StackFrames, LuaValue.Nil, skipFrames);
+        return CreateTracebackMessage(GlobalState, StackFrames, LuaValue.Nil, skipFrames);
     }
     }
 
 
-    public static string CreateTracebackMessage(LuaThread thread, LuaValue message, int stackFramesSkipCount = 0)
+    public static string CreateTracebackMessage(LuaState state, LuaValue message, int stackFramesSkipCount = 0)
     {
     {
-        return CreateTracebackMessage(thread.State, thread.GetCallStackFrames(), message, stackFramesSkipCount);
+        return CreateTracebackMessage(state.GlobalState, state.GetCallStackFrames(), message, stackFramesSkipCount);
     }
     }
 
 
-    internal static string CreateTracebackMessage(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames, LuaValue message, int skipCount = 0)
+    internal static string CreateTracebackMessage(LuaGlobalState globalState, ReadOnlySpan<CallStackFrame> stackFrames, LuaValue message, int skipCount = 0)
     {
     {
         using var list = new PooledList<char>(64);
         using var list = new PooledList<char>(64);
         if (message.Type is not LuaValueType.Nil)
         if (message.Type is not LuaValueType.Nil)
@@ -200,7 +202,7 @@ public class Traceback(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames)
                     goto Next;
                     goto Next;
                 }
                 }
 
 
-                foreach (var pair in state.Environment.Dictionary)
+                foreach (var pair in globalState.Environment.Dictionary)
                 {
                 {
                     if (pair.Key.TryReadString(out var name)
                     if (pair.Key.TryReadString(out var name)
                         && pair.Value.TryReadFunction(out var result) &&
                         && pair.Value.TryReadFunction(out var result) &&

+ 5 - 5
src/Lua/Runtime/UpValue.cs

@@ -6,18 +6,18 @@ public sealed class UpValue
 {
 {
     LuaValue value;
     LuaValue value;
 
 
-    public LuaThread? Thread { get; }
+    public LuaState? Thread { get; }
     public bool IsClosed { get; private set; }
     public bool IsClosed { get; private set; }
     public int RegisterIndex { get; private set; }
     public int RegisterIndex { get; private set; }
 
 
-    UpValue(LuaThread? thread)
+    UpValue(LuaState? state)
     {
     {
-        Thread = thread;
+        Thread = state;
     }
     }
 
 
-    public static UpValue Open(LuaThread thread, int registerIndex)
+    public static UpValue Open(LuaState state, int registerIndex)
     {
     {
-        return new(thread) { RegisterIndex = registerIndex };
+        return new(state) { RegisterIndex = registerIndex };
     }
     }
 
 
     public static UpValue Closed(LuaValue value)
     public static UpValue Closed(LuaValue value)

+ 33 - 34
src/Lua/Standard/BasicLibrary.cs

@@ -74,7 +74,7 @@ public sealed class BasicLibrary
                 message = context.GetArgument<string>(1);
                 message = context.GetArgument<string>(1);
             }
             }
 
 
-            throw new LuaAssertionException(context.Thread, message);
+            throw new LuaAssertionException(context.State, message);
         }
         }
 
 
         return new(context.Return(context.Arguments));
         return new(context.Return(context.Arguments));
@@ -94,9 +94,9 @@ public sealed class BasicLibrary
     public async ValueTask<int> DoFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> DoFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<string>(0);
         var arg0 = context.GetArgument<string>(0);
-        context.Thread.Stack.PopUntil(context.ReturnFrameBase);
+        context.State.Stack.PopUntil(context.ReturnFrameBase);
         var closure = await context.State.LoadFileAsync(arg0, "bt", null, cancellationToken);
         var closure = await context.State.LoadFileAsync(arg0, "bt", null, cancellationToken);
-        return await context.Access.RunAsync(closure, cancellationToken);
+        return await context.State.RunAsync(closure, cancellationToken);
     }
     }
 
 
     public ValueTask<int> Error(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Error(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
@@ -108,14 +108,14 @@ public sealed class BasicLibrary
             ? context.GetArgument<int>(1)
             ? context.GetArgument<int>(1)
             : 1;
             : 1;
 
 
-        throw new LuaRuntimeException(context.Thread, value, level);
+        throw new LuaRuntimeException(context.State, value, level);
     }
     }
 
 
     public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument(0);
         var arg0 = context.GetArgument(0);
 
 
-        if (context.State.TryGetMetatable(arg0, out var metatable))
+        if (context.GlobalState.TryGetMetatable(arg0, out var metatable))
         {
         {
             if (metatable.TryGetValue(Metamethods.Metatable, out var metaMetatable))
             if (metatable.TryGetValue(Metamethods.Metatable, out var metaMetatable))
             {
             {
@@ -141,12 +141,12 @@ public sealed class BasicLibrary
         // If table has a metamethod __ipairs, calls it with table as argument and returns the first three results from the call.
         // If table has a metamethod __ipairs, calls it with table as argument and returns the first three results from the call.
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.IPairs, out var metamethod))
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.IPairs, out var metamethod))
         {
         {
-            var stack = context.Thread.Stack;
+            var stack = context.State.Stack;
             var top = stack.Count;
             var top = stack.Count;
             stack.Push(metamethod);
             stack.Push(metamethod);
             stack.Push(arg0);
             stack.Push(arg0);
 
 
-            await LuaVirtualMachine.Call(context.Access.Thread, top, context.ReturnFrameBase, cancellationToken);
+            await LuaVirtualMachine.Call(context.State, top, context.ReturnFrameBase, cancellationToken);
             stack.SetTop(context.ReturnFrameBase + 3);
             stack.SetTop(context.ReturnFrameBase + 3);
             return 3;
             return 3;
         }
         }
@@ -206,7 +206,7 @@ public sealed class BasicLibrary
             }
             }
             else
             else
             {
             {
-                LuaRuntimeException.BadArgument(context.Thread, 1, ["string", "function,binary data"], arg0.TypeToString());
+                LuaRuntimeException.BadArgument(context.State, 1, ["string", "function,binary data"], arg0.TypeToString());
                 return default; // dummy
                 return default; // dummy
             }
             }
         }
         }
@@ -238,12 +238,12 @@ public sealed class BasicLibrary
         // If table has a metamethod __pairs, calls it with table as argument and returns the first three results from the call.
         // If table has a metamethod __pairs, calls it with table as argument and returns the first three results from the call.
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Pairs, out var metamethod))
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Pairs, out var metamethod))
         {
         {
-            var stack = context.Thread.Stack;
+            var stack = context.State.Stack;
             var top = stack.Count;
             var top = stack.Count;
             stack.Push(metamethod);
             stack.Push(metamethod);
             stack.Push(arg0);
             stack.Push(arg0);
 
 
-            await LuaVirtualMachine.Call(context.Access.Thread, top, context.ReturnFrameBase, cancellationToken);
+            await LuaVirtualMachine.Call(context.State, top, context.ReturnFrameBase, cancellationToken);
             stack.SetTop(context.ReturnFrameBase + 3);
             stack.SetTop(context.ReturnFrameBase + 3);
             return 3;
             return 3;
         }
         }
@@ -253,23 +253,23 @@ public sealed class BasicLibrary
 
 
     public async ValueTask<int> PCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> PCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var frameCount = context.Thread.CallStackFrameCount;
+        var frameCount = context.State.CallStackFrameCount;
         try
         try
         {
         {
-            var count = await LuaVirtualMachine.Call(context.Access.Thread, context.FrameBase, context.ReturnFrameBase + 1, cancellationToken);
+            var count = await LuaVirtualMachine.Call(context.State, context.FrameBase, context.ReturnFrameBase + 1, cancellationToken);
 
 
-            context.Thread.Stack.Get(context.ReturnFrameBase) = true;
+            context.State.Stack.Get(context.ReturnFrameBase) = true;
             return count + 1;
             return count + 1;
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            context.Thread.PopCallStackFrameUntil(frameCount);
+            context.State.PopCallStackFrameUntil(frameCount);
             switch (ex)
             switch (ex)
             {
             {
                 case LuaCanceledException:
                 case LuaCanceledException:
                     throw;
                     throw;
                 case OperationCanceledException:
                 case OperationCanceledException:
-                    throw new LuaCanceledException(context.Thread, cancellationToken, ex);
+                    throw new LuaCanceledException(context.State, cancellationToken, ex);
                 case LuaRuntimeException luaEx:
                 case LuaRuntimeException luaEx:
                     {
                     {
                         if (luaEx.InnerException == null && luaEx.ErrorObject.Type != LuaValueType.String)
                         if (luaEx.InnerException == null && luaEx.ErrorObject.Type != LuaValueType.String)
@@ -290,12 +290,12 @@ public sealed class BasicLibrary
 
 
     public async ValueTask<int> Print(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Print(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var stdout = context.State.StandardIO.Output;
+        var stdout = context.GlobalState.Platform.StandardIO.Output;
 
 
         for (var i = 0; i < context.ArgumentCount; i++)
         for (var i = 0; i < context.ArgumentCount; i++)
         {
         {
             await context.Arguments[i].CallToStringAsync(context, cancellationToken);
             await context.Arguments[i].CallToStringAsync(context, cancellationToken);
-            await stdout.WriteAsync(context.Thread.Stack.Pop().Read<string>(), cancellationToken);
+            await stdout.WriteAsync(context.State.Stack.Pop().Read<string>(), cancellationToken);
             if (i < context.ArgumentCount - 1)
             if (i < context.ArgumentCount - 1)
             {
             {
                 await stdout.WriteAsync("\t", cancellationToken);
                 await stdout.WriteAsync("\t", cancellationToken);
@@ -336,7 +336,7 @@ public sealed class BasicLibrary
         }
         }
         else
         else
         {
         {
-            LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.String, LuaValueType.Table], arg0.Type);
+            LuaRuntimeException.BadArgument(context.State, 2, [LuaValueType.String, LuaValueType.Table], arg0.Type);
             return default;
             return default;
         }
         }
     }
     }
@@ -359,7 +359,7 @@ public sealed class BasicLibrary
         {
         {
             if (Math.Abs(index) > context.ArgumentCount)
             if (Math.Abs(index) > context.ArgumentCount)
             {
             {
-                throw new LuaRuntimeException(context.Thread, "bad argument #1 to 'select' (index out of range)");
+                throw new LuaRuntimeException(context.State, "bad argument #1 to 'select' (index out of range)");
             }
             }
 
 
             var span = index >= 0
             var span = index >= 0
@@ -374,7 +374,7 @@ public sealed class BasicLibrary
         }
         }
         else
         else
         {
         {
-            LuaRuntimeException.BadArgument(context.Thread, 1, LuaValueType.Number, arg0.Type);
+            LuaRuntimeException.BadArgument(context.State, 1, LuaValueType.Number, arg0.Type);
             return default;
             return default;
         }
         }
     }
     }
@@ -386,12 +386,12 @@ public sealed class BasicLibrary
 
 
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         {
         {
-            LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.Nil, LuaValueType.Table], arg1.Type);
+            LuaRuntimeException.BadArgument(context.State, 2, [LuaValueType.Nil, LuaValueType.Table], arg1.Type);
         }
         }
 
 
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Metatable, out _))
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Metatable, out _))
         {
         {
-            throw new LuaRuntimeException(context.Thread, "cannot change a protected metatable");
+            throw new LuaRuntimeException(context.State, "cannot change a protected metatable");
         }
         }
         else if (arg1.Type is LuaValueType.Nil)
         else if (arg1.Type is LuaValueType.Nil)
         {
         {
@@ -415,7 +415,7 @@ public sealed class BasicLibrary
 
 
         if (toBase != null && (toBase < 2 || toBase > 36))
         if (toBase != null && (toBase < 2 || toBase > 36))
         {
         {
-            throw new LuaRuntimeException(context.Thread, "bad argument #2 to 'tonumber' (base out of range)");
+            throw new LuaRuntimeException(context.State, "bad argument #2 to 'tonumber' (base out of range)");
         }
         }
 
 
         double? value = null;
         double? value = null;
@@ -577,40 +577,39 @@ public sealed class BasicLibrary
 
 
     public async ValueTask<int> XPCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> XPCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var frameCount = context.Thread.CallStackFrameCount;
+        var frameCount = context.State.CallStackFrameCount;
         var arg0 = context.GetArgument(0);
         var arg0 = context.GetArgument(0);
         var arg1 = context.GetArgument<LuaFunction>(1);
         var arg1 = context.GetArgument<LuaFunction>(1);
 
 
         try
         try
         {
         {
-            var stack = context.Thread.Stack;
+            var stack = context.State.Stack;
             stack.Get(context.FrameBase + 1) = arg0;
             stack.Get(context.FrameBase + 1) = arg0;
-            var count = await LuaVirtualMachine.Call(context.Access.Thread, context.FrameBase + 1, context.ReturnFrameBase + 1, cancellationToken);
+            var count = await LuaVirtualMachine.Call(context.State, context.FrameBase + 1, context.ReturnFrameBase + 1, cancellationToken);
 
 
-            context.Thread.Stack.Get(context.ReturnFrameBase) = true;
+            context.State.Stack.Get(context.ReturnFrameBase) = true;
             return count + 1;
             return count + 1;
         }
         }
         catch (Exception ex)
         catch (Exception ex)
         {
         {
-            var thread = context.Thread;
-            thread.PopCallStackFrameUntil(frameCount);
+            var state = context.State;
+            state.PopCallStackFrameUntil(frameCount);
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            var access = thread.CurrentAccess;
             if (ex is LuaRuntimeException luaEx)
             if (ex is LuaRuntimeException luaEx)
             {
             {
                 luaEx.Forget();
                 luaEx.Forget();
-                access.Push(luaEx.ErrorObject);
+                state.Push(luaEx.ErrorObject);
             }
             }
             else
             else
             {
             {
-                access.Push(ex.Message);
+                state.Push(ex.Message);
             }
             }
 
 
 
 
             // invoke error handler
             // invoke error handler
-            var count = await access.RunAsync(arg1, 1, context.ReturnFrameBase + 1, cancellationToken);
-            context.Thread.Stack.Get(context.ReturnFrameBase) = false;
+            var count = await state.RunAsync(arg1, 1, context.ReturnFrameBase + 1, cancellationToken);
+            context.State.Stack.Get(context.ReturnFrameBase) = false;
             return count + 1;
             return count + 1;
         }
         }
     }
     }

+ 28 - 28
src/Lua/Standard/BitwiseLibrary.cs

@@ -33,8 +33,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
         var disp = context.GetArgument<double>(1);
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, disp);
 
 
         var v = Bit32Helper.ToInt32(x);
         var v = Bit32Helper.ToInt32(x);
         var a = (int)disp;
         var a = (int)disp;
@@ -60,14 +60,14 @@ public sealed class BitwiseLibrary
         }
         }
 
 
         var arg0 = context.GetArgument<double>(0);
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
 
 
         var value = Bit32Helper.ToUInt32(arg0);
         var value = Bit32Helper.ToUInt32(arg0);
 
 
         for (var i = 1; i < context.ArgumentCount; i++)
         for (var i = 1; i < context.ArgumentCount; i++)
         {
         {
             var arg = context.GetArgument<double>(i);
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1 + i, arg);
 
 
             var v = Bit32Helper.ToUInt32(arg);
             var v = Bit32Helper.ToUInt32(arg);
             value &= v;
             value &= v;
@@ -80,7 +80,7 @@ public sealed class BitwiseLibrary
     public ValueTask<int> BNot(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> BNot(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<double>(0);
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
 
 
         var value = Bit32Helper.ToUInt32(arg0);
         var value = Bit32Helper.ToUInt32(arg0);
         return new(context.Return(~value));
         return new(context.Return(~value));
@@ -94,14 +94,14 @@ public sealed class BitwiseLibrary
         }
         }
 
 
         var arg0 = context.GetArgument<double>(0);
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
 
 
         var value = Bit32Helper.ToUInt32(arg0);
         var value = Bit32Helper.ToUInt32(arg0);
 
 
         for (var i = 1; i < context.ArgumentCount; i++)
         for (var i = 1; i < context.ArgumentCount; i++)
         {
         {
             var arg = context.GetArgument<double>(i);
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1 + i, arg);
 
 
             var v = Bit32Helper.ToUInt32(arg);
             var v = Bit32Helper.ToUInt32(arg);
             value |= v;
             value |= v;
@@ -119,14 +119,14 @@ public sealed class BitwiseLibrary
         }
         }
 
 
         var arg0 = context.GetArgument<double>(0);
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
 
 
         var value = Bit32Helper.ToUInt32(arg0);
         var value = Bit32Helper.ToUInt32(arg0);
 
 
         for (var i = 1; i < context.ArgumentCount; i++)
         for (var i = 1; i < context.ArgumentCount; i++)
         {
         {
             var arg = context.GetArgument<double>(i);
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1 + i, arg);
 
 
             var v = Bit32Helper.ToUInt32(arg);
             var v = Bit32Helper.ToUInt32(arg);
             value &= v;
             value &= v;
@@ -143,14 +143,14 @@ public sealed class BitwiseLibrary
         }
         }
 
 
         var arg0 = context.GetArgument<double>(0);
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
 
 
         var value = Bit32Helper.ToUInt32(arg0);
         var value = Bit32Helper.ToUInt32(arg0);
 
 
         for (var i = 1; i < context.ArgumentCount; i++)
         for (var i = 1; i < context.ArgumentCount; i++)
         {
         {
             var arg = context.GetArgument<double>(i);
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1 + i, arg);
 
 
             var v = Bit32Helper.ToUInt32(arg);
             var v = Bit32Helper.ToUInt32(arg);
             value ^= v;
             value ^= v;
@@ -167,15 +167,15 @@ public sealed class BitwiseLibrary
             ? context.GetArgument<double>(2)
             ? context.GetArgument<double>(2)
             : 1;
             : 1;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, arg1);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, arg2);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 3, arg2);
 
 
         var n = Bit32Helper.ToUInt32(arg0);
         var n = Bit32Helper.ToUInt32(arg0);
         var field = (int)arg1;
         var field = (int)arg1;
         var width = (int)arg2;
         var width = (int)arg2;
 
 
-        Bit32Helper.ValidateFieldAndWidth(context.Thread, "extract", 2, field, width);
+        Bit32Helper.ValidateFieldAndWidth(context.State, "extract", 2, field, width);
 
 
         if (field == 0 && width == 32)
         if (field == 0 && width == 32)
         {
         {
@@ -193,8 +193,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
         var disp = context.GetArgument<double>(1);
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, disp);
 
 
         var v = Bit32Helper.ToUInt32(x);
         var v = Bit32Helper.ToUInt32(x);
         var a = (int)disp % 32;
         var a = (int)disp % 32;
@@ -217,8 +217,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
         var disp = context.GetArgument<double>(1);
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, disp);
 
 
         var v = Bit32Helper.ToUInt32(x);
         var v = Bit32Helper.ToUInt32(x);
         var a = (int)disp;
         var a = (int)disp;
@@ -248,17 +248,17 @@ public sealed class BitwiseLibrary
             ? context.GetArgument<double>(3)
             ? context.GetArgument<double>(3)
             : 1;
             : 1;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, arg1);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, arg2);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 4, arg3);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 3, arg2);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 4, arg3);
 
 
         var n = Bit32Helper.ToUInt32(arg0);
         var n = Bit32Helper.ToUInt32(arg0);
         var v = Bit32Helper.ToUInt32(arg1);
         var v = Bit32Helper.ToUInt32(arg1);
         var field = (int)arg2;
         var field = (int)arg2;
         var width = (int)arg3;
         var width = (int)arg3;
 
 
-        Bit32Helper.ValidateFieldAndWidth(context.Thread, "replace", 2, field, width);
+        Bit32Helper.ValidateFieldAndWidth(context.State, "replace", 2, field, width);
         uint mask;
         uint mask;
         if (width == 32)
         if (width == 32)
         {
         {
@@ -279,8 +279,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
         var disp = context.GetArgument<double>(1);
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, disp);
 
 
         var v = Bit32Helper.ToUInt32(x);
         var v = Bit32Helper.ToUInt32(x);
         var a = (int)disp % 32;
         var a = (int)disp % 32;
@@ -302,8 +302,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
         var disp = context.GetArgument<double>(1);
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, disp);
 
 
         var v = Bit32Helper.ToUInt32(x);
         var v = Bit32Helper.ToUInt32(x);
         var a = (int)disp;
         var a = (int)disp;

+ 18 - 18
src/Lua/Standard/CoroutineLibrary.cs

@@ -25,24 +25,24 @@ public sealed class CoroutineLibrary
     public ValueTask<int> Create(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Create(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
         var arg0 = context.GetArgument<LuaFunction>(0);
-        return new(context.Return(LuaCoroutine.Create(context.Thread, arg0, true)));
+        return new(context.Return(LuaState.CreateCoroutine(context.State.GlobalState, arg0, true)));
     }
     }
 
 
     public ValueTask<int> Resume(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Resume(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var thread = context.GetArgument<LuaThread>(0);
-        return thread.ResumeAsync(context with { ArgumentCount = context.ArgumentCount - 1 }, cancellationToken);
+        var state = context.GetArgument<LuaState>(0);
+        return state.ResumeAsync(context with { ArgumentCount = context.ArgumentCount - 1 }, cancellationToken);
     }
     }
 
 
     public ValueTask<int> Running(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Running(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        return new(context.Return(context.Thread, context.Thread == context.State.MainThread));
+        return new(context.Return(context.State, context.State == context.GlobalState.MainThread));
     }
     }
 
 
     public ValueTask<int> Status(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Status(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var thread = context.GetArgument<LuaThread>(0);
-        return new(context.Return(thread.GetStatus() switch
+        var state = context.GetArgument<LuaState>(0);
+        return new(context.Return(state.GetStatus() switch
         {
         {
             LuaThreadStatus.Normal => "normal",
             LuaThreadStatus.Normal => "normal",
             LuaThreadStatus.Suspended => "suspended",
             LuaThreadStatus.Suspended => "suspended",
@@ -55,36 +55,36 @@ public sealed class CoroutineLibrary
     public ValueTask<int> Wrap(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Wrap(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
         var arg0 = context.GetArgument<LuaFunction>(0);
-        var thread = LuaCoroutine.Create(context.Thread, arg0, false);
-        return new(context.Return(new CSharpClosure("wrap", [thread],
+        var state = LuaState.CreateCoroutine(context.State.GlobalState, arg0, false);
+        return new(context.Return(new CSharpClosure("wrap", [state],
             static async (context, cancellationToken) =>
             static async (context, cancellationToken) =>
             {
             {
-                var thread = context.GetCsClosure()!.UpValues[0].Read<LuaThread>();
-                if (thread is not LuaCoroutine coroutine)
+                var state = context.GetCsClosure()!.UpValues[0].Read<LuaState>();
+                if (!state.IsCoroutine)
                 {
                 {
-                    return await thread.ResumeAsync(context, cancellationToken);
+                    return await state.ResumeAsync(context, cancellationToken);
                 }
                 }
 
 
-                var stack = context.Thread.Stack;
+                var stack = context.State.Stack;
                 var frameBase = stack.Count;
                 var frameBase = stack.Count;
-                context.Thread.PushCallStackFrame(new() { Base = frameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = coroutine.Function });
+                context.State.PushCallStackFrame(new() { Base = frameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = state.CoroutineFunction! });
                 try
                 try
                 {
                 {
-                    await thread.ResumeAsync(context, cancellationToken);
-                    var result = context.GetReturnBuffer(context.Thread.Stack.Count - context.ReturnFrameBase);
+                    await state.ResumeAsync(context, cancellationToken);
+                    var result = context.GetReturnBuffer(context.State.Stack.Count - context.ReturnFrameBase);
                     result[1..].CopyTo(result);
                     result[1..].CopyTo(result);
-                    context.Thread.Stack.Pop();
+                    context.State.Stack.Pop();
                     return result.Length - 1;
                     return result.Length - 1;
                 }
                 }
                 finally
                 finally
                 {
                 {
-                    context.Thread.PopCallStackFrame();
+                    context.State.PopCallStackFrame();
                 }
                 }
             })));
             })));
     }
     }
 
 
     public ValueTask<int> Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        return context.Thread.YieldAsync(context, cancellationToken);
+        return context.State.YieldAsync(context, cancellationToken);
     }
     }
 }
 }

+ 48 - 51
src/Lua/Standard/DebugLibrary.cs

@@ -35,26 +35,26 @@ public class DebugLibrary
     public readonly LibraryFunction[] Functions;
     public readonly LibraryFunction[] Functions;
 
 
 
 
-    static LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset)
+    static LuaState GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset)
     {
     {
         if (context.ArgumentCount < 1)
         if (context.ArgumentCount < 1)
         {
         {
             argOffset = 0;
             argOffset = 0;
-            return context.Thread;
+            return context.State;
         }
         }
 
 
-        if (context.GetArgument(0).TryRead<LuaThread>(out var thread))
+        if (context.GetArgument(0).TryRead<LuaState>(out var state))
         {
         {
             argOffset = 1;
             argOffset = 1;
-            return thread;
+            return state;
         }
         }
 
 
         argOffset = 0;
         argOffset = 0;
-        return context.Thread;
+        return context.State;
     }
     }
 
 
 
 
-    static ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name)
+    static ref LuaValue FindLocal(LuaState state, int level, int index, out string? name)
     {
     {
         if (index == 0)
         if (index == 0)
         {
         {
@@ -62,7 +62,7 @@ public class DebugLibrary
             return ref Unsafe.NullRef<LuaValue>();
             return ref Unsafe.NullRef<LuaValue>();
         }
         }
 
 
-        var callStack = thread.GetCallStackFrames();
+        var callStack = state.GetCallStackFrames();
         var frame = callStack[^(level + 1)];
         var frame = callStack[^(level + 1)];
         if (index < 0)
         if (index < 0)
         {
         {
@@ -71,7 +71,7 @@ public class DebugLibrary
             if (frameVariableArgumentCount > 0 && index < frameVariableArgumentCount)
             if (frameVariableArgumentCount > 0 && index < frameVariableArgumentCount)
             {
             {
                 name = "(*vararg)";
                 name = "(*vararg)";
-                return ref thread.Stack.Get(frame.Base - frameVariableArgumentCount + index);
+                return ref state.Stack.Get(frame.Base - frameVariableArgumentCount + index);
             }
             }
 
 
             name = null;
             name = null;
@@ -115,13 +115,13 @@ public class DebugLibrary
                 if (localId == 0)
                 if (localId == 0)
                 {
                 {
                     name = l.Name;
                     name = l.Name;
-                    return ref thread.Stack.Get(frameBase + index);
+                    return ref state.Stack.Get(frameBase + index);
                 }
                 }
             }
             }
         }
         }
         else
         else
         {
         {
-            var nextFrameBase = level != 0 ? callStack[^level].Base : thread.Stack.Count;
+            var nextFrameBase = level != 0 ? callStack[^level].Base : state.Stack.Count;
 
 
             if (nextFrameBase - 1 < frameBase + index)
             if (nextFrameBase - 1 < frameBase + index)
             {
             {
@@ -131,7 +131,7 @@ public class DebugLibrary
         }
         }
 
 
         name = "(*temporary)";
         name = "(*temporary)";
-        return ref thread.Stack.Get(frameBase + index);
+        return ref state.Stack.Get(frameBase + index);
     }
     }
 
 
     public ValueTask<int> GetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> GetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
@@ -150,7 +150,7 @@ public class DebugLibrary
             return LuaValue.Nil;
             return LuaValue.Nil;
         }
         }
 
 
-        var thread = GetLuaThread(context, out var argOffset);
+        var state = GetLuaThread(context, out var argOffset);
 
 
         var index = context.GetArgument<int>(argOffset + 1);
         var index = context.GetArgument<int>(argOffset + 1);
         if (context.GetArgument(argOffset).TryReadFunction(out var f))
         if (context.GetArgument(argOffset).TryReadFunction(out var f))
@@ -161,12 +161,12 @@ public class DebugLibrary
         var level = context.GetArgument<int>(argOffset);
         var level = context.GetArgument<int>(argOffset);
 
 
 
 
-        if (level < 0 || level >= thread.GetCallStackFrames().Length)
+        if (level < 0 || level >= state.GetCallStackFrames().Length)
         {
         {
             context.ThrowBadArgument(1, "level out of range");
             context.ThrowBadArgument(1, "level out of range");
         }
         }
 
 
-        ref var local = ref FindLocal(thread, level, index, out var name);
+        ref var local = ref FindLocal(state, level, index, out var name);
         if (name is null)
         if (name is null)
         {
         {
             return new(context.Return(LuaValue.Nil));
             return new(context.Return(LuaValue.Nil));
@@ -177,19 +177,19 @@ public class DebugLibrary
 
 
     public ValueTask<int> SetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> SetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var thread = GetLuaThread(context, out var argOffset);
+        var state = GetLuaThread(context, out var argOffset);
 
 
         var value = context.GetArgument(argOffset + 2);
         var value = context.GetArgument(argOffset + 2);
         var index = context.GetArgument<int>(argOffset + 1);
         var index = context.GetArgument<int>(argOffset + 1);
         var level = context.GetArgument<int>(argOffset);
         var level = context.GetArgument<int>(argOffset);
 
 
 
 
-        if (level < 0 || level >= thread.GetCallStackFrames().Length)
+        if (level < 0 || level >= state.GetCallStackFrames().Length)
         {
         {
             context.ThrowBadArgument(1, "level out of range");
             context.ThrowBadArgument(1, "level out of range");
         }
         }
 
 
-        ref var local = ref FindLocal(thread, level, index, out var name);
+        ref var local = ref FindLocal(state, level, index, out var name);
         if (name is null)
         if (name is null)
         {
         {
             return new(context.Return(LuaValue.Nil));
             return new(context.Return(LuaValue.Nil));
@@ -270,7 +270,7 @@ public class DebugLibrary
     {
     {
         var arg0 = context.GetArgument(0);
         var arg0 = context.GetArgument(0);
 
 
-        if (context.State.TryGetMetatable(arg0, out var table))
+        if (context.GlobalState.TryGetMetatable(arg0, out var table))
         {
         {
             return new(context.Return(table));
             return new(context.Return(table));
         }
         }
@@ -287,10 +287,10 @@ public class DebugLibrary
 
 
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         {
         {
-            LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.Nil, LuaValueType.Table], arg1.Type);
+            LuaRuntimeException.BadArgument(context.State, 2, [LuaValueType.Nil, LuaValueType.Table], arg1.Type);
         }
         }
 
 
-        context.State.SetMetatable(arg0, arg1.UnsafeRead<LuaTable>());
+        context.GlobalState.SetMetatable(arg0, arg1.UnsafeRead<LuaTable>());
 
 
         return new(context.Return(arg0));
         return new(context.Return(arg0));
     }
     }
@@ -333,7 +333,7 @@ public class DebugLibrary
 
 
     public ValueTask<int> Traceback(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Traceback(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var thread = GetLuaThread(context, out var argOffset);
+        var state = GetLuaThread(context, out var argOffset);
 
 
         var message = context.GetArgumentOrDefault(argOffset);
         var message = context.GetArgumentOrDefault(argOffset);
         var level = context.GetArgumentOrDefault<int>(argOffset + 1, argOffset == 0 ? 1 : 0);
         var level = context.GetArgumentOrDefault<int>(argOffset + 1, argOffset == 0 ? 1 : 0);
@@ -348,15 +348,12 @@ public class DebugLibrary
             return new(context.Return(LuaValue.Nil));
             return new(context.Return(LuaValue.Nil));
         }
         }
 
 
-        if (thread is LuaCoroutine coroutine)
+        if (state is { IsCoroutine: true, LuaTraceback: {} traceback })
         {
         {
-            if (coroutine.LuaTraceback is not null)
-            {
-                return new(context.Return(coroutine.LuaTraceback.ToString(level)));
-            }
+            return new(context.Return(traceback.ToString(level)));
         }
         }
 
 
-        var callStack = thread.GetCallStackFrames();
+        var callStack = state.GetCallStackFrames();
         if (callStack.Length == 0)
         if (callStack.Length == 0)
         {
         {
             return new(context.Return("stack traceback:"));
             return new(context.Return("stack traceback:"));
@@ -364,12 +361,12 @@ public class DebugLibrary
 
 
         var skipCount = Math.Min(Math.Max(level - 1, 0), callStack.Length - 1);
         var skipCount = Math.Min(Math.Max(level - 1, 0), callStack.Length - 1);
         var frames = callStack[..^skipCount];
         var frames = callStack[..^skipCount];
-        return new(context.Return(Runtime.Traceback.CreateTracebackMessage(context.State, frames, message, level == 1 ? 1 : 0)));
+        return new(context.Return(Runtime.Traceback.CreateTracebackMessage(context.GlobalState, frames, message, level == 1 ? 1 : 0)));
     }
     }
 
 
     public ValueTask<int> GetRegistry(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> GetRegistry(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        return new(context.Return(context.State.Registry));
+        return new(context.Return(context.GlobalState.Registry));
     }
     }
 
 
     public ValueTask<int> UpValueId(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> UpValueId(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
@@ -421,34 +418,34 @@ public class DebugLibrary
 
 
     public async ValueTask<int> SetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> SetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var thread = GetLuaThread(context, out var argOffset);
+        var state = GetLuaThread(context, out var argOffset);
         var hook = context.GetArgumentOrDefault<LuaFunction?>(argOffset);
         var hook = context.GetArgumentOrDefault<LuaFunction?>(argOffset);
         var mask = context.GetArgumentOrDefault<string?>(argOffset + 1) ?? "";
         var mask = context.GetArgumentOrDefault<string?>(argOffset + 1) ?? "";
         var count = context.GetArgumentOrDefault<int>(argOffset + 2);
         var count = context.GetArgumentOrDefault<int>(argOffset + 2);
-        thread.SetHook(hook, mask, count);
+        state.SetHook(hook, mask, count);
         if (hook is null)
         if (hook is null)
         {
         {
             return 0;
             return 0;
         }
         }
 
 
-        if (thread.IsReturnHookEnabled && context.Thread == thread)
+        if (state.IsReturnHookEnabled && context.State == state)
         {
         {
-            var stack = thread.Stack;
+            var stack = state.Stack;
             var top = stack.Count;
             var top = stack.Count;
             stack.Push("return");
             stack.Push("return");
             stack.Push(LuaValue.Nil);
             stack.Push(LuaValue.Nil);
-            context.Thread.IsInHook = true;
-            var frame = context.Thread.CurrentAccess.CreateCallStackFrame(hook, 2, top, 0);
-            var access = context.Thread.PushCallStackFrame(frame);
-            LuaFunctionExecutionContext funcContext = new() { Access = access, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
+            context.State.IsInHook = true;
+            var frame = context.State.CreateCallStackFrame(hook, 2, top, 0);
+            context.State.PushCallStackFrame(frame);
+            LuaFunctionExecutionContext funcContext = new() { State = context.State, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
             try
             try
             {
             {
                 await hook.Func(funcContext, cancellationToken);
                 await hook.Func(funcContext, cancellationToken);
             }
             }
             finally
             finally
             {
             {
-                context.Thread.IsInHook = false;
-                context.Thread.PopCallStackFrameWithStackPop();
+                context.State.IsInHook = false;
+                context.State.PopCallStackFrameWithStackPop();
             }
             }
         }
         }
 
 
@@ -457,23 +454,23 @@ public class DebugLibrary
 
 
     public ValueTask<int> GetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> GetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var thread = GetLuaThread(context, out var argOffset);
-        if (thread.Hook is null)
+        var state = GetLuaThread(context, out var argOffset);
+        if (state.Hook is null)
         {
         {
             return new(context.Return(LuaValue.Nil, LuaValue.Nil, LuaValue.Nil));
             return new(context.Return(LuaValue.Nil, LuaValue.Nil, LuaValue.Nil));
         }
         }
 
 
-        return new(context.Return(thread.Hook,
-            (thread.IsCallHookEnabled ? "c" : "") +
-            (thread.IsReturnHookEnabled ? "r" : "") +
-            (thread.IsLineHookEnabled ? "l" : "")
-            , thread.BaseHookCount));
+        return new(context.Return(state.Hook,
+            (state.IsCallHookEnabled ? "c" : "") +
+            (state.IsReturnHookEnabled ? "r" : "") +
+            (state.IsLineHookEnabled ? "l" : "")
+            , state.BaseHookCount));
     }
     }
 
 
     public ValueTask<int> GetInfo(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> GetInfo(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         //return new(0);
         //return new(0);
-        var thread = GetLuaThread(context, out var argOffset);
+        var state = GetLuaThread(context, out var argOffset);
         var what = context.GetArgumentOrDefault<string>(argOffset + 1, "flnStu");
         var what = context.GetArgumentOrDefault<string>(argOffset + 1, "flnStu");
         CallStackFrame? previousFrame = null;
         CallStackFrame? previousFrame = null;
         CallStackFrame? currentFrame = null;
         CallStackFrame? currentFrame = null;
@@ -488,7 +485,7 @@ public class DebugLibrary
         {
         {
             var level = context.GetArgument<int>(argOffset) + 1;
             var level = context.GetArgument<int>(argOffset) + 1;
 
 
-            var callStack = thread.GetCallStackFrames();
+            var callStack = state.GetCallStackFrames();
 
 
             if (level <= 0 || level > callStack.Length)
             if (level <= 0 || level > callStack.Length)
             {
             {
@@ -496,11 +493,11 @@ public class DebugLibrary
             }
             }
 
 
 
 
-            currentFrame = thread.GetCallStackFrames()[^level];
+            currentFrame = state.GetCallStackFrames()[^level];
             previousFrame = level + 1 <= callStack.Length ? callStack[^(level + 1)] : null;
             previousFrame = level + 1 <= callStack.Length ? callStack[^(level + 1)] : null;
             if (level != 1)
             if (level != 1)
             {
             {
-                pc = thread.GetCallStackFrames()[^(level - 1)].CallerInstructionIndex;
+                pc = state.GetCallStackFrames()[^(level - 1)].CallerInstructionIndex;
             }
             }
 
 
             functionToInspect = currentFrame.Value.Function;
             functionToInspect = currentFrame.Value.Function;
@@ -510,7 +507,7 @@ public class DebugLibrary
             context.ThrowBadArgument(argOffset, "function or level expected");
             context.ThrowBadArgument(argOffset, "function or level expected");
         }
         }
 
 
-        using var debug = LuaDebug.Create(context.State, previousFrame, currentFrame, functionToInspect, pc, what, out var isValid);
+        using var debug = LuaDebug.Create(context.GlobalState, previousFrame, currentFrame, functionToInspect, pc, what, out var isValid);
         if (!isValid)
         if (!isValid)
         {
         {
             context.ThrowBadArgument(argOffset + 1, "invalid option");
             context.ThrowBadArgument(argOffset + 1, "invalid option");

+ 3 - 3
src/Lua/Standard/FileHandle.cs

@@ -170,7 +170,7 @@ public class FileHandle : ILuaUserData
             var upValues = context.GetCsClosure()!.UpValues.AsMemory();
             var upValues = context.GetCsClosure()!.UpValues.AsMemory();
             var file = upValues.Span[0].Read<FileHandle>();
             var file = upValues.Span[0].Read<FileHandle>();
             context.Return();
             context.Return();
-            var resultCount = await IOHelper.ReadAsync(context.Thread, file, "file.lines", 0, upValues[1..], true, cancellationToken);
+            var resultCount = await IOHelper.ReadAsync(context.State, file, "file.lines", 0, upValues[1..], true, cancellationToken);
             return resultCount;
             return resultCount;
         })));
         })));
     });
     });
@@ -180,7 +180,7 @@ public class FileHandle : ILuaUserData
         var file = context.GetArgument<FileHandle>(0);
         var file = context.GetArgument<FileHandle>(0);
         var args = context.Arguments[1..].ToArray();
         var args = context.Arguments[1..].ToArray();
         context.Return();
         context.Return();
-        var resultCount = await IOHelper.ReadAsync(context.Thread, file, "file.read", 1, args, false, cancellationToken);
+        var resultCount = await IOHelper.ReadAsync(context.State, file, "file.read", 1, args, false, cancellationToken);
         return resultCount;
         return resultCount;
     });
     });
 
 
@@ -196,7 +196,7 @@ public class FileHandle : ILuaUserData
 
 
         if (whence is not ("set" or "cur" or "end"))
         if (whence is not ("set" or "cur" or "end"))
         {
         {
-            throw new LuaRuntimeException(context.Thread, $"bad argument #2 to 'file.seek' (invalid option '{whence}')");
+            throw new LuaRuntimeException(context.State, $"bad argument #2 to 'file.seek' (invalid option '{whence}')");
         }
         }
 
 
         try
         try

+ 18 - 18
src/Lua/Standard/IOLibrary.cs

@@ -32,7 +32,7 @@ public sealed class IOLibrary
     {
     {
         var file = context.HasArgument(0)
         var file = context.HasArgument(0)
             ? context.GetArgument<FileHandle>(0)
             ? context.GetArgument<FileHandle>(0)
-            : context.State.Registry["_IO_output"].Read<FileHandle>();
+            : context.GlobalState.Registry["_IO_output"].Read<FileHandle>();
 
 
         try
         try
         {
         {
@@ -47,7 +47,7 @@ public sealed class IOLibrary
 
 
     public async ValueTask<int> Flush(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Flush(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var file = context.State.Registry["_IO_output"].Read<FileHandle>();
+        var file = context.GlobalState.Registry["_IO_output"].Read<FileHandle>();
 
 
         try
         try
         {
         {
@@ -62,7 +62,7 @@ public sealed class IOLibrary
 
 
     public async ValueTask<int> Input(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Input(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var registry = context.State.Registry;
+        var registry = context.GlobalState.Registry;
 
 
         if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
         if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
         {
         {
@@ -77,7 +77,7 @@ public sealed class IOLibrary
         }
         }
         else
         else
         {
         {
-            var stream = await context.State.FileSystem.Open(arg.ToString(), LuaFileOpenMode.Read, cancellationToken);
+            var stream = await context.GlobalState.Platform.FileSystem.Open(arg.ToString(), LuaFileOpenMode.Read, cancellationToken);
             FileHandle handle = new(stream);
             FileHandle handle = new(stream);
             registry["_IO_input"] = new(handle);
             registry["_IO_input"] = new(handle);
             return context.Return(new LuaValue(handle));
             return context.Return(new LuaValue(handle));
@@ -88,13 +88,13 @@ public sealed class IOLibrary
     {
     {
         if (context.ArgumentCount == 0)
         if (context.ArgumentCount == 0)
         {
         {
-            var file = context.State.Registry["_IO_input"].Read<FileHandle>();
+            var file = context.GlobalState.Registry["_IO_input"].Read<FileHandle>();
             return context.Return(new CSharpClosure("iterator", [new(file)], static async (context, cancellationToken) =>
             return context.Return(new CSharpClosure("iterator", [new(file)], static async (context, cancellationToken) =>
             {
             {
                 var file = context.GetCsClosure()!.UpValues[0].Read<FileHandle>();
                 var file = context.GetCsClosure()!.UpValues[0].Read<FileHandle>();
                 context.Return();
                 context.Return();
-                var resultCount = await IOHelper.ReadAsync(context.Thread, file, "io.lines", 0, Memory<LuaValue>.Empty, true, cancellationToken);
-                if (resultCount > 0 && context.Thread.Stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
+                var resultCount = await IOHelper.ReadAsync(context.State, file, "io.lines", 0, Memory<LuaValue>.Empty, true, cancellationToken);
+                if (resultCount > 0 && context.State.Stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 {
                 {
                     await file.Close(cancellationToken);
                     await file.Close(cancellationToken);
                 }
                 }
@@ -105,10 +105,10 @@ public sealed class IOLibrary
         else
         else
         {
         {
             var fileName = context.GetArgument<string>(0);
             var fileName = context.GetArgument<string>(0);
-            var stack = context.Thread.Stack;
+            var stack = context.State.Stack;
             context.Return();
             context.Return();
 
 
-            await IOHelper.Open(context.Thread, fileName, "r", true, cancellationToken);
+            await IOHelper.Open(context.State, fileName, "r", true, cancellationToken);
 
 
             var file = stack.Get(context.ReturnFrameBase).Read<FileHandle>();
             var file = stack.Get(context.ReturnFrameBase).Read<FileHandle>();
             var upValues = new LuaValue[context.Arguments.Length];
             var upValues = new LuaValue[context.Arguments.Length];
@@ -120,9 +120,9 @@ public sealed class IOLibrary
                 var upValues = context.GetCsClosure()!.UpValues;
                 var upValues = context.GetCsClosure()!.UpValues;
                 var file = upValues[0].Read<FileHandle>();
                 var file = upValues[0].Read<FileHandle>();
                 var formats = upValues.AsMemory(1);
                 var formats = upValues.AsMemory(1);
-                var stack = context.Thread.Stack;
+                var stack = context.State.Stack;
                 context.Return();
                 context.Return();
-                var resultCount = await IOHelper.ReadAsync(context.Thread, file, "io.lines", 0, formats, true, cancellationToken);
+                var resultCount = await IOHelper.ReadAsync(context.State, file, "io.lines", 0, formats, true, cancellationToken);
                 if (resultCount > 0 && stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 if (resultCount > 0 && stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 {
                 {
                     await file.Close(cancellationToken);
                     await file.Close(cancellationToken);
@@ -142,7 +142,7 @@ public sealed class IOLibrary
         context.Return();
         context.Return();
         try
         try
         {
         {
-            var resultCount = await IOHelper.Open(context.Thread, fileName, mode, true, cancellationToken);
+            var resultCount = await IOHelper.Open(context.State, fileName, mode, true, cancellationToken);
             return resultCount;
             return resultCount;
         }
         }
         catch (IOException ex)
         catch (IOException ex)
@@ -153,7 +153,7 @@ public sealed class IOLibrary
 
 
     public async ValueTask<int> Output(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Output(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var io = context.State.Registry;
+        var io = context.GlobalState.Registry;
 
 
         if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
         if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
         {
         {
@@ -168,7 +168,7 @@ public sealed class IOLibrary
         }
         }
         else
         else
         {
         {
-            var stream = await context.State.FileSystem.Open(arg.ToString(), LuaFileOpenMode.WriteUpdate, cancellationToken);
+            var stream = await context.GlobalState.Platform.FileSystem.Open(arg.ToString(), LuaFileOpenMode.WriteUpdate, cancellationToken);
             FileHandle handle = new(stream);
             FileHandle handle = new(stream);
             io["_IO_output"] = new(handle);
             io["_IO_output"] = new(handle);
             return context.Return(new LuaValue(handle));
             return context.Return(new LuaValue(handle));
@@ -177,11 +177,11 @@ public sealed class IOLibrary
 
 
     public async ValueTask<int> Read(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Read(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var file = context.State.Registry["_IO_input"].Read<FileHandle>();
+        var file = context.GlobalState.Registry["_IO_input"].Read<FileHandle>();
         var args = context.Arguments.ToArray();
         var args = context.Arguments.ToArray();
         context.Return();
         context.Return();
 
 
-        var resultCount = await IOHelper.ReadAsync(context.Thread, file, "io.read", 0, args, false, cancellationToken);
+        var resultCount = await IOHelper.ReadAsync(context.State, file, "io.read", 0, args, false, cancellationToken);
         return resultCount;
         return resultCount;
     }
     }
 
 
@@ -201,13 +201,13 @@ public sealed class IOLibrary
 
 
     public async ValueTask<int> Write(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Write(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var file = context.State.Registry["_IO_output"].Read<FileHandle>();
+        var file = context.GlobalState.Registry["_IO_output"].Read<FileHandle>();
         var resultCount = await IOHelper.WriteAsync(file, "io.write", context, cancellationToken);
         var resultCount = await IOHelper.WriteAsync(file, "io.write", context, cancellationToken);
         return resultCount;
         return resultCount;
     }
     }
 
 
     public async ValueTask<int> TmpFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> TmpFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        return context.Return(LuaValue.FromUserData(new FileHandle(await context.State.FileSystem.OpenTempFileStream(cancellationToken))));
+        return context.Return(LuaValue.FromUserData(new FileHandle(await context.GlobalState.Platform.FileSystem.OpenTempFileStream(cancellationToken))));
     }
     }
 }
 }

+ 4 - 4
src/Lua/Standard/Internal/Bit32Helper.cs

@@ -18,14 +18,14 @@ static class Bit32Helper
         return (int)(long)Math.IEEERemainder(d, Bit32);
         return (int)(long)Math.IEEERemainder(d, Bit32);
     }
     }
 
 
-    public static void ValidateFieldAndWidth(LuaThread thread, string functionName, int argumentId, int field, int width)
+    public static void ValidateFieldAndWidth(LuaState state, string functionName, int argumentId, int field, int width)
     {
     {
         if (field > 31 || field + width > 32)
         if (field > 31 || field + width > 32)
-            throw new LuaRuntimeException(thread, "trying to access non-existent bits");
+            throw new LuaRuntimeException(state, "trying to access non-existent bits");
         if (field < 0)
         if (field < 0)
-            throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' (field cannot be negative)");
+            throw new LuaRuntimeException(state, $"bad argument #{argumentId} to '{functionName}' (field cannot be negative)");
 
 
         if (width <= 0)
         if (width <= 0)
-            throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' (width must be positive)");
+            throw new LuaRuntimeException(state, $"bad argument #{argumentId} to '{functionName}' (width must be positive)");
     }
     }
 }
 }

+ 12 - 12
src/Lua/Standard/Internal/DateTimeHelper.cs

@@ -30,15 +30,15 @@ static class DateTimeHelper
         return DateTime.UnixEpoch + ts;
         return DateTime.UnixEpoch + ts;
     }
     }
 
 
-    public static DateTime ParseTimeTable(LuaThread thread, LuaTable table)
+    public static DateTime ParseTimeTable(LuaState state, LuaTable table)
     {
     {
-        static int GetTimeField(LuaThread thread, LuaTable table, string key, bool required = true, int defaultValue = 0)
+        static int GetTimeField(LuaState state, LuaTable table, string key, bool required = true, int defaultValue = 0)
         {
         {
             if (!table.TryGetValue(key, out var value))
             if (!table.TryGetValue(key, out var value))
             {
             {
                 if (required)
                 if (required)
                 {
                 {
-                    throw new LuaRuntimeException(thread, $"field '{key}' missing in date table");
+                    throw new LuaRuntimeException(state, $"field '{key}' missing in date table");
                 }
                 }
                 else
                 else
                 {
                 {
@@ -51,20 +51,20 @@ static class DateTimeHelper
                 return (int)d;
                 return (int)d;
             }
             }
 
 
-            throw new LuaRuntimeException(thread, $"field '{key}' is not an integer");
+            throw new LuaRuntimeException(state, $"field '{key}' is not an integer");
         }
         }
 
 
-        var day = GetTimeField(thread, table, "day");
-        var month = GetTimeField(thread, table, "month");
-        var year = GetTimeField(thread, table, "year");
-        var sec = GetTimeField(thread, table, "sec", false, 0);
-        var min = GetTimeField(thread, table, "min", false, 0);
-        var hour = GetTimeField(thread, table, "hour", false, 12);
+        var day = GetTimeField(state, table, "day");
+        var month = GetTimeField(state, table, "month");
+        var year = GetTimeField(state, table, "year");
+        var sec = GetTimeField(state, table, "sec", false, 0);
+        var min = GetTimeField(state, table, "min", false, 0);
+        var hour = GetTimeField(state, table, "hour", false, 12);
 
 
         return new(year, month, day, hour, min, sec);
         return new(year, month, day, hour, min, sec);
     }
     }
 
 
-    public static string StrFTime(LuaThread thread, ReadOnlySpan<char> format, DateTime d)
+    public static string StrFTime(LuaState state, ReadOnlySpan<char> format, DateTime d)
     {
     {
         // reference: http://www.cplusplus.com/reference/ctime/strftime/
         // reference: http://www.cplusplus.com/reference/ctime/strftime/
 
 
@@ -194,7 +194,7 @@ static class DateTimeHelper
             }
             }
             else
             else
             {
             {
-                throw new LuaRuntimeException(thread, $"bad argument #1 to 'date' (invalid conversion specifier '{format.ToString()}')");
+                throw new LuaRuntimeException(state, $"bad argument #1 to 'date' (invalid conversion specifier '{format.ToString()}')");
             }
             }
         }
         }
 
 

+ 15 - 15
src/Lua/Standard/Internal/IOHelper.cs

@@ -7,19 +7,19 @@ namespace Lua.Standard.Internal;
 
 
 static class IOHelper
 static class IOHelper
 {
 {
-    public static async ValueTask<int> Open(LuaThread thread, string fileName, string mode, bool throwError, CancellationToken cancellationToken)
+    public static async ValueTask<int> Open(LuaState state, string fileName, string mode, bool throwError, CancellationToken cancellationToken)
     {
     {
         var fileMode = LuaFileOpenModeExtensions.ParseModeFromString(mode);
         var fileMode = LuaFileOpenModeExtensions.ParseModeFromString(mode);
         if (!fileMode.IsValid())
         if (!fileMode.IsValid())
         {
         {
-            throw new LuaRuntimeException(thread, "bad argument #2 to 'open' (invalid mode)");
+            throw new LuaRuntimeException(state, "bad argument #2 to 'open' (invalid mode)");
         }
         }
 
 
         try
         try
         {
         {
-            var stream = await thread.State.FileSystem.Open(fileName, fileMode, cancellationToken);
+            var stream = await state.GlobalState.Platform.FileSystem.Open(fileName, fileMode, cancellationToken);
 
 
-            thread.Stack.Push(new(new FileHandle(stream)));
+            state.Stack.Push(new(new FileHandle(stream)));
             return 1;
             return 1;
         }
         }
         catch (IOException ex)
         catch (IOException ex)
@@ -29,9 +29,9 @@ static class IOHelper
                 throw;
                 throw;
             }
             }
 
 
-            thread.Stack.Push(LuaValue.Nil);
-            thread.Stack.Push(ex.Message);
-            thread.Stack.Push(ex.HResult);
+            state.Stack.Push(LuaValue.Nil);
+            state.Stack.Push(ex.Message);
+            state.Stack.Push(ex.HResult);
             return 3;
             return 3;
         }
         }
     }
     }
@@ -58,35 +58,35 @@ static class IOHelper
                 }
                 }
                 else
                 else
                 {
                 {
-                    LuaRuntimeException.BadArgument(context.Thread, i + 1, name);
+                    LuaRuntimeException.BadArgument(context.State, i + 1, name);
                 }
                 }
             }
             }
         }
         }
         catch (IOException ex)
         catch (IOException ex)
         {
         {
-            context.Thread.Stack.PopUntil(context.ReturnFrameBase);
-            var stack = context.Thread.Stack;
+            context.State.Stack.PopUntil(context.ReturnFrameBase);
+            var stack = context.State.Stack;
             stack.Push(LuaValue.Nil);
             stack.Push(LuaValue.Nil);
             stack.Push(ex.Message);
             stack.Push(ex.Message);
             stack.Push(ex.HResult);
             stack.Push(ex.HResult);
             return 3;
             return 3;
         }
         }
 
 
-        context.Thread.Stack.PopUntil(context.ReturnFrameBase);
-        context.Thread.Stack.Push(new(file));
+        context.State.Stack.PopUntil(context.ReturnFrameBase);
+        context.State.Stack.Push(new(file));
         return 1;
         return 1;
     }
     }
 
 
     static readonly LuaValue[] defaultReadFormat = ["*l"];
     static readonly LuaValue[] defaultReadFormat = ["*l"];
 
 
-    public static async ValueTask<int> ReadAsync(LuaThread thread, FileHandle file, string name, int startArgumentIndex, ReadOnlyMemory<LuaValue> formats, bool throwError, CancellationToken cancellationToken)
+    public static async ValueTask<int> ReadAsync(LuaState state, FileHandle file, string name, int startArgumentIndex, ReadOnlyMemory<LuaValue> formats, bool throwError, CancellationToken cancellationToken)
     {
     {
         if (formats.Length == 0)
         if (formats.Length == 0)
         {
         {
             formats = defaultReadFormat;
             formats = defaultReadFormat;
         }
         }
 
 
-        var stack = thread.Stack;
+        var stack = state.Stack;
         var top = stack.Count;
         var top = stack.Count;
 
 
         try
         try
@@ -133,7 +133,7 @@ static class IOHelper
                 }
                 }
                 else
                 else
                 {
                 {
-                    LuaRuntimeException.BadArgument(thread, i + 1, ["string", "integer"], format.TypeToString());
+                    LuaRuntimeException.BadArgument(state, i + 1, ["string", "integer"], format.TypeToString());
                 }
                 }
             }
             }
 
 

+ 9 - 9
src/Lua/Standard/Internal/MatchState.cs

@@ -2,7 +2,7 @@ using System.Buffers;
 
 
 namespace Lua.Standard.Internal;
 namespace Lua.Standard.Internal;
 
 
-class MatchState(LuaThread thread, string source, string pattern)
+class MatchState(LuaState state, string source, string pattern)
 {
 {
     internal const int LuaMaxCaptures = 32;
     internal const int LuaMaxCaptures = 32;
     const int CapUnfinished = -1;
     const int CapUnfinished = -1;
@@ -19,7 +19,7 @@ class MatchState(LuaThread thread, string source, string pattern)
         public bool IsPosition => Len == CapPosition;
         public bool IsPosition => Len == CapPosition;
     }
     }
 
 
-    public readonly LuaThread Thread = thread;
+    public readonly LuaState State = state;
     public readonly string Source = source;
     public readonly string Source = source;
     public readonly string Pattern = pattern;
     public readonly string Pattern = pattern;
     public int Level = 0;
     public int Level = 0;
@@ -39,7 +39,7 @@ class MatchState(LuaThread thread, string source, string pattern)
     {
     {
         if (Level >= LuaMaxCaptures)
         if (Level >= LuaMaxCaptures)
         {
         {
-            throw new LuaRuntimeException(Thread, "too many captures");
+            throw new LuaRuntimeException(State, "too many captures");
         }
         }
 
 
         Captures[Level].Init = sIdx;
         Captures[Level].Init = sIdx;
@@ -71,7 +71,7 @@ class MatchState(LuaThread thread, string source, string pattern)
     {
     {
         if (MatchDepth-- == 0)
         if (MatchDepth-- == 0)
         {
         {
-            throw new LuaRuntimeException(Thread, "pattern too complex");
+            throw new LuaRuntimeException(State, "pattern too complex");
         }
         }
 
 
         var endIdx = Pattern.Length;
         var endIdx = Pattern.Length;
@@ -331,7 +331,7 @@ class MatchState(LuaThread thread, string source, string pattern)
             }
             }
         }
         }
 
 
-        throw new LuaRuntimeException(Thread, "invalid pattern capture");
+        throw new LuaRuntimeException(State, "invalid pattern capture");
     }
     }
 
 
     int MatchCapture(int sIdx, int l)
     int MatchCapture(int sIdx, int l)
@@ -354,7 +354,7 @@ class MatchState(LuaThread thread, string source, string pattern)
     {
     {
         if (l < 0 || l >= Level || Captures[l].Len == CapUnfinished)
         if (l < 0 || l >= Level || Captures[l].Len == CapUnfinished)
         {
         {
-            throw new LuaRuntimeException(Thread, $"invalid capture index %{l + 1}");
+            throw new LuaRuntimeException(State, $"invalid capture index %{l + 1}");
         }
         }
 
 
         return l;
         return l;
@@ -364,7 +364,7 @@ class MatchState(LuaThread thread, string source, string pattern)
     {
     {
         if (pIdx + 1 >= Pattern.Length)
         if (pIdx + 1 >= Pattern.Length)
         {
         {
-            throw new LuaRuntimeException(Thread, "malformed pattern (missing arguments to '%b')");
+            throw new LuaRuntimeException(State, "malformed pattern (missing arguments to '%b')");
         }
         }
 
 
         if (sIdx >= Source.Length || Source[sIdx] != Pattern[pIdx])
         if (sIdx >= Source.Length || Source[sIdx] != Pattern[pIdx])
@@ -404,7 +404,7 @@ class MatchState(LuaThread thread, string source, string pattern)
             case LEsc:
             case LEsc:
                 if (pIdx >= pattern.Length)
                 if (pIdx >= pattern.Length)
                 {
                 {
-                    throw new LuaRuntimeException(Thread, "malformed pattern (ends with %)");
+                    throw new LuaRuntimeException(State, "malformed pattern (ends with %)");
                 }
                 }
 
 
                 return pIdx + 1;
                 return pIdx + 1;
@@ -425,7 +425,7 @@ class MatchState(LuaThread thread, string source, string pattern)
 
 
                     if (pIdx >= pattern.Length)
                     if (pIdx >= pattern.Length)
                     {
                     {
-                        throw new LuaRuntimeException(Thread, "malformed pattern (missing ']')");
+                        throw new LuaRuntimeException(State, "malformed pattern (missing ']')");
                     }
                     }
                 } while (pIdx < pattern.Length && pattern[pIdx] != ']');
                 } while (pIdx < pattern.Length && pattern[pIdx] != ']');
 
 

+ 2 - 2
src/Lua/Standard/MathematicsLibrary.cs

@@ -206,7 +206,7 @@ public sealed class MathematicsLibrary
 
 
     public ValueTask<int> Random(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Random(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        var rand = context.State.Environment[RandomInstanceKey].Read<RandomUserData>().Random;
+        var rand = context.GlobalState.Environment[RandomInstanceKey].Read<RandomUserData>().Random;
 
 
         if (context.ArgumentCount == 0)
         if (context.ArgumentCount == 0)
         {
         {
@@ -228,7 +228,7 @@ public sealed class MathematicsLibrary
     public ValueTask<int> RandomSeed(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> RandomSeed(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<double>(0);
         var arg0 = context.GetArgument<double>(0);
-        context.State.Environment[RandomInstanceKey] = new(new RandomUserData(new((int)BitConverter.DoubleToInt64Bits(arg0))));
+        context.GlobalState.Environment[RandomInstanceKey] = new(new RandomUserData(new((int)BitConverter.DoubleToInt64Bits(arg0))));
         return new(context.Return());
         return new(context.Return());
     }
     }
 
 

+ 23 - 25
src/Lua/Standard/ModuleLibrary.cs

@@ -20,12 +20,12 @@ public sealed class ModuleLibrary
     public async ValueTask<int> Require(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Require(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<string>(0);
         var arg0 = context.GetArgument<string>(0);
-        var loaded = context.State.LoadedModules;
+        var loaded = context.GlobalState.LoadedModules;
 
 
         if (!loaded.TryGetValue(arg0, out var loadedTable))
         if (!loaded.TryGetValue(arg0, out var loadedTable))
         {
         {
             LuaFunction loader;
             LuaFunction loader;
-            var moduleLoader = context.State.ModuleLoader;
+            var moduleLoader = context.GlobalState.ModuleLoader;
             if (moduleLoader != null && moduleLoader.Exists(arg0))
             if (moduleLoader != null && moduleLoader.Exists(arg0))
             {
             {
                 var module = await moduleLoader.LoadAsync(arg0, cancellationToken);
                 var module = await moduleLoader.LoadAsync(arg0, cancellationToken);
@@ -35,26 +35,25 @@ public sealed class ModuleLibrary
             }
             }
             else
             else
             {
             {
-                loader = await FindLoader(context.Access, arg0, cancellationToken);
+                loader = await FindLoader(context.State, arg0, cancellationToken);
             }
             }
 
 
-            await context.Access.RunAsync(loader, 0, context.ReturnFrameBase, cancellationToken);
-            loadedTable = context.Thread.Stack.Get(context.ReturnFrameBase);
+            await context.State.RunAsync(loader, 0, context.ReturnFrameBase, cancellationToken);
+            loadedTable = context.State.Stack.Get(context.ReturnFrameBase);
             loaded[arg0] = loadedTable;
             loaded[arg0] = loadedTable;
         }
         }
 
 
         return context.Return(loadedTable);
         return context.Return(loadedTable);
     }
     }
 
 
-    internal static async ValueTask<string?> FindFile(LuaThreadAccess access, string name, string pName, string dirSeparator)
+    internal static async ValueTask<string?> FindFile(LuaState state, string name, string pName, string dirSeparator)
     {
     {
-        var thread = access.Thread;
-        var state = thread.State;
-        var package = state.Environment["package"];
-        var p = await access.GetTable(package, pName);
+        var globalState = state.GlobalState;
+        var package = globalState.Environment["package"];
+        var p = await state.GetTable(package, pName);
         if (!p.TryReadString(out var path))
         if (!p.TryReadString(out var path))
         {
         {
-            throw new LuaRuntimeException(thread, $"package.{pName} must be a string");
+            throw new LuaRuntimeException(state, $"package.{pName} must be a string");
         }
         }
 
 
         return SearchPath(state, name, path, ".", dirSeparator);
         return SearchPath(state, name, path, ".", dirSeparator);
@@ -88,7 +87,7 @@ public sealed class ModuleLibrary
         {
         {
             path = pathSpan[..nextIndex].ToString();
             path = pathSpan[..nextIndex].ToString();
             var fileName = path.Replace("?", name);
             var fileName = path.Replace("?", name);
-            if (state.FileSystem.IsReadable(fileName))
+            if (state.GlobalState.Platform.FileSystem.IsReadable(fileName))
             {
             {
                 return fileName;
                 return fileName;
             }
             }
@@ -109,10 +108,9 @@ public sealed class ModuleLibrary
         return null;
         return null;
     }
     }
 
 
-    internal static async ValueTask<LuaFunction> FindLoader(LuaThreadAccess access, string name, CancellationToken cancellationToken)
+    internal static async ValueTask<LuaFunction> FindLoader(LuaState state, string name, CancellationToken cancellationToken)
     {
     {
-        var state = access.State;
-        var package = state.Environment["package"].Read<LuaTable>();
+        var package = state.GlobalState.Environment["package"].Read<LuaTable>();
         var searchers = package["searchers"].Read<LuaTable>();
         var searchers = package["searchers"].Read<LuaTable>();
         for (var i = 0; i < searchers.GetArraySpan().Length; i++)
         for (var i = 0; i < searchers.GetArraySpan().Length; i++)
         {
         {
@@ -123,30 +121,30 @@ public sealed class ModuleLibrary
             }
             }
 
 
             var loader = searcher;
             var loader = searcher;
-            var top = access.Stack.Count;
-            access.Stack.Push(loader);
-            access.Stack.Push(name);
-            var resultCount = await access.Call(top, top, cancellationToken);
+            var top = state.Stack.Count;
+            state.Stack.Push(loader);
+            state.Stack.Push(name);
+            var resultCount = await state.Call(top, top, cancellationToken);
             if (0 < resultCount)
             if (0 < resultCount)
             {
             {
-                var result = access.Stack.Get(top);
+                var result = state.Stack.Get(top);
                 if (result.Type == LuaValueType.Function)
                 if (result.Type == LuaValueType.Function)
                 {
                 {
-                    access.Stack.SetTop(top);
+                    state.Stack.SetTop(top);
                     return result.Read<LuaFunction>();
                     return result.Read<LuaFunction>();
                 }
                 }
             }
             }
 
 
-            access.Stack.SetTop(top);
+            state.Stack.SetTop(top);
         }
         }
 
 
-        throw new LuaRuntimeException(access.Thread, $"Module '{name}' not found");
+        throw new LuaRuntimeException(state, $"Module '{name}' not found");
     }
     }
 
 
     public ValueTask<int> SearcherPreload(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> SearcherPreload(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var name = context.GetArgument<string>(0);
         var name = context.GetArgument<string>(0);
-        var preload = context.State.PreloadModules[name];
+        var preload = context.GlobalState.PreloadModules[name];
         if (preload == LuaValue.Nil)
         if (preload == LuaValue.Nil)
         {
         {
             return new(context.Return());
             return new(context.Return());
@@ -158,7 +156,7 @@ public sealed class ModuleLibrary
     public async ValueTask<int> SearcherLua(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> SearcherLua(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var name = context.GetArgument<string>(0);
         var name = context.GetArgument<string>(0);
-        var fileName = await FindFile(context.Access, name, "path", context.State.FileSystem.DirectorySeparator);
+        var fileName = await FindFile(context.State, name, "path", context.GlobalState.Platform.FileSystem.DirectorySeparator);
         if (fileName == null)
         if (fileName == null)
         {
         {
             return context.Return(LuaValue.Nil);
             return context.Return(LuaValue.Nil);

+ 39 - 27
src/Lua/Standard/OpenLibsExtensions.cs

@@ -7,47 +7,52 @@ public static class OpenLibsExtensions
 {
 {
     public static void OpenBasicLibrary(this LuaState state)
     public static void OpenBasicLibrary(this LuaState state)
     {
     {
-        state.Environment["_G"] = state.Environment;
-        state.Environment["_VERSION"] = "Lua 5.2";
+        var globalState = state.GlobalState;
+        globalState.Environment["_G"] = globalState.Environment;
+        globalState.Environment["_VERSION"] = "Lua 5.2";
         foreach (var func in BasicLibrary.Instance.Functions)
         foreach (var func in BasicLibrary.Instance.Functions)
         {
         {
-            state.Environment[func.Name] = func;
+            globalState.Environment[func.Name] = func;
         }
         }
     }
     }
 
 
     public static void OpenBitwiseLibrary(this LuaState state)
     public static void OpenBitwiseLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
+
         LuaTable bit32 = new(0, BitwiseLibrary.Instance.Functions.Length);
         LuaTable bit32 = new(0, BitwiseLibrary.Instance.Functions.Length);
         foreach (var func in BitwiseLibrary.Instance.Functions)
         foreach (var func in BitwiseLibrary.Instance.Functions)
         {
         {
             bit32[func.Name] = func.Func;
             bit32[func.Name] = func.Func;
         }
         }
 
 
-        state.Environment["bit32"] = bit32;
-        state.LoadedModules["bit32"] = bit32;
+        globalState.Environment["bit32"] = bit32;
+        globalState.LoadedModules["bit32"] = bit32;
     }
     }
 
 
     public static void OpenCoroutineLibrary(this LuaState state)
     public static void OpenCoroutineLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable coroutine = new(0, CoroutineLibrary.Instance.Functions.Length);
         LuaTable coroutine = new(0, CoroutineLibrary.Instance.Functions.Length);
         foreach (var func in CoroutineLibrary.Instance.Functions)
         foreach (var func in CoroutineLibrary.Instance.Functions)
         {
         {
             coroutine[func.Name] = func.Func;
             coroutine[func.Name] = func.Func;
         }
         }
 
 
-        state.Environment["coroutine"] = coroutine;
+        globalState.Environment["coroutine"] = coroutine;
     }
     }
 
 
     public static void OpenIOLibrary(this LuaState state)
     public static void OpenIOLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable io = new(0, IOLibrary.Instance.Functions.Length);
         LuaTable io = new(0, IOLibrary.Instance.Functions.Length);
         foreach (var func in IOLibrary.Instance.Functions)
         foreach (var func in IOLibrary.Instance.Functions)
         {
         {
             io[func.Name] = func.Func;
             io[func.Name] = func.Func;
         }
         }
 
 
-        var registry = state.Registry;
-        var standardIO = state.StandardIO;
+        var registry = globalState.Registry;
+        var standardIO = globalState.Platform.StandardIO;
         LuaValue stdin = new(new FileHandle(standardIO.Input));
         LuaValue stdin = new(new FileHandle(standardIO.Input));
         LuaValue stdout = new(new FileHandle(standardIO.Output));
         LuaValue stdout = new(new FileHandle(standardIO.Output));
         LuaValue stderr = new(new FileHandle(standardIO.Error));
         LuaValue stderr = new(new FileHandle(standardIO.Error));
@@ -57,13 +62,14 @@ public static class OpenLibsExtensions
         io["stdout"] = stdout;
         io["stdout"] = stdout;
         io["stderr"] = stderr;
         io["stderr"] = stderr;
 
 
-        state.Environment["io"] = io;
-        state.LoadedModules["io"] = io;
+        globalState.Environment["io"] = io;
+        globalState.LoadedModules["io"] = io;
     }
     }
 
 
     public static void OpenMathLibrary(this LuaState state)
     public static void OpenMathLibrary(this LuaState state)
     {
     {
-        state.Environment[MathematicsLibrary.RandomInstanceKey] = new(new MathematicsLibrary.RandomUserData(new()));
+        var globalState = state.GlobalState;
+        globalState.Environment[MathematicsLibrary.RandomInstanceKey] = new(new MathematicsLibrary.RandomUserData(new()));
 
 
         LuaTable math = new(0, MathematicsLibrary.Instance.Functions.Length);
         LuaTable math = new(0, MathematicsLibrary.Instance.Functions.Length);
         foreach (var func in MathematicsLibrary.Instance.Functions)
         foreach (var func in MathematicsLibrary.Instance.Functions)
@@ -74,15 +80,16 @@ public static class OpenLibsExtensions
         math["pi"] = Math.PI;
         math["pi"] = Math.PI;
         math["huge"] = double.PositiveInfinity;
         math["huge"] = double.PositiveInfinity;
 
 
-        state.Environment["math"] = math;
-        state.LoadedModules["math"] = math;
+        globalState.Environment["math"] = math;
+        globalState.LoadedModules["math"] = math;
     }
     }
 
 
     public static void OpenModuleLibrary(this LuaState state)
     public static void OpenModuleLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable package = new(0, 8);
         LuaTable package = new(0, 8);
-        package["loaded"] = state.LoadedModules;
-        package["preload"] = state.PreloadModules;
+        package["loaded"] = globalState.LoadedModules;
+        package["preload"] = globalState.PreloadModules;
         var moduleLibrary = ModuleLibrary.Instance;
         var moduleLibrary = ModuleLibrary.Instance;
         LuaTable searchers = new();
         LuaTable searchers = new();
         searchers[1] = new LuaFunction("preload", moduleLibrary.SearcherPreload);
         searchers[1] = new LuaFunction("preload", moduleLibrary.SearcherPreload);
@@ -91,39 +98,41 @@ public static class OpenLibsExtensions
         package["path"] = "?.lua";
         package["path"] = "?.lua";
         package["searchpath"] = moduleLibrary.SearchPathFunction;
         package["searchpath"] = moduleLibrary.SearchPathFunction;
         package["config"] = $"{Path.DirectorySeparatorChar}\n;\n?\n!\n-";
         package["config"] = $"{Path.DirectorySeparatorChar}\n;\n?\n!\n-";
-        state.Environment["package"] = package;
-        state.Environment["require"] = moduleLibrary.RequireFunction;
+        globalState.Environment["package"] = package;
+        globalState.Environment["require"] = moduleLibrary.RequireFunction;
     }
     }
 
 
     public static void OpenOperatingSystemLibrary(this LuaState state)
     public static void OpenOperatingSystemLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable os = new(0, OperatingSystemLibrary.Instance.Functions.Length);
         LuaTable os = new(0, OperatingSystemLibrary.Instance.Functions.Length);
         foreach (var func in OperatingSystemLibrary.Instance.Functions)
         foreach (var func in OperatingSystemLibrary.Instance.Functions)
         {
         {
             os[func.Name] = func.Func;
             os[func.Name] = func.Func;
         }
         }
 
 
-        state.Environment["os"] = os;
-        state.LoadedModules["os"] = os;
+        globalState.Environment["os"] = os;
+        globalState.LoadedModules["os"] = os;
     }
     }
 
 
     public static void OpenStringLibrary(this LuaState state)
     public static void OpenStringLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable @string = new(0, StringLibrary.Instance.Functions.Length);
         LuaTable @string = new(0, StringLibrary.Instance.Functions.Length);
         foreach (var func in StringLibrary.Instance.Functions)
         foreach (var func in StringLibrary.Instance.Functions)
         {
         {
             @string[func.Name] = func.Func;
             @string[func.Name] = func.Func;
         }
         }
 
 
-        state.Environment["string"] = @string;
-        state.LoadedModules["string"] = @string;
+        globalState.Environment["string"] = @string;
+        globalState.LoadedModules["string"] = @string;
 
 
         // set __index
         // set __index
         LuaValue key = new("");
         LuaValue key = new("");
-        if (!state.TryGetMetatable(key, out var metatable))
+        if (!globalState.TryGetMetatable(key, out var metatable))
         {
         {
             metatable = new();
             metatable = new();
-            state.SetMetatable(key, metatable);
+            globalState.SetMetatable(key, metatable);
         }
         }
 
 
         metatable[Metamethods.Index] = new LuaFunction("index", (context, cancellationToken) =>
         metatable[Metamethods.Index] = new LuaFunction("index", (context, cancellationToken) =>
@@ -136,26 +145,29 @@ public static class OpenLibsExtensions
 
 
     public static void OpenTableLibrary(this LuaState state)
     public static void OpenTableLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable table = new(0, TableLibrary.Instance.Functions.Length);
         LuaTable table = new(0, TableLibrary.Instance.Functions.Length);
         foreach (var func in TableLibrary.Instance.Functions)
         foreach (var func in TableLibrary.Instance.Functions)
         {
         {
             table[func.Name] = func.Func;
             table[func.Name] = func.Func;
         }
         }
 
 
-        state.Environment["table"] = table;
-        state.LoadedModules["table"] = table;
+        globalState.Environment["table"] = table;
+        globalState.LoadedModules["table"] = table;
     }
     }
 
 
+
     public static void OpenDebugLibrary(this LuaState state)
     public static void OpenDebugLibrary(this LuaState state)
     {
     {
+        var globalState = state.GlobalState;
         LuaTable debug = new(0, DebugLibrary.Instance.Functions.Length);
         LuaTable debug = new(0, DebugLibrary.Instance.Functions.Length);
         foreach (var func in DebugLibrary.Instance.Functions)
         foreach (var func in DebugLibrary.Instance.Functions)
         {
         {
             debug[func.Name] = func.Func;
             debug[func.Name] = func.Func;
         }
         }
 
 
-        state.Environment["debug"] = debug;
-        state.LoadedModules["debug"] = debug;
+        globalState.Environment["debug"] = debug;
+        globalState.LoadedModules["debug"] = debug;
     }
     }
 
 
     public static void OpenStandardLibraries(this LuaState state)
     public static void OpenStandardLibraries(this LuaState state)

+ 12 - 12
src/Lua/Standard/OperatingSystemLibrary.cs

@@ -28,7 +28,7 @@ public sealed class OperatingSystemLibrary
 
 
     public ValueTask<int> Clock(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Clock(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        return new(context.Return(context.State.OsEnvironment.GetTotalProcessorTime()));
+        return new(context.Return(context.GlobalState.Platform.OsEnvironment.GetTotalProcessorTime()));
     }
     }
 
 
     public ValueTask<int> Date(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> Date(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
@@ -45,7 +45,7 @@ public sealed class OperatingSystemLibrary
         }
         }
         else
         else
         {
         {
-            now = context.State.TimeProvider.GetUtcNow().DateTime;
+            now = context.GlobalState.Platform.TimeProvider.GetUtcNow().DateTime;
         }
         }
 
 
         var isDst = false;
         var isDst = false;
@@ -55,7 +55,7 @@ public sealed class OperatingSystemLibrary
         }
         }
         else
         else
         {
         {
-            now = context.State.TimeProvider.GetLocalNow().DateTime;
+            now = context.GlobalState.Platform.TimeProvider.GetLocalNow().DateTime;
             isDst = now.IsDaylightSavingTime();
             isDst = now.IsDaylightSavingTime();
         }
         }
 
 
@@ -78,7 +78,7 @@ public sealed class OperatingSystemLibrary
         }
         }
         else
         else
         {
         {
-            return new(context.Return(DateTimeHelper.StrFTime(context.Thread, format, now)));
+            return new(context.Return(DateTimeHelper.StrFTime(context.State, format, now)));
         }
         }
     }
     }
 
 
@@ -121,18 +121,18 @@ public sealed class OperatingSystemLibrary
             }
             }
             else
             else
             {
             {
-                LuaRuntimeException.BadArgument(context.Thread, 1, LuaValueType.Nil, code.Type);
+                LuaRuntimeException.BadArgument(context.State, 1, LuaValueType.Nil, code.Type);
             }
             }
         }
         }
 
 
-        await context.State.OsEnvironment.Exit(exitCode, cancellationToken);
+        await context.GlobalState.Platform.OsEnvironment.Exit(exitCode, cancellationToken);
         throw new InvalidOperationException("Unreachable code.. reached.");
         throw new InvalidOperationException("Unreachable code.. reached.");
     }
     }
 
 
     public ValueTask<int> GetEnv(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> GetEnv(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var variable = context.GetArgument<string>(0);
         var variable = context.GetArgument<string>(0);
-        return new(context.Return(context.State.OsEnvironment.GetEnvironmentVariable(variable) ?? LuaValue.Nil));
+        return new(context.Return(context.GlobalState.Platform.OsEnvironment.GetEnvironmentVariable(variable) ?? LuaValue.Nil));
     }
     }
 
 
     public async ValueTask<int> Remove(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Remove(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
@@ -140,7 +140,7 @@ public sealed class OperatingSystemLibrary
         var fileName = context.GetArgument<string>(0);
         var fileName = context.GetArgument<string>(0);
         try
         try
         {
         {
-            await context.State.FileSystem.Remove(fileName, cancellationToken);
+            await context.GlobalState.Platform.FileSystem.Remove(fileName, cancellationToken);
             return context.Return(true);
             return context.Return(true);
         }
         }
         catch (IOException ex)
         catch (IOException ex)
@@ -155,7 +155,7 @@ public sealed class OperatingSystemLibrary
         var newName = context.GetArgument<string>(1);
         var newName = context.GetArgument<string>(1);
         try
         try
         {
         {
-            await context.State.FileSystem.Rename(oldName, newName, cancellationToken);
+            await context.GlobalState.Platform.FileSystem.Rename(oldName, newName, cancellationToken);
             return context.Return(true);
             return context.Return(true);
         }
         }
         catch (IOException ex)
         catch (IOException ex)
@@ -175,17 +175,17 @@ public sealed class OperatingSystemLibrary
         if (context.HasArgument(0))
         if (context.HasArgument(0))
         {
         {
             var table = context.GetArgument<LuaTable>(0);
             var table = context.GetArgument<LuaTable>(0);
-            var date = DateTimeHelper.ParseTimeTable(context.Thread, table);
+            var date = DateTimeHelper.ParseTimeTable(context.State, table);
             return new(context.Return(DateTimeHelper.GetUnixTime(date)));
             return new(context.Return(DateTimeHelper.GetUnixTime(date)));
         }
         }
         else
         else
         {
         {
-            return new(context.Return(DateTimeHelper.GetUnixTime(context.State.TimeProvider.GetUtcNow().DateTime)));
+            return new(context.Return(DateTimeHelper.GetUnixTime(context.GlobalState.Platform.TimeProvider.GetUtcNow().DateTime)));
         }
         }
     }
     }
 
 
     public ValueTask<int> TmpName(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public ValueTask<int> TmpName(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
-        return new(context.Return(context.State.FileSystem.GetTempFileName()));
+        return new(context.Return(context.GlobalState.Platform.FileSystem.GetTempFileName()));
     }
     }
 }
 }

+ 31 - 31
src/Lua/Standard/StringLibrary.cs

@@ -45,8 +45,8 @@ public sealed class StringLibrary
             ? context.GetArgument<double>(2)
             ? context.GetArgument<double>(2)
             : i;
             : i;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, i);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, j);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, i);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 3, j);
 
 
         var span = StringHelper.Slice(s, (int)i, (int)j);
         var span = StringHelper.Slice(s, (int)i, (int)j);
         var buffer = context.GetReturnBuffer(span.Length);
         var buffer = context.GetReturnBuffer(span.Length);
@@ -69,7 +69,7 @@ public sealed class StringLibrary
         for (var i = 0; i < context.ArgumentCount; i++)
         for (var i = 0; i < context.ArgumentCount; i++)
         {
         {
             var arg = context.GetArgument<double>(i);
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, i + 1, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, i + 1, arg);
             builder.Append((char)arg);
             builder.Append((char)arg);
         }
         }
 
 
@@ -89,7 +89,7 @@ public sealed class StringLibrary
     public async ValueTask<int> Format(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     public async ValueTask<int> Format(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
     {
         var format = context.GetArgument<string>(0);
         var format = context.GetArgument<string>(0);
-        var stack = context.Thread.Stack;
+        var stack = context.State.Stack;
         // TODO: pooling StringBuilder
         // TODO: pooling StringBuilder
         StringBuilder builder = new(format.Length * 2);
         StringBuilder builder = new(format.Length * 2);
         var parameterIndex = 1;
         var parameterIndex = 1;
@@ -124,7 +124,7 @@ public sealed class StringLibrary
                         case '-':
                         case '-':
                             if (leftJustify)
                             if (leftJustify)
                             {
                             {
-                                throw new LuaRuntimeException(context.Thread, "invalid format (repeated flags)");
+                                throw new LuaRuntimeException(context.State, "invalid format (repeated flags)");
                             }
                             }
 
 
                             leftJustify = true;
                             leftJustify = true;
@@ -132,7 +132,7 @@ public sealed class StringLibrary
                         case '+':
                         case '+':
                             if (plusSign)
                             if (plusSign)
                             {
                             {
-                                throw new LuaRuntimeException(context.Thread, "invalid format (repeated flags)");
+                                throw new LuaRuntimeException(context.State, "invalid format (repeated flags)");
                             }
                             }
 
 
                             plusSign = true;
                             plusSign = true;
@@ -140,7 +140,7 @@ public sealed class StringLibrary
                         case '0':
                         case '0':
                             if (zeroPadding)
                             if (zeroPadding)
                             {
                             {
-                                throw new LuaRuntimeException(context.Thread, "invalid format (repeated flags)");
+                                throw new LuaRuntimeException(context.State, "invalid format (repeated flags)");
                             }
                             }
 
 
                             zeroPadding = true;
                             zeroPadding = true;
@@ -148,7 +148,7 @@ public sealed class StringLibrary
                         case '#':
                         case '#':
                             if (alternateForm)
                             if (alternateForm)
                             {
                             {
-                                throw new LuaRuntimeException(context.Thread, "invalid format (repeated flags)");
+                                throw new LuaRuntimeException(context.State, "invalid format (repeated flags)");
                             }
                             }
 
 
                             alternateForm = true;
                             alternateForm = true;
@@ -156,7 +156,7 @@ public sealed class StringLibrary
                         case ' ':
                         case ' ':
                             if (blank)
                             if (blank)
                             {
                             {
-                                throw new LuaRuntimeException(context.Thread, "invalid format (repeated flags)");
+                                throw new LuaRuntimeException(context.State, "invalid format (repeated flags)");
                             }
                             }
 
 
                             blank = true;
                             blank = true;
@@ -182,7 +182,7 @@ public sealed class StringLibrary
 
 
                     if (char.IsDigit(format[i]))
                     if (char.IsDigit(format[i]))
                     {
                     {
-                        throw new LuaRuntimeException(context.Thread, "invalid format (width or precision too long)");
+                        throw new LuaRuntimeException(context.State, "invalid format (width or precision too long)");
                     }
                     }
 
 
                     width = int.Parse(format.AsSpan()[start..i]);
                     width = int.Parse(format.AsSpan()[start..i]);
@@ -205,7 +205,7 @@ public sealed class StringLibrary
 
 
                     if (char.IsDigit(format[i]))
                     if (char.IsDigit(format[i]))
                     {
                     {
-                        throw new LuaRuntimeException(context.Thread, "invalid format (width or precision too long)");
+                        throw new LuaRuntimeException(context.State, "invalid format (width or precision too long)");
                     }
                     }
 
 
                     precision = int.Parse(format.AsSpan()[start..i]);
                     precision = int.Parse(format.AsSpan()[start..i]);
@@ -216,7 +216,7 @@ public sealed class StringLibrary
 
 
                 if (context.ArgumentCount <= parameterIndex)
                 if (context.ArgumentCount <= parameterIndex)
                 {
                 {
-                    throw new LuaRuntimeException(context.Thread, $"bad argument #{parameterIndex + 1} to 'format' (no value)");
+                    throw new LuaRuntimeException(context.State, $"bad argument #{parameterIndex + 1} to 'format' (no value)");
                 }
                 }
 
 
                 var parameter = context.GetArgument(parameterIndex++);
                 var parameter = context.GetArgument(parameterIndex++);
@@ -231,7 +231,7 @@ public sealed class StringLibrary
                     case 'G':
                     case 'G':
                         if (!parameter.TryRead<double>(out var f))
                         if (!parameter.TryRead<double>(out var f))
                         {
                         {
-                            LuaRuntimeException.BadArgument(context.Thread, parameterIndex + 1, LuaValueType.Number, parameter.Type);
+                            LuaRuntimeException.BadArgument(context.State, parameterIndex + 1, LuaValueType.Number, parameter.Type);
                         }
                         }
 
 
                         switch (specifier)
                         switch (specifier)
@@ -322,10 +322,10 @@ public sealed class StringLibrary
                     case 'X':
                     case 'X':
                         if (!parameter.TryRead<double>(out var x))
                         if (!parameter.TryRead<double>(out var x))
                         {
                         {
-                            LuaRuntimeException.BadArgument(context.Thread, parameterIndex + 1, LuaValueType.Number, parameter.Type);
+                            LuaRuntimeException.BadArgument(context.State, parameterIndex + 1, LuaValueType.Number, parameter.Type);
                         }
                         }
 
 
-                        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, parameterIndex + 1, x);
+                        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, parameterIndex + 1, x);
 
 
                         switch (specifier)
                         switch (specifier)
                         {
                         {
@@ -380,7 +380,7 @@ public sealed class StringLibrary
 
 
                         break;
                         break;
                     default:
                     default:
-                        throw new LuaRuntimeException(context.Thread, $"invalid option '%{specifier}' to 'format'");
+                        throw new LuaRuntimeException(context.State, $"invalid option '%{specifier}' to 'format'");
                 }
                 }
 
 
                 // Apply blank (' ') flag for positive numbers
                 // Apply blank (' ') flag for positive numbers
@@ -428,7 +428,7 @@ public sealed class StringLibrary
             var pattern = upValues[1].Read<string>();
             var pattern = upValues[1].Read<string>();
             var start = upValues[2].Read<int>();
             var start = upValues[2].Read<int>();
 
 
-            MatchState matchState = new(context.Thread, s, pattern);
+            MatchState matchState = new(context.State, s, pattern);
             var captures = matchState.Captures;
             var captures = matchState.Captures;
 
 
             // Check for anchor at start
             // Check for anchor at start
@@ -500,12 +500,12 @@ public sealed class StringLibrary
             ? context.GetArgument<double>(3)
             ? context.GetArgument<double>(3)
             : s.Length + 1;
             : s.Length + 1;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 4, n_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 4, n_arg);
 
 
         var n = (int)n_arg;
         var n = (int)n_arg;
 
 
         // Use MatchState instead of regex
         // Use MatchState instead of regex
-        MatchState matchState = new(context.Thread, s, pattern);
+        MatchState matchState = new(context.State, s, pattern);
         var captures = matchState.Captures;
         var captures = matchState.Captures;
 
 
         StringBuilder builder = new();
         StringBuilder builder = new();
@@ -610,14 +610,14 @@ public sealed class StringLibrary
                 else if (repl.TryRead<LuaFunction>(out var func))
                 else if (repl.TryRead<LuaFunction>(out var func))
                 {
                 {
                     // Function call with captures as arguments
                     // Function call with captures as arguments
-                    var stack = context.Thread.Stack;
+                    var stack = context.State.Stack;
 
 
                     if (matchState.Level == 0)
                     if (matchState.Level == 0)
                     {
                     {
                         // No captures, pass whole match
                         // No captures, pass whole match
                         stack.Push(s.AsSpan(sIdx, res - sIdx).ToString());
                         stack.Push(s.AsSpan(sIdx, res - sIdx).ToString());
-                        var retCount = await context.Access.RunAsync(func, 1, cancellationToken);
-                        using var results = context.Access.ReadTopValues(retCount);
+                        var retCount = await context.State.RunAsync(func, 1, cancellationToken);
+                        using var results = context.State.ReadTopValues(retCount);
                         result = results.Count > 0 ? results[0] : LuaValue.Nil;
                         result = results.Count > 0 ? results[0] : LuaValue.Nil;
                     }
                     }
                     else
                     else
@@ -636,14 +636,14 @@ public sealed class StringLibrary
                             }
                             }
                         }
                         }
 
 
-                        var retCount = await context.Access.RunAsync(func, matchState.Level, cancellationToken);
-                        using var results = context.Access.ReadTopValues(retCount);
+                        var retCount = await context.State.RunAsync(func, matchState.Level, cancellationToken);
+                        using var results = context.State.ReadTopValues(retCount);
                         result = results.Count > 0 ? results[0] : LuaValue.Nil;
                         result = results.Count > 0 ? results[0] : LuaValue.Nil;
                     }
                     }
                 }
                 }
                 else
                 else
                 {
                 {
-                    throw new LuaRuntimeException(context.Thread, "bad argument #3 to 'gsub' (string/function/table expected)");
+                    throw new LuaRuntimeException(context.State, "bad argument #3 to 'gsub' (string/function/table expected)");
                 }
                 }
 
 
                 // Handle replacement result
                 // Handle replacement result
@@ -662,7 +662,7 @@ public sealed class StringLibrary
                 }
                 }
                 else
                 else
                 {
                 {
-                    throw new LuaRuntimeException(context.Thread, $"invalid replacement value (a {result.Type})");
+                    throw new LuaRuntimeException(context.State, $"invalid replacement value (a {result.Type})");
                 }
                 }
 
 
                 replaceCount++;
                 replaceCount++;
@@ -731,7 +731,7 @@ public sealed class StringLibrary
             ? context.GetArgument<int>(2)
             ? context.GetArgument<int>(2)
             : 1;
             : 1;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, init);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 3, init);
 
 
         // Convert to 0-based index
         // Convert to 0-based index
         if (init < 0)
         if (init < 0)
@@ -772,7 +772,7 @@ public sealed class StringLibrary
 
 
     static ValueTask<int> PatternSearch(LuaFunctionExecutionContext context, string s, string pattern, int init, bool find)
     static ValueTask<int> PatternSearch(LuaFunctionExecutionContext context, string s, string pattern, int init, bool find)
     {
     {
-        MatchState matchState = new(context.Thread, s, pattern);
+        MatchState matchState = new(context.State, s, pattern);
         var captures = matchState.Captures;
         var captures = matchState.Captures;
 
 
         // Check for anchor at start
         // Check for anchor at start
@@ -847,7 +847,7 @@ public sealed class StringLibrary
             ? context.GetArgument<string>(2)
             ? context.GetArgument<string>(2)
             : null;
             : null;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, n_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, n_arg);
 
 
         var n = (int)n_arg;
         var n = (int)n_arg;
 
 
@@ -882,8 +882,8 @@ public sealed class StringLibrary
             ? context.GetArgument<double>(2)
             ? context.GetArgument<double>(2)
             : -1;
             : -1;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, i);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, j);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, i);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 3, j);
 
 
         return new(context.Return(StringHelper.Slice(s, (int)i, (int)j).ToString()));
         return new(context.Return(StringHelper.Slice(s, (int)i, (int)j).ToString()));
     }
     }

+ 14 - 14
src/Lua/Standard/TableLibrary.cs

@@ -70,7 +70,7 @@ public sealed class TableLibrary
             }
             }
             else
             else
             {
             {
-                throw new LuaRuntimeException(context.Thread, $"invalid value ({value.Type}) at index {i} in table for 'concat'");
+                throw new LuaRuntimeException(context.State, $"invalid value ({value.Type}) at index {i} in table for 'concat'");
             }
             }
 
 
             if (i != arg3)
             if (i != arg3)
@@ -94,13 +94,13 @@ public sealed class TableLibrary
             ? context.GetArgument<double>(1)
             ? context.GetArgument<double>(1)
             : table.ArrayLength + 1;
             : table.ArrayLength + 1;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, pos_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, pos_arg);
 
 
         var pos = (int)pos_arg;
         var pos = (int)pos_arg;
 
 
         if (pos <= 0 || pos > table.ArrayLength + 1)
         if (pos <= 0 || pos > table.ArrayLength + 1)
         {
         {
-            throw new LuaRuntimeException(context.Thread, "bad argument #2 to 'insert' (position out of bounds)");
+            throw new LuaRuntimeException(context.State, "bad argument #2 to 'insert' (position out of bounds)");
         }
         }
 
 
         table.Insert(pos, value);
         table.Insert(pos, value);
@@ -129,7 +129,7 @@ public sealed class TableLibrary
             ? context.GetArgument<double>(1)
             ? context.GetArgument<double>(1)
             : table.ArrayLength;
             : table.ArrayLength;
 
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, n_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, 2, n_arg);
 
 
         var n = (int)n_arg;
         var n = (int)n_arg;
 
 
@@ -140,7 +140,7 @@ public sealed class TableLibrary
 
 
         if (n <= 0 || n > table.GetArraySpan().Length)
         if (n <= 0 || n > table.GetArraySpan().Length)
         {
         {
-            throw new LuaRuntimeException(context.Thread, "bad argument #2 to 'remove' (position out of bounds)");
+            throw new LuaRuntimeException(context.State, "bad argument #2 to 'remove' (position out of bounds)");
         }
         }
 
 
         return new(context.Return(table.RemoveAt(n)));
         return new(context.Return(table.RemoveAt(n)));
@@ -151,13 +151,13 @@ public sealed class TableLibrary
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.HasArgument(1)
         var arg1 = context.HasArgument(1)
             ? context.GetArgument<LuaFunction>(1)
             ? context.GetArgument<LuaFunction>(1)
-            : new LuaClosure(context.Thread, defaultComparer);
+            : new LuaClosure(context.State, defaultComparer);
 
 
         // discard extra  arguments
         // discard extra  arguments
         context = context with { ArgumentCount = 2 };
         context = context with { ArgumentCount = 2 };
-        context.Thread.Stack.PopUntil(context.FrameBase + 2);
+        context.State.Stack.PopUntil(context.FrameBase + 2);
 
 
-        context.Thread.PushCallStackFrame(new() { Base = context.FrameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = arg1 });
+        context.State.PushCallStackFrame(new() { Base = context.FrameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = arg1 });
         try
         try
         {
         {
             await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken);
             await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken);
@@ -165,7 +165,7 @@ public sealed class TableLibrary
         }
         }
         finally
         finally
         {
         {
-            context.Thread.PopCallStackFrameWithStackPop();
+            context.State.PopCallStackFrameWithStackPop();
         }
         }
     }
     }
 
 
@@ -183,23 +183,23 @@ public sealed class TableLibrary
     {
     {
         var pivot = memory.Span[high];
         var pivot = memory.Span[high];
         var i = low - 1;
         var i = low - 1;
-        var access = context.Thread.CurrentAccess;
+        var state = context.State;
 
 
         for (var j = low; j < high; j++)
         for (var j = low; j < high; j++)
         {
         {
-            var stack = context.Thread.Stack;
+            var stack = state.Stack;
             var top = stack.Count;
             var top = stack.Count;
             stack.Push(memory.Span[j]);
             stack.Push(memory.Span[j]);
             stack.Push(pivot);
             stack.Push(pivot);
-            await access.RunAsync(comparer, 2, cancellationToken);
+            await state.RunAsync(comparer, 2, cancellationToken);
 
 
-            if (context.Thread.Stack.Get(top).ToBoolean())
+            if (state.Stack.Get(top).ToBoolean())
             {
             {
                 i++;
                 i++;
                 Swap(memory.Span, i, j);
                 Swap(memory.Span, i, j);
             }
             }
 
 
-            context.Thread.Stack.PopUntil(top);
+            state.Stack.PopUntil(top);
         }
         }
 
 
         Swap(memory.Span, i + 1, high);
         Swap(memory.Span, i + 1, high);

+ 10 - 10
tests/Lua.Tests/AbstractFileTests.cs

@@ -30,11 +30,11 @@ public class AbstractFileTests
     {
     {
         var fileContent = "line1\nline2\r\nline3";
         var fileContent = "line1\nline2\r\nline3";
         var fileSystem = new ReadOnlyFileSystem(new() { { "test.txt", fileContent } });
         var fileSystem = new ReadOnlyFileSystem(new() { { "test.txt", fileContent } });
-        var state = LuaState.Create(new(
-            fileSystem: fileSystem,
-            osEnvironment: null!,
-            standardIO: new ConsoleStandardIO(),
-            timeProvider: TimeProvider.System
+        var state = LuaState.Create(new LuaPlatform(
+            FileSystem: fileSystem,
+            OsEnvironment: null!,
+            StandardIO: new ConsoleStandardIO(),
+            TimeProvider: TimeProvider.System
         ));
         ));
         state.OpenStandardLibraries();
         state.OpenStandardLibraries();
         try
         try
@@ -64,11 +64,11 @@ public class AbstractFileTests
     {
     {
         var fileContent = "Hello, World!";
         var fileContent = "Hello, World!";
         var fileSystem = new ReadOnlyFileSystem(new() { { "test.txt", fileContent } });
         var fileSystem = new ReadOnlyFileSystem(new() { { "test.txt", fileContent } });
-        var state = LuaState.Create(new(
-            fileSystem: fileSystem,
-            osEnvironment: null!,
-            standardIO: new ConsoleStandardIO(),
-            timeProvider: TimeProvider.System));
+        var state = LuaState.Create(new LuaPlatform(
+            FileSystem: fileSystem,
+            OsEnvironment: null!,
+            StandardIO: new ConsoleStandardIO(),
+            TimeProvider: TimeProvider.System));
         state.OpenStandardLibraries();
         state.OpenStandardLibraries();
 
 
         await state.DoStringAsync(
         await state.DoStringAsync(

+ 1 - 1
tests/Lua.Tests/AsyncTests.cs

@@ -26,7 +26,7 @@ public class AsyncTests
                         message = context.GetArgument<string>(1);
                         message = context.GetArgument<string>(1);
                     }
                     }
 
 
-                    throw new LuaAssertionException(context.Thread, message);
+                    throw new LuaAssertionException(context.State, message);
                 }
                 }
 
 
                 return context.Return(context.Arguments);
                 return context.Return(context.Arguments);

+ 2 - 2
tests/Lua.Tests/CancellationTest.cs

@@ -27,7 +27,7 @@ public class CancellationTest
                         message = context.GetArgument<string>(1);
                         message = context.GetArgument<string>(1);
                     }
                     }
 
 
-                    throw new LuaAssertionException(context.Thread, message);
+                    throw new LuaAssertionException(context.State, message);
                 }
                 }
 
 
                 return context.Return(context.Arguments);
                 return context.Return(context.Arguments);
@@ -191,7 +191,7 @@ public class CancellationTest
                      """;
                      """;
         var cancellationTokenSource = new CancellationTokenSource();
         var cancellationTokenSource = new CancellationTokenSource();
         var sw = Stopwatch.StartNew();
         var sw = Stopwatch.StartNew();
-        state.MainThread.SetHook(new("timeout", async (context, cancellationToken) =>
+        state.SetHook(new("timeout", async (context, cancellationToken) =>
         {
         {
             if (sw.ElapsedMilliseconds > 100)
             if (sw.ElapsedMilliseconds > 100)
             {
             {

+ 24 - 32
tests/Lua.Tests/LuaApiTests.cs

@@ -34,12 +34,11 @@ public class LuaApiTests
                      setmetatable(a, metatable)
                      setmetatable(a, metatable)
                      return a, b
                      return a, b
                      """;
                      """;
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0].Read<LuaTable>();
         var a = result[0].Read<LuaTable>();
         var b = result[1].Read<LuaTable>();
         var b = result[1].Read<LuaTable>();
 
 
-        var c = await access.Add(a, b);
+        var c = await state.Add(a, b);
         var table = c.Read<LuaTable>();
         var table = c.Read<LuaTable>();
         Assert.Multiple(() =>
         Assert.Multiple(() =>
         {
         {
@@ -68,11 +67,10 @@ public class LuaApiTests
                      setmetatable(a, metatable)
                      setmetatable(a, metatable)
                      return a
                      return a
                      """;
                      """;
-        var access = state.RootAccess;
 
 
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0].Read<LuaTable>();
         var a = result[0].Read<LuaTable>();
-        var c = await access.Unm(a);
+        var c = await state.Unm(a);
         var table = c.Read<LuaTable>();
         var table = c.Read<LuaTable>();
         Assert.Multiple(() =>
         Assert.Multiple(() =>
         {
         {
@@ -106,14 +104,13 @@ public class LuaApiTests
                      setmetatable(a, metatable)
                      setmetatable(a, metatable)
                      return a, b, c
                      return a, b, c
                      """;
                      """;
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0].Read<LuaTable>();
         var a = result[0].Read<LuaTable>();
         var b = result[1].Read<LuaTable>();
         var b = result[1].Read<LuaTable>();
         var c = result[2].Read<LuaTable>();
         var c = result[2].Read<LuaTable>();
-        var ab = await access.Equals(a, b);
+        var ab = await state.Equals(a, b);
         Assert.False(ab);
         Assert.False(ab);
-        var ac = await access.Equals(a, c);
+        var ac = await state.Equals(a, c);
         Assert.True(ac);
         Assert.True(ac);
     }
     }
 
 
@@ -129,12 +126,11 @@ public class LuaApiTests
                      setmetatable(a, metatable)
                      setmetatable(a, metatable)
                      return a
                      return a
                      """;
                      """;
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0].Read<LuaTable>();
         var a = result[0].Read<LuaTable>();
-        Assert.That(await access.GetTable(a, "x"), Is.EqualTo(new LuaValue(1)));
+        Assert.That(await state.GetTable(a, "x"), Is.EqualTo(new LuaValue(1)));
         a.Metatable!["__index"] = state.DoStringAsync("return function(a,b) return b end").Result[0];
         a.Metatable!["__index"] = state.DoStringAsync("return function(a,b) return b end").Result[0];
-        Assert.That(await access.GetTable(a, "x"), Is.EqualTo(new LuaValue("x")));
+        Assert.That(await state.GetTable(a, "x"), Is.EqualTo(new LuaValue("x")));
     }
     }
 
 
     [Test]
     [Test]
@@ -150,10 +146,9 @@ public class LuaApiTests
                      setmetatable(a, metatable)
                      setmetatable(a, metatable)
                      return a
                      return a
                      """;
                      """;
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0].Read<LuaTable>();
         var a = result[0].Read<LuaTable>();
-        await access.SetTable(a, "a", "b");
+        await state.SetTable(a, "a", "b");
         var b = a.Metatable!["__newindex"].Read<LuaTable>()["a"];
         var b = a.Metatable!["__newindex"].Read<LuaTable>()["a"];
         Assert.True(b.Read<string>() == "b");
         Assert.True(b.Read<string>() == "b");
     }
     }
@@ -183,14 +178,13 @@ setmetatable(c, metatable)
 
 
 return a,b,c
 return a,b,c
 ";
 ";
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         Assert.That(result, Has.Length.EqualTo(3));
         Assert.That(result, Has.Length.EqualTo(3));
 
 
         var a = result[0];
         var a = result[0];
         var b = result[1];
         var b = result[1];
         var c = result[2];
         var c = result[2];
-        var d = await access.Concat([a, b, c]);
+        var d = await state.Concat([a, b, c]);
 
 
         var table = d.Read<LuaTable>();
         var table = d.Read<LuaTable>();
         Assert.That(table.ArrayLength, Is.EqualTo(9));
         Assert.That(table.ArrayLength, Is.EqualTo(9));
@@ -229,22 +223,21 @@ return a,b,c
                      local c ={name ="c"}
                      local c ={name ="c"}
                      return a,b,c
                      return a,b,c
                      """;
                      """;
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0];
         var a = result[0];
         var b = result[1];
         var b = result[1];
         var c = result[2];
         var c = result[2];
-        var d = await access.Add(b, c);
+        var d = await state.Add(b, c);
         Assert.True(d.TryRead(out string s));
         Assert.True(d.TryRead(out string s));
         Assert.That(s, Is.EqualTo("abc"));
         Assert.That(s, Is.EqualTo("abc"));
-        d = await access.Unm(b);
+        d = await state.Unm(b);
         Assert.True(d.TryRead(out s));
         Assert.True(d.TryRead(out s));
         Assert.That(s, Is.EqualTo("abb"));
         Assert.That(s, Is.EqualTo("abb"));
-        d = await access.Concat([c, b]);
+        d = await state.Concat([c, b]);
         Assert.True(d.TryRead(out s));
         Assert.True(d.TryRead(out s));
         Assert.That(s, Is.EqualTo("acb"));
         Assert.That(s, Is.EqualTo("acb"));
 
 
-        var aResult = await access.Call(a, [b, c]);
+        var aResult = await state.Call(a, [b, c]);
         Assert.That(aResult, Has.Length.EqualTo(1));
         Assert.That(aResult, Has.Length.EqualTo(1));
         Assert.That(aResult[0].Read<string>(), Is.EqualTo("abc"));
         Assert.That(aResult[0].Read<string>(), Is.EqualTo("abc"));
     }
     }
@@ -272,22 +265,21 @@ return a,b,c
                      local c ={name ="c"}
                      local c ={name ="c"}
                      return a,b,c
                      return a,b,c
                      """;
                      """;
-        var access = state.RootAccess;
-        var result = await access.DoStringAsync(source);
+        var result = await state.DoStringAsync(source);
         var a = result[0];
         var a = result[0];
         var b = result[1];
         var b = result[1];
         var c = result[2];
         var c = result[2];
-        var d = await access.Add(b, c);
+        var d = await state.Add(b, c);
         Assert.True(d.TryRead(out string s));
         Assert.True(d.TryRead(out string s));
         Assert.That(s, Is.EqualTo("abc"));
         Assert.That(s, Is.EqualTo("abc"));
-        d = await access.Unm(b);
+        d = await state.Unm(b);
         Assert.True(d.TryRead(out s));
         Assert.True(d.TryRead(out s));
         Assert.That(s, Is.EqualTo("abb"));
         Assert.That(s, Is.EqualTo("abb"));
-        d = await access.Concat([c, b]);
+        d = await state.Concat([c, b]);
         Assert.True(d.TryRead(out s));
         Assert.True(d.TryRead(out s));
         Assert.That(s, Is.EqualTo("acb"));
         Assert.That(s, Is.EqualTo("acb"));
 
 
-        var aResult = await access.Call(a, [b, c]);
+        var aResult = await state.Call(a, [b, c]);
         Assert.That(aResult, Has.Length.EqualTo(1));
         Assert.That(aResult, Has.Length.EqualTo(1));
         Assert.That(aResult[0].Read<string>(), Is.EqualTo("abc"));
         Assert.That(aResult[0].Read<string>(), Is.EqualTo("abc"));
     }
     }

+ 1 - 1
tests/Lua.Tests/LuaTests.cs

@@ -30,7 +30,7 @@ public class LuaTests
     public async Task Test_Lua(string file)
     public async Task Test_Lua(string file)
     {
     {
         var state = LuaState.Create();
         var state = LuaState.Create();
-        state.Platform.StandardIO = new TestStandardIO();
+        state.Platform = state.Platform with { StandardIO = new TestStandardIO() };
         state.OpenStandardLibraries();
         state.OpenStandardLibraries();
         var path = FileHelper.GetAbsolutePath(file);
         var path = FileHelper.GetAbsolutePath(file);
         Directory.SetCurrentDirectory(Path.GetDirectoryName(path)!);
         Directory.SetCurrentDirectory(Path.GetDirectoryName(path)!);

+ 0 - 55
tests/Lua.Tests/ValidationTests.cs

@@ -1,55 +0,0 @@
-using Lua.Runtime;
-using Lua.Standard;
-
-namespace Lua.Tests;
-
-public class ValidationTests
-{
-    [Test]
-    public async Task Test_Simple()
-    {
-        var state = LuaState.Create();
-        state.OpenStandardLibraries();
-        LuaThreadAccess innerAccess = default!;
-        state.Environment["wait"] = new LuaFunction("wait",
-            async (context, ct) =>
-            {
-                innerAccess = context.Access;
-                await Task.Delay((int)(context.GetArgument<double>(0) * 1000), ct);
-                return context.Return(context.Arguments);
-            });
-
-        var task = state.DoStringAsync("wait(0.5)");
-
-        await Task.Delay(100);
-        Assert.That(task.IsCompleted, Is.False);
-        Assert.ThrowsAsync<InvalidOperationException>(async () =>
-        {
-            await state.DoStringAsync("print('hello')");
-        });
-        await task;
-
-        Assert.ThrowsAsync<InvalidOperationException>(async () =>
-        {
-            await innerAccess.DoStringAsync("print('hello')");
-        });
-        Assert.DoesNotThrowAsync(async () =>
-        {
-            await state.DoStringAsync("wait(0.5)");
-        });
-    }
-
-    [Test]
-    public async Task Test_Recursive()
-    {
-        var state = LuaState.Create();
-        state.OpenStandardLibraries();
-        state.Environment["dostring"] = new LuaFunction("dostring",
-            async (context, ct) => context.Return(await context.Access.DoStringAsync(context.GetArgument<string>(0), null, ct)));
-
-        var result = await state.DoStringAsync("""return dostring("return 1")""");
-
-        Assert.That(result.Length, Is.EqualTo(1));
-        Assert.That(result[0].Read<double>(), Is.EqualTo(1));
-    }
-}

Some files were not shown because too many files changed in this diff