Browse Source

Change: Return values on LuaStack

Akeit0 7 months ago
parent
commit
5210c37ad1
41 changed files with 1055 additions and 1476 deletions
  1. 4 3
      sandbox/Benchmark/AddBenchmark.cs
  2. 3 1
      sandbox/Benchmark/InterpreterSteps.cs
  3. 3 4
      sandbox/ConsoleApp1/Program.cs
  4. 16 14
      src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs
  5. 16 79
      src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs
  6. 1 1
      src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs
  7. 1 1
      src/Lua/Internal/BitFlags.cs
  8. 2 1
      src/Lua/Internal/BitFlags256.cs
  9. 26 0
      src/Lua/Internal/LuaResult.cs
  10. 17 45
      src/Lua/LuaCoroutine.cs
  11. 7 16
      src/Lua/LuaFunction.cs
  12. 62 0
      src/Lua/LuaFunctionExecutionContext.cs
  13. 6 6
      src/Lua/LuaFunctionExtensions.cs
  14. 4 6
      src/Lua/LuaMainThread.cs
  15. 18 33
      src/Lua/LuaState.cs
  16. 18 25
      src/Lua/LuaStateExtensions.cs
  17. 5 5
      src/Lua/LuaThread.cs
  18. 14 12
      src/Lua/LuaThreadExtensions.cs
  19. 6 9
      src/Lua/LuaValue.cs
  20. 2 2
      src/Lua/Runtime/CSharpClosure.cs
  21. 3 2
      src/Lua/Runtime/CallStackFrame.cs
  22. 1 1
      src/Lua/Runtime/LuaClosure.cs
  23. 12 0
      src/Lua/Runtime/LuaStack.cs
  24. 30 19
      src/Lua/Runtime/LuaVirtualMachine.Debug.cs
  25. 161 391
      src/Lua/Runtime/LuaVirtualMachine.cs
  26. 41 0
      src/Lua/Runtime/Metamethods.cs
  27. 42 41
      src/Lua/Runtime/OpCode.cs
  28. 89 153
      src/Lua/Standard/BasicLibrary.cs
  29. 35 47
      src/Lua/Standard/BitwiseLibrary.cs
  30. 39 52
      src/Lua/Standard/CoroutineLibrary.cs
  31. 61 103
      src/Lua/Standard/DebugLibrary.cs
  32. 26 40
      src/Lua/Standard/FileHandle.cs
  33. 48 56
      src/Lua/Standard/IOLibrary.cs
  34. 25 19
      src/Lua/Standard/Internal/IOHelper.cs
  35. 70 106
      src/Lua/Standard/MathematicsLibrary.cs
  36. 4 7
      src/Lua/Standard/ModuleLibrary.cs
  37. 5 7
      src/Lua/Standard/OpenLibsExtensions.cs
  38. 28 45
      src/Lua/Standard/OperatingSystemLibrary.cs
  39. 64 80
      src/Lua/Standard/StringLibrary.cs
  40. 36 42
      src/Lua/Standard/TableLibrary.cs
  41. 4 2
      tests/Lua.Tests/MetatableTests.cs

+ 4 - 3
sandbox/Benchmark/AddBenchmark.cs

@@ -22,10 +22,11 @@ public class AddBenchmark
         core.Setup("add.lua");
         core.LuaCSharpState.OpenStandardLibraries();
 
-        core.LuaCSharpState.Environment["add"] = new LuaFunction("add", (context, buffer, ct) =>
+        core.LuaCSharpState.Environment["add"] = new LuaFunction("add", (context, ct) =>
         {
-            buffer.Span[0] = context.GetArgument<double>(0) + context.GetArgument<double>(1);
-            return new(1);
+            var a = context.GetArgument<double>(0);
+            var b = context.GetArgument<double>(1);
+            return new(context.Return(a + b));
         });
         core.MoonSharpState.Globals["add"] = (Func<double, double, double>)Add;
         core.NLuaState.RegisterFunction("add", typeof(AddBenchmark).GetMethod(nameof(Add), BindingFlags.Static | BindingFlags.Public));

+ 3 - 1
sandbox/Benchmark/InterpreterSteps.cs

@@ -92,6 +92,8 @@ public class InterpreterSteps
     [Benchmark]
     public async ValueTask RunAsync()
     {
-        await state.RunAsync(chunk, results);
+        using (await state.RunAsync(chunk))
+        {
+        }
     }
 }

+ 3 - 4
sandbox/ConsoleApp1/Program.cs

@@ -27,12 +27,11 @@ try
 
     Console.WriteLine("Output " + new string('-', 50));
 
-    var results = new LuaValue[64];
-    var resultCount = await state.RunAsync(chunk, results);
+    using var results = await state.RunAsync(chunk);
 
     Console.WriteLine("Result " + new string('-', 50));
 
-    for (int i = 0; i < resultCount; i++)
+    for (int i = 0; i < results.Count; i++)
     {
         Console.WriteLine(results[i]);
     }
@@ -42,7 +41,7 @@ try
 catch (Exception ex)
 {
     Console.WriteLine(ex);
-    if(ex is LuaRuntimeException { InnerException: not null } luaEx)
+    if (ex is LuaRuntimeException { InnerException: not null } luaEx)
     {
         Console.WriteLine(luaEx.InnerException);
     }

+ 16 - 14
src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs

@@ -178,7 +178,7 @@ partial class LuaObjectGenerator
 
         PARAMETERS:
             foreach (var typeSymbol in method.Symbol.Parameters
-                .Select(x => x.Type))
+                         .Select(x => x.Type))
             {
                 if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.LuaValue)) continue;
                 if (SymbolEqualityComparer.Default.Equals(typeSymbol, typeMetadata.Symbol)) continue;
@@ -201,7 +201,7 @@ partial class LuaObjectGenerator
 
     static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context)
     {
-        builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_index = new global::Lua.LuaFunction(""index"", (context, buffer, ct) =>");
+        builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_index = new global::Lua.LuaFunction(""index"", (context, ct) =>");
 
         using (builder.BeginBlockScope())
         {
@@ -224,17 +224,17 @@ partial class LuaObjectGenerator
                 }
 
                 foreach (var methodMetadata in typeMetadata.Methods
-                    .Where(x => x.HasMemberAttribute))
+                             .Where(x => x.HasMemberAttribute))
                 {
                     builder.AppendLine(@$"""{methodMetadata.LuaMemberName}"" => new global::Lua.LuaValue(__function_{methodMetadata.LuaMemberName}),");
                 }
 
                 builder.AppendLine(@$"_ => global::Lua.LuaValue.Nil,");
             }
+
             builder.AppendLine(";");
 
-            builder.AppendLine("buffer.Span[0] = result;");
-            builder.AppendLine("return new(1);");
+            builder.AppendLine("return new global::System.Threading.Tasks.ValueTask<int>(context.Return(result));");
         }
 
         builder.AppendLine(");");
@@ -244,7 +244,7 @@ partial class LuaObjectGenerator
 
     static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context)
     {
-        builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_newindex = new global::Lua.LuaFunction(""newindex"", (context, buffer, ct) =>");
+        builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_newindex = new global::Lua.LuaFunction(""newindex"", (context, ct) =>");
 
         using (builder.BeginBlockScope())
         {
@@ -278,7 +278,7 @@ partial class LuaObjectGenerator
                 }
 
                 foreach (var methodMetadata in typeMetadata.Methods
-                    .Where(x => x.HasMemberAttribute))
+                             .Where(x => x.HasMemberAttribute))
                 {
                     builder.AppendLine(@$"case ""{methodMetadata.LuaMemberName}"":");
 
@@ -296,7 +296,7 @@ partial class LuaObjectGenerator
                 }
             }
 
-            builder.AppendLine("return new(0);");
+            builder.AppendLine("return new global::System.Threading.Tasks.ValueTask<int>(context.Return());");
         }
 
         builder.AppendLine(");");
@@ -348,7 +348,7 @@ partial class LuaObjectGenerator
 
     static void EmitMethodFunction(string functionName, string chunkName, TypeMetadata typeMetadata, MethodMetadata methodMetadata, CodeBuilder builder, SymbolReferences references)
     {
-        builder.AppendLine($@"static readonly global::Lua.LuaFunction {functionName} = new global::Lua.LuaFunction(""{chunkName}"", {(methodMetadata.IsAsync ? "async" : "")} (context, buffer, ct) =>");
+        builder.AppendLine($@"static readonly global::Lua.LuaFunction {functionName} = new global::Lua.LuaFunction(""{chunkName}"", {(methodMetadata.IsAsync ? "async" : "")} (context, ct) =>");
 
         using (builder.BeginBlockScope())
         {
@@ -388,6 +388,7 @@ partial class LuaObjectGenerator
                         builder.AppendLine($"var arg{index} = context.GetArgument<{parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>({index});");
                     }
                 }
+
                 index++;
             }
 
@@ -414,24 +415,24 @@ partial class LuaObjectGenerator
                 builder.AppendLine(");", false);
             }
 
+            builder.Append("return ");
             if (methodMetadata.HasReturnValue)
             {
                 if (SymbolEqualityComparer.Default.Equals(methodMetadata.Symbol.ReturnType, references.LuaValue))
                 {
-                    builder.AppendLine("buffer.Span[0] = result;");
+                    builder.AppendLine(methodMetadata.IsAsync ? "context.Return(result));" : "new global::System.Threading.Tasks.ValueTask<int>(context.Return(result));");
                 }
                 else
                 {
-                    builder.AppendLine("buffer.Span[0] = new global::Lua.LuaValue(result);");
+                    builder.AppendLine(methodMetadata.IsAsync ? "context.Return(new global::Lua.LuaValue(result))));" : "new global::System.Threading.Tasks.ValueTask<int>(context.Return(new global::Lua.LuaValue(result)));");
                 }
-
-                builder.AppendLine($"return {(methodMetadata.IsAsync ? "1" : "new(1)")};");
             }
             else
             {
-                builder.AppendLine($"return {(methodMetadata.IsAsync ? "0" : "new(0)")};");
+                builder.AppendLine(methodMetadata.IsAsync ? "context.Return();" : "new global::System.Threading.Tasks.ValueTask<int>(context.Return());");
             }
         }
+
         builder.AppendLine(");");
         builder.AppendLine();
     }
@@ -453,6 +454,7 @@ partial class LuaObjectGenerator
                 {
                     builder.AppendLine($"__metatable[global::Lua.Runtime.Metamethods.{metamethod}] = __metamethod_{metamethod};");
                 }
+
                 builder.AppendLine("return __metatable;");
             }
 

+ 16 - 79
src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs

@@ -26,10 +26,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         // set global enviroment upvalue
         context.AddUpValue(new()
         {
-            Name = "_ENV".AsMemory(),
-            Id = 0,
-            Index = -1,
-            IsInRegister = false,
+            Name = "_ENV".AsMemory(), Id = 0, Index = -1, IsInRegister = false,
         });
 
         context.ChunkName = chunkName;
@@ -471,11 +468,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             else
             {
                 // register local variable
-                context.AddLocalVariable(identifier.Name, new()
-                {
-                    RegisterIndex = (byte)(context.StackPosition - 1),
-                    StartPc = context.Function.Instructions.Length,
-                });
+                context.AddLocalVariable(identifier.Name, new() { RegisterIndex = (byte)(context.StackPosition - 1), StartPc = context.Function.Instructions.Length, });
             }
         }
 
@@ -605,11 +598,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationStatementNode node, ScopeCompilationContext context)
     {
         // assign local variable
-        context.AddLocalVariable(node.Name, new()
-        {
-            RegisterIndex = context.StackPosition,
-            StartPc = context.Function.Instructions.Length,
-        });
+        context.AddLocalVariable(node.Name, new() { RegisterIndex = context.StackPosition, StartPc = context.Function.Instructions.Length, });
 
         // compile function
         var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.EndPosition.Line);
@@ -688,11 +677,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
 
         if (hasSelfParameter)
         {
-            funcContext.Scope.AddLocalVariable("self".AsMemory(), new()
-            {
-                RegisterIndex = 0,
-                StartPc = 0,
-            });
+            funcContext.Scope.AddLocalVariable("self".AsMemory(), new() { RegisterIndex = 0, StartPc = 0, });
 
             funcContext.Scope.StackPosition++;
         }
@@ -701,11 +686,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         for (int i = 0; i < parameters.Length; i++)
         {
             var parameter = parameters[i];
-            funcContext.Scope.AddLocalVariable(parameter.Name, new()
-            {
-                RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)),
-                StartPc = 0,
-            });
+            funcContext.Scope.AddLocalVariable(parameter.Name, new() { RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)), StartPc = 0, });
 
             funcContext.Scope.StackPosition++;
         }
@@ -749,10 +730,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
 
     public bool VisitBreakStatementNode(BreakStatementNode node, ScopeCompilationContext context)
     {
-        context.Function.AddUnresolvedBreak(new()
-        {
-            Index = context.Function.Instructions.Length
-        }, node.Position);
+        context.Function.AddUnresolvedBreak(new() { Index = context.Function.Instructions.Length }, node.Position);
         context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
 
         return true;
@@ -921,30 +899,14 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         context.Function.LoopLevel++;
         using var scopeContext = context.CreateChildScope();
         {
-            scopeContext.AddLocalVariable("(for index)".AsMemory(), new()
-            {
-                RegisterIndex = startPosition,
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable("(for index)".AsMemory(), new() { RegisterIndex = startPosition, StartPc = context.Function.Instructions.Length, });
 
-            scopeContext.AddLocalVariable("(for limit)".AsMemory(), new()
-            {
-                RegisterIndex = (byte)(startPosition + 1),
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable("(for limit)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 1), StartPc = context.Function.Instructions.Length, });
 
-            scopeContext.AddLocalVariable("(for step)".AsMemory(), new()
-            {
-                RegisterIndex = (byte)(startPosition + 2),
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable("(for step)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 2), StartPc = context.Function.Instructions.Length, });
 
             // add local variable
-            scopeContext.AddLocalVariable(node.VariableName, new()
-            {
-                RegisterIndex = (byte)(startPosition + 3),
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable(node.VariableName, new() { RegisterIndex = (byte)(startPosition + 3), StartPc = context.Function.Instructions.Length, });
 
             foreach (var childNode in node.StatementNodes)
             {
@@ -984,33 +946,17 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         {
             scopeContext.StackPosition = (byte)(startPosition + 3 + node.Names.Length);
 
-            scopeContext.AddLocalVariable("(for generator)".AsMemory(), new()
-            {
-                RegisterIndex = (byte)(startPosition),
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable("(for generator)".AsMemory(), new() { RegisterIndex = (byte)(startPosition), StartPc = context.Function.Instructions.Length, });
 
-            scopeContext.AddLocalVariable("(for state)".AsMemory(), new()
-            {
-                RegisterIndex = (byte)(startPosition + 1),
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable("(for state)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 1), StartPc = context.Function.Instructions.Length, });
 
-            scopeContext.AddLocalVariable("(for control)".AsMemory(), new()
-            {
-                RegisterIndex = (byte)(startPosition + 2),
-                StartPc = context.Function.Instructions.Length,
-            });
+            scopeContext.AddLocalVariable("(for control)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 2), StartPc = context.Function.Instructions.Length, });
 
             // add local variables
             for (int i = 0; i < node.Names.Length; i++)
             {
                 var name = node.Names[i];
-                scopeContext.AddLocalVariable(name.Name, new()
-                {
-                    RegisterIndex = (byte)(startPosition + 3 + i),
-                    StartPc = context.Function.Instructions.Length,
-                });
+                scopeContext.AddLocalVariable(name.Name, new() { RegisterIndex = (byte)(startPosition + 3 + i), StartPc = context.Function.Instructions.Length, });
             }
 
             foreach (var childNode in node.StatementNodes)
@@ -1037,12 +983,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
 
     public bool VisitLabelStatementNode(LabelStatementNode node, ScopeCompilationContext context)
     {
-        var desc = new LabelDescription()
-        {
-            Name = node.Name,
-            Index = context.Function.Instructions.Length,
-            RegisterIndex = context.StackPosition
-        };
+        var desc = new LabelDescription() { Name = node.Name, Index = context.Function.Instructions.Length, RegisterIndex = context.StackPosition };
 
         context.AddLabel(desc);
         context.Function.ResolveGoto(desc);
@@ -1058,11 +999,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         }
         else
         {
-            context.Function.AddUnresolvedGoto(new()
-            {
-                Name = node.Name,
-                JumpInstructionIndex = context.Function.Instructions.Length
-            });
+            context.Function.AddUnresolvedGoto(new() { Name = node.Name, JumpInstructionIndex = context.Function.Instructions.Length });
 
             // add uninitialized jmp instruction
             context.PushInstruction(Instruction.Jmp(0, 0), node.Position);

+ 1 - 1
src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs

@@ -6,4 +6,4 @@ public record BooleanLiteralNode(bool Value, SourcePosition Position) : Expressi
     {
         return visitor.VisitBooleanLiteralNode(this, context);
     }
-}
+}

+ 1 - 1
src/Lua/Internal/BitFlags.cs

@@ -19,7 +19,7 @@ internal struct BitFlags2
             }
         }
     }
-    
+
     public bool Flag1
     {
         get => (Value & 2) == 2;

+ 2 - 1
src/Lua/Internal/BitFlags256.cs

@@ -3,7 +3,7 @@ namespace Lua.Internal;
 internal unsafe struct BitFlags256
 {
     internal fixed long Data[4];
-    
+
     public bool this[int index]
     {
         get => (Data[index >> 6] & (1L << (index & 63))) != 0;
@@ -19,5 +19,6 @@ internal unsafe struct BitFlags256
             }
         }
     }
+
     public void Set(int index) => Data[index >> 6] |= 1L << (index & 63);
 }

+ 26 - 0
src/Lua/Internal/LuaResult.cs

@@ -0,0 +1,26 @@
+using Lua.Runtime;
+
+namespace Lua.Internal;
+
+public readonly struct LuaResult : IDisposable
+{
+    readonly LuaStack stack;
+    readonly int returnBase;
+
+    internal LuaResult(LuaStack stack, int returnBase)
+    {
+        this.stack = stack;
+        this.returnBase = returnBase;
+    }
+
+    public int Count => stack.Count - returnBase;
+    public int Length => stack.Count - returnBase;
+    public ReadOnlySpan<LuaValue> AsSpan() => stack.AsSpan()[returnBase..];
+
+    public LuaValue this[int index] => AsSpan()[index];
+
+    public void Dispose()
+    {
+        stack.PopUntil(returnBase);
+    }
+}

+ 17 - 45
src/Lua/LuaCoroutine.cs

@@ -20,19 +20,17 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
     byte status;
     bool isFirstCall = true;
     ValueTask<int> functionTask;
-    LuaValue[] buffer;
+    int returnFrameBase;
 
     ManualResetValueTaskSourceCore<ResumeContext> resume;
     ManualResetValueTaskSourceCore<YieldContext> yield;
     Traceback? traceback;
+    internal int ReturnFrameBase => returnFrameBase;
 
     public LuaCoroutine(LuaFunction function, bool isProtectedMode)
     {
         IsProtectedMode = isProtectedMode;
         Function = function;
-
-        buffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-        buffer.AsSpan().Clear();
     }
 
     public override LuaThreadStatus GetStatus() => (LuaThreadStatus)status;
@@ -44,11 +42,9 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
 
     public bool IsProtectedMode { get; }
     public LuaFunction Function { get; }
-    
-    
     internal Traceback? LuaTraceback => traceback;
 
-    public override async ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public override async ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
     {
         var baseThread = context.Thread;
         baseThread.UnsafeSetStatus(LuaThreadStatus.Normal);
@@ -84,9 +80,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 case LuaThreadStatus.Running:
                     if (IsProtectedMode)
                     {
-                        buffer.Span[0] = false;
-                        buffer.Span[1] = "cannot resume non-suspended coroutine";
-                        return 2;
+                        return context.Return(false, "cannot resume non-suspended coroutine");
                     }
                     else
                     {
@@ -95,9 +89,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 case LuaThreadStatus.Dead:
                     if (IsProtectedMode)
                     {
-                        buffer.Span[0] = false;
-                        buffer.Span[1] = "cannot resume dead coroutine";
-                        return 2;
+                        return context.Return(false, "cannot resume dead coroutine");
                     }
                     else
                     {
@@ -121,6 +113,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
             {
                 if (isFirstCall)
                 {
+                    returnFrameBase = Stack.Count;
                     int frameBase;
                     var variableArgumentCount = Function.GetVariableArgumentCount(context.ArgumentCount - 1);
 
@@ -128,7 +121,6 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                     {
                         var fixedArgumentCount = context.ArgumentCount - 1 - variableArgumentCount;
                         var args = context.Arguments;
-
                         Stack.PushRange(args.Slice(1 + fixedArgumentCount, variableArgumentCount));
 
                         frameBase = Stack.Count;
@@ -147,47 +139,35 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                         State = context.State,
                         Thread = this,
                         ArgumentCount = context.ArgumentCount - 1,
-                        FrameBase = frameBase
-                    }, this.buffer, cancellationToken).Preserve();
+                        FrameBase = frameBase,
+                        ReturnFrameBase = returnFrameBase
+                    }, cancellationToken).Preserve();
 
                     Volatile.Write(ref isFirstCall, false);
                 }
 
                 var (index, result0, result1) = await ValueTaskEx.WhenAny(resumeTask, functionTask!);
 
-                var bufferSpan = buffer.Span;
                 if (index == 0)
                 {
                     var results = result0.Results;
-
-                    bufferSpan[0] = true;
-                    results.CopyTo(bufferSpan[1..]);
-
-                    return results.Length + 1;
+                    return context.Return(true, results.AsSpan());
                 }
                 else
                 {
-                    var resultCount = functionTask!.Result;
-
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
-                    bufferSpan[0] = true;
-                    this.buffer.AsSpan()[..resultCount].CopyTo(bufferSpan[1..]);
-
-                    ArrayPool<LuaValue>.Shared.Return(this.buffer);
-
-                    return 1 + resultCount;
+                    var count = context.Return(true, Stack.AsSpan()[returnFrameBase..]);
+                    Stack.PopUntil(returnFrameBase);
+                    return count;
                 }
             }
             catch (Exception ex) when (ex is not OperationCanceledException)
             {
                 if (IsProtectedMode)
                 {
-                    ArrayPool<LuaValue>.Shared.Return(this.buffer);
                     traceback = (ex as LuaRuntimeException)?.LuaTraceback;
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
-                    buffer.Span[0] = false;
-                    buffer.Span[1] = ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message;
-                    return 2;
+                    return context.Return(false, ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message);
                 }
                 else
                 {
@@ -207,7 +187,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
         }
     }
 
-    public override async ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public override async ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
     {
         if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
         {
@@ -219,10 +199,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
             throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield across a C#-call boundary");
         }
 
-        resume.SetResult(new()
-        {
-            Results = context.Arguments.ToArray(),
-        });
+        resume.SetResult(new() { Results = context.Arguments.ToArray(), });
 
         Volatile.Write(ref status, (byte)LuaThreadStatus.Suspended);
 
@@ -240,12 +217,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
         try
         {
             var result = await new ValueTask<YieldContext>(this, yield.Version);
-            for (int i = 0; i < result.Results.Length; i++)
-            {
-                buffer.Span[i] = result.Results[i];
-            }
-
-            return result.Results.Length;
+            return (context.Return(result.Results));
         }
         catch (Exception ex) when (ex is not OperationCanceledException)
         {

+ 7 - 16
src/Lua/LuaFunction.cs

@@ -1,37 +1,28 @@
-using System.Runtime.CompilerServices;
 using Lua.Runtime;
 
 namespace Lua;
 
-public class LuaFunction(string name, Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> func)
+public class LuaFunction(string name, Func<LuaFunctionExecutionContext, CancellationToken, ValueTask<int>> func)
 {
     public string Name { get; } = name;
-    internal Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> Func { get; } = func;
+    internal Func<LuaFunctionExecutionContext, CancellationToken, ValueTask<int>> Func { get; } = func;
 
-    public LuaFunction(Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> func) : this("anonymous", func)
+    public LuaFunction(Func<LuaFunctionExecutionContext, CancellationToken, ValueTask<int>> func) : this("anonymous", func)
     {
     }
 
-    public async ValueTask<int> InvokeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> InvokeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        var frame = new CallStackFrame
-        {
-            Base = context.FrameBase,
-            VariableArgumentCount = this is LuaClosure closure ? Math.Max(context.ArgumentCount - closure.Proto.ParameterCount, 0) : 0,
-            Function = this,
-        };
-
+        var frame = new CallStackFrame { Base = context.FrameBase, VariableArgumentCount = this.GetVariableArgumentCount(context.ArgumentCount), Function = this, ReturnBase = context.ReturnFrameBase };
         context.Thread.PushCallStackFrame(frame);
-
-
         try
         {
             if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
             {
-                return await LuaVirtualMachine.ExecuteCallHook(context, buffer, cancellationToken);
+                return await LuaVirtualMachine.ExecuteCallHook(context, cancellationToken);
             }
 
-            return await Func(context, buffer, cancellationToken);
+            return await Func(context, cancellationToken);
         }
         finally
         {

+ 62 - 0
src/Lua/LuaFunctionExecutionContext.cs

@@ -12,6 +12,7 @@ public readonly record struct LuaFunctionExecutionContext
     public required LuaThread Thread { get; init; }
     public required int ArgumentCount { get; init; }
     public required int FrameBase { get; init; }
+    public required int ReturnFrameBase { get; init; }
     public SourcePosition? SourcePosition { get; init; }
     public string? RootChunkName { get; init; }
     public string? ChunkName { get; init; }
@@ -116,6 +117,67 @@ public readonly record struct LuaFunctionExecutionContext
         return argValue;
     }
 
+    public int Return()
+    {
+        Thread.Stack.PopUntil(ReturnFrameBase);
+        return 0;
+    }
+
+    public int Return(LuaValue result)
+    {
+        var stack = Thread.Stack;
+        stack.SetTop(ReturnFrameBase + 1);
+        stack.FastGet(ReturnFrameBase) = result;
+        return 1;
+    }
+
+    public int Return(LuaValue result0, LuaValue result1)
+    {
+        var stack = Thread.Stack;
+        stack.SetTop(ReturnFrameBase + 2);
+        stack.FastGet(ReturnFrameBase) = result0;
+        stack.FastGet(ReturnFrameBase + 1) = result1;
+        return 2;
+    }
+
+    public int Return(LuaValue result0, LuaValue result1, LuaValue result2)
+    {
+        var stack = Thread.Stack;
+        stack.SetTop(ReturnFrameBase + 3);
+        stack.FastGet(ReturnFrameBase) = result0;
+        stack.FastGet(ReturnFrameBase + 1) = result1;
+        stack.FastGet(ReturnFrameBase + 2) = result2;
+        return 3;
+    }
+
+    public int Return(ReadOnlySpan<LuaValue> results)
+    {
+        var stack = Thread.Stack;
+        stack.EnsureCapacity(ReturnFrameBase + results.Length);
+        results.CopyTo(stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + results.Length)]);
+        stack.SetTop(ReturnFrameBase + results.Length);
+        return results.Length;
+    }
+
+    internal int Return(LuaValue result0, ReadOnlySpan<LuaValue> results)
+    {
+        var stack = Thread.Stack;
+        stack.EnsureCapacity(ReturnFrameBase + results.Length);
+        stack.SetTop(ReturnFrameBase + results.Length + 1);
+        var buffer = stack.GetBuffer();
+        buffer[ReturnFrameBase] = result0;
+        results.CopyTo(buffer[(ReturnFrameBase + 1)..(ReturnFrameBase + results.Length + 1)]);
+        return results.Length + 1;
+    }
+
+    public Span<LuaValue> GetReturnBuffer(int count)
+    {
+        var stack = Thread.Stack;
+        stack.SetTop(ReturnFrameBase + count);
+        var buffer = stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + count)];
+        return buffer;
+    }
+
     public CSharpClosure? GetCsClosure()
     {
         return Thread.GetCurrentFrame().Function as CSharpClosure;

+ 6 - 6
src/Lua/LuaFunctionExtensions.cs

@@ -6,11 +6,9 @@ public static class LuaFunctionExtensions
 {
     public static async ValueTask<LuaValue[]> InvokeAsync(this LuaFunction function, LuaState state, LuaValue[] arguments, CancellationToken cancellationToken = default)
     {
-        using var buffer = new PooledArray<LuaValue>(1024);
-
         var thread = state.CurrentThread;
         var frameBase = thread.Stack.Count;
-        
+
         for (int i = 0; i < arguments.Length; i++)
         {
             thread.Stack.Push(arguments[i]);
@@ -22,8 +20,10 @@ public static class LuaFunctionExtensions
             Thread = thread,
             ArgumentCount = arguments.Length,
             FrameBase = frameBase,
-        }, buffer.AsMemory(), cancellationToken);
-
-        return buffer.AsSpan()[0..resultCount].ToArray();
+            ReturnFrameBase = frameBase,
+        }, cancellationToken);
+        var r = thread.Stack.GetBuffer()[frameBase..(frameBase + resultCount)].ToArray();
+        thread.Stack.PopUntil(frameBase);
+        return r;
     }
 }

+ 4 - 6
src/Lua/LuaMainThread.cs

@@ -12,15 +12,13 @@ public sealed class LuaMainThread : LuaThread
         // Do nothing
     }
 
-    public override ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public override ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
     {
-        buffer.Span[0] = false;
-        buffer.Span[1] = "cannot resume non-suspended coroutine";
-        return new(2);
+        return new(context.Return(false, "cannot resume non-suspended coroutine"));
     }
 
-    public override ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public override ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
     {
         throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield from outside a coroutine");
     }
-}
+}

+ 18 - 33
src/Lua/LuaState.cs

@@ -31,6 +31,7 @@ public sealed class LuaState
     public LuaTable Registry => registry;
     public LuaTable LoadedModules => packages;
     public LuaMainThread MainThread => mainThread;
+
     public LuaThread CurrentThread
     {
         get
@@ -61,24 +62,28 @@ public sealed class LuaState
         envUpValue = UpValue.Closed(environment);
     }
 
-    public async ValueTask<int> RunAsync(Chunk chunk, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
+    public async ValueTask<LuaResult> RunAsync(Chunk chunk, CancellationToken cancellationToken = default)
     {
+        ThrowIfResultNotDisposed();
         ThrowIfRunning();
 
         Volatile.Write(ref isRunning, true);
         try
         {
             var closure = new LuaClosure(this, chunk);
-            return await closure.InvokeAsync(new()
+            await closure.InvokeAsync(new()
             {
                 State = this,
                 Thread = CurrentThread,
                 ArgumentCount = 0,
                 FrameBase = 0,
+                ReturnFrameBase = 0,
                 SourcePosition = null,
                 RootChunkName = chunk.Name,
                 ChunkName = chunk.Name,
-            }, buffer, cancellationToken);
+            }, cancellationToken);
+
+            return new LuaResult(CurrentThread.Stack, 0);
         }
         finally
         {
@@ -93,36 +98,7 @@ public sealed class LuaState
 
     public Traceback GetTraceback()
     {
-        if (threadStack.Count == 0)
-        {
-            return new(this)
-            {
-                RootFunc = (LuaClosure)MainThread.GetCallStackFrames()[0].Function,
-                StackFrames = MainThread.GetCallStackFrames()[1..]
-                    .ToArray()
-            };
-        }
-
-        using var list = new PooledList<CallStackFrame>(8);
-        foreach (var frame in MainThread.GetCallStackFrames()[1..])
-        {
-            list.Add(frame);
-        }
-
-        foreach (var thread in threadStack.AsSpan())
-        {
-            if (thread.CallStack.Count == 0) continue;
-            foreach (var frame in thread.GetCallStackFrames()[1..])
-            {
-                list.Add(frame);
-            }
-        }
-
-        return new(this)
-        {
-            RootFunc = (LuaClosure)MainThread.GetCallStackFrames()[0].Function,
-            StackFrames = list.AsSpan().ToArray()
-        };
+        return GetTraceback(CurrentThread);
     }
 
     internal Traceback GetTraceback(LuaThread thread)
@@ -132,6 +108,7 @@ public sealed class LuaState
         {
             list.Add(frame);
         }
+
         LuaClosure rootFunc;
         if (thread.GetCallStackFrames()[0].Function is LuaClosure closure)
         {
@@ -231,6 +208,14 @@ public sealed class LuaState
         }
     }
 
+    void ThrowIfResultNotDisposed()
+    {
+        if (MainThread.Stack.Count != 0)
+        {
+            throw new InvalidOperationException("LuaResult is not disposed");
+        }
+    }
+
     void ThrowIfRunning()
     {
         if (Volatile.Read(ref isRunning))

+ 18 - 25
src/Lua/LuaStateExtensions.cs

@@ -1,4 +1,3 @@
-using System.Buffers;
 using Lua.CodeAnalysis.Compilation;
 using Lua.CodeAnalysis.Syntax;
 
@@ -6,47 +5,41 @@ namespace Lua;
 
 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<int> DoStringAsync(this LuaState state, string source, Memory<LuaValue> buffer, string? chunkName = null, CancellationToken cancellationToken = default)
     {
         var syntaxTree = LuaSyntaxTree.Parse(source, chunkName);
         var chunk = LuaCompiler.Default.Compile(syntaxTree, chunkName);
-        return state.RunAsync(chunk, buffer, cancellationToken);
+        using var result = await state.RunAsync(chunk, cancellationToken);
+        result.AsSpan().CopyTo(buffer.Span);
+        return result.Count;
     }
 
     public static async ValueTask<LuaValue[]> DoStringAsync(this LuaState state, string source, string? chunkName = null, CancellationToken cancellationToken = default)
     {
-        var buffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-        try
-        {
-            var resultCount = await DoStringAsync(state, source, buffer, chunkName, cancellationToken);
-            return buffer.AsSpan(0, resultCount).ToArray();
-        }
-        finally
-        {
-            ArrayPool<LuaValue>.Shared.Return(buffer);
-        }
+        var syntaxTree = LuaSyntaxTree.Parse(source, chunkName);
+        var chunk = LuaCompiler.Default.Compile(syntaxTree, chunkName);
+        using var result = await state.RunAsync(chunk, cancellationToken);
+        return result.AsSpan().ToArray();
     }
 
     public static async ValueTask<int> DoFileAsync(this LuaState state, string path, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
         var text = await File.ReadAllTextAsync(path, cancellationToken);
-        var fileName = "@"+Path.GetFileName(path);
+        var fileName = "@" + Path.GetFileName(path);
         var syntaxTree = LuaSyntaxTree.Parse(text, fileName);
         var chunk = LuaCompiler.Default.Compile(syntaxTree, fileName);
-        return await state.RunAsync(chunk, buffer, cancellationToken);
+        using var result = await state.RunAsync(chunk, cancellationToken);
+        result.AsSpan().CopyTo(buffer.Span);
+        return result.Count;
     }
 
     public static async ValueTask<LuaValue[]> DoFileAsync(this LuaState state, string path, CancellationToken cancellationToken = default)
     {
-        var buffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-        try
-        {
-            var resultCount = await DoFileAsync(state, path, buffer, cancellationToken);
-            return buffer.AsSpan(0, resultCount).ToArray();
-        }
-        finally
-        {
-            ArrayPool<LuaValue>.Shared.Return(buffer);
-        }
+        var text = await File.ReadAllTextAsync(path, cancellationToken);
+        var fileName = "@" + Path.GetFileName(path);
+        var syntaxTree = LuaSyntaxTree.Parse(text, fileName);
+        var chunk = LuaCompiler.Default.Compile(syntaxTree, fileName);
+        using var result = await state.RunAsync(chunk, cancellationToken);
+        return result.AsSpan().ToArray();
     }
 }

+ 5 - 5
src/Lua/LuaThread.cs

@@ -8,8 +8,8 @@ public abstract class LuaThread
 {
     public abstract LuaThreadStatus GetStatus();
     public abstract void UnsafeSetStatus(LuaThreadStatus status);
-    public abstract ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default);
-    public abstract ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default);
+    public abstract ValueTask<int> ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default);
+    public abstract ValueTask<int> YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default);
 
     LuaStack stack = new();
     FastStackCore<CallStackFrame> callStack;
@@ -72,7 +72,7 @@ public abstract class LuaThread
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrame()
+    internal void PopCallStackFrameWithStackPop()
     {
         if (callStack.TryPop(out var frame))
         {
@@ -85,7 +85,7 @@ public abstract class LuaThread
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameUnsafe(int frameBase)
+    internal void PopCallStackFrameWithStackPop(int frameBase)
     {
         if (callStack.TryPop())
         {
@@ -98,7 +98,7 @@ public abstract class LuaThread
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    internal void PopCallStackFrameUnsafe()
+    internal void PopCallStackFrame()
     {
         if (!callStack.TryPop())
         {

+ 14 - 12
src/Lua/LuaThreadExtensions.cs

@@ -6,26 +6,25 @@ public static class LuaThreadExtensions
 {
     public static async ValueTask<LuaValue[]> ResumeAsync(this LuaThread thread, LuaState state, CancellationToken cancellationToken = default)
     {
-        using var buffer = new PooledArray<LuaValue>(1024);
-
         var frameBase = thread.Stack.Count;
         thread.Stack.Push(thread);
 
-        var resultCount = await thread.ResumeAsync(new()
+        await thread.ResumeAsync(new()
         {
             State = state,
             Thread = state.CurrentThread,
             ArgumentCount = 1,
             FrameBase = frameBase,
-        }, buffer.AsMemory(), cancellationToken);
-
-        return buffer.AsSpan()[0..resultCount].ToArray();
+            ReturnFrameBase = frameBase,
+        }, cancellationToken);
+        var returnBase = ((LuaCoroutine)thread).ReturnFrameBase;
+        var results = thread.Stack.AsSpan()[returnBase..].ToArray();
+        thread.Stack.PopUntil(returnBase);
+        return results;
     }
 
     public static async ValueTask<LuaValue[]> ResumeAsync(this LuaThread thread, LuaState state, LuaValue[] arguments, CancellationToken cancellationToken = default)
     {
-        using var buffer = new PooledArray<LuaValue>(1024);
-
         var frameBase = thread.Stack.Count;
         thread.Stack.Push(thread);
         for (int i = 0; i < arguments.Length; i++)
@@ -33,14 +32,17 @@ public static class LuaThreadExtensions
             thread.Stack.Push(arguments[i]);
         }
 
-        var resultCount = await thread.ResumeAsync(new()
+        await thread.ResumeAsync(new()
         {
             State = state,
             Thread = state.CurrentThread,
             ArgumentCount = 1 + arguments.Length,
             FrameBase = frameBase,
-        }, buffer.AsMemory(), cancellationToken);
-
-        return buffer.AsSpan()[0..resultCount].ToArray();
+            ReturnFrameBase = frameBase,
+        }, cancellationToken);
+        var returnBase = ((LuaCoroutine)thread).ReturnFrameBase;
+        var results = thread.Stack.AsSpan()[returnBase..].ToArray();
+        thread.Stack.PopUntil(returnBase);
+        return results;
     }
 }

+ 6 - 9
src/Lua/LuaValue.cs

@@ -584,7 +584,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
         return false;
     }
 
-    internal ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    internal ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (this.TryGetMetamethod(context.State, Metamethods.ToString, out var metamethod))
         {
@@ -593,18 +593,15 @@ public readonly struct LuaValue : IEquatable<LuaValue>
                 LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
             }
 
-            context.State.Push(this);
+            var stack = context.Thread.Stack;
+            stack.Push(this);
 
-            return func.InvokeAsync(context with
-            {
-                ArgumentCount = 1,
-                FrameBase = context.Thread.Stack.Count - 1,
-            }, buffer, cancellationToken);
+            return func.InvokeAsync(context with { ArgumentCount = 1, FrameBase = stack.Count - 1, ReturnFrameBase = stack.Count - 1, }, cancellationToken);
         }
         else
         {
-            buffer.Span[0] = ToString();
-            return new(1);
+            context.Thread.Stack.Push(ToString());
+            return default;
         }
     }
 }

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

@@ -1,6 +1,6 @@
 namespace Lua.Runtime;
 
-public sealed class CSharpClosure(string name,LuaValue[] upValues,Func<LuaFunctionExecutionContext, Memory<LuaValue>, CancellationToken, ValueTask<int>> func) : LuaFunction(name, func)
+public sealed class CSharpClosure(string name, LuaValue[] upValues, Func<LuaFunctionExecutionContext, CancellationToken, ValueTask<int>> func) : LuaFunction(name, func)
 {
-   public readonly LuaValue[] UpValues = upValues;
+    public readonly LuaValue[] UpValues = upValues;
 }

+ 3 - 2
src/Lua/Runtime/CallStackFrame.cs

@@ -6,18 +6,19 @@ namespace Lua.Runtime;
 public record struct CallStackFrame
 {
     public required int Base;
+    public required int ReturnBase;
     public required LuaFunction Function;
     public required int VariableArgumentCount;
     public int CallerInstructionIndex;
     internal CallStackFrameFlags Flags;
-    internal bool IsTailCall => (Flags & CallStackFrameFlags.TailCall) ==CallStackFrameFlags.TailCall;
+    internal bool IsTailCall => (Flags & CallStackFrameFlags.TailCall) == CallStackFrameFlags.TailCall;
 }
 
 [Flags]
 public enum CallStackFrameFlags
 {
     //None = 0,
-    ReversedLe  = 1,
+    ReversedLe = 1,
     TailCall = 2,
     InHook = 4,
 }

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

@@ -9,7 +9,7 @@ public sealed class LuaClosure : LuaFunction
     FastListCore<UpValue> upValues;
 
     public LuaClosure(LuaState state, Chunk proto, LuaTable? environment = null)
-        : base(proto.Name, (context, buffer, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, buffer, ct))
+        : base(proto.Name, (context, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, ct))
     {
         this.proto = proto;
 

+ 12 - 0
src/Lua/Runtime/LuaStack.cs

@@ -27,6 +27,11 @@ public sealed class LuaStack(int initialSize = 256)
                 size *= 2;
             }
 
+            if (1000000 < size)
+            {
+                throw new LuaException("Lua Stack overflow");
+            }
+
             Array.Resize(ref array, size);
         }
     }
@@ -134,4 +139,11 @@ public sealed class LuaStack(int initialSize = 256)
     {
         throw new InvalidOperationException("Empty stack");
     }
+
+    internal void SetTop(int top)
+    {
+        EnsureCapacity(top);
+        NotifyTop(top);
+        PopUntil(top);
+    }
 }

+ 30 - 19
src/Lua/Runtime/LuaVirtualMachine.Debug.cs

@@ -12,7 +12,7 @@ public static partial class LuaVirtualMachine
         {
             if (r.Result == 0)
             {
-                context.Thread.PopCallStackFrame();
+                context.Thread.PopCallStackFrameWithStackPop();
             }
 
             return false;
@@ -39,18 +39,20 @@ public static partial class LuaVirtualMachine
                     Thread = context.Thread,
                     ArgumentCount = 2,
                     FrameBase = context.Thread.Stack.Count - 2,
+                    ReturnFrameBase = context.Thread.Stack.Count - 2,
                 };
                 var frame = new CallStackFrame
                 {
                     Base = funcContext.FrameBase,
-                    VariableArgumentCount = hook is LuaClosure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0,
+                    ReturnBase = funcContext.ReturnFrameBase,
+                    VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount),
                     Function = hook,
                     CallerInstructionIndex = context.Pc,
                 };
                 frame.Flags |= CallStackFrameFlags.InHook;
                 context.Thread.IsInHook = true;
                 context.Thread.PushCallStackFrame(frame);
-                await hook.Func(funcContext, Memory<LuaValue>.Empty, context.CancellationToken);
+                await hook.Func(funcContext, context.CancellationToken);
                 context.Thread.IsInHook = false;
 
 
@@ -68,7 +70,7 @@ public static partial class LuaVirtualMachine
                 {
                     if (countHookIsDone)
                     {
-                        context.Thread.PopCallStackFrame();
+                        context.Thread.PopCallStackFrameWithStackPop();
                     }
 
 
@@ -82,18 +84,20 @@ public static partial class LuaVirtualMachine
                         Thread = context.Thread,
                         ArgumentCount = 2,
                         FrameBase = context.Thread.Stack.Count - 2,
+                        ReturnFrameBase = context.Thread.Stack.Count - 2,
                     };
                     var frame = new CallStackFrame
                     {
                         Base = funcContext.FrameBase,
-                        VariableArgumentCount = hook is LuaClosure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0,
+                        ReturnBase = funcContext.ReturnFrameBase,
+                        VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount),
                         Function = hook,
                         CallerInstructionIndex = pc,
                     };
                     frame.Flags |= CallStackFrameFlags.InHook;
                     context.Thread.IsInHook = true;
                     context.Thread.PushCallStackFrame(frame);
-                    await hook.Func(funcContext, Memory<LuaValue>.Empty, context.CancellationToken);
+                    await hook.Func(funcContext, context.CancellationToken);
                     context.Thread.IsInHook = false;
                     context.Pc--;
                     context.Thread.LastPc = pc;
@@ -122,11 +126,12 @@ public static partial class LuaVirtualMachine
             Thread = context.Thread,
             ArgumentCount = arguments,
             FrameBase = frame.Base,
+            ReturnFrameBase = frame.ReturnBase,
             CallerInstructionIndex = frame.CallerInstructionIndex,
-        }, context.ResultsBuffer, context.CancellationToken, isTailCall);
+        }, context.CancellationToken, isTailCall);
     }
 
-    internal static async ValueTask<int> ExecuteCallHook(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken, bool isTailCall = false)
+    internal static async ValueTask<int> ExecuteCallHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken, bool isTailCall = false)
     {
         var argCount = context.ArgumentCount;
         var hook = context.Thread.Hook!;
@@ -142,10 +147,12 @@ public static partial class LuaVirtualMachine
                 Thread = context.Thread,
                 ArgumentCount = 2,
                 FrameBase = context.Thread.Stack.Count - 2,
+                ReturnFrameBase = context.Thread.Stack.Count - 2,
             };
             CallStackFrame frame = new()
             {
                 Base = funcContext.FrameBase,
+                ReturnBase = funcContext.ReturnFrameBase,
                 VariableArgumentCount = hook.GetVariableArgumentCount(2),
                 Function = hook,
                 CallerInstructionIndex = 0,
@@ -156,29 +163,31 @@ public static partial class LuaVirtualMachine
             try
             {
                 context.Thread.IsInHook = true;
-                await hook.Func(funcContext, Memory<LuaValue>.Empty, cancellationToken);
+                await hook.Func(funcContext, cancellationToken);
             }
             finally
             {
                 context.Thread.IsInHook = false;
-                context.Thread.PopCallStackFrame();
+                context.Thread.PopCallStackFrameWithStackPop();
             }
         }
 
         {
-            var frame = context.Thread.GetCurrentFrame();
+            ref readonly var frame = ref context.Thread.GetCurrentFrame();
             var task = frame.Function.Func(new()
             {
                 State = context.State,
                 Thread = context.Thread,
                 ArgumentCount = argCount,
                 FrameBase = frame.Base,
-            }, buffer, cancellationToken);
+                ReturnFrameBase = frame.ReturnBase,
+            }, cancellationToken);
+            var r = await task;
             if (isTailCall || !context.Thread.IsReturnHookEnabled)
             {
-                return await task;
+                return r;
             }
-            var result = await task;
+
             stack.Push("return");
             stack.Push(LuaValue.Nil);
             var funcContext = new LuaFunctionExecutionContext
@@ -187,12 +196,14 @@ public static partial class LuaVirtualMachine
                 Thread = context.Thread,
                 ArgumentCount = 2,
                 FrameBase = context.Thread.Stack.Count - 2,
+                ReturnFrameBase = context.Thread.Stack.Count - 2,
             };
-           
 
-            context.Thread.PushCallStackFrame( new()
+
+            context.Thread.PushCallStackFrame(new()
             {
                 Base = funcContext.FrameBase,
+                ReturnBase = funcContext.ReturnFrameBase,
                 VariableArgumentCount = hook.GetVariableArgumentCount(2),
                 Function = hook,
                 CallerInstructionIndex = 0,
@@ -201,15 +212,15 @@ public static partial class LuaVirtualMachine
             try
             {
                 context.Thread.IsInHook = true;
-                await hook.Func(funcContext, Memory<LuaValue>.Empty, cancellationToken);
+                await hook.Func(funcContext, cancellationToken);
             }
             finally
             {
                 context.Thread.IsInHook = false;
             }
 
-            context.Thread.PopCallStackFrame();
-            return result;
+            context.Thread.PopCallStackFrameWithStackPop();
+            return r;
         }
     }
 }

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


+ 41 - 0
src/Lua/Runtime/Metamethods.cs

@@ -21,4 +21,45 @@ public static class Metamethods
     public const string Pairs = "__pairs";
     public const string IPairs = "__ipairs";
     public new const string ToString = "__tostring";
+
+    internal static (string Name, string Description) GetNameAndDescription(this OpCode opCode)
+    {
+        switch (opCode)
+        {
+            case OpCode.GetTabUp:
+            case OpCode.GetTable:
+            case OpCode.Self:
+                return (Index, "index");
+            case OpCode.SetTabUp:
+            case OpCode.SetTable:
+                return (NewIndex, "new index");
+            case OpCode.Add:
+                return (Add, "add");
+            case OpCode.Sub:
+                return (Sub, "sub");
+            case OpCode.Mul:
+                return (Mul, "mul");
+            case OpCode.Div:
+                return (Div, "div");
+            case OpCode.Mod:
+                return (Mod, "mod");
+            case OpCode.Pow:
+                return (Pow, "pow");
+            case OpCode.Unm:
+                return (Unm, "unm");
+            case OpCode.Len:
+                return (Len, "get length of");
+            case OpCode.Eq:
+                return (Eq, "eq");
+            case OpCode.Lt:
+                return (Lt, "lt");
+            case OpCode.Le:
+                return (Le, "le");
+            case OpCode.Call:
+                return (Call, "call");
+            case OpCode.Concat:
+                return (Concat, "concat");
+            default: return (opCode.ToString(), opCode.ToString());
+        }
+    }
 }

+ 42 - 41
src/Lua/Runtime/OpCode.cs

@@ -2,60 +2,61 @@ namespace Lua.Runtime;
 
 public enum OpCode : byte
 {
-    Move,       // A B     R(A) := R(B)
-    LoadK,      // A Bx    R(A) := Kst(Bx)
-    LoadKX,     // A       R(A) := Kst(extra arg)
-    LoadBool,   // A B C   R(A) := (Bool)B; if (C) pc++
-    LoadNil,    // A B     R(A), R(A+1), ..., R(A+B) := nil
+    Move, // A B     R(A) := R(B)
+    LoadK, // A Bx    R(A) := Kst(Bx)
+    LoadKX, // A       R(A) := Kst(extra arg)
+    LoadBool, // A B C   R(A) := (Bool)B; if (C) pc++
+    LoadNil, // A B     R(A), R(A+1), ..., R(A+B) := nil
 
-    GetUpVal,   // A B     R(A) := UpValue[B]
-    GetTabUp,   // A B C   R(A) := UpValue[B][RK(C)]
-    GetTable,   // A B C   R(A) := R(B)[RK(C)]
+    GetUpVal, // A B     R(A) := UpValue[B]
+    GetTabUp, // A B C   R(A) := UpValue[B][RK(C)]
+    GetTable, // A B C   R(A) := R(B)[RK(C)]
 
-    SetTabUp,   // A B C   UpValue[A][RK(B)] := RK(C)
-    SetUpVal,   // A B     UpValue[B] := R(A)
-    SetTable,   // A B C   R(A)[RK(B)] := RK(C)
+    SetTabUp, // A B C   UpValue[A][RK(B)] := RK(C)
+    SetUpVal, // A B     UpValue[B] := R(A)
+    SetTable, // A B C   R(A)[RK(B)] := RK(C)
 
-    NewTable,   // A B C   R(A) := {} (size = B,C)
+    NewTable, // A B C   R(A) := {} (size = B,C)
 
-    Self,       // A B C   R(A+1) := R(B); R(A) := R(B)[RK(C)]
+    Self, // A B C   R(A+1) := R(B); R(A) := R(B)[RK(C)]
 
-    Add,        // A B C   R(A) := RK(B) + RK(C)
-    Sub,        // A B C   R(A) := RK(B) - RK(C)
-    Mul,        // A B C   R(A) := RK(B) * RK(C)
-    Div,        // A B C   R(A) := RK(B) / RK(C)
-    Mod,        // A B C   R(A) := RK(B) % RK(C)
-    Pow,        // A B C   R(A) := RK(B) ^ RK(C)
-    Unm,        // A B     R(A) := -R(B)
-    Not,        // A B     R(A) := not R(B)
-    Len,        // A B     R(A) := length of R(B)
+    Add, // A B C   R(A) := RK(B) + RK(C)
+    Sub, // A B C   R(A) := RK(B) - RK(C)
+    Mul, // A B C   R(A) := RK(B) * RK(C)
+    Div, // A B C   R(A) := RK(B) / RK(C)
+    Mod, // A B C   R(A) := RK(B) % RK(C)
+    Pow, // A B C   R(A) := RK(B) ^ RK(C)
+    Unm, // A B     R(A) := -R(B)
+    Not, // A B     R(A) := not R(B)
+    Len, // A B     R(A) := length of R(B)
 
-    Concat,     // A B C   R(A) := R(B).. ... ..R(C)
+    Concat, // A B C   R(A) := R(B).. ... ..R(C)
 
-    Jmp,        // A sBx   pc+=sBx; if (A) close all upvalues >= R(A - 1)
-    Eq,         // A B C   if ((RK(B) == RK(C)) ~= A) then pc++
-    Lt,         // A B C   if ((RK(B) <  RK(C)) ~= A) then pc++
-    Le,         // A B C   if ((RK(B) <= RK(C)) ~= A) then pc++
+    Jmp, // A sBx   pc+=sBx; if (A) close all upvalues >= R(A - 1)
+    Eq, // A B C   if ((RK(B) == RK(C)) ~= A) then pc++
+    Lt, // A B C   if ((RK(B) <  RK(C)) ~= A) then pc++
+    Le, // A B C   if ((RK(B) <= RK(C)) ~= A) then pc++
 
-    Test,       // A C     if not (R(A) <=> C) then pc++
-    TestSet,    // A B C   if (R(B) <=> C) then R(A) := R(B) else pc++
+    Test, // A C     if not (R(A) <=> C) then pc++
+    TestSet, // A B C   if (R(B) <=> C) then R(A) := R(B) else pc++
 
-    Call,       // A B C   R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
-    TailCall,   // A B C   return R(A)(R(A+1), ... ,R(A+B-1))
-    Return,     // A B     return R(A), ... ,R(A+B-2)      (see note)
+    Call, // A B C   R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
+    TailCall, // A B C   return R(A)(R(A+1), ... ,R(A+B-1))
+    Return, // A B     return R(A), ... ,R(A+B-2)      (see note)
 
-    ForLoop,    // A sBx   R(A)+=R(A+2);
-                //         if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }
-    ForPrep,    // A sBx   R(A)-=R(A+2); pc+=sBx
+    ForLoop, // A sBx   R(A)+=R(A+2);
 
-    TForCall,   // A C     R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
-    TForLoop,   // A sBx   if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }
+    //         if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }
+    ForPrep, // A sBx   R(A)-=R(A+2); pc+=sBx
 
-    SetList,    // A B C   R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
+    TForCall, // A C     R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
+    TForLoop, // A sBx   if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }
 
-    Closure,    // A Bx    R(A) := closure(KPROTO[Bx])
+    SetList, // A B C   R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
 
-    VarArg,     // A B     R(A), R(A+1), ..., R(A+B-2) = vararg
+    Closure, // A Bx    R(A) := closure(KPROTO[Bx])
 
-    ExtraArg    // Ax      extra (larger) argument for previous opcode
+    VarArg, // A B     R(A), R(A+1), ..., R(A+B-2) = vararg
+
+    ExtraArg // Ax      extra (larger) argument for previous opcode
 }

+ 89 - 153
src/Lua/Standard/BasicLibrary.cs

@@ -11,7 +11,8 @@ public sealed class BasicLibrary
 
     public BasicLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("assert", Assert),
             new("collectgarbage", CollectGarbage),
             new("dofile", DoFile),
@@ -36,7 +37,7 @@ public sealed class BasicLibrary
             new("xpcall", XPCall),
         ];
 
-        IPairsIterator = new("iterator", (context, buffer, cancellationToken) =>
+        IPairsIterator = new("iterator", (context, cancellationToken) =>
         {
             var table = context.GetArgument<LuaTable>(0);
             var i = context.GetArgument<double>(1);
@@ -44,16 +45,12 @@ public sealed class BasicLibrary
             i++;
             if (table.TryGetValue(i, out var value))
             {
-                buffer.Span[0] = i;
-                buffer.Span[1] = value;
+                return new(context.Return(i, value));
             }
             else
             {
-                buffer.Span[0] = LuaValue.Nil;
-                buffer.Span[1] = LuaValue.Nil;
+                return new(context.Return(LuaValue.Nil, LuaValue.Nil));
             }
-
-            return new(2);
         });
 
         PairsIterator = new("iterator", Next);
@@ -63,7 +60,7 @@ public sealed class BasicLibrary
     readonly LuaFunction IPairsIterator;
     readonly LuaFunction PairsIterator;
 
-    public ValueTask<int> Assert(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Assert(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
@@ -78,49 +75,43 @@ public sealed class BasicLibrary
             throw new LuaAssertionException(context.State.GetTraceback(), message);
         }
 
-        context.Arguments.CopyTo(buffer.Span);
-        return new(context.ArgumentCount);
+        return new(context.Return(context.Arguments));
     }
 
-    public ValueTask<int> CollectGarbage(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> CollectGarbage(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         GC.Collect();
-        return new(0);
+        return new(context.Return());
     }
 
-    public async ValueTask<int> DoFile(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> DoFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<string>(0);
 
         // do not use LuaState.DoFileAsync as it uses the newExecutionContext
         var text = await File.ReadAllTextAsync(arg0, cancellationToken);
-        var fileName = Path.GetFileName(arg0);
-        var chunk = LuaCompiler.Default.Compile(text, "@"+fileName);
+        var fileName = "@" + Path.GetFileName(arg0);
+        var chunk = LuaCompiler.Default.Compile(text, fileName);
 
-        return await new LuaClosure(context.State, chunk).InvokeAsync(context, buffer, cancellationToken);
+        return await new LuaClosure(context.State, chunk).InvokeAsync(context, cancellationToken);
     }
 
-    public ValueTask<int> Error(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Error(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        var value = context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil
-            ? "(error object is a nil value)"
+        var value = context.ArgumentCount == 0
+            ? LuaValue.Nil
             : context.Arguments[0];
 
-        Traceback t;
-        try
-        {
-           t = context.State.GetTraceback(context.Thread);
-            
-        }
-        catch (Exception e)
+        var traceback = context.State.GetTraceback();
+        if (value.TryReadString(out var str))
         {
-            Console.WriteLine(e);
-            throw;
+            value = $"{traceback.RootChunkName}:{traceback.LastPosition.Line}: {str}";
         }
-        throw new LuaRuntimeException(t, value);
+
+        throw new LuaRuntimeException(traceback, value);
     }
 
-    public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
@@ -128,26 +119,26 @@ public sealed class BasicLibrary
         {
             if (table.Metatable == null)
             {
-                buffer.Span[0] = LuaValue.Nil;
+                context.Return(LuaValue.Nil);
             }
             else if (table.Metatable.TryGetValue(Metamethods.Metatable, out var metatable))
             {
-                buffer.Span[0] = metatable;
+                context.Return(metatable);
             }
             else
             {
-                buffer.Span[0] = table.Metatable;
+                context.Return(table.Metatable);
             }
         }
         else
         {
-            buffer.Span[0] = LuaValue.Nil;
+            context.Return(LuaValue.Nil);
         }
 
-        return new(1);
+        return default;
     }
 
-    public ValueTask<int> IPairs(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> IPairs(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
 
@@ -159,16 +150,13 @@ public sealed class BasicLibrary
                 LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
             }
 
-            return function.InvokeAsync(context, buffer, cancellationToken);
+            return function.InvokeAsync(context, cancellationToken);
         }
 
-        buffer.Span[0] = IPairsIterator;
-        buffer.Span[1] = arg0;
-        buffer.Span[2] = 0;
-        return new(3);
+        return new(context.Return(IPairsIterator, arg0, 0));
     }
 
-    public async ValueTask<int> LoadFile(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> LoadFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         // Lua-CSharp does not support binary chunks, the mode argument is ignored.
         var arg0 = context.GetArgument<string>(0);
@@ -180,20 +168,17 @@ public sealed class BasicLibrary
         try
         {
             var text = await File.ReadAllTextAsync(arg0, cancellationToken);
-            var fileName = Path.GetFileName(arg0);
+            var fileName = "@" + Path.GetFileName(arg0);
             var chunk = LuaCompiler.Default.Compile(text, fileName);
-            buffer.Span[0] = new LuaClosure(context.State, chunk, arg2);
-            return 1;
+            return context.Return(new LuaClosure(context.State, chunk, arg2));
         }
         catch (Exception ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            return 2;
+            return context.Return(LuaValue.Nil, ex.Message);
         }
     }
 
-    public ValueTask<int> Load(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Load(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         // Lua-CSharp does not support binary chunks, the mode argument is ignored.
         var arg0 = context.GetArgument(0);
@@ -212,8 +197,7 @@ public sealed class BasicLibrary
             if (arg0.TryRead<string>(out var str))
             {
                 var chunk = LuaCompiler.Default.Compile(str, arg1 ?? str);
-                buffer.Span[0] = new LuaClosure(context.State, chunk, arg3);
-                return new(1);
+                return new(context.Return(new LuaClosure(context.State, chunk, arg3)));
             }
             else if (arg0.TryRead<LuaFunction>(out var function))
             {
@@ -228,31 +212,26 @@ public sealed class BasicLibrary
         }
         catch (Exception ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            return new(2);
+            return new(context.Return(LuaValue.Nil, ex.Message));
         }
     }
 
-    public ValueTask<int> Next(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Next(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil;
 
         if (arg0.TryGetNext(arg1, out var kv))
         {
-            buffer.Span[0] = kv.Key;
-            buffer.Span[1] = kv.Value;
-            return new(2);
+            return new(context.Return(kv.Key, kv.Value));
         }
         else
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
     }
 
-    public ValueTask<int> Pairs(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Pairs(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
 
@@ -264,115 +243,94 @@ public sealed class BasicLibrary
                 LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod);
             }
 
-            return function.InvokeAsync(context, buffer, cancellationToken);
+            return function.InvokeAsync(context, cancellationToken);
         }
 
-        buffer.Span[0] = PairsIterator;
-        buffer.Span[1] = arg0;
-        buffer.Span[2] = LuaValue.Nil;
-        return new(3);
+        return new(context.Return(PairsIterator, arg0, LuaValue.Nil));
     }
 
-    public async ValueTask<int> PCall(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> PCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
-
         try
         {
-            using var methodBuffer = new PooledArray<LuaValue>(1024);
+            var count = await arg0.InvokeAsync(context with { State = context.State, ArgumentCount = context.ArgumentCount - 1, FrameBase = context.FrameBase + 1, ReturnFrameBase = context.ReturnFrameBase + 1 }, cancellationToken);
 
-            var resultCount = await arg0.InvokeAsync(context with
-            {
-                State = context.State,
-                ArgumentCount = context.ArgumentCount - 1,
-                FrameBase = context.FrameBase + 1,
-            }, methodBuffer.AsMemory(), cancellationToken);
-
-            buffer.Span[0] = true;
-            methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]);
-
-            return resultCount + 1;
+            context.Thread.Stack.Get(context.ReturnFrameBase) = true;
+            return count + 1;
         }
         catch (Exception ex)
         {
-            buffer.Span[0] = false;
             if (ex is LuaRuntimeException { ErrorObject: not null } luaEx)
             {
-                buffer.Span[1] = luaEx.ErrorObject.Value;
+                return context.Return(false, luaEx.ErrorObject.Value);
             }
             else
             {
-                buffer.Span[1] = ex.Message;
+                return context.Return(false, ex.Message);
             }
-
-            return 2;
         }
     }
 
-    public async ValueTask<int> Print(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> Print(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        using var methodBuffer = new PooledArray<LuaValue>(1);
-
         for (int i = 0; i < context.ArgumentCount; i++)
         {
-            await context.Arguments[i].CallToStringAsync(context, methodBuffer.AsMemory(), cancellationToken);
-            Console.Write(methodBuffer[0]);
+            var top = context.Thread.Stack.Count;
+            await context.Arguments[i].CallToStringAsync(context, cancellationToken);
+            Console.Write(context.Thread.Stack.Get(top).ToString());
             Console.Write('\t');
         }
 
         Console.WriteLine();
-        return 0;
+        return context.Return();
     }
 
-    public ValueTask<int> RawEqual(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RawEqual(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
         var arg1 = context.GetArgument(1);
 
-        buffer.Span[0] = arg0 == arg1;
-        return new(1);
+        return new(context.Return(arg0 == arg1));
     }
 
-    public ValueTask<int> RawGet(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RawGet(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.GetArgument(1);
-
-        buffer.Span[0] = arg0[arg1];
-        return new(1);
+        return new(context.Return(arg0[arg1]));
     }
 
-    public ValueTask<int> RawLen(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RawLen(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
         if (arg0.TryRead<LuaTable>(out var table))
         {
-            buffer.Span[0] = table.ArrayLength;
+            return new(context.Return(table.ArrayLength));
         }
         else if (arg0.TryRead<string>(out var str))
         {
-            buffer.Span[0] = str.Length;
+            return new(context.Return(str.Length));
         }
         else
         {
             LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, "rawlen", [LuaValueType.String, LuaValueType.Table]);
+            return default;
         }
-
-        return new(1);
     }
 
-    public ValueTask<int> RawSet(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RawSet(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.GetArgument(1);
         var arg2 = context.GetArgument(2);
 
         arg0[arg1] = arg2;
-        return new(0);
+        return new(context.Return());
     }
 
-    public ValueTask<int> Select(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Select(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
@@ -387,14 +345,11 @@ public sealed class BasicLibrary
                 ? context.Arguments[index..]
                 : context.Arguments[(context.ArgumentCount + index)..];
 
-            span.CopyTo(buffer.Span);
-
-            return new(span.Length);
+            return new(context.Return(span));
         }
         else if (arg0.TryRead<string>(out var str) && str == "#")
         {
-            buffer.Span[0] = context.ArgumentCount - 1;
-            return new(1);
+            return new(context.Return(context.ArgumentCount - 1));
         }
         else
         {
@@ -403,7 +358,7 @@ public sealed class BasicLibrary
         }
     }
 
-    public ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.GetArgument(1);
@@ -426,11 +381,11 @@ public sealed class BasicLibrary
             arg0.Metatable = arg1.Read<LuaTable>();
         }
 
-        buffer.Span[0] = arg0;
-        return new(1);
+
+        return new(context.Return(arg0));
     }
 
-    public ValueTask<int> ToNumber(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> ToNumber(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var e = context.GetArgument(0);
         int? toBase = context.HasArgument(1)
@@ -477,6 +432,7 @@ public sealed class BasicLibrary
                     {
                         span = span[1..];
                     }
+
                     if (span.Length == 0) goto END;
 
                     if (toBase == 16 && span.Length > 2 && span[0] is '0' && span[1] is 'x' or 'X')
@@ -500,13 +456,13 @@ public sealed class BasicLibrary
         }
 
     END:
-        if (value != null && double.IsNaN(value.Value))
+        if (value is double.NaN)
         {
             value = null;
         }
 
-        buffer.Span[0] = value == null ? LuaValue.Nil : value.Value;
-        return new(1);
+
+        return new(context.Return(value ?? LuaValue.Nil));
     }
 
     static double StringToDouble(ReadOnlySpan<char> text, int toBase)
@@ -566,17 +522,18 @@ public sealed class BasicLibrary
         return value;
     }
 
-    public ValueTask<int> ToString(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> ToString(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
-        return arg0.CallToStringAsync(context, buffer, cancellationToken);
+        context.Return();
+        return arg0.CallToStringAsync(context, cancellationToken);
     }
 
-    public ValueTask<int> Type(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Type(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
-        buffer.Span[0] = arg0.Type switch
+        return new(context.Return(arg0.Type switch
         {
             LuaValueType.Nil => "nil",
             LuaValueType.Boolean => "boolean",
@@ -584,56 +541,35 @@ public sealed class BasicLibrary
             LuaValueType.Number => "number",
             LuaValueType.Function => "function",
             LuaValueType.Thread => "thread",
+            LuaValueType.LightUserData => "userdata",
             LuaValueType.UserData => "userdata",
             LuaValueType.Table => "table",
             _ => throw new NotImplementedException(),
-        };
-
-        return new(1);
+        }));
     }
 
-    public async ValueTask<int> XPCall(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> XPCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
         var arg1 = context.GetArgument<LuaFunction>(1);
 
-        using var methodBuffer = new PooledArray<LuaValue>(1024);
-        methodBuffer.AsSpan().Clear();
-
         try
         {
-            var resultCount = await arg0.InvokeAsync(context with
-            {
-                State = context.State,
-                ArgumentCount = context.ArgumentCount - 2,
-                FrameBase = context.FrameBase + 2,
-            }, methodBuffer.AsMemory(), cancellationToken);
-
-            buffer.Span[0] = true;
-            methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]);
+            var count = await arg0.InvokeAsync(context with { State = context.State, ArgumentCount = context.ArgumentCount - 2, FrameBase = context.FrameBase + 2, ReturnFrameBase = context.ReturnFrameBase + 1 }, cancellationToken);
 
-            return resultCount + 1;
+            context.Thread.Stack.Get(context.ReturnFrameBase) = true;
+            return count + 1;
         }
         catch (Exception ex)
         {
-            methodBuffer.AsSpan().Clear();
             var error = ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message;
 
             context.State.Push(error);
 
             // invoke error handler
-            await arg1.InvokeAsync(context with
-            {
-                State = context.State,
-                ArgumentCount = 1,
-                FrameBase = context.Thread.Stack.Count - 1,
-            }, methodBuffer.AsMemory(), cancellationToken);
-
-            buffer.Span[0] = false;
-            buffer.Span[1] = methodBuffer[0];
-
-
-            return 2;
+            var count = await arg1.InvokeAsync(context with { State = context.State, ArgumentCount = 1, FrameBase = context.Thread.Stack.Count - 1, ReturnFrameBase = context.ReturnFrameBase + 1 }, cancellationToken);
+            context.Thread.Stack.Get(context.ReturnFrameBase) = false;
+            return count + 1;
         }
     }
 }

+ 35 - 47
src/Lua/Standard/BitwiseLibrary.cs

@@ -8,7 +8,8 @@ public sealed class BitwiseLibrary
 
     public BitwiseLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("arshift", ArShift),
             new("band", BAnd),
             new("bnot", BNot),
@@ -26,7 +27,7 @@ public sealed class BitwiseLibrary
 
     public readonly LuaFunction[] Functions;
 
-    public ValueTask<int> ArShift(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> ArShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
@@ -46,16 +47,15 @@ public sealed class BitwiseLibrary
             v >>= a;
         }
 
-        buffer.Span[0] = (uint)v;
-        return new(1);
+        return new(context.Return((uint)v));
     }
 
-    public ValueTask<int> BAnd(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> BAnd(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.ArgumentCount == 0)
         {
-            buffer.Span[0] = uint.MaxValue;
-            return new(1);
+            context.Return(uint.MaxValue);
+            return default;
         }
 
         var arg0 = context.GetArgument<double>(0);
@@ -72,26 +72,24 @@ public sealed class BitwiseLibrary
             value &= v;
         }
 
-        buffer.Span[0] = value;
-        return new(1);
+
+        return new(context.Return(value));
     }
 
-    public ValueTask<int> BNot(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> BNot(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bnot", 1, arg0);
 
         var value = Bit32Helper.ToUInt32(arg0);
-        buffer.Span[0] = ~value;
-        return new(1);
+        return new(context.Return(~value));
     }
 
-    public ValueTask<int> BOr(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> BOr(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.ArgumentCount == 0)
         {
-            buffer.Span[0] = 0;
-            return new(1);
+            return new(context.Return(0));
         }
 
         var arg0 = context.GetArgument<double>(0);
@@ -108,16 +106,15 @@ public sealed class BitwiseLibrary
             value |= v;
         }
 
-        buffer.Span[0] = value;
-        return new(1);
+        return new(context.Return(value));
     }
 
-    public ValueTask<int> BTest(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> BTest(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.ArgumentCount == 0)
         {
-            buffer.Span[0] = true;
-            return new(1);
+            ;
+            return new(context.Return(true));
         }
 
         var arg0 = context.GetArgument<double>(0);
@@ -134,16 +131,14 @@ public sealed class BitwiseLibrary
             value &= v;
         }
 
-        buffer.Span[0] = value != 0;
-        return new(1);
+        return new(context.Return(value != 0));
     }
 
-    public ValueTask<int> BXor(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> BXor(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.ArgumentCount == 0)
         {
-            buffer.Span[0] = 0;
-            return new(1);
+            return new(context.Return(0));
         }
 
         var arg0 = context.GetArgument<double>(0);
@@ -160,11 +155,10 @@ public sealed class BitwiseLibrary
             value ^= v;
         }
 
-        buffer.Span[0] = value;
-        return new(1);
+        return new(context.Return(value));
     }
 
-    public ValueTask<int> Extract(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Extract(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var arg1 = context.GetArgument<double>(1);
@@ -184,18 +178,16 @@ public sealed class BitwiseLibrary
 
         if (field == 0 && width == 32)
         {
-            buffer.Span[0] = n;
+            return new(context.Return(n));
         }
         else
         {
             var mask = (uint)((1 << width) - 1);
-            buffer.Span[0] = (n >> field) & mask;
+            return new(context.Return((n >> field) & mask));
         }
-
-        return new(1);
     }
 
-    public ValueTask<int> LRotate(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> LRotate(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
@@ -215,11 +207,11 @@ public sealed class BitwiseLibrary
             v = (v << a) | (v >> (32 - a));
         }
 
-        buffer.Span[0] = v;
-        return new(1);
+        ;
+        return new(context.Return(v));
     }
 
-    public ValueTask<int> LShift(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> LShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
@@ -243,11 +235,10 @@ public sealed class BitwiseLibrary
             v <<= a;
         }
 
-        buffer.Span[0] = v;
-        return new(1);
+        return new(context.Return(v));
     }
 
-    public ValueTask<int> Replace(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Replace(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var arg1 = context.GetArgument<double>(1);
@@ -279,11 +270,10 @@ public sealed class BitwiseLibrary
 
         v = v & mask;
         n = (n & ~(mask << field)) | (v << field);
-        buffer.Span[0] = n;
-        return new(1);
+        return new(context.Return(n));
     }
 
-    public ValueTask<int> RRotate(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RRotate(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
@@ -303,11 +293,10 @@ public sealed class BitwiseLibrary
             v = (v >> a) | (v << (32 - a));
         }
 
-        buffer.Span[0] = v;
-        return new(1);
+        return new(context.Return(v));
     }
 
-    public ValueTask<int> RShift(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
@@ -331,7 +320,6 @@ public sealed class BitwiseLibrary
             v >>= a;
         }
 
-        buffer.Span[0] = v;
-        return new(1);
+        return new(context.Return(v));
     }
 }

+ 39 - 52
src/Lua/Standard/CoroutineLibrary.cs

@@ -8,7 +8,8 @@ public sealed class CoroutineLibrary
 
     public CoroutineLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("create", Create),
             new("resume", Resume),
             new("running", Running),
@@ -20,87 +21,73 @@ public sealed class CoroutineLibrary
 
     public readonly LuaFunction[] Functions;
 
-    public ValueTask<int> Create(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Create(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
-        buffer.Span[0] = new LuaCoroutine(arg0, true);
-        return new(1);
+        return new(context.Return(new LuaCoroutine(arg0, true)));
     }
 
-    public ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Resume(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var thread = context.GetArgument<LuaThread>(0);
-        return thread.ResumeAsync(context, buffer, cancellationToken);
+        return thread.ResumeAsync(context, cancellationToken);
     }
 
-    public ValueTask<int> Running(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Running(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        buffer.Span[0] = context.Thread;
-        buffer.Span[1] = context.Thread == context.State.MainThread;
-        return new(2);
+        return new(context.Return(context.Thread, context.Thread == context.State.MainThread));
     }
 
-    public ValueTask<int> Status(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Status(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var thread = context.GetArgument<LuaThread>(0);
-        buffer.Span[0] = thread.GetStatus() switch
+        return new(context.Return(thread.GetStatus() switch
         {
             LuaThreadStatus.Normal => "normal",
             LuaThreadStatus.Suspended => "suspended",
             LuaThreadStatus.Running => "running",
             LuaThreadStatus.Dead => "dead",
             _ => "",
-        };
-        return new(1);
+        }));
     }
 
-    public ValueTask<int> Wrap(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+
+    public ValueTask<int> Wrap(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
         var thread = new LuaCoroutine(arg0, false);
-
-        buffer.Span[0] = new CSharpClosure("wrap", [thread],static async (context, buffer, cancellationToken) =>
-        {
-            var thread = context.GetCsClosure()!.UpValues[0].Read<LuaThread>();
-            if (thread is not LuaCoroutine coroutine)
-            {
-                return await thread.ResumeAsync(context, buffer, cancellationToken);
-            }
-            var stack = context.Thread.Stack;
-            var frameBase = stack.Count;
-
-            stack.Push(thread);
-            stack.PushRange(context.Arguments);
-            context.Thread.PushCallStackFrame(new()
+        return new(context.Return(new CSharpClosure("wrap", [thread],
+            static async (context, cancellationToken) =>
             {
-                Base = frameBase,
-                VariableArgumentCount = 0,
-                Function = coroutine.Function,
-            });
-            try
-            {
-                var resultCount = await thread.ResumeAsync(context with
+                var thread = context.GetCsClosure()!.UpValues[0].Read<LuaThread>();
+                if (thread is not LuaCoroutine coroutine)
                 {
-                    ArgumentCount = context.ArgumentCount + 1,
-                    FrameBase = frameBase,
-                }, buffer, cancellationToken);
-
-                buffer.Span[1..].CopyTo(buffer.Span[0..]);
-                return resultCount - 1;
-            }
-            finally
-            {
-                context.Thread.PopCallStackFrame();
-            }
+                    return await thread.ResumeAsync(context, cancellationToken);
+                }
 
-           
-        });
+                var stack = context.Thread.Stack;
+                var frameBase = stack.Count;
 
-        return new(1);
+                stack.Push(thread);
+                stack.PushRange(context.Arguments);
+                context.Thread.PushCallStackFrame(new() { Base = frameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = coroutine.Function });
+                try
+                {
+                    await thread.ResumeAsync(context with { ArgumentCount = context.ArgumentCount + 1, FrameBase = frameBase, ReturnFrameBase = context.ReturnFrameBase, }, cancellationToken);
+                    var result = context.GetReturnBuffer(context.Thread.Stack.Count - context.ReturnFrameBase);
+                    result[1..].CopyTo(result);
+                    context.Thread.Stack.Pop();
+                    return result.Length - 1;
+                }
+                finally
+                {
+                    context.Thread.PopCallStackFrame();
+                }
+            })));
     }
 
-    public ValueTask<int> Yield(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        return context.Thread.YieldAsync(context, buffer, cancellationToken);
+        return context.Thread.YieldAsync(context, cancellationToken);
     }
 }

+ 61 - 103
src/Lua/Standard/DebugLibrary.cs

@@ -124,7 +124,7 @@ public class DebugLibrary
         return ref thread.Stack.Get(frameBase + index);
     }
 
-    public ValueTask<int> GetLocal(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         static LuaValue GetParam(LuaFunction function, int index)
         {
@@ -145,8 +145,7 @@ public class DebugLibrary
         var index = context.GetArgument<int>(argOffset + 1);
         if (context.GetArgument(argOffset).TryReadFunction(out var f))
         {
-            buffer.Span[0] = GetParam(f, index - 1);
-            return new(1);
+            return new(context.Return(GetParam(f, index - 1)));
         }
 
         var level = context.GetArgument<int>(argOffset);
@@ -160,16 +159,13 @@ public class DebugLibrary
         ref var local = ref FindLocal(thread, level, index, out var name);
         if (name is null)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
-        buffer.Span[0] = name;
-        buffer.Span[1] = local;
-        return new(2);
+        return new(context.Return(name, local));
     }
 
-    public ValueTask<int> SetLocal(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> SetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var thread = GetLuaThread(context, out var argOffset);
 
@@ -186,16 +182,14 @@ public class DebugLibrary
         ref var local = ref FindLocal(thread, level, index, out var name);
         if (name is null)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
-        buffer.Span[0] = name;
         local = value;
-        return new(1);
+        return new(context.Return(name));
     }
 
-    public ValueTask<int> GetUpValue(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetUpValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var func = context.GetArgument<LuaFunction>(0);
         var index = context.GetArgument<int>(1) - 1;
@@ -206,15 +200,13 @@ public class DebugLibrary
                 var upValues = csClosure.UpValues;
                 if (index < 0 || index >= upValues.Length)
                 {
-                    return new(0);
+                    return new(context.Return());
                 }
 
-                buffer.Span[0] = "";
-                buffer.Span[1] = upValues[index];
-                return new(1);
+                return new(context.Return("", upValues[index]));
             }
 
-            return new(0);
+            return new(context.Return());
         }
 
         {
@@ -222,17 +214,15 @@ public class DebugLibrary
             var descriptions = closure.Proto.UpValues;
             if (index < 0 || index >= descriptions.Length)
             {
-                return new(0);
+                return new(context.Return());
             }
 
             var description = descriptions[index];
-            buffer.Span[0] = description.Name.ToString();
-            buffer.Span[1] = upValues[index].GetValue();
-            return new(2);
+            return new(context.Return(description.Name.ToString(), upValues[index].GetValue()));
         }
     }
 
-    public ValueTask<int> SetUpValue(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> SetUpValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var func = context.GetArgument<LuaFunction>(0);
         var index = context.GetArgument<int>(1) - 1;
@@ -244,15 +234,12 @@ public class DebugLibrary
                 var upValues = csClosure.UpValues;
                 if (index >= 0 && index < upValues.Length)
                 {
-                    buffer.Span[0] = "";
                     upValues[index] = value;
-                    return new(0);
+                    return new(context.Return(""));
                 }
-
-                return new(0);
             }
 
-            return new(0);
+            return new(context.Return());
         }
 
         {
@@ -260,33 +247,30 @@ public class DebugLibrary
             var descriptions = closure.Proto.UpValues;
             if (index < 0 || index >= descriptions.Length)
             {
-                return new(0);
+                return new(context.Return());
             }
 
             var description = descriptions[index];
-            buffer.Span[0] = description.Name.ToString();
             upValues[index].SetValue(value);
-            return new(1);
+            return new(context.Return(description.Name.ToString()));
         }
     }
 
-    public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
         if (context.State.TryGetMetatable(arg0, out var table))
         {
-            buffer.Span[0] = table;
+            return new(context.Return(table));
         }
         else
         {
-            buffer.Span[0] = LuaValue.Nil;
+            return new(context.Return(LuaValue.Nil));
         }
-
-        return new(1);
     }
 
-    public ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
         var arg1 = context.GetArgument(1);
@@ -298,16 +282,14 @@ public class DebugLibrary
 
         context.State.SetMetatable(arg0, arg1.UnsafeRead<LuaTable>());
 
-        buffer.Span[0] = arg0;
-        return new(1);
+        return new(context.Return(arg0));
     }
 
-    public ValueTask<int> GetUserValue(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetUserValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (!context.GetArgumentOrDefault(0).TryRead<ILuaUserData>(out var iUserData))
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
         var index = 1; // context.GetArgument<int>(1); //for lua 5.4
@@ -316,15 +298,13 @@ public class DebugLibrary
             //index < 1 ||  // for lua 5.4
            )
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
-        buffer.Span[0] = userValues[index - 1];
-        return new(1);
+        return new(context.Return(userValues[index - 1]));
     }
 
-    public ValueTask<int> SetUserValue(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> SetUserValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var iUserData = context.GetArgument<ILuaUserData>(0);
         var value = context.GetArgument(1);
@@ -334,16 +314,14 @@ public class DebugLibrary
             //|| index < 1 // for lua 5.4
            )
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
         userValues[index - 1] = value;
-        buffer.Span[0] = new LuaValue(iUserData);
-        return new(1);
+        return new(context.Return(new LuaValue(iUserData)));
     }
 
-    public ValueTask<int> Traceback(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Traceback(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var thread = GetLuaThread(context, out var argOffset);
 
@@ -352,67 +330,58 @@ public class DebugLibrary
 
         if (message.Type is not (LuaValueType.Nil or LuaValueType.String or LuaValueType.Number))
         {
-            buffer.Span[0] = message;
-            return new(1);
+            return new(context.Return(message));
         }
 
         if (level < 0)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
         if (thread is LuaCoroutine coroutine)
         {
             if (coroutine.LuaTraceback is not null)
             {
-                buffer.Span[0] = coroutine.LuaTraceback.ToString(level);
-                return new(1);
+                return new(context.Return(coroutine.LuaTraceback.ToString(level)));
             }
         }
 
         var callStack = thread.GetCallStackFrames();
         if (callStack.Length == 0)
         {
-            buffer.Span[0] = "stack traceback:";
-            return new(1);
+            return new(context.Return("stack traceback:"));
         }
 
         var skipCount = Math.Min(Math.Max(level - 1, 0), callStack.Length - 1);
         var frames = callStack[1..^skipCount];
-        buffer.Span[0] = Runtime.Traceback.GetTracebackString(context.State, (LuaClosure)callStack[0].Function, frames, message, level == 1);
-        return new(1);
+        return new(context.Return(Runtime.Traceback.GetTracebackString(context.State, (LuaClosure)callStack[0].Function, frames, message, level == 1)));
     }
 
-    public ValueTask<int> GetRegistry(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetRegistry(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        buffer.Span[0] = context.State.Registry;
-        return new(1);
+        return new(context.Return(context.State.Registry));
     }
 
-    public ValueTask<int> UpValueId(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> UpValueId(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var n1 = context.GetArgument<int>(1);
         var f1 = context.GetArgument<LuaFunction>(0);
 
         if (f1 is not LuaClosure closure)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
         var upValues = closure.GetUpValuesSpan();
         if (n1 <= 0 || n1 > upValues.Length)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
-        buffer.Span[0] = new LuaValue(upValues[n1 - 1]);
-        return new(1);
+        return new(context.Return(new LuaValue(upValues[n1 - 1])));
     }
 
-    public ValueTask<int> UpValueJoin(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> UpValueJoin(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var n2 = context.GetArgument<int>(3);
         var f2 = context.GetArgument<LuaFunction>(2);
@@ -421,8 +390,7 @@ public class DebugLibrary
 
         if (f1 is not LuaClosure closure1 || f2 is not LuaClosure closure2)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
         var upValues1 = closure1.GetUpValuesSpan();
@@ -441,7 +409,7 @@ public class DebugLibrary
         return new(0);
     }
 
-    public async ValueTask<int> SetHook(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> SetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var thread = GetLuaThread(context, out var argOffset);
         LuaFunction? hook = context.GetArgumentOrDefault<LuaFunction?>(argOffset);
@@ -496,54 +464,49 @@ public class DebugLibrary
                 Thread = context.Thread,
                 ArgumentCount = 2,
                 FrameBase = stack.Count - 2,
+                ReturnFrameBase = stack.Count - 2,
             };
             var frame = new CallStackFrame
             {
-                Base = funcContext.FrameBase,
-                VariableArgumentCount = hook.GetVariableArgumentCount(2),
-                Function = hook,
+                Base = funcContext.FrameBase, ReturnBase = funcContext.ReturnFrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook,
             };
             frame.Flags |= CallStackFrameFlags.InHook;
             thread.PushCallStackFrame(frame);
             try
             {
                 thread.IsInHook = true;
-                await hook.Func(funcContext, Memory<LuaValue>.Empty, cancellationToken);
+                await hook.Func(funcContext, cancellationToken);
             }
             finally
             {
                 thread.IsInHook = false;
             }
 
-            thread.PopCallStackFrame();
+            thread.PopCallStackFrameWithStackPop();
         }
 
         return 0;
     }
 
 
-    public ValueTask<int> GetHook(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var thread = GetLuaThread(context, out var argOffset);
         if (thread.Hook is null)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = LuaValue.Nil;
-            buffer.Span[2] = LuaValue.Nil;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, LuaValue.Nil, LuaValue.Nil));
         }
 
-        buffer.Span[0] = thread.Hook;
-        buffer.Span[1] = (
-            (thread.IsCallHookEnabled ? "c" : "") +
-            (thread.IsReturnHookEnabled ? "r" : "") +
-            (thread.IsLineHookEnabled ? "l" : "")
-        );
-        buffer.Span[2] = thread.BaseHookCount;
-        return new(3);
+        return new(context.Return(thread.Hook,
+            (
+                (thread.IsCallHookEnabled ? "c" : "") +
+                (thread.IsReturnHookEnabled ? "r" : "") +
+                (thread.IsLineHookEnabled ? "l" : "")
+            )
+            , thread.BaseHookCount));
     }
 
-    public ValueTask<int> GetInfo(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetInfo(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         //return new(0);
         var thread = GetLuaThread(context, out var argOffset);
@@ -565,8 +528,7 @@ public class DebugLibrary
 
             if (level <= 0 || level > callStack.Length)
             {
-                buffer.Span[0] = LuaValue.Nil;
-                return new(1);
+                return new(context.Return(LuaValue.Nil));
             }
 
 
@@ -616,9 +578,7 @@ public class DebugLibrary
         if (what.Contains('n'))
         {
             table["name"] = debug.Name ?? LuaValue.Nil;
-            ;
             table["namewhat"] = debug.NameWhat ?? LuaValue.Nil;
-            ;
         }
 
         if (what.Contains('t'))
@@ -645,8 +605,6 @@ public class DebugLibrary
             }
         }
 
-        buffer.Span[0] = table;
-
-        return new(1);
+        return new(context.Return(table));
     }
 }

+ 26 - 40
src/Lua/Standard/FileHandle.cs

@@ -7,14 +7,14 @@ namespace Lua.Standard;
 
 public class FileHandle : ILuaUserData
 {
-    public static readonly LuaFunction IndexMetamethod = new("index", (context, buffer, ct) =>
+    public static readonly LuaFunction IndexMetamethod = new("index", (context, ct) =>
     {
         context.GetArgument<FileHandle>(0);
         var key = context.GetArgument(1);
 
         if (key.TryRead<string>(out var name))
         {
-            buffer.Span[0] = name switch
+            return new(context.Return(name switch
             {
                 "close" => CloseFunction!,
                 "flush" => FlushFunction!,
@@ -24,14 +24,12 @@ public class FileHandle : ILuaUserData
                 "setvbuf" => SetVBufFunction!,
                 "write" => WriteFunction!,
                 _ => LuaValue.Nil,
-            };
+            }));
         }
         else
         {
-            buffer.Span[0] = LuaValue.Nil;
+            return new(context.Return(LuaValue.Nil));
         }
-
-        return new(1);
     });
 
     Stream stream;
@@ -131,45 +129,37 @@ public class FileHandle : ILuaUserData
         }
     }
 
-    static readonly LuaFunction CloseFunction = new("close", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction CloseFunction = new("close", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
 
         try
         {
             file.Close();
-            buffer.Span[0] = true;
-            return new(1);
+            return new(context.Return(true));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     });
 
-    static readonly LuaFunction FlushFunction = new("flush", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction FlushFunction = new("flush", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
 
         try
         {
             file.Flush();
-            buffer.Span[0] = true;
-            return new(1);
+            return new(context.Return(true));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     });
 
-    static readonly LuaFunction LinesFunction = new("lines", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction LinesFunction = new("lines", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var format = context.HasArgument(1)
@@ -177,25 +167,25 @@ public class FileHandle : ILuaUserData
             : "*l";
 
 
-        buffer.Span[0] = new CSharpClosure("iterator", [new (file),format],static (context, buffer, cancellationToken) =>
+        return new(context.Return(new CSharpClosure("iterator", [new(file), format], static (context, cancellationToken) =>
         {
             var upValues = context.GetCsClosure()!.UpValues.AsSpan();
             var file = upValues[0].Read<FileHandle>();
-            var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], buffer, true);
+            context.Return();
+            var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], context.Thread.Stack, true);
             return new(resultCount);
-        });
-
-        return new(1);
+        })));
     });
 
-    static readonly LuaFunction ReadFunction = new("read", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction ReadFunction = new("read", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
-        var resultCount = IOHelper.Read(context.State, file, "read", 1, context.Arguments[1..], buffer, false);
+        context.Return();
+        var resultCount = IOHelper.Read(context.State, file, "read", 1, context.Arguments[1..], context.Thread.Stack, false);
         return new(resultCount);
     });
 
-    static readonly LuaFunction SeekFunction = new("seek", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction SeekFunction = new("seek", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var whence = context.HasArgument(1)
@@ -212,19 +202,15 @@ public class FileHandle : ILuaUserData
 
         try
         {
-            buffer.Span[0] = file.Seek(whence, (long)offset);
-            return new(1);
+            return new(context.Return(file.Seek(whence, (long)offset)));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     });
 
-    static readonly LuaFunction SetVBufFunction = new("setvbuf", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction SetVBufFunction = new("setvbuf", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var mode = context.GetArgument<string>(1);
@@ -234,14 +220,14 @@ public class FileHandle : ILuaUserData
 
         file.SetVBuf(mode, size);
 
-        buffer.Span[0] = true;
-        return new(1);
+        return new(context.Return(true));
     });
 
-    static readonly LuaFunction WriteFunction = new("write", (context, buffer, cancellationToken) =>
+    static readonly LuaFunction WriteFunction = new("write", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
-        var resultCount = IOHelper.Write(file, "write", context, buffer);
+        context.Return();
+        var resultCount = IOHelper.Write(file, "write", context);
         return new(resultCount);
     });
 }

+ 48 - 56
src/Lua/Standard/IOLibrary.cs

@@ -10,7 +10,8 @@ public sealed class IOLibrary
 
     public IOLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("close", Close),
             new("flush", Flush),
             new("input", Input),
@@ -25,7 +26,7 @@ public sealed class IOLibrary
 
     public readonly LuaFunction[] Functions;
 
-    public ValueTask<int> Close(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Close(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var file = context.HasArgument(0)
             ? context.GetArgument<FileHandle>(0)
@@ -34,175 +35,166 @@ public sealed class IOLibrary
         try
         {
             file.Close();
-            buffer.Span[0] = true;
-            return new(1);
+            return new(context.Return(true));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     }
 
-    public ValueTask<int> Flush(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Flush(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var file = context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
 
         try
         {
             file.Flush();
-            buffer.Span[0] = true;
-            return new(1);
+            return new(context.Return(true));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     }
 
-    public ValueTask<int> Input(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Input(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var io = context.State.Environment["io"].Read<LuaTable>();
 
         if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
         {
-            buffer.Span[0] = io["stdio"];
-            return new(1);
+            return new(context.Return(io["stdio"]));
         }
 
         var arg = context.Arguments[0];
         if (arg.TryRead<FileHandle>(out var file))
         {
             io["stdio"] = new(file);
-            buffer.Span[0] = new(file);
-            return new(1);
+            return new(context.Return(new LuaValue(file)));
         }
         else
         {
             var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite);
             var handle = new FileHandle(stream);
             io["stdio"] = new(handle);
-            buffer.Span[0] = new(handle);
-            return new(1);
+            return new(context.Return(new LuaValue(handle)));
         }
     }
 
-    public ValueTask<int> Lines(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Lines(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.ArgumentCount == 0)
         {
             var file = context.State.Environment["io"].Read<LuaTable>()["stdio"].Read<FileHandle>();
-            buffer.Span[0] = new CSharpClosure("iterator",[new (file)] ,static (context, buffer, ct) =>
+            return new(context.Return(new CSharpClosure("iterator", [new(file)], static (context, ct) =>
             {
                 var file = context.GetCsClosure()!.UpValues[0].Read<FileHandle>();
-                var resultCount = IOHelper.Read(context.State, file, "lines", 0, [], buffer, true);
-                if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil)
+                context.Return();
+                var resultCount = IOHelper.Read(context.State, file, "lines", 0, [], context.Thread.Stack, true);
+                if (resultCount > 0 && context.Thread.Stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 {
                     file.Close();
                 }
+
                 return new(resultCount);
-            });
-            return new(1);
+            })));
         }
         else
         {
             var fileName = context.GetArgument<string>(0);
+            var stack = context.Thread.Stack;
+            context.Return();
 
-            using var methodBuffer = new PooledArray<LuaValue>(32);
-            IOHelper.Open(context.State, fileName, "r", methodBuffer.AsMemory(), true);
+            IOHelper.Open(context.State, fileName, "r", stack, true);
 
-            var file = methodBuffer[0].Read<FileHandle>();
+            var file = stack.Get(context.ReturnFrameBase).Read<FileHandle>();
             var upValues = new LuaValue[context.Arguments.Length];
             upValues[0] = new(file);
             context.Arguments[1..].CopyTo(upValues[1..]);
 
-            buffer.Span[0] = new CSharpClosure("iterator", upValues, static (context, buffer, ct) =>
+            return new(context.Return(new CSharpClosure("iterator", upValues, static (context, ct) =>
             {
                 var upValues = context.GetCsClosure()!.UpValues;
                 var file = upValues[0].Read<FileHandle>();
                 var formats = upValues.AsSpan(1);
-                var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, buffer, true);
-                if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil)
+                var stack = context.Thread.Stack;
+                context.Return();
+                var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, stack, true);
+                if (resultCount > 0 && stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 {
                     file.Close();
                 }
-                return new(resultCount);
-            });
 
-            return new(1);
+                return new(resultCount);
+            })));
         }
     }
 
-    public ValueTask<int> Open(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Open(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var fileName = context.GetArgument<string>(0);
         var mode = context.HasArgument(1)
             ? context.GetArgument<string>(1)
             : "r";
-
-        var resultCount = IOHelper.Open(context.State, fileName, mode, buffer, false);
+        context.Return();
+        var resultCount = IOHelper.Open(context.State, fileName, mode, context.Thread.Stack, false);
         return new(resultCount);
     }
 
-    public ValueTask<int> Output(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Output(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var io = context.State.Environment["io"].Read<LuaTable>();
 
         if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil)
         {
-            buffer.Span[0] = io["stdout"];
-            return new(1);
+            return new(context.Return(io["stdout"]));
         }
 
         var arg = context.Arguments[0];
         if (arg.TryRead<FileHandle>(out var file))
         {
             io["stdout"] = new(file);
-            buffer.Span[0] = new(file);
-            return new(1);
+            return new(context.Return(new LuaValue(file)));
         }
         else
         {
             var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite);
             var handle = new FileHandle(stream);
             io["stdout"] = new(handle);
-            buffer.Span[0] = new(handle);
-            return new(1);
+            return new(context.Return(new LuaValue(handle)));
         }
     }
 
-    public ValueTask<int> Read(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Read(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var file = context.State.Environment["io"].Read<LuaTable>()["stdio"].Read<FileHandle>();
-        var resultCount = IOHelper.Read(context.State, file, "read", 0, context.Arguments, buffer, false);
+        context.Return();
+        var stack = context.Thread.Stack;
+
+        var resultCount = IOHelper.Read(context.State, file, "read", 0, context.Arguments, stack, false);
         return new(resultCount);
     }
 
-    public ValueTask<int> Type(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Type(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument(0);
 
         if (arg0.TryRead<FileHandle>(out var file))
         {
-            buffer.Span[0] = file.IsClosed ? "closed file" : "file";
+            return new(context.Return(file.IsClosed ? "closed file" : "file"));
         }
         else
         {
-            buffer.Span[0] = LuaValue.Nil;
+            return new(context.Return(LuaValue.Nil));
         }
-
-        return new(1);
     }
 
-    public ValueTask<int> Write(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Write(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var file = context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
-        var resultCount = IOHelper.Write(file, "write", context, buffer);
+        context.Return();
+        var resultCount = IOHelper.Write(file, "write", context);
         return new(resultCount);
     }
 }

+ 25 - 19
src/Lua/Standard/Internal/IOHelper.cs

@@ -1,11 +1,12 @@
 using System.Text;
 using Lua.Internal;
+using Lua.Runtime;
 
 namespace Lua.Standard.Internal;
 
 internal static class IOHelper
 {
-    public static int Open(LuaState state, string fileName, string mode, Memory<LuaValue> buffer, bool throwError)
+    public static int Open(LuaState state, string fileName, string mode, LuaStack stack, bool throwError)
     {
         var fileMode = mode switch
         {
@@ -25,7 +26,7 @@ internal static class IOHelper
         try
         {
             var stream = File.Open(fileName, fileMode, fileAccess);
-            buffer.Span[0] = new LuaValue(new FileHandle(stream));
+            stack.Push(new LuaValue(new FileHandle(stream)));
             return 1;
         }
         catch (IOException ex)
@@ -35,16 +36,16 @@ internal static class IOHelper
                 throw;
             }
 
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
+            stack.Push(LuaValue.Nil);
+            stack.Push(ex.Message);
+            stack.Push(ex.HResult);
             return 3;
         }
     }
 
     // TODO: optimize (use IBuffertWrite<byte>, async)
 
-    public static int Write(FileHandle file, string name, LuaFunctionExecutionContext context, Memory<LuaValue> buffer)
+    public static int Write(FileHandle file, string name, LuaFunctionExecutionContext context)
     {
         try
         {
@@ -70,25 +71,28 @@ internal static class IOHelper
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
+            var stack = context.Thread.Stack;
+            stack.Push(LuaValue.Nil);
+            stack.Push(ex.Message);
+            stack.Push(ex.HResult);
             return 3;
         }
 
-        buffer.Span[0] = new(file);
+        context.Thread.Stack.Push(new(file));
         return 1;
     }
 
     static readonly LuaValue[] defaultReadFormat = ["*l"];
 
-    public static int Read(LuaState state, FileHandle file, string name, int startArgumentIndex, ReadOnlySpan<LuaValue> formats, Memory<LuaValue> buffer, bool throwError)
+    public static int Read(LuaState state, FileHandle file, string name, int startArgumentIndex, ReadOnlySpan<LuaValue> formats, LuaStack stack, bool throwError)
     {
         if (formats.Length == 0)
         {
             formats = defaultReadFormat;
         }
 
+        var top = stack.Count;
+
         try
         {
             for (int i = 0; i < formats.Length; i++)
@@ -104,16 +108,16 @@ internal static class IOHelper
                             throw new NotImplementedException();
                         case "*a":
                         case "*all":
-                            buffer.Span[i] = file.ReadToEnd();
+                            stack.Push(file.ReadToEnd());
                             break;
                         case "*l":
                         case "*line":
-                            buffer.Span[i] = file.ReadLine() ?? LuaValue.Nil;
+                            stack.Push(file.ReadLine() ?? LuaValue.Nil);
                             break;
                         case "L":
                         case "*L":
                             var text = file.ReadLine();
-                            buffer.Span[i] = text == null ? LuaValue.Nil : text + Environment.NewLine;
+                            stack.Push(text == null ? LuaValue.Nil : text + Environment.NewLine);
                             break;
                     }
                 }
@@ -126,14 +130,15 @@ internal static class IOHelper
                         var b = file.ReadByte();
                         if (b == -1)
                         {
-                            buffer.Span[0] = LuaValue.Nil;
+                            stack.PopUntil(top);
+                            stack.Push(LuaValue.Nil);
                             return 1;
                         }
 
                         byteBuffer[j] = (byte)b;
                     }
 
-                    buffer.Span[i] = Encoding.UTF8.GetString(byteBuffer.AsSpan());
+                    stack.Push(Encoding.UTF8.GetString(byteBuffer.AsSpan()));
                 }
                 else
                 {
@@ -150,9 +155,10 @@ internal static class IOHelper
                 throw;
             }
 
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
+            stack.PopUntil(top);
+            stack.Push(LuaValue.Nil);
+            stack.Push(ex.Message);
+            stack.Push(ex.HResult);
             return 3;
         }
     }

+ 70 - 106
src/Lua/Standard/MathematicsLibrary.cs

@@ -7,7 +7,8 @@ public sealed class MathematicsLibrary
 
     public MathematicsLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("abs", Abs),
             new("acos", Acos),
             new("asin", Asin),
@@ -43,134 +44,123 @@ public sealed class MathematicsLibrary
     public sealed class RandomUserData(Random random) : ILuaUserData
     {
         LuaTable? SharedMetatable;
-        public LuaTable? Metatable { get => SharedMetatable; set => SharedMetatable = value; }
+
+        public LuaTable? Metatable
+        {
+            get => SharedMetatable;
+            set => SharedMetatable = value;
+        }
+
         public Random Random { get; } = random;
     }
 
-    public ValueTask<int> Abs(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Abs(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Abs(arg0);
-        return new(1);
+        return new(context.Return(Math.Abs(arg0)));
     }
 
-    public ValueTask<int> Acos(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Acos(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Acos(arg0);
-        return new(1);
+        return new(context.Return(Math.Acos(arg0)));
     }
 
-    public ValueTask<int> Asin(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Asin(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Asin(arg0);
-        return new(1);
+        return new(context.Return(Math.Asin(arg0)));
     }
 
-    public ValueTask<int> Atan2(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Atan2(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var arg1 = context.GetArgument<double>(1);
 
-        buffer.Span[0] = Math.Atan2(arg0, arg1);
-        return new(1);
+        return new(context.Return(Math.Atan2(arg0, arg1)));
     }
 
-    public ValueTask<int> Atan(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Atan(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Atan(arg0);
-        return new(1);
+        return new(context.Return(Math.Atan(arg0)));
     }
 
-    public ValueTask<int> Ceil(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Ceil(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Ceiling(arg0);
-        return new(1);
+        return new(context.Return(Math.Ceiling(arg0)));
     }
 
-    public ValueTask<int> Cos(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Cos(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Cos(arg0);
-        return new(1);
+        return new(context.Return(Math.Cos(arg0)));
     }
 
-    public ValueTask<int> Cosh(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Cosh(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Cosh(arg0);
-        return new(1);
+        return new(context.Return(Math.Cosh(arg0)));
     }
 
-    public ValueTask<int> Deg(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Deg(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = arg0 * (180.0 / Math.PI);
-        return new(1);
+        return new(context.Return(arg0 * (180.0 / Math.PI)));
     }
 
-    public ValueTask<int> Exp(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Exp(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Exp(arg0);
-        return new(1);
+        return new(context.Return(Math.Exp(arg0)));
     }
 
-    public ValueTask<int> Floor(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Floor(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Floor(arg0);
-        return new(1);
+        return new(context.Return(Math.Floor(arg0)));
     }
 
-    public ValueTask<int> Fmod(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Fmod(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var arg1 = context.GetArgument<double>(1);
-        buffer.Span[0] = arg0 % arg1;
-        return new(1);
+        return new(context.Return(arg0 % arg1));
     }
 
-    public ValueTask<int> Frexp(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Frexp(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
 
         var (m, e) = MathEx.Frexp(arg0);
-        buffer.Span[0] = m;
-        buffer.Span[1] = e;
-        return new(2);
+        return new(context.Return(m, e));
     }
 
-    public ValueTask<int> Ldexp(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Ldexp(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var arg1 = context.GetArgument<double>(1);
 
-        buffer.Span[0] = arg0 * Math.Pow(2, arg1);
-        return new(1);
+        return new(context.Return(arg0 * Math.Pow(2, arg1)));
     }
 
-    public ValueTask<int> Log(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Log(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
 
         if (context.ArgumentCount == 1)
         {
-            buffer.Span[0] = Math.Log(arg0);
+            return new(context.Return(Math.Log(arg0)));
         }
         else
         {
             var arg1 = context.GetArgument<double>(1);
-            buffer.Span[0] = Math.Log(arg0, arg1);
+            return new(context.Return(Math.Log(arg0, arg1)));
         }
-
-        return new(1);
     }
 
-    public ValueTask<int> Max(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Max(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         for (int i = 1; i < context.ArgumentCount; i++)
@@ -178,12 +168,10 @@ public sealed class MathematicsLibrary
             x = Math.Max(x, context.GetArgument<double>(i));
         }
 
-        buffer.Span[0] = x;
-
-        return new(1);
+        return new(context.Return(x));
     }
 
-    public ValueTask<int> Min(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Min(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         for (int i = 1; i < context.ArgumentCount; i++)
@@ -191,109 +179,85 @@ public sealed class MathematicsLibrary
             x = Math.Min(x, context.GetArgument<double>(i));
         }
 
-        buffer.Span[0] = x;
-
-        return new(1);
+        return new(context.Return(x));
     }
 
-    public ValueTask<int> Modf(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Modf(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var (i, f) = MathEx.Modf(arg0);
-        buffer.Span[0] = i;
-        buffer.Span[1] = f;
-        return new(2);
+        return new(context.Return(i, f));
     }
 
-    public ValueTask<int> Pow(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Pow(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         var arg1 = context.GetArgument<double>(1);
 
-        buffer.Span[0] = Math.Pow(arg0, arg1);
-        return new(1);
+        return new(context.Return(Math.Pow(arg0, arg1)));
     }
 
-    public ValueTask<int> Rad(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Rad(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = arg0 * (Math.PI / 180.0);
-        return new(1);
+        return new(context.Return(arg0 * (Math.PI / 180.0)));
     }
 
-    public ValueTask<int> Random(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Random(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var rand = context.State.Environment[RandomInstanceKey].Read<RandomUserData>().Random;
-        // When we call it without arguments, it returns a pseudo-random real number with uniform distribution in the interval [0,1
+
         if (context.ArgumentCount == 0)
         {
-            buffer.Span[0] = rand.NextDouble();
+            return new(context.Return(rand.NextDouble()));
         }
-        // When we call it with only one argument, an integer n, it returns an integer pseudo-random number such that 1 <= x <= n.
-        // This is different from the C# random functions.
-        // See: https://www.lua.org/pil/18.html
         else if (context.ArgumentCount == 1)
         {
-            var arg0 = context.GetArgument<int>(0);
-            if (arg0 < 0)
-            {
-                LuaRuntimeException.BadArgument(context.State.GetTraceback(), 0, "random");
-            }
-            buffer.Span[0] = rand.Next(1, arg0 + 1);
+            var arg0 = context.GetArgument<double>(0);
+            return new(context.Return(rand.NextDouble() * (arg0 - 1) + 1));
         }
-        // Finally, we can call random with two integer arguments, l and u, to get a pseudo-random integer x such that l <= x <= u.
         else
         {
-            var arg0 = context.GetArgument<int>(0);
-            var arg1 = context.GetArgument<int>(1);
-            if (arg0 < 1 || arg1 <= arg0)
-            {
-                LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, "random");
-            }
-            buffer.Span[0] = rand.Next(arg0, arg1 + 1);
+            var arg0 = context.GetArgument<double>(0);
+            var arg1 = context.GetArgument<double>(1);
+            return new(context.Return(rand.NextDouble() * (arg1 - arg0) + arg0));
         }
-        return new(1);
     }
 
-    public ValueTask<int> RandomSeed(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> RandomSeed(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
         context.State.Environment[RandomInstanceKey] = new(new RandomUserData(new Random((int)BitConverter.DoubleToInt64Bits(arg0))));
-        return new(0);
+        return new(context.Return());
     }
 
-    public ValueTask<int> Sin(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Sin(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Sin(arg0);
-        return new(1);
+        return new(context.Return(Math.Sin(arg0)));
     }
 
-    public ValueTask<int> Sinh(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Sinh(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Sinh(arg0);
-        return new(1);
+        return new(context.Return(Math.Sinh(arg0)));
     }
 
-    public ValueTask<int> Sqrt(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Sqrt(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Sqrt(arg0);
-        return new(1);
+        return new(context.Return(Math.Sqrt(arg0)));
     }
 
-    public ValueTask<int> Tan(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Tan(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Tan(arg0);
-        return new(1);
+        return new(context.Return(Math.Tan(arg0)));
     }
 
-    public ValueTask<int> Tanh(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Tanh(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        buffer.Span[0] = Math.Tanh(arg0);
-        return new(1);
+        return new(context.Return(Math.Tanh(arg0)));
     }
 }

+ 4 - 7
src/Lua/Standard/ModuleLibrary.cs

@@ -15,7 +15,7 @@ public sealed class ModuleLibrary
 
     public readonly LuaFunction RequireFunction;
 
-    public async ValueTask<int> Require(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> Require(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<string>(0);
         var loaded = context.State.LoadedModules;
@@ -24,15 +24,12 @@ public sealed class ModuleLibrary
         {
             var module = await context.State.ModuleLoader.LoadAsync(arg0, cancellationToken);
             var chunk = LuaCompiler.Default.Compile(module.ReadText(), module.Name);
+            await new LuaClosure(context.State, chunk).InvokeAsync(context, cancellationToken);
 
-            using var methodBuffer = new PooledArray<LuaValue>(1);
-            await new LuaClosure(context.State, chunk).InvokeAsync(context, methodBuffer.AsMemory(), cancellationToken);
-
-            loadedTable = methodBuffer[0];
+            loadedTable = context.Thread.Stack.Get(context.ReturnFrameBase);
             loaded[arg0] = loadedTable;
         }
 
-        buffer.Span[0] = loadedTable;
-        return 1;
+        return context.Return(loadedTable);
     }
 }

+ 5 - 7
src/Lua/Standard/OpenLibsExtensions.cs

@@ -40,16 +40,16 @@ public static class OpenLibsExtensions
 
     public static void OpenIOLibrary(this LuaState state)
     {
-        
         var io = new LuaTable(0, IOLibrary.Instance.Functions.Length);
         foreach (var func in IOLibrary.Instance.Functions)
         {
             io[func.Name] = func;
         }
+
         io["stdio"] = new LuaValue(new FileHandle(ConsoleHelper.OpenStandardInput()));
         io["stdout"] = new LuaValue(new FileHandle(ConsoleHelper.OpenStandardOutput()));
         io["stderr"] = new LuaValue(new FileHandle(ConsoleHelper.OpenStandardError()));
-        
+
         state.Environment["io"] = io;
         state.LoadedModules["io"] = io;
     }
@@ -110,13 +110,11 @@ public static class OpenLibsExtensions
             state.SetMetatable(key, metatable);
         }
 
-        metatable[Metamethods.Index] = new LuaFunction("index", (context, buffer, cancellationToken) =>
+        metatable[Metamethods.Index] = new LuaFunction("index", (context, cancellationToken) =>
         {
             context.GetArgument<string>(0);
             var key = context.GetArgument(1);
-
-            buffer.Span[0] = @string[key];
-            return new(1);
+            return new(context.Return(@string[key]));
         });
     }
 
@@ -131,7 +129,7 @@ public static class OpenLibsExtensions
         state.Environment["table"] = table;
         state.LoadedModules["table"] = table;
     }
-    
+
     public static void OpenDebugLibrary(this LuaState state)
     {
         var debug = new LuaTable(0, DebugLibrary.Instance.Functions.Length);

+ 28 - 45
src/Lua/Standard/OperatingSystemLibrary.cs

@@ -9,7 +9,8 @@ public sealed class OperatingSystemLibrary
 
     public OperatingSystemLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("clock", Clock),
             new("date", Date),
             new("difftime", DiffTime),
@@ -26,13 +27,12 @@ public sealed class OperatingSystemLibrary
 
     public readonly LuaFunction[] Functions;
 
-    public ValueTask<int> Clock(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Clock(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        buffer.Span[0] = DateTimeHelper.GetUnixTime(DateTime.UtcNow, Process.GetCurrentProcess().StartTime);
-        return new(1);
+        return new(context.Return(DateTimeHelper.GetUnixTime(DateTime.UtcNow, Process.GetCurrentProcess().StartTime)));
     }
 
-    public ValueTask<int> Date(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Date(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var format = context.HasArgument(0)
             ? context.GetArgument<string>(0).AsSpan()
@@ -74,25 +74,22 @@ public sealed class OperatingSystemLibrary
             table["yday"] = now.DayOfYear;
             table["isdst"] = isDst;
 
-            buffer.Span[0] = table;
+            return new(context.Return(table));
         }
         else
         {
-            buffer.Span[0] = DateTimeHelper.StrFTime(context.State, format, now);
+            return new(context.Return(DateTimeHelper.StrFTime(context.State, format, now)));
         }
-
-        return new(1);
     }
 
-    public ValueTask<int> DiffTime(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> DiffTime(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var t2 = context.GetArgument<double>(0);
         var t1 = context.GetArgument<double>(1);
-        buffer.Span[0] = t2 - t1;
-        return new(1);
+        return new(context.Return(t2 - t1));
     }
 
-    public ValueTask<int> Execute(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Execute(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         // os.execute(command) is not supported
 
@@ -102,12 +99,11 @@ public sealed class OperatingSystemLibrary
         }
         else
         {
-            buffer.Span[0] = false;
-            return new(1);
+            return new(context.Return(false));
         }
     }
 
-    public ValueTask<int> Exit(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Exit(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         // Ignore 'close' parameter
 
@@ -133,80 +129,67 @@ public sealed class OperatingSystemLibrary
             Environment.Exit(0);
         }
 
-        return new(0);
+        return new(context.Return());
     }
 
-    public ValueTask<int> GetEnv(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GetEnv(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var variable = context.GetArgument<string>(0);
-        buffer.Span[0] = Environment.GetEnvironmentVariable(variable) ?? LuaValue.Nil;
-        return new(1);
+        return new(context.Return(Environment.GetEnvironmentVariable(variable) ?? LuaValue.Nil));
     }
 
-    public ValueTask<int> Remove(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Remove(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var fileName = context.GetArgument<string>(0);
         try
         {
             File.Delete(fileName);
-            buffer.Span[0] = true;
-            return new(1);
+            return new(context.Return(true));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     }
 
-    public ValueTask<int> Rename(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Rename(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var oldName = context.GetArgument<string>(0);
         var newName = context.GetArgument<string>(1);
         try
         {
             File.Move(oldName, newName);
-            buffer.Span[0] = true;
-            return new(1);
+            return new(context.Return(true));
         }
         catch (IOException ex)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            buffer.Span[1] = ex.Message;
-            buffer.Span[2] = ex.HResult;
-            return new(3);
+            return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult));
         }
     }
 
-    public ValueTask<int> SetLocale(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> SetLocale(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         // os.setlocale is not supported (always return nil)
 
-        buffer.Span[0] = LuaValue.Nil;
-        return new(1);
+        return new(context.Return(LuaValue.Nil));
     }
 
-    public ValueTask<int> Time(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Time(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.HasArgument(0))
         {
             var table = context.GetArgument<LuaTable>(0);
             var date = DateTimeHelper.ParseTimeTable(context.State, table);
-            buffer.Span[0] = DateTimeHelper.GetUnixTime(date);
-            return new(1);
+            return new(context.Return(DateTimeHelper.GetUnixTime(date)));
         }
         else
         {
-            buffer.Span[0] = DateTimeHelper.GetUnixTime(DateTime.UtcNow);
-            return new(1);
+            return new(context.Return(DateTimeHelper.GetUnixTime(DateTime.UtcNow)));
         }
     }
 
-    public ValueTask<int> TmpName(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> TmpName(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
-        buffer.Span[0] = Path.GetTempFileName();
-        return new(1);
+        return new(context.Return(Path.GetTempFileName()));
     }
 }

+ 64 - 80
src/Lua/Standard/StringLibrary.cs

@@ -12,7 +12,8 @@ public sealed class StringLibrary
 
     public StringLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("byte", Byte),
             new("char", Char),
             new("dump", Dump),
@@ -31,7 +32,7 @@ public sealed class StringLibrary
 
     public readonly LuaFunction[] Functions;
 
-    public ValueTask<int> Byte(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Byte(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         var i = context.HasArgument(1)
@@ -45,20 +46,20 @@ public sealed class StringLibrary
         LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "byte", 3, j);
 
         var span = StringHelper.Slice(s, (int)i, (int)j);
+        var buffer = context.GetReturnBuffer(span.Length);
         for (int k = 0; k < span.Length; k++)
         {
-            buffer.Span[k] = span[k];
+            buffer[k] = span[k];
         }
 
         return new(span.Length);
     }
 
-    public ValueTask<int> Char(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Char(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         if (context.ArgumentCount == 0)
         {
-            buffer.Span[0] = "";
-            return new(1);
+            return new(context.Return(""));
         }
 
         var builder = new ValueStringBuilder(context.ArgumentCount);
@@ -69,17 +70,16 @@ public sealed class StringLibrary
             builder.Append((char)arg);
         }
 
-        buffer.Span[0] = builder.ToString();
-        return new(1);
+        return new(context.Return(builder.ToString()));
     }
 
-    public ValueTask<int> Dump(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Dump(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         // stirng.dump is not supported (throw exception)
         throw new NotSupportedException("stirng.dump is not supported");
     }
 
-    public ValueTask<int> Find(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Find(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         var pattern = context.GetArgument<string>(1);
@@ -101,16 +101,13 @@ public sealed class StringLibrary
         // out of range
         if (init != 1 && (init < 1 || init > s.Length))
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
         // empty pattern
         if (pattern.Length == 0)
         {
-            buffer.Span[0] = 1;
-            buffer.Span[1] = 1;
-            return new(2);
+            return new(context.Return(1, 1));
         }
 
         var source = s.AsSpan()[(int)(init - 1)..];
@@ -120,14 +117,11 @@ public sealed class StringLibrary
             var start = source.IndexOf(pattern);
             if (start == -1)
             {
-                buffer.Span[0] = LuaValue.Nil;
-                return new(1);
+                return new(context.Return(LuaValue.Nil));
             }
 
             // 1-based
-            buffer.Span[0] = start + 1;
-            buffer.Span[1] = start + pattern.Length;
-            return new(2);
+            return new(context.Return(start + 1, start + pattern.Length));
         }
         else
         {
@@ -137,22 +131,19 @@ public sealed class StringLibrary
             if (match.Success)
             {
                 // 1-based
-                buffer.Span[0] = init + match.Index;
-                buffer.Span[1] = init + match.Index + match.Length - 1;
-                return new(2);
+                return new(context.Return(init + match.Index, init + match.Index + match.Length - 1));
             }
             else
             {
-                buffer.Span[0] = LuaValue.Nil;
-                return new(1);
+                return new(context.Return(LuaValue.Nil));
             }
         }
     }
 
-    public async ValueTask<int> Format(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> Format(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var format = context.GetArgument<string>(0);
-
+        var stack = context.Thread.Stack;
         // TODO: pooling StringBuilder
         var builder = new StringBuilder(format.Length * 2);
         var parameterIndex = 1;
@@ -241,6 +232,7 @@ public sealed class StringLibrary
                 {
                     throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #{parameterIndex + 1} to 'format' (no value)");
                 }
+
                 var parameter = context.GetArgument(parameterIndex++);
 
                 // TODO: reduce allocation
@@ -261,22 +253,22 @@ public sealed class StringLibrary
                             case 'f':
                                 formattedValue = precision < 0
                                     ? f.ToString(CultureInfo.InvariantCulture)
-                                    : f.ToString($"F{precision}",CultureInfo.InvariantCulture);
+                                    : f.ToString($"F{precision}", CultureInfo.InvariantCulture);
                                 break;
                             case 'e':
                                 formattedValue = precision < 0
                                     ? f.ToString(CultureInfo.InvariantCulture)
-                                    : f.ToString($"E{precision}",CultureInfo.InvariantCulture);
+                                    : f.ToString($"E{precision}", CultureInfo.InvariantCulture);
                                 break;
                             case 'g':
                                 formattedValue = precision < 0
                                     ? f.ToString(CultureInfo.InvariantCulture)
-                                    : f.ToString($"G{precision}",CultureInfo.InvariantCulture);
+                                    : f.ToString($"G{precision}", CultureInfo.InvariantCulture);
                                 break;
                             case 'G':
                                 formattedValue = precision < 0
                                     ? f.ToString(CultureInfo.InvariantCulture).ToUpper()
-                                    : f.ToString($"G{precision}",CultureInfo.InvariantCulture).ToUpper();
+                                    : f.ToString($"G{precision}", CultureInfo.InvariantCulture).ToUpper();
                                 break;
                         }
 
@@ -284,18 +276,21 @@ public sealed class StringLibrary
                         {
                             formattedValue = $"+{formattedValue}";
                         }
+
                         break;
                     case 's':
-                        using (var strBuffer = new PooledArray<LuaValue>(1))
                         {
-                            await parameter.CallToStringAsync(context, strBuffer.AsMemory(), cancellationToken);
-                            formattedValue = strBuffer[0].Read<string>();
+                            var top = stack.Count;
+                            stack.Push(default);
+                            await parameter.CallToStringAsync(context with { ReturnFrameBase = top }, cancellationToken);
+                            formattedValue = stack.Pop().Read<string>();
                         }
 
                         if (specifier is 's' && precision > 0 && precision <= formattedValue.Length)
                         {
                             formattedValue = formattedValue[..precision];
                         }
+
                         break;
                     case 'q':
                         switch (parameter.Type)
@@ -314,13 +309,16 @@ public sealed class StringLibrary
                                 formattedValue = parameter.Read<double>().ToString(CultureInfo.InvariantCulture);
                                 break;
                             default:
-                                using (var strBuffer = new PooledArray<LuaValue>(1))
+
                                 {
-                                    await parameter.CallToStringAsync(context, strBuffer.AsMemory(), cancellationToken);
-                                    formattedValue = strBuffer[0].Read<string>();
+                                    var top = stack.Count;
+                                    stack.Push(default);
+                                    await parameter.CallToStringAsync(context with { ReturnFrameBase = top }, cancellationToken);
+                                    formattedValue = stack.Pop().Read<string>();
                                 }
                                 break;
                         }
+
                         break;
                     case 'i':
                     case 'd':
@@ -385,6 +383,7 @@ public sealed class StringLibrary
                         {
                             formattedValue = $"+{formattedValue}";
                         }
+
                         break;
                     default:
                         throw new LuaRuntimeException(context.State.GetTraceback(), $"invalid option '%{specifier}' to 'format'");
@@ -420,12 +419,10 @@ public sealed class StringLibrary
             }
         }
 
-
-        buffer.Span[0] = builder.ToString();
-        return 1;
+        return context.Return(builder.ToString());
     }
 
-    public ValueTask<int> GMatch(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> GMatch(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         var pattern = context.GetArgument<string>(1);
@@ -433,7 +430,7 @@ public sealed class StringLibrary
         var regex = StringHelper.ToRegex(pattern);
         var matches = regex.Matches(s);
 
-        buffer.Span[0] = new CSharpClosure("iterator",[new LuaValue(matches),0],static (context, buffer, cancellationToken) =>
+        return new(context.Return(new CSharpClosure("iterator", [new LuaValue(matches), 0], static (context, cancellationToken) =>
         {
             var upValues = context.GetCsClosure()!.UpValues;
             var matches = upValues[0].Read<MatchCollection>();
@@ -444,32 +441,30 @@ public sealed class StringLibrary
                 var groups = match.Groups;
 
                 i++;
-                 upValues[1] = i;
+                upValues[1] = i;
                 if (groups.Count == 1)
                 {
-                    buffer.Span[0] = match.Value;
+                    return new(context.Return(match.Value));
                 }
                 else
                 {
+                    var buffer = context.GetReturnBuffer(groups.Count);
                     for (int j = 0; j < groups.Count; j++)
                     {
-                        buffer.Span[j] = groups[j + 1].Value;
+                        buffer[j] = groups[j + 1].Value;
                     }
-                }
 
-                return new(groups.Count);
+                    return new(buffer.Length);
+                }
             }
             else
             {
-                buffer.Span[0] = LuaValue.Nil;
-                return new(1);
+                return new(context.Return(LuaValue.Nil));
             }
-        });
-
-        return new(1);
+        })));
     }
 
-    public async ValueTask<int> GSub(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> GSub(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         var pattern = context.GetArgument<string>(1);
@@ -521,14 +516,10 @@ public sealed class StringLibrary
                     context.State.Push(match.Groups[k].Value);
                 }
 
-                using var methodBuffer = new PooledArray<LuaValue>(1024);
-                await func.InvokeAsync(context with
-                {
-                    ArgumentCount = match.Groups.Count,
-                    FrameBase = context.Thread.Stack.Count - context.ArgumentCount,
-                }, methodBuffer.AsMemory(), cancellationToken);
 
-                result = methodBuffer[0];
+                await func.InvokeAsync(context with { ArgumentCount = match.Groups.Count, FrameBase = context.Thread.Stack.Count - context.ArgumentCount, }, cancellationToken);
+
+                result = context.Thread.Stack.Get(context.ReturnFrameBase);
             }
             else
             {
@@ -558,25 +549,22 @@ public sealed class StringLibrary
 
         builder.Append(s.AsSpan()[lastIndex..s.Length]);
 
-        buffer.Span[0] = builder.ToString();
-        return 1;
+        return context.Return(builder.ToString());
     }
 
-    public ValueTask<int> Len(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Len(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
-        buffer.Span[0] = s.Length;
-        return new(1);
+        return new(context.Return(s.Length));
     }
 
-    public ValueTask<int> Lower(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Lower(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
-        buffer.Span[0] = s.ToLower();
-        return new(1);
+        return new(context.Return(s.ToLower()));
     }
 
-    public ValueTask<int> Rep(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Rep(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         var n_arg = context.GetArgument<double>(1);
@@ -598,22 +586,20 @@ public sealed class StringLibrary
             }
         }
 
-        buffer.Span[0] = builder.ToString();
-        return new(1);
+        return new(context.Return(builder.ToString()));
     }
 
-    public ValueTask<int> Reverse(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Reverse(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         using var strBuffer = new PooledArray<char>(s.Length);
         var span = strBuffer.AsSpan()[..s.Length];
         s.AsSpan().CopyTo(span);
         span.Reverse();
-        buffer.Span[0] = span.ToString();
-        return new(1);
+        return new(context.Return(span.ToString()));
     }
 
-    public ValueTask<int> Sub(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Sub(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
         var i = context.GetArgument<double>(1);
@@ -624,14 +610,12 @@ public sealed class StringLibrary
         LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "sub", 2, i);
         LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "sub", 3, j);
 
-        buffer.Span[0] = StringHelper.Slice(s, (int)i, (int)j).ToString();
-        return new(1);
+        return new(context.Return(StringHelper.Slice(s, (int)i, (int)j).ToString()));
     }
 
-    public ValueTask<int> Upper(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Upper(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var s = context.GetArgument<string>(0);
-        buffer.Span[0] = s.ToUpper();
-        return new(1);
+        return new(context.Return(s.ToUpper()));
     }
 }

+ 36 - 42
src/Lua/Standard/TableLibrary.cs

@@ -8,11 +8,12 @@ namespace Lua.Standard;
 public sealed class TableLibrary
 {
     public static readonly TableLibrary Instance = new();
-    
+
 
     public TableLibrary()
     {
-        Functions = [
+        Functions =
+        [
             new("concat", Concat),
             new("insert", Insert),
             new("pack", Pack),
@@ -30,25 +31,27 @@ public sealed class TableLibrary
         Name = "comp",
         Functions = [],
         Constants = [],
-        Instructions = [
+        Instructions =
+        [
             Instruction.Le(1, 0, 1),
             Instruction.LoadBool(2, 1, 1),
             Instruction.LoadBool(2, 0, 0),
             Instruction.Return(2, 2),
         ],
-        SourcePositions = [
+        SourcePositions =
+        [
             default, default, default, default,
         ],
         ParameterCount = 2,
         UpValues = [],
-        Locals = [new LocalValueInfo(){Name = "a".AsMemory(),StartPc = 0,Index = 0,EndPc = 4}, new LocalValueInfo(){Name = "b".AsMemory(),StartPc = 0,Index = 1,EndPc = 4}],
+        Locals = [new LocalValueInfo() { Name = "a".AsMemory(), StartPc = 0, Index = 0, EndPc = 4 }, new LocalValueInfo() { Name = "b".AsMemory(), StartPc = 0, Index = 1, EndPc = 4 }],
         MaxStackPosition = 2,
         HasVariableArguments = false,
         LineDefined = 0,
         LastLineDefined = 0,
     };
 
-    public ValueTask<int> Concat(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Concat(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.HasArgument(1)
@@ -83,11 +86,10 @@ public sealed class TableLibrary
             if (i != arg3) builder.Append(arg1);
         }
 
-        buffer.Span[0] = builder.ToString();
-        return new(1);
+        return new(context.Return(builder.ToString()));
     }
 
-    public ValueTask<int> Insert(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Insert(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var table = context.GetArgument<LuaTable>(0);
 
@@ -109,10 +111,10 @@ public sealed class TableLibrary
         }
 
         table.Insert(pos, value);
-        return new(0);
+        return new(context.Return());
     }
 
-    public ValueTask<int> Pack(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Pack(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var table = new LuaTable(context.ArgumentCount, 1);
 
@@ -121,13 +123,13 @@ public sealed class TableLibrary
         {
             table[i + 1] = span[i];
         }
+
         table["n"] = span.Length;
 
-        buffer.Span[0] = table;
-        return new(1);
+        return new(context.Return(table));
     }
 
-    public ValueTask<int> Remove(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Remove(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var table = context.GetArgument<LuaTable>(0);
         var n_arg = context.HasArgument(1)
@@ -142,43 +144,35 @@ public sealed class TableLibrary
         {
             if (!context.HasArgument(1) && n == 0)
             {
-                buffer.Span[0] = LuaValue.Nil;
-                return new(1);
+                return new(context.Return(LuaValue.Nil));
             }
 
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'remove' (position out of bounds)");
         }
         else if (n > table.ArrayLength)
         {
-            buffer.Span[0] = LuaValue.Nil;
-            return new(1);
+            return new(context.Return(LuaValue.Nil));
         }
 
-        buffer.Span[0] = table.RemoveAt(n);
-        return new(1);
+        return new(context.Return(table.RemoveAt(n)));
     }
 
-    public async ValueTask<int> Sort(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public async ValueTask<int> Sort(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.HasArgument(1)
             ? context.GetArgument<LuaFunction>(1)
             : new LuaClosure(context.State, defaultComparer);
-        
-        context.Thread.PushCallStackFrame(new ()
-        {
-            Base = context.FrameBase,
-            VariableArgumentCount = 0,
-            Function = arg1
-        });
+
+        context.Thread.PushCallStackFrame(new() { Base = context.FrameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = arg1 });
         try
         {
             await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken);
-            return 0;
+            return context.Return();
         }
         finally
         {
-            context.Thread.PopCallStackFrameUnsafe(context.FrameBase);
+            context.Thread.PopCallStackFrameWithStackPop();
         }
     }
 
@@ -194,26 +188,24 @@ public sealed class TableLibrary
 
     async ValueTask<int> PartitionAsync(LuaFunctionExecutionContext context, Memory<LuaValue> memory, int low, int high, LuaFunction comparer, CancellationToken cancellationToken)
     {
-        using var methodBuffer = new PooledArray<LuaValue>(1);
-
         var pivot = memory.Span[high];
         int i = low - 1;
 
         for (int j = low; j < high; j++)
         {
-            context.State.Push(memory.Span[j]);
-            context.State.Push(pivot);
-            await comparer.InvokeAsync(context with
-            {
-                ArgumentCount = 2,
-                FrameBase = context.Thread.Stack.Count - context.ArgumentCount,
-            }, methodBuffer.AsMemory(), cancellationToken);
+            var stack = context.Thread.Stack;
+            var top = stack.Count;
+            stack.Push(memory.Span[j]);
+            stack.Push(pivot);
+            await comparer.InvokeAsync(context with { ArgumentCount = 2, FrameBase = stack.Count - context.ArgumentCount, ReturnFrameBase = top }, cancellationToken);
 
-            if (methodBuffer[0].ToBoolean())
+            if (context.Thread.Stack.Get(top).ToBoolean())
             {
                 i++;
                 Swap(memory.Span, i, j);
             }
+
+            context.Thread.Stack.PopUntil(top);
         }
 
         Swap(memory.Span, i + 1, high);
@@ -226,7 +218,7 @@ public sealed class TableLibrary
         (span[i], span[j]) = (span[j], span[i]);
     }
 
-    public ValueTask<int> Unpack(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    public ValueTask<int> Unpack(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.HasArgument(1)
@@ -237,9 +229,11 @@ public sealed class TableLibrary
             : arg0.ArrayLength;
 
         var index = 0;
+        var count = arg2 - arg1 + 1;
+        var buffer = context.GetReturnBuffer(count);
         for (int i = arg1; i <= arg2; i++)
         {
-            buffer.Span[index] = arg0[i];
+            buffer[index] = arg0[i];
             index++;
         }
 

+ 4 - 2
tests/Lua.Tests/MetatableTests.cs

@@ -32,7 +32,7 @@ local b = { 4, 5, 6 }
 
 setmetatable(a, metatable)
 
-return a + b
+return a +b
 ";
 
         var result = await state.DoStringAsync(source);
@@ -109,7 +109,7 @@ assert(tail(a, 3) == 4)
 ";
         await state.DoStringAsync(source);
     }
-    
+
     [Test]
     public async Task Test_Metamethod_TForCall()
     {
@@ -137,6 +137,7 @@ end
 ";
         await state.DoStringAsync(source);
     }
+
     [Test]
     public async Task Test_Hook_Metamethods()
     {
@@ -146,6 +147,7 @@ end
 
                      debug.sethook(function () table.insert(t,debug.traceback()) end,"c")
                      a =a+a
+                     debug.sethook()
                      return t
                      """;
         var r = await state.DoStringAsync(source);

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