Browse Source

Merge pull request #19 from AnnulusGames/fix-bugs

Fix bugs
Annulus Games 1 year ago
parent
commit
11e12293e4
35 changed files with 1218 additions and 911 deletions
  1. 24 38
      src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs
  2. 3 3
      src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs
  3. 11 11
      src/Lua/CodeAnalysis/Syntax/DisplayStringSyntaxVisitor.cs
  4. 1 1
      src/Lua/CodeAnalysis/Syntax/Nodes/GenericForStatementNode.cs
  5. 5 2
      src/Lua/CodeAnalysis/Syntax/Parser.cs
  6. 35 1
      src/Lua/Internal/FastStackCore.cs
  7. 38 7
      src/Lua/Internal/HexConverter.cs
  8. 113 33
      src/Lua/LuaCoroutine.cs
  9. 11 4
      src/Lua/LuaFunction.cs
  10. 6 7
      src/Lua/LuaFunctionExecutionContext.cs
  11. 6 1
      src/Lua/LuaMainThread.cs
  12. 4 8
      src/Lua/LuaState.cs
  13. 25 29
      src/Lua/LuaTable.cs
  14. 6 3
      src/Lua/LuaThread.cs
  15. 1 1
      src/Lua/LuaThreadStatus.cs
  16. 16 4
      src/Lua/LuaValue.cs
  17. 7 5
      src/Lua/Runtime/Closure.cs
  18. 9 0
      src/Lua/Runtime/LuaValueRuntimeExtensions.cs
  19. 693 665
      src/Lua/Runtime/LuaVirtualMachine.cs
  20. 7 7
      src/Lua/Runtime/UpValue.cs
  21. 11 4
      src/Lua/Standard/Basic/NextFunction.cs
  22. 1 1
      src/Lua/Standard/Basic/PCallFunction.cs
  23. 101 26
      src/Lua/Standard/Basic/ToNumberFunction.cs
  24. 2 2
      src/Lua/Standard/Basic/XPCallFunction.cs
  25. 3 4
      src/Lua/Standard/Coroutines/CoroutineCreateFunction.cs
  26. 2 3
      src/Lua/Standard/Coroutines/CoroutineResumeFunction.cs
  27. 4 4
      src/Lua/Standard/Coroutines/CoroutineRunningFunction.cs
  28. 2 3
      src/Lua/Standard/Coroutines/CoroutineStatusFunction.cs
  29. 27 4
      src/Lua/Standard/Coroutines/CoroutineWrapFunction.cs
  30. 4 6
      src/Lua/Standard/Coroutines/CoroutineYieldFunction.cs
  31. 14 7
      src/Lua/Standard/OpenLibExtensions.cs
  32. 5 6
      src/Lua/Standard/Table/InsertFunction.cs
  33. 19 9
      src/Lua/Standard/Table/RemoveFunction.cs
  34. 1 1
      src/Lua/Standard/Table/SortFunction.cs
  35. 1 1
      src/Lua/Standard/Text/GSubFunction.cs

+ 24 - 38
src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs

@@ -164,12 +164,12 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
                 break;
                 break;
             case BinaryOperator.GreaterThan:
             case BinaryOperator.GreaterThan:
-                context.PushInstruction(Instruction.Le(0, b, c), node.Position);
+                context.PushInstruction(Instruction.Lt(1, c, b), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
                 break;
                 break;
             case BinaryOperator.GreaterThanOrEqual:
             case BinaryOperator.GreaterThanOrEqual:
-                context.PushInstruction(Instruction.Lt(0, b, c), node.Position);
+                context.PushInstruction(Instruction.Le(1, c, b), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
                 context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position);
                 break;
                 break;
@@ -700,7 +700,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             childNode.Accept(this, scopeContext);
             childNode.Accept(this, scopeContext);
         }
         }
 
 
-        scopeContext.TryPushCloseUpValue(node.Position);
+        scopeContext.TryPushCloseUpValue(scopeContext.StackTopPosition, node.Position);
 
 
         return true;
         return true;
     }
     }
@@ -737,12 +737,12 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             if (hasElse)
             if (hasElse)
             {
             {
                 endJumpIndexList.Add(scopeContext.Function.Instructions.Length);
                 endJumpIndexList.Add(scopeContext.Function.Instructions.Length);
-                var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackTopPosition : (byte)0;
+                var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
                 scopeContext.PushInstruction(Instruction.Jmp(a, 0), node.Position, true);
                 scopeContext.PushInstruction(Instruction.Jmp(a, 0), node.Position, true);
             }
             }
             else
             else
             {
             {
-                scopeContext.TryPushCloseUpValue(node.Position);
+                scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
             }
             }
 
 
             scopeContext.Function.Instructions[ifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - ifPosition;
             scopeContext.Function.Instructions[ifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - ifPosition;
@@ -767,12 +767,12 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             if (hasElse)
             if (hasElse)
             {
             {
                 endJumpIndexList.Add(scopeContext.Function.Instructions.Length);
                 endJumpIndexList.Add(scopeContext.Function.Instructions.Length);
-                var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackTopPosition : (byte)0;
+                var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
                 scopeContext.PushInstruction(Instruction.Jmp(a, 0), node.Position);
                 scopeContext.PushInstruction(Instruction.Jmp(a, 0), node.Position);
             }
             }
             else
             else
             {
             {
-                scopeContext.TryPushCloseUpValue(node.Position);
+                scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
             }
             }
 
 
             scopeContext.Function.Instructions[elseifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - elseifPosition;
             scopeContext.Function.Instructions[elseifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - elseifPosition;
@@ -786,7 +786,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 childNode.Accept(this, scopeContext);
                 childNode.Accept(this, scopeContext);
             }
             }
 
 
-            scopeContext.TryPushCloseUpValue(node.Position);
+            scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
         }
         }
 
 
         // set JMP sBx
         // set JMP sBx
@@ -812,14 +812,14 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         }
         }
 
 
         CompileConditionNode(node.ConditionNode, scopeContext, true);
         CompileConditionNode(node.ConditionNode, scopeContext, true);
-        var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackTopPosition : (byte)0;
+        var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
         scopeContext.PushInstruction(Instruction.Jmp(a, startIndex - scopeContext.Function.Instructions.Length - 1), node.Position);
         scopeContext.PushInstruction(Instruction.Jmp(a, startIndex - scopeContext.Function.Instructions.Length - 1), node.Position);
-        scopeContext.TryPushCloseUpValue(node.Position);
+        scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
 
 
         context.Function.LoopLevel--;
         context.Function.LoopLevel--;
 
 
         // resolve break statements inside repeat block
         // resolve break statements inside repeat block
-        context.Function.ResolveAllBreaks(context.StackPosition, context.Function.Instructions.Length - 1, scopeContext);
+        context.Function.ResolveAllBreaks(a, context.Function.Instructions.Length - 1, scopeContext);
 
 
         return true;
         return true;
     }
     }
@@ -841,14 +841,15 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         context.Function.LoopLevel--;
         context.Function.LoopLevel--;
 
 
         // set JMP sBx
         // set JMP sBx
-        context.Function.Instructions[conditionIndex].SBx = context.Function.Instructions.Length - 1 - conditionIndex;
+        scopeContext.Function.Instructions[conditionIndex].SBx = scopeContext.Function.Instructions.Length - 1 - conditionIndex;
 
 
-        CompileConditionNode(node.ConditionNode, context, false);
-        var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackTopPosition : (byte)0;
-        context.PushInstruction(Instruction.Jmp(a, conditionIndex - context.Function.Instructions.Length), node.Position);
+        CompileConditionNode(node.ConditionNode, scopeContext, false);
+        var a = scopeContext.HasCapturedLocalVariables ? scopeContext.StackPosition : (byte)0;
+        scopeContext.PushInstruction(Instruction.Jmp(a, conditionIndex - context.Function.Instructions.Length), node.Position);
+        scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
 
 
         // resolve break statements inside while block
         // resolve break statements inside while block
-        context.Function.ResolveAllBreaks(context.StackPosition, context.Function.Instructions.Length - 1, scopeContext);
+        context.Function.ResolveAllBreaks(scopeContext.StackPosition, context.Function.Instructions.Length - 1, scopeContext);
 
 
         return true;
         return true;
     }
     }
@@ -887,7 +888,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 childNode.Accept(this, scopeContext);
                 childNode.Accept(this, scopeContext);
             }
             }
 
 
-            scopeContext.TryPushCloseUpValue(node.Position);
+            scopeContext.TryPushCloseUpValue((byte)(startPosition + 1), node.Position);
         }
         }
         context.Function.LoopLevel--;
         context.Function.LoopLevel--;
 
 
@@ -897,7 +898,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         // push ForLoop
         // push ForLoop
         context.PushInstruction(Instruction.ForLoop(startPosition, prepIndex - context.Function.Instructions.Length), node.Position);
         context.PushInstruction(Instruction.ForLoop(startPosition, prepIndex - context.Function.Instructions.Length), node.Position);
 
 
-        context.Function.ResolveAllBreaks(startPosition, context.Function.Instructions.Length - 1, scopeContext);
+        context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext);
 
 
         context.StackPosition = startPosition;
         context.StackPosition = startPosition;
 
 
@@ -908,22 +909,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     {
     {
         // get iterator
         // get iterator
         var startPosition = context.StackPosition;
         var startPosition = context.StackPosition;
-        if (node.ExpressionNode is CallFunctionExpressionNode call)
-        {
-            CompileCallFunctionExpression(call, context, false, 3);
-        }
-        else if (node.ExpressionNode is CallTableMethodExpressionNode method)
-        {
-            CompileTableMethod(method, context, false, 3);
-        }
-        else if (node.ExpressionNode is VariableArgumentsExpressionNode varArg)
-        {
-            CompileVariableArgumentsExpression(varArg, context, 3);
-        }
-        else
-        {
-            node.ExpressionNode.Accept(this, context);
-        }
+        CompileExpressionList(node, node.ExpressionNodes, 3, context);
 
 
         // jump to TFORCALL
         // jump to TFORCALL
         var startJumpIndex = context.Function.Instructions.Length;
         var startJumpIndex = context.Function.Instructions.Length;
@@ -950,7 +936,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 childNode.Accept(this, scopeContext);
                 childNode.Accept(this, scopeContext);
             }
             }
 
 
-            scopeContext.TryPushCloseUpValue(node.Position);
+            scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position);
         }
         }
         context.Function.LoopLevel--;
         context.Function.LoopLevel--;
 
 
@@ -961,7 +947,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         context.PushInstruction(Instruction.TForCall(startPosition, (ushort)node.Names.Length), node.Position);
         context.PushInstruction(Instruction.TForCall(startPosition, (ushort)node.Names.Length), node.Position);
         context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.Position);
         context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.Position);
 
 
-        context.Function.ResolveAllBreaks(startPosition, context.Function.Instructions.Length - 1, scopeContext);
+        context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext);
         context.StackPosition = startPosition;
         context.StackPosition = startPosition;
 
 
         return true;
         return true;
@@ -1088,13 +1074,13 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 case BinaryOperator.GreaterThan:
                 case BinaryOperator.GreaterThan:
                     {
                     {
                         (var b, var c) = GetBAndC(binaryExpression, context);
                         (var b, var c) = GetBAndC(binaryExpression, context);
-                        context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)1 : (byte)0, b, c), node.Position);
+                        context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position);
                         return;
                         return;
                     }
                     }
                 case BinaryOperator.GreaterThanOrEqual:
                 case BinaryOperator.GreaterThanOrEqual:
                     {
                     {
                         (var b, var c) = GetBAndC(binaryExpression, context);
                         (var b, var c) = GetBAndC(binaryExpression, context);
-                        context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)1 : (byte)0, b, c), node.Position);
+                        context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position);
                         return;
                         return;
                     }
                     }
             }
             }

+ 3 - 3
src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs

@@ -77,11 +77,11 @@ public class ScopeCompilationContext : IDisposable
     }
     }
 
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void TryPushCloseUpValue(SourcePosition position)
+    public void TryPushCloseUpValue(byte top, SourcePosition position)
     {
     {
-        if (HasCapturedLocalVariables)
+        if (HasCapturedLocalVariables && top != 0)
         {
         {
-            Function.PushInstruction(Instruction.Jmp(StackTopPosition, 0), position);
+            Function.PushInstruction(Instruction.Jmp(top, 0), position);
         }
         }
     }
     }
 
 

+ 11 - 11
src/Lua/CodeAnalysis/Syntax/DisplayStringSyntaxVisitor.cs

@@ -111,7 +111,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
     {
     {
         node.FunctionNode.Accept(this, context);
         node.FunctionNode.Accept(this, context);
         context.Append("(");
         context.Append("(");
-        AddStatementList(node.ArgumentNodes, context);
+        VisitSyntaxNodes(node.ArgumentNodes, context);
         context.Append(")");
         context.Append(")");
         return true;
         return true;
     }
     }
@@ -141,7 +141,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
     public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, Context context)
     public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, Context context)
     {
     {
         context.Append("function(");
         context.Append("function(");
-        AddStatementList(node.ParameterNodes, context);
+        VisitSyntaxNodes(node.ParameterNodes, context);
         if (node.HasVariableArguments)
         if (node.HasVariableArguments)
         {
         {
             if (node.ParameterNodes.Length > 0) context.Append(", ");
             if (node.ParameterNodes.Length > 0) context.Append(", ");
@@ -168,7 +168,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
         context.Append("function ");
         context.Append("function ");
         context.Append(node.Name.ToString());
         context.Append(node.Name.ToString());
         context.Append("(");
         context.Append("(");
-        AddStatementList(node.ParameterNodes, context);
+        VisitSyntaxNodes(node.ParameterNodes, context);
         if (node.HasVariableArguments)
         if (node.HasVariableArguments)
         {
         {
             if (node.ParameterNodes.Length > 0) context.Append(", ");
             if (node.ParameterNodes.Length > 0) context.Append(", ");
@@ -209,7 +209,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
         }
         }
 
 
         context.Append("(");
         context.Append("(");
-        AddStatementList(node.ParameterNodes, context);
+        VisitSyntaxNodes(node.ParameterNodes, context);
         if (node.HasVariableArguments)
         if (node.HasVariableArguments)
         {
         {
             if (node.ParameterNodes.Length > 0) context.Append(", ");
             if (node.ParameterNodes.Length > 0) context.Append(", ");
@@ -234,9 +234,9 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
     public bool VisitGenericForStatementNode(GenericForStatementNode node, Context context)
     public bool VisitGenericForStatementNode(GenericForStatementNode node, Context context)
     {
     {
         context.Append($"for ");
         context.Append($"for ");
-        AddStatementList(node.Names, context);
+        VisitSyntaxNodes(node.Names, context);
         context.Append(" in ");
         context.Append(" in ");
-        node.ExpressionNode.Accept(this, context);
+        VisitSyntaxNodes(node.ExpressionNodes, context);
         context.AppendLine(" do");
         context.AppendLine(" do");
         using (context.BeginIndentScope())
         using (context.BeginIndentScope())
         {
         {
@@ -321,12 +321,12 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
 
 
     public bool VisitAssignmentStatementNode(AssignmentStatementNode node, Context context)
     public bool VisitAssignmentStatementNode(AssignmentStatementNode node, Context context)
     {
     {
-        AddStatementList(node.LeftNodes, context);
+        VisitSyntaxNodes(node.LeftNodes, context);
 
 
         if (node.RightNodes.Length > 0)
         if (node.RightNodes.Length > 0)
         {
         {
             context.Append(" = ");
             context.Append(" = ");
-            AddStatementList(node.RightNodes, context);
+            VisitSyntaxNodes(node.RightNodes, context);
         }
         }
 
 
         return true;
         return true;
@@ -405,7 +405,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
     public bool VisitReturnStatementNode(ReturnStatementNode node, Context context)
     public bool VisitReturnStatementNode(ReturnStatementNode node, Context context)
     {
     {
         context.Append("return ");
         context.Append("return ");
-        AddStatementList(node.Nodes, context);
+        VisitSyntaxNodes(node.Nodes, context);
         return true;
         return true;
     }
     }
 
 
@@ -491,7 +491,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
     {
     {
         node.TableNode.Accept(this, context);
         node.TableNode.Accept(this, context);
         context.Append($":{node.MethodName}(");
         context.Append($":{node.MethodName}(");
-        AddStatementList(node.ArgumentNodes, context);
+        VisitSyntaxNodes(node.ArgumentNodes, context);
         context.Append(")");
         context.Append(")");
         return true;
         return true;
     }
     }
@@ -536,7 +536,7 @@ public sealed class DisplayStringSyntaxVisitor : ISyntaxNodeVisitor<DisplayStrin
         return true;
         return true;
     }
     }
 
 
-    void AddStatementList(SyntaxNode[] nodes, Context context)
+    void VisitSyntaxNodes(SyntaxNode[] nodes, Context context)
     {
     {
         for (int i = 0; i < nodes.Length; i++)
         for (int i = 0; i < nodes.Length; i++)
         {
         {

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

@@ -1,6 +1,6 @@
 namespace Lua.CodeAnalysis.Syntax.Nodes;
 namespace Lua.CodeAnalysis.Syntax.Nodes;
 
 
-public record GenericForStatementNode(IdentifierNode[] Names, ExpressionNode ExpressionNode, StatementNode[] StatementNodes, SourcePosition Position) : StatementNode(Position)
+public record GenericForStatementNode(IdentifierNode[] Names, ExpressionNode[] ExpressionNodes, StatementNode[] StatementNodes, SourcePosition Position) : StatementNode(Position)
 {
 {
     public override TResult Accept<TContext, TResult>(ISyntaxNodeVisitor<TContext, TResult> visitor, TContext context)
     public override TResult Accept<TContext, TResult>(ISyntaxNodeVisitor<TContext, TResult> visitor, TContext context)
     {
     {

+ 5 - 2
src/Lua/CodeAnalysis/Syntax/Parser.cs

@@ -44,6 +44,7 @@ public ref struct Parser
     {
     {
         switch (enumerator.Current.Type)
         switch (enumerator.Current.Type)
         {
         {
+            case SyntaxTokenType.LParen:
             case SyntaxTokenType.Identifier:
             case SyntaxTokenType.Identifier:
                 {
                 {
                     var firstExpression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
                     var firstExpression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
@@ -435,7 +436,7 @@ public ref struct Parser
         CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.In, out _);
         CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.In, out _);
         enumerator.SkipEoL();
         enumerator.SkipEoL();
 
 
-        var expression = ParseExpression(ref enumerator, OperatorPrecedence.NonOperator);
+        var expressions = ParseExpressionList(ref enumerator);
         MoveNextWithValidation(ref enumerator);
         MoveNextWithValidation(ref enumerator);
         enumerator.SkipEoL();
         enumerator.SkipEoL();
 
 
@@ -445,7 +446,7 @@ public ref struct Parser
         // parse statements
         // parse statements
         var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
         var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End);
 
 
-        return new GenericForStatementNode(identifiers, expression, statements, forToken.Position);
+        return new GenericForStatementNode(identifiers, expressions, statements, forToken.Position);
     }
     }
 
 
     FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
     FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken)
@@ -902,6 +903,8 @@ public ref struct Parser
 
 
         while (true)
         while (true)
         {
         {
+            enumerator.SkipEoL();
+
             if (!TryParseExpression(ref enumerator, OperatorPrecedence.NonOperator, out var expression))
             if (!TryParseExpression(ref enumerator, OperatorPrecedence.NonOperator, out var expression))
             {
             {
                 enumerator.MovePrevious();
                 enumerator.MovePrevious();

+ 35 - 1
src/Lua/Internal/FastStackCore.cs

@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
 namespace Lua.Internal;
 namespace Lua.Internal;
@@ -10,7 +11,7 @@ public struct FastStackCore<T>
     T?[] array;
     T?[] array;
     int tail;
     int tail;
 
 
-    public int Size => tail;
+    public int Count => tail;
 
 
     public readonly ReadOnlySpan<T> AsSpan()
     public readonly ReadOnlySpan<T> AsSpan()
     {
     {
@@ -18,6 +19,12 @@ public struct FastStackCore<T>
         return array.AsSpan(0, tail);
         return array.AsSpan(0, tail);
     }
     }
 
 
+    public readonly Span<T?> GetBuffer()
+    {
+        if (array == null) return [];
+        return array.AsSpan();
+    }
+
     public readonly T? this[int index]
     public readonly T? this[int index]
     {
     {
         get
         get
@@ -41,6 +48,7 @@ public struct FastStackCore<T>
         tail++;
         tail++;
     }
     }
 
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryPop(out T value)
     public bool TryPop(out T value)
     {
     {
         if (tail == 0)
         if (tail == 0)
@@ -62,6 +70,7 @@ public struct FastStackCore<T>
         return result;
         return result;
     }
     }
 
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool TryPeek(out T value)
     public bool TryPeek(out T value)
     {
     {
         if (tail == 0)
         if (tail == 0)
@@ -80,6 +89,31 @@ public struct FastStackCore<T>
         return result;
         return result;
     }
     }
 
 
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void EnsureCapacity(int capacity)
+    {
+        if (array == null)
+        {
+            array = new T[InitialCapacity];
+        }
+
+        var newSize = array.Length;
+        while (newSize < capacity)
+        {
+            newSize *= 2;
+        }
+
+        Array.Resize(ref array, newSize);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void NotifyTop(int top)
+    {
+        if (tail < top) tail = top;
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void Clear()
     public void Clear()
     {
     {
         array.AsSpan(0, tail).Clear();
         array.AsSpan(0, tail).Clear();

+ 38 - 7
src/Lua/Internal/HexConverter.cs

@@ -1,4 +1,5 @@
 using System.Globalization;
 using System.Globalization;
+using System.Numerics;
 
 
 namespace Lua.Internal;
 namespace Lua.Internal;
 
 
@@ -7,7 +8,15 @@ public static class HexConverter
     public static double ToDouble(ReadOnlySpan<char> text)
     public static double ToDouble(ReadOnlySpan<char> text)
     {
     {
         var sign = 1;
         var sign = 1;
-        if (text[0] == '-')
+        text = text.Trim();
+        var first = text[0];
+        if (first == '+')
+        {
+            // Remove the "+0x"
+            sign = 1;
+            text = text[3..];
+        }
+        else if (first == '-')
         {
         {
             // Remove the "-0x"
             // Remove the "-0x"
             sign = -1;
             sign = -1;
@@ -24,14 +33,36 @@ public static class HexConverter
 
 
         if (dotIndex == -1 && expIndex == -1)
         if (dotIndex == -1 && expIndex == -1)
         {
         {
-            return long.Parse(text, NumberStyles.AllowHexSpecifier);
+            // unsigned big integer
+            // TODO: optimize
+            using var buffer = new PooledArray<char>(text.Length + 1);
+            text.CopyTo(buffer.AsSpan()[1..]);
+            buffer[0] = '0';
+            return sign * (double)BigInteger.Parse(buffer.AsSpan()[..(text.Length + 1)], NumberStyles.AllowHexSpecifier);
         }
         }
 
 
-        var intPart = dotIndex == -1 ? [] : text[..dotIndex];
-        var decimalPart = expIndex == -1
-            ? text.Slice(dotIndex + 1)
-            : text.Slice(dotIndex + 1, expIndex - dotIndex - 1);
-        var expPart = expIndex == -1 ? [] : text[(expIndex + 1)..];
+        ReadOnlySpan<char> intPart;
+        ReadOnlySpan<char> decimalPart;
+        ReadOnlySpan<char> expPart;
+
+        if (dotIndex == -1)
+        {
+            intPart = text[..expIndex];
+            decimalPart = [];
+            expPart = text[(expIndex + 1)..];
+        }
+        else if (expIndex == -1)
+        {
+            intPart = text[..dotIndex];
+            decimalPart = text[(dotIndex + 1)..];
+            expPart = [];
+        }
+        else
+        {
+            intPart = text[..dotIndex];
+            decimalPart = text.Slice(dotIndex + 1, expIndex - dotIndex - 1);
+            expPart = text[(expIndex + 1)..];
+        }
 
 
         var value = intPart.Length == 0
         var value = intPart.Length == 0
             ? 0
             ? 0

+ 113 - 33
src/Lua/LuaCoroutine.cs

@@ -1,5 +1,7 @@
+using System.Buffers;
 using System.Threading.Tasks.Sources;
 using System.Threading.Tasks.Sources;
 using Lua.Internal;
 using Lua.Internal;
+using Lua.Runtime;
 
 
 namespace Lua;
 namespace Lua;
 
 
@@ -7,60 +9,77 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
 {
 {
     struct YieldContext
     struct YieldContext
     {
     {
+        public required LuaValue[] Results;
     }
     }
 
 
     struct ResumeContext
     struct ResumeContext
     {
     {
-        public LuaValue[] Results;
+        public required LuaValue[] Results;
     }
     }
 
 
     byte status;
     byte status;
-    LuaState threadState;
+    bool isFirstCall = true;
     ValueTask<int> functionTask;
     ValueTask<int> functionTask;
+    LuaValue[] buffer;
 
 
     ManualResetValueTaskSourceCore<ResumeContext> resume;
     ManualResetValueTaskSourceCore<ResumeContext> resume;
     ManualResetValueTaskSourceCore<YieldContext> yield;
     ManualResetValueTaskSourceCore<YieldContext> yield;
 
 
-    public override LuaThreadStatus GetStatus() => (LuaThreadStatus)status;
-    public bool IsProtectedMode { get; }
-    public LuaFunction Function { get; }
-
-    internal LuaCoroutine(LuaState state, LuaFunction function, bool isProtectedMode)
+    public LuaCoroutine(LuaFunction function, bool isProtectedMode)
     {
     {
         IsProtectedMode = isProtectedMode;
         IsProtectedMode = isProtectedMode;
-        threadState = state;
         Function = function;
         Function = function;
+
+        buffer = ArrayPool<LuaValue>.Shared.Rent(1024);
+        buffer.AsSpan().Clear();
     }
     }
 
 
+    public override LuaThreadStatus GetStatus() => (LuaThreadStatus)status;
+
+    public override void UnsafeSetStatus(LuaThreadStatus status)
+    {
+        this.status = (byte)status;
+    }
+
+    public bool IsProtectedMode { get; }
+    public LuaFunction Function { get; }
+
     public override async ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     public override async ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
+        var baseThread = context.Thread;
+        baseThread.UnsafeSetStatus(LuaThreadStatus.Normal);
+
         context.State.ThreadStack.Push(this);
         context.State.ThreadStack.Push(this);
         try
         try
         {
         {
             switch ((LuaThreadStatus)Volatile.Read(ref status))
             switch ((LuaThreadStatus)Volatile.Read(ref status))
             {
             {
-                case LuaThreadStatus.Normal:
+                case LuaThreadStatus.Suspended:
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
 
 
-                    // first argument is LuaThread object
-                    for (int i = 0; i < context.ArgumentCount - 1; i++)
+                    if (isFirstCall)
                     {
                     {
-                        threadState.Push(context.Arguments[i + 1]);
-                    }
+                        // copy stack value
+                        Stack.EnsureCapacity(baseThread.Stack.Count);
+                        baseThread.Stack.AsSpan().CopyTo(Stack.GetBuffer());
+                        Stack.NotifyTop(baseThread.Stack.Count);
 
 
-                    functionTask = Function.InvokeAsync(new()
+                        // copy callstack value
+                        CallStack.EnsureCapacity(baseThread.CallStack.Count);
+                        baseThread.CallStack.AsSpan().CopyTo(CallStack.GetBuffer());
+                        CallStack.NotifyTop(baseThread.CallStack.Count);
+                    }
+                    else
                     {
                     {
-                        State = threadState,
-                        ArgumentCount = context.ArgumentCount - 1,
-                        ChunkName = Function.Name,
-                        RootChunkName = context.RootChunkName,
-                    }, buffer[1..], cancellationToken).Preserve();
-
-                    break;
-                case LuaThreadStatus.Suspended:
-                    Volatile.Write(ref status, (byte)LuaThreadStatus.Running);
-                    yield.SetResult(new());
+                        yield.SetResult(new()
+                        {
+                            Results = context.ArgumentCount == 1
+                                ? []
+                                : context.Arguments[1..].ToArray()
+                        });
+                    }
                     break;
                     break;
+                case LuaThreadStatus.Normal:
                 case LuaThreadStatus.Running:
                 case LuaThreadStatus.Running:
                     if (IsProtectedMode)
                     if (IsProtectedMode)
                     {
                     {
@@ -70,7 +89,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                     }
                     }
                     else
                     else
                     {
                     {
-                        throw new InvalidOperationException("cannot resume non-suspended coroutine");
+                        throw new LuaRuntimeException(context.State.GetTraceback(), "cannot resume non-suspended coroutine");
                     }
                     }
                 case LuaThreadStatus.Dead:
                 case LuaThreadStatus.Dead:
                     if (IsProtectedMode)
                     if (IsProtectedMode)
@@ -81,7 +100,7 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                     }
                     }
                     else
                     else
                     {
                     {
-                        throw new InvalidOperationException("cannot resume dead coroutine");
+                        throw new LuaRuntimeException(context.State.GetTraceback(), "cannot resume dead coroutine");
                     }
                     }
             }
             }
 
 
@@ -99,6 +118,50 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
 
 
             try
             try
             {
             {
+                if (isFirstCall)
+                {
+                    int frameBase;
+                    var variableArgumentCount = Function.GetVariableArgumentCount(context.ArgumentCount - 1);
+                    
+                    if (variableArgumentCount > 0)
+                    {
+                        var fixedArgumentCount = context.ArgumentCount - 1 - variableArgumentCount;
+
+                        for (int i = 0; i < variableArgumentCount; i++)
+                        {
+                            Stack.Push(context.GetArgument(i + fixedArgumentCount + 1));
+                        }
+
+                        frameBase = Stack.Count;
+
+                        for (int i = 0; i < fixedArgumentCount; i++)
+                        {
+                            Stack.Push(context.GetArgument(i + 1));
+                        }
+                    }
+                    else
+                    {
+                        frameBase = Stack.Count;
+
+                        for (int i = 0; i < context.ArgumentCount - 1; i++)
+                        {
+                            Stack.Push(context.GetArgument(i + 1));
+                        }
+                    }
+
+                    functionTask = Function.InvokeAsync(new()
+                    {
+                        State = context.State,
+                        Thread = this,
+                        ArgumentCount = context.ArgumentCount - 1,
+                        FrameBase = frameBase,
+                        ChunkName = Function.Name,
+                        RootChunkName = context.RootChunkName,
+                    }, this.buffer, cancellationToken).Preserve();
+
+                    Volatile.Write(ref isFirstCall, false);
+                }
+
                 (var index, var result0, var result1) = await ValueTaskEx.WhenAny(resumeTask, functionTask!);
                 (var index, var result0, var result1) = await ValueTaskEx.WhenAny(resumeTask, functionTask!);
 
 
                 if (index == 0)
                 if (index == 0)
@@ -115,15 +178,23 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                 }
                 }
                 else
                 else
                 {
                 {
+                    var resultCount = functionTask!.Result;
+
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
                     buffer.Span[0] = true;
                     buffer.Span[0] = true;
-                    return 1 + functionTask!.Result;
+                    this.buffer[0..resultCount].CopyTo(buffer.Span[1..]);
+
+                    ArrayPool<LuaValue>.Shared.Return(this.buffer);
+
+                    return 1 + resultCount;
                 }
                 }
             }
             }
             catch (Exception ex) when (ex is not OperationCanceledException)
             catch (Exception ex) when (ex is not OperationCanceledException)
             {
             {
                 if (IsProtectedMode)
                 if (IsProtectedMode)
                 {
                 {
+                    ArrayPool<LuaValue>.Shared.Return(this.buffer);
+
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
                     Volatile.Write(ref status, (byte)LuaThreadStatus.Dead);
                     buffer.Span[0] = false;
                     buffer.Span[0] = false;
                     buffer.Span[1] = ex.Message;
                     buffer.Span[1] = ex.Message;
@@ -143,14 +214,15 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
         finally
         finally
         {
         {
             context.State.ThreadStack.Pop();
             context.State.ThreadStack.Pop();
+            baseThread.UnsafeSetStatus(LuaThreadStatus.Running);
         }
         }
     }
     }
 
 
-    public override async ValueTask Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
+    public override async ValueTask<int> Yield(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
         if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
         if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running)
         {
         {
-            throw new InvalidOperationException("cannot call yield on a coroutine that is not currently running");
+            throw new LuaRuntimeException(context.State.GetTraceback(), "cannot call yield on a coroutine that is not currently running");
         }
         }
 
 
         resume.SetResult(new()
         resume.SetResult(new()
@@ -173,16 +245,24 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
     RETRY:
     RETRY:
         try
         try
         {
         {
-            await new ValueTask<YieldContext>(this, yield.Version);
+            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;
         }
         }
         catch (Exception ex) when (ex is not OperationCanceledException)
         catch (Exception ex) when (ex is not OperationCanceledException)
         {
         {
             yield.Reset();
             yield.Reset();
             goto RETRY;
             goto RETRY;
         }
         }
-
-        registration.Dispose();
-        yield.Reset();
+        finally
+        {
+            registration.Dispose();
+            yield.Reset();
+        }
     }
     }
 
 
     YieldContext IValueTaskSource<YieldContext>.GetResult(short token)
     YieldContext IValueTaskSource<YieldContext>.GetResult(short token)

+ 11 - 4
src/Lua/LuaFunction.cs

@@ -9,11 +9,18 @@ public abstract partial class LuaFunction
     public async ValueTask<int> InvokeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     public async ValueTask<int> InvokeAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
         var state = context.State;
         var state = context.State;
-        var thread = state.CurrentThread;
+        
+        if (context.FrameBase == null)
+        {
+            context = context with
+            {
+                FrameBase = context.Thread.Stack.Count - context.ArgumentCount
+            };
+        }
 
 
         var frame = new CallStackFrame
         var frame = new CallStackFrame
         {
         {
-            Base = context.StackPosition == null ? thread.Stack.Count - context.ArgumentCount : context.StackPosition.Value,
+            Base = context.FrameBase.Value,
             CallPosition = context.SourcePosition,
             CallPosition = context.SourcePosition,
             ChunkName = context.ChunkName ?? LuaState.DefaultChunkName,
             ChunkName = context.ChunkName ?? LuaState.DefaultChunkName,
             RootChunkName = context.RootChunkName ?? LuaState.DefaultChunkName,
             RootChunkName = context.RootChunkName ?? LuaState.DefaultChunkName,
@@ -21,14 +28,14 @@ public abstract partial class LuaFunction
             Function = this,
             Function = this,
         };
         };
 
 
-        thread.PushCallStackFrame(frame);
+        context.Thread.PushCallStackFrame(frame);
         try
         try
         {
         {
             return await InvokeAsyncCore(context, buffer, cancellationToken);
             return await InvokeAsyncCore(context, buffer, cancellationToken);
         }
         }
         finally
         finally
         {
         {
-            thread.PopCallStackFrame();
+            context.Thread.PopCallStackFrame();
         }
         }
     }
     }
 
 

+ 6 - 7
src/Lua/LuaFunctionExecutionContext.cs

@@ -6,8 +6,9 @@ namespace Lua;
 public readonly record struct LuaFunctionExecutionContext
 public readonly record struct LuaFunctionExecutionContext
 {
 {
     public required LuaState State { get; init; }
     public required LuaState State { get; init; }
+    public required LuaThread Thread { get; init; }
     public required int ArgumentCount { get; init; }
     public required int ArgumentCount { get; init; }
-    public int? StackPosition { get; init; }
+    public int? FrameBase { get; init; }
     public SourcePosition? SourcePosition { get; init; }
     public SourcePosition? SourcePosition { get; init; }
     public string? RootChunkName { get; init; }
     public string? RootChunkName { get; init; }
     public string? ChunkName { get; init; }
     public string? ChunkName { get; init; }
@@ -16,8 +17,7 @@ public readonly record struct LuaFunctionExecutionContext
     {
     {
         get
         get
         {
         {
-            var thread = State.CurrentThread;
-            return thread.GetStackValues().Slice(thread.GetCurrentFrame().Base, ArgumentCount);
+            return Thread.GetStackValues().Slice(FrameBase!.Value, ArgumentCount);
         }
         }
     }
     }
 
 
@@ -42,14 +42,13 @@ public readonly record struct LuaFunctionExecutionContext
         var arg = Arguments[index];
         var arg = Arguments[index];
         if (!arg.TryRead<T>(out var argValue))
         if (!arg.TryRead<T>(out var argValue))
         {
         {
-            var thread = State.CurrentThread;
             if (LuaValue.TryGetLuaValueType(typeof(T), out var type))
             if (LuaValue.TryGetLuaValueType(typeof(T), out var type))
             {
             {
-                LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString());
+                LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString());
             }
             }
             else
             else
             {
             {
-                LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, thread.GetCurrentFrame().Function.Name, typeof(T).Name, arg.Type.ToString());
+                LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, typeof(T).Name, arg.Type.ToString());
             }
             }
         }
         }
 
 
@@ -60,7 +59,7 @@ public readonly record struct LuaFunctionExecutionContext
     {
     {
         if (ArgumentCount <= index)
         if (ArgumentCount <= index)
         {
         {
-            LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, State.CurrentThread.GetCurrentFrame().Function.Name);
+            LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name);
         }
         }
     }
     }
 }
 }

+ 6 - 1
src/Lua/LuaMainThread.cs

@@ -7,6 +7,11 @@ public sealed class LuaMainThread : LuaThread
         return LuaThreadStatus.Running;
         return LuaThreadStatus.Running;
     }
     }
 
 
+    public override void UnsafeSetStatus(LuaThreadStatus status)
+    {
+        // Do nothing
+    }
+
     public override ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     public override ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
         buffer.Span[0] = false;
         buffer.Span[0] = false;
@@ -14,7 +19,7 @@ public sealed class LuaMainThread : LuaThread
         return new(2);
         return new(2);
     }
     }
 
 
-    public override ValueTask Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default)
+    public override ValueTask<int> Yield(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     {
     {
         throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield from outside a coroutine");
         throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield from outside a coroutine");
     }
     }

+ 4 - 8
src/Lua/LuaState.cs

@@ -50,7 +50,7 @@ public sealed class LuaState
     LuaState()
     LuaState()
     {
     {
         environment = new();
         environment = new();
-        envUpValue = UpValue.Closed(mainThread, environment);
+        envUpValue = UpValue.Closed(environment);
     }
     }
 
 
     public async ValueTask<int> RunAsync(Chunk chunk, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
     public async ValueTask<int> RunAsync(Chunk chunk, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
@@ -64,8 +64,9 @@ public sealed class LuaState
             return await closure.InvokeAsync(new()
             return await closure.InvokeAsync(new()
             {
             {
                 State = this,
                 State = this,
+                Thread = CurrentThread,
                 ArgumentCount = 0,
                 ArgumentCount = 0,
-                StackPosition = 0,
+                FrameBase = 0,
                 SourcePosition = null,
                 SourcePosition = null,
                 RootChunkName = chunk.Name ?? DefaultChunkName,
                 RootChunkName = chunk.Name ?? DefaultChunkName,
                 ChunkName = chunk.Name ?? DefaultChunkName,
                 ChunkName = chunk.Name ?? DefaultChunkName,
@@ -82,11 +83,6 @@ public sealed class LuaState
         CurrentThread.Stack.Push(value);
         CurrentThread.Stack.Push(value);
     }
     }
 
 
-    public LuaThread CreateThread(LuaFunction function, bool isProtectedMode = true)
-    {
-        return new LuaCoroutine(this, function, isProtectedMode);
-    }
-
     public Traceback GetTraceback()
     public Traceback GetTraceback()
     {
     {
         // TODO: optimize
         // TODO: optimize
@@ -94,7 +90,7 @@ public sealed class LuaState
         {
         {
             StackFrames = threadStack.AsSpan().ToArray()
             StackFrames = threadStack.AsSpan().ToArray()
                 .Append(MainThread)
                 .Append(MainThread)
-                .SelectMany(x => x.GetStackFrames())
+                .SelectMany(x => x.GetCallStackFrames()[1..].ToArray())
                 .ToArray()
                 .ToArray()
         };
         };
     }
     }

+ 25 - 29
src/Lua/LuaTable.cs

@@ -9,18 +9,6 @@ public sealed class LuaTable
     {
     {
     }
     }
 
 
-    public LuaTable(IEnumerable<LuaValue> values)
-    {
-        array = values.ToArray();
-        dictionary = [];
-    }
-
-    public LuaTable(IEnumerable<KeyValuePair<LuaValue, LuaValue>> values)
-    {
-        array = [];
-        dictionary = new Dictionary<LuaValue, LuaValue>(values);
-    }
-
     public LuaTable(int arrayCapacity, int dictionaryCapacity)
     public LuaTable(int arrayCapacity, int dictionaryCapacity)
     {
     {
         array = new LuaValue[arrayCapacity];
         array = new LuaValue[arrayCapacity];
@@ -54,6 +42,11 @@ public sealed class LuaTable
         }
         }
         set
         set
         {
         {
+            if (key.Type is LuaValueType.Number && double.IsNaN(key.Read<double>()))
+            {
+                throw new ArgumentException("table index is NaN");
+            }
+
             if (TryGetInteger(key, out var index))
             if (TryGetInteger(key, out var index))
             {
             {
                 if (0 < index && index <= Math.Max(array.Length * 2, 8))
                 if (0 < index && index <= Math.Max(array.Length * 2, 8))
@@ -64,20 +57,13 @@ public sealed class LuaTable
                 }
                 }
             }
             }
 
 
-            if (value.Type is LuaValueType.Nil)
-            {
-                dictionary.Remove(key);
-            }
-            else
-            {
-                dictionary[key] = value;
-            }
+            dictionary[key] = value;
         }
         }
     }
     }
 
 
     public int HashMapCount
     public int HashMapCount
     {
     {
-        get => dictionary.Count;
+        get => dictionary.Count(x => x.Value.Type is not LuaValueType.Nil);
     }
     }
 
 
     public int ArrayLength
     public int ArrayLength
@@ -170,7 +156,7 @@ public sealed class LuaTable
         array[arrayIndex] = value;
         array[arrayIndex] = value;
     }
     }
 
 
-    public KeyValuePair<LuaValue, LuaValue> GetNext(LuaValue key)
+    public bool TryGetNext(LuaValue key, out KeyValuePair<LuaValue, LuaValue> pair)
     {
     {
         var index = -1;
         var index = -1;
         if (key.Type is LuaValueType.Nil)
         if (key.Type is LuaValueType.Nil)
@@ -189,30 +175,40 @@ public sealed class LuaTable
             {
             {
                 if (span[i].Type is not LuaValueType.Nil)
                 if (span[i].Type is not LuaValueType.Nil)
                 {
                 {
-                    return new(index + i + 1, span[i]);
+                    pair = new(index + i + 1, span[i]);
+                    return true;
                 }
                 }
             }
             }
 
 
-            foreach (var pair in dictionary)
+            foreach (var kv in dictionary)
             {
             {
-                return pair;
+                if (kv.Value.Type is not LuaValueType.Nil)
+                {
+                    pair = kv;
+                    return true;
+                }
             }
             }
         }
         }
         else
         else
         {
         {
             var foundKey = false;
             var foundKey = false;
-            foreach (var pair in dictionary)
+            foreach (var kv in dictionary)
             {
             {
-                if (foundKey) return pair;
+                if (foundKey && kv.Value.Type is not LuaValueType.Nil)
+                {
+                    pair = kv;
+                    return true;
+                }
 
 
-                if (pair.Key.Equals(key))
+                if (kv.Key.Equals(key))
                 {
                 {
                     foundKey = true;
                     foundKey = true;
                 }
                 }
             }
             }
         }
         }
 
 
-        return default;
+        pair = default;
+        return false;
     }
     }
 
 
     public void Clear()
     public void Clear()

+ 6 - 3
src/Lua/LuaThread.cs

@@ -1,3 +1,4 @@
+using System.Diagnostics;
 using Lua.Internal;
 using Lua.Internal;
 using Lua.Runtime;
 using Lua.Runtime;
 
 
@@ -6,13 +7,15 @@ namespace Lua;
 public abstract class LuaThread
 public abstract class LuaThread
 {
 {
     public abstract LuaThreadStatus GetStatus();
     public abstract LuaThreadStatus GetStatus();
+    public abstract void UnsafeSetStatus(LuaThreadStatus status);
     public abstract ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default);
     public abstract ValueTask<int> Resume(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default);
-    public abstract ValueTask Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default);
+    public abstract ValueTask<int> Yield(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken = default);
 
 
     LuaStack stack = new();
     LuaStack stack = new();
     FastStackCore<CallStackFrame> callStack;
     FastStackCore<CallStackFrame> callStack;
 
 
     internal LuaStack Stack => stack;
     internal LuaStack Stack => stack;
+    internal ref FastStackCore<CallStackFrame> CallStack => ref callStack;
 
 
     public CallStackFrame GetCurrentFrame()
     public CallStackFrame GetCurrentFrame()
     {
     {
@@ -24,9 +27,9 @@ public abstract class LuaThread
         return stack.AsSpan();
         return stack.AsSpan();
     }
     }
 
 
-    internal CallStackFrame[] GetStackFrames()
+    public ReadOnlySpan<CallStackFrame> GetCallStackFrames()
     {
     {
-        return callStack.AsSpan()[1..].ToArray();
+        return callStack.AsSpan();
     }
     }
 
 
     internal void PushCallStackFrame(CallStackFrame frame)
     internal void PushCallStackFrame(CallStackFrame frame)

+ 1 - 1
src/Lua/LuaThreadStatus.cs

@@ -2,8 +2,8 @@ namespace Lua;
 
 
 public enum LuaThreadStatus : byte
 public enum LuaThreadStatus : byte
 {
 {
-    Normal,
     Suspended,
     Suspended,
+    Normal,
     Running,
     Running,
     Dead,
     Dead,
 }
 }

+ 16 - 4
src/Lua/LuaValue.cs

@@ -84,8 +84,20 @@ public readonly struct LuaValue : IEquatable<LuaValue>
                     var str = (string)referenceValue!;
                     var str = (string)referenceValue!;
                     var span = str.AsSpan().Trim();
                     var span = str.AsSpan().Trim();
 
 
+                    if (span.Length == 0)
+                    {
+                        result = default!;
+                        return false;
+                    }
+
                     var sign = 1;
                     var sign = 1;
-                    if (span.Length > 0 && span[0] == '-')
+                    var first = span[0];
+                    if (first is '+')
+                    {
+                        sign = 1;
+                        span = span[1..];
+                    }
+                    else if (first is '-')
                     {
                     {
                         sign = -1;
                         sign = -1;
                         span = span[1..];
                         span = span[1..];
@@ -307,11 +319,11 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             LuaValueType.Nil => true,
             LuaValueType.Nil => true,
             LuaValueType.Boolean => Read<bool>().Equals(other.Read<bool>()),
             LuaValueType.Boolean => Read<bool>().Equals(other.Read<bool>()),
             LuaValueType.String => Read<string>().Equals(other.Read<string>()),
             LuaValueType.String => Read<string>().Equals(other.Read<string>()),
-            LuaValueType.Number => Read<double>().Equals(other.Read<double>()),
+            LuaValueType.Number => Read<double>() == other.Read<double>(),
             LuaValueType.Function => Read<LuaFunction>().Equals(other.Read<LuaFunction>()),
             LuaValueType.Function => Read<LuaFunction>().Equals(other.Read<LuaFunction>()),
             LuaValueType.Thread => Read<LuaThread>().Equals(other.Read<LuaThread>()),
             LuaValueType.Thread => Read<LuaThread>().Equals(other.Read<LuaThread>()),
             LuaValueType.Table => Read<LuaTable>().Equals(other.Read<LuaTable>()),
             LuaValueType.Table => Read<LuaTable>().Equals(other.Read<LuaTable>()),
-            LuaValueType.UserData => referenceValue == other.referenceValue,
+            LuaValueType.UserData => Read<LuaUserData>().Equals(other.Read<LuaUserData>()),
             _ => false,
             _ => false,
         };
         };
     }
     }
@@ -398,7 +410,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             return func.InvokeAsync(context with
             return func.InvokeAsync(context with
             {
             {
                 ArgumentCount = 1,
                 ArgumentCount = 1,
-                StackPosition = context.State.CurrentThread.Stack.Count,
+                FrameBase = context.Thread.Stack.Count,
             }, buffer, cancellationToken);
             }, buffer, cancellationToken);
         }
         }
         else
         else

+ 7 - 5
src/Lua/Runtime/Closure.cs

@@ -15,7 +15,7 @@ public sealed class Closure : LuaFunction
         for (int i = 0; i < proto.UpValues.Length; i++)
         for (int i = 0; i < proto.UpValues.Length; i++)
         {
         {
             var description = proto.UpValues[i];
             var description = proto.UpValues[i];
-            var upValue = GetUpValueFromDescription(state, environment == null ? state.EnvUpValue : UpValue.Closed(state.CurrentThread, environment), proto, description);
+            var upValue = GetUpValueFromDescription(state, environment == null ? state.EnvUpValue : UpValue.Closed(environment), proto, description, 1);
             upValues.Add(upValue);
             upValues.Add(upValue);
         }
         }
     }
     }
@@ -27,15 +27,17 @@ public sealed class Closure : LuaFunction
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
-        return LuaVirtualMachine.ExecuteClosureAsync(context.State, this, context.State.CurrentThread.GetCurrentFrame(), buffer, cancellationToken);
+        return LuaVirtualMachine.ExecuteClosureAsync(context.State, this, context.Thread.GetCurrentFrame(), buffer, cancellationToken);
     }
     }
 
 
-    static UpValue GetUpValueFromDescription(LuaState state, UpValue envUpValue, Chunk proto, UpValueInfo description)
+    static UpValue GetUpValueFromDescription(LuaState state, UpValue envUpValue, Chunk proto, UpValueInfo description, int depth)
     {
     {
         if (description.IsInRegister)
         if (description.IsInRegister)
         {
         {
             var thread = state.CurrentThread;
             var thread = state.CurrentThread;
-            return state.GetOrAddUpValue(thread, thread.GetCurrentFrame().Base + description.Index);
+            var callStack = thread.GetCallStackFrames();
+            var frame = callStack[^depth];
+            return state.GetOrAddUpValue(thread, frame.Base + description.Index);
         }
         }
         else if (description.Index == -1) // -1 is global environment
         else if (description.Index == -1) // -1 is global environment
         {
         {
@@ -43,7 +45,7 @@ public sealed class Closure : LuaFunction
         }
         }
         else
         else
         {
         {
-            return GetUpValueFromDescription(state, envUpValue, proto.Parent!, proto.Parent!.UpValues[description.Index]);
+            return GetUpValueFromDescription(state, envUpValue, proto.Parent!, proto.Parent!.UpValues[description.Index], depth + 1);
         }
         }
     }
     }
 }
 }

+ 9 - 0
src/Lua/Runtime/LuaValueRuntimeExtensions.cs

@@ -5,6 +5,7 @@ namespace Lua.Runtime;
 
 
 internal static class LuaRuntimeExtensions
 internal static class LuaRuntimeExtensions
 {
 {
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool TryGetMetamethod(this LuaValue value, LuaState state, string methodName, out LuaValue result)
     public static bool TryGetMetamethod(this LuaValue value, LuaState state, string methodName, out LuaValue result)
     {
     {
         result = default;
         result = default;
@@ -12,6 +13,14 @@ internal static class LuaRuntimeExtensions
             metatable.TryGetValue(methodName, out result);
             metatable.TryGetValue(methodName, out result);
     }
     }
 
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int GetVariableArgumentCount(this LuaFunction function, int argumentCount)
+    {
+        return function is Closure luaClosure
+            ? argumentCount - luaClosure.Proto.ParameterCount
+            : 0;
+    }
+
 #if NET6_0_OR_GREATER
 #if NET6_0_OR_GREATER
     [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
     [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
 #endif
 #endif

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


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

@@ -7,11 +7,11 @@ public sealed class UpValue
 {
 {
     LuaValue value;
     LuaValue value;
 
 
-    public LuaThread Thread { get; }
+    public LuaThread? Thread { get; }
     public bool IsClosed { get; private set; }
     public bool IsClosed { get; private set; }
     public int RegisterIndex { get; private set; }
     public int RegisterIndex { get; private set; }
 
 
-    UpValue(LuaThread thread)
+    UpValue(LuaThread? thread)
     {
     {
         Thread = thread;
         Thread = thread;
     }
     }
@@ -24,9 +24,9 @@ public sealed class UpValue
         };
         };
     }
     }
 
 
-    public static UpValue Closed(LuaThread thread, LuaValue value)
+    public static UpValue Closed(LuaValue value)
     {
     {
-        return new(thread)
+        return new(null)
         {
         {
             IsClosed = true,
             IsClosed = true,
             value = value
             value = value
@@ -42,7 +42,7 @@ public sealed class UpValue
         }
         }
         else
         else
         {
         {
-            return Thread.Stack.UnsafeGet(RegisterIndex);
+            return Thread!.Stack.UnsafeGet(RegisterIndex);
         }
         }
     }
     }
 
 
@@ -55,7 +55,7 @@ public sealed class UpValue
         }
         }
         else
         else
         {
         {
-            Thread.Stack.UnsafeGet(RegisterIndex) = value;
+            Thread!.Stack.UnsafeGet(RegisterIndex) = value;
         }
         }
     }
     }
 
 
@@ -64,7 +64,7 @@ public sealed class UpValue
     {
     {
         if (!IsClosed)
         if (!IsClosed)
         {
         {
-            value = Thread.Stack.UnsafeGet(RegisterIndex);
+            value = Thread!.Stack.UnsafeGet(RegisterIndex);
         }
         }
 
 
         IsClosed = true;
         IsClosed = true;

+ 11 - 4
src/Lua/Standard/Basic/NextFunction.cs

@@ -10,9 +10,16 @@ public sealed class NextFunction : LuaFunction
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg0 = context.GetArgument<LuaTable>(0);
         var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil;
         var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil;
 
 
-        var kv = arg0.GetNext(arg1);
-        buffer.Span[0] = kv.Key;
-        buffer.Span[1] = kv.Value;
-        return new(2);
+        if (arg0.TryGetNext(arg1, out var kv))
+        {
+            buffer.Span[0] = kv.Key;
+            buffer.Span[1] = kv.Value;
+            return new(2);
+        }
+        else
+        {
+            buffer.Span[0] = LuaValue.Nil;
+            return new(1);
+        }
     }
     }
 }
 }

+ 1 - 1
src/Lua/Standard/Basic/PCallFunction.cs

@@ -19,7 +19,7 @@ public sealed class PCallFunction : LuaFunction
             {
             {
                 State = context.State,
                 State = context.State,
                 ArgumentCount = context.ArgumentCount - 1,
                 ArgumentCount = context.ArgumentCount - 1,
-                StackPosition = context.StackPosition + 1,
+                FrameBase = context.FrameBase + 1,
             }, methodBuffer.AsMemory(), cancellationToken);
             }, methodBuffer.AsMemory(), cancellationToken);
 
 
             buffer.Span[0] = true;
             buffer.Span[0] = true;

+ 101 - 26
src/Lua/Standard/Basic/ToNumberFunction.cs

@@ -1,5 +1,4 @@
-using System.Globalization;
-using Lua.Runtime;
+using Lua.Internal;
 
 
 namespace Lua.Standard.Basic;
 namespace Lua.Standard.Basic;
 
 
@@ -10,61 +9,137 @@ public sealed class ToNumberFunction : LuaFunction
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
-        var arg0 = context.GetArgument(0);
-        var arg1 = context.HasArgument(1)
+        var e = context.GetArgument(0);
+        int? toBase = context.HasArgument(1)
             ? (int)context.GetArgument<double>(1)
             ? (int)context.GetArgument<double>(1)
-            : 10;
+            : null;
 
 
-        if (arg1 < 2 || arg1 > 36)
+        if (toBase != null && (toBase < 2 || toBase > 36))
         {
         {
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'tonumber' (base out of range)");
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'tonumber' (base out of range)");
         }
         }
 
 
-        if (arg0.Type is LuaValueType.Number)
+        double? value = null;
+        if (e.Type is LuaValueType.Number)
         {
         {
-            buffer.Span[0] = arg0;
+            value = e.Read<double>();
         }
         }
-        else if (arg0.TryRead<string>(out var str))
+        else if (e.TryRead<string>(out var str))
         {
         {
-            if (arg1 == 10 || arg1 == 16)
+            if (toBase == null)
             {
             {
-                if (arg0.TryRead<double>(out var result))
+                if (e.TryRead<double>(out var result))
                 {
                 {
-                    buffer.Span[0] = result;
-                }
-                else
-                {
-                    buffer.Span[0] = LuaValue.Nil;
+                    value = result;
                 }
                 }
             }
             }
-            else if (arg0 == 10)
+            else if (toBase == 10)
             {
             {
-                if (double.TryParse(str, out double result))
+                if (double.TryParse(str, out var result))
                 {
                 {
-                    buffer.Span[0] = result;
-                }
-                else
-                {
-                    buffer.Span[0] = LuaValue.Nil;
+                    value = result;
                 }
                 }
             }
             }
             else
             else
             {
             {
                 try
                 try
                 {
                 {
-                    buffer.Span[0] = Convert.ToInt64(str, arg1);
+                    // if the base is not 10, str cannot contain a minus sign
+                    var span = str.AsSpan().Trim();
+                    if (span.Length == 0) goto END;
+
+                    var first = span[0];
+                    var sign = first == '-' ? -1 : 1;
+                    if (first is '+' or '-')
+                    {
+                        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')
+                    {
+                        value = sign * HexConverter.ToDouble(span);
+                    }
+                    else
+                    {
+                        value = sign * StringToDouble(span, toBase.Value);
+                    }
                 }
                 }
                 catch (FormatException)
                 catch (FormatException)
                 {
                 {
-                    buffer.Span[0] = LuaValue.Nil;
+                    goto END;
                 }
                 }
             }
             }
         }
         }
         else
         else
         {
         {
-            buffer.Span[0] = LuaValue.Nil;
+            goto END;
         }
         }
 
 
+    END:
+        if (value != null && double.IsNaN(value.Value))
+        {
+            value = null;
+        }
+
+        buffer.Span[0] = value == null ? LuaValue.Nil : value.Value;
         return new(1);
         return new(1);
     }
     }
+
+    static double StringToDouble(ReadOnlySpan<char> text, int toBase)
+    {
+        var value = 0.0;
+        for (int i = 0; i < text.Length; i++)
+        {
+            var v = text[i] switch
+            {
+                '0' => 0,
+                '1' => 1,
+                '2' => 2,
+                '3' => 3,
+                '4' => 4,
+                '5' => 5,
+                '6' => 6,
+                '7' => 7,
+                '8' => 8,
+                '9' => 9,
+                'a' or 'A' => 10,
+                'b' or 'B' => 11,
+                'c' or 'C' => 12,
+                'd' or 'D' => 13,
+                'e' or 'E' => 14,
+                'f' or 'F' => 15,
+                'g' or 'G' => 16,
+                'h' or 'H' => 17,
+                'i' or 'I' => 18,
+                'j' or 'J' => 19,
+                'k' or 'K' => 20,
+                'l' or 'L' => 21,
+                'm' or 'M' => 22,
+                'n' or 'N' => 23,
+                'o' or 'O' => 24,
+                'p' or 'P' => 25,
+                'q' or 'Q' => 26,
+                'r' or 'R' => 27,
+                's' or 'S' => 28,
+                't' or 'T' => 29,
+                'u' or 'U' => 30,
+                'v' or 'V' => 31,
+                'w' or 'W' => 32,
+                'x' or 'X' => 33,
+                'y' or 'Y' => 34,
+                'z' or 'Z' => 35,
+                _ => 0,
+            };
+
+            if (v >= toBase)
+            {
+                throw new FormatException();
+            }
+
+            value += v * Math.Pow(toBase, text.Length - i - 1);
+        }
+
+        return value;
+    }
 }
 }

+ 2 - 2
src/Lua/Standard/Basic/XPCallFunction.cs

@@ -21,7 +21,7 @@ public sealed class XPCallFunction : LuaFunction
             {
             {
                 State = context.State,
                 State = context.State,
                 ArgumentCount = context.ArgumentCount - 2,
                 ArgumentCount = context.ArgumentCount - 2,
-                StackPosition = context.StackPosition + 2,
+                FrameBase = context.FrameBase + 2,
             }, methodBuffer.AsMemory(), cancellationToken);
             }, methodBuffer.AsMemory(), cancellationToken);
 
 
             buffer.Span[0] = true;
             buffer.Span[0] = true;
@@ -40,7 +40,7 @@ public sealed class XPCallFunction : LuaFunction
             {
             {
                 State = context.State,
                 State = context.State,
                 ArgumentCount = 1,
                 ArgumentCount = 1,
-                StackPosition = null,
+                FrameBase = null,
             }, methodBuffer.AsMemory(), cancellationToken);
             }, methodBuffer.AsMemory(), cancellationToken);
 
 
             buffer.Span[0] = false;
             buffer.Span[0] = false;

+ 3 - 4
src/Lua/Standard/Coroutines/CoroutineCreateFunction.cs

@@ -3,14 +3,13 @@ namespace Lua.Standard.Coroutines;
 
 
 public sealed class CoroutineCreateFunction : LuaFunction
 public sealed class CoroutineCreateFunction : LuaFunction
 {
 {
-    public const string FunctionName = "create";
-
-    public override string Name => FunctionName;
+    public static readonly CoroutineCreateFunction Instance = new();
+    public override string Name => "create";
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
         var arg0 = context.GetArgument<LuaFunction>(0);
-        buffer.Span[0] = context.State.CreateThread(arg0, true);
+        buffer.Span[0] = new LuaCoroutine(arg0, true);
         return new(1);
         return new(1);
     }
     }
 }
 }

+ 2 - 3
src/Lua/Standard/Coroutines/CoroutineResumeFunction.cs

@@ -3,9 +3,8 @@ namespace Lua.Standard.Coroutines;
 
 
 public sealed class CoroutineResumeFunction : LuaFunction
 public sealed class CoroutineResumeFunction : LuaFunction
 {
 {
-    public const string FunctionName = "resume";
-
-    public override string Name => FunctionName;
+    public static readonly CoroutineResumeFunction Instance = new();
+    public override string Name => "resume";
 
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {

+ 4 - 4
src/Lua/Standard/Coroutines/CoroutineRunningFunction.cs

@@ -3,14 +3,14 @@ namespace Lua.Standard.Coroutines;
 
 
 public sealed class CoroutineRunningFunction : LuaFunction
 public sealed class CoroutineRunningFunction : LuaFunction
 {
 {
-    public const string FunctionName = "running";
+    public static readonly CoroutineRunningFunction Instance = new();
+    public override string Name => "running";
 
 
-    public override string Name => FunctionName;
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
-        buffer.Span[0] = context.State.CurrentThread;
-        buffer.Span[1] = context.State.CurrentThread == context.State.MainThread;
+        buffer.Span[0] = context.Thread;
+        buffer.Span[1] = context.Thread == context.State.MainThread;
         return new(2);
         return new(2);
     }
     }
 }
 }

+ 2 - 3
src/Lua/Standard/Coroutines/CoroutineStatusFunction.cs

@@ -3,9 +3,8 @@ namespace Lua.Standard.Coroutines;
 
 
 public sealed class CoroutineStatusFunction : LuaFunction
 public sealed class CoroutineStatusFunction : LuaFunction
 {
 {
-    public const string FunctionName = "status";
-
-    public override string Name => FunctionName;
+    public static readonly CoroutineStatusFunction Instance = new();
+    public override string Name => "status";
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {

+ 27 - 4
src/Lua/Standard/Coroutines/CoroutineWrapFunction.cs

@@ -1,16 +1,18 @@
 
 
+using Lua.Runtime;
+
 namespace Lua.Standard.Coroutines;
 namespace Lua.Standard.Coroutines;
 
 
 public sealed class CoroutineWrapFunction : LuaFunction
 public sealed class CoroutineWrapFunction : LuaFunction
 {
 {
-    public const string FunctionName = "wrap";
+    public static readonly CoroutineWrapFunction Instance = new();
+    public override string Name => "wrap";
 
 
-    public override string Name => FunctionName;
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
         var arg0 = context.GetArgument<LuaFunction>(0);
         var arg0 = context.GetArgument<LuaFunction>(0);
-        var thread = context.State.CreateThread(arg0, false);
+        var thread = new LuaCoroutine(arg0, false);
         buffer.Span[0] = new Wrapper(thread);
         buffer.Span[0] = new Wrapper(thread);
         return new(1);
         return new(1);
     }
     }
@@ -19,7 +21,28 @@ public sealed class CoroutineWrapFunction : LuaFunction
     {
     {
         protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
         protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
         {
         {
-            return await targetThread.Resume(context, buffer, cancellationToken);
+            var stack = context.Thread.Stack;
+            var frameBase = stack.Count;
+
+            stack.Push(targetThread);
+            PushArguments(stack, context.Arguments);
+
+            var resultCount = await targetThread.Resume(context with
+            {
+                ArgumentCount = context.ArgumentCount + 1,
+                FrameBase = frameBase,
+            }, buffer, cancellationToken);
+
+            buffer.Span[1..].CopyTo(buffer.Span[0..]);
+            return resultCount - 1;
+        }
+
+        static void PushArguments(LuaStack stack, ReadOnlySpan<LuaValue> arguments)
+        {
+            foreach (var arg in arguments)
+            {
+                stack.Push(arg);
+            }
         }
         }
     }
     }
 }
 }

+ 4 - 6
src/Lua/Standard/Coroutines/CoroutineYieldFunction.cs

@@ -3,13 +3,11 @@ namespace Lua.Standard.Coroutines;
 
 
 public sealed class CoroutineYieldFunction : LuaFunction
 public sealed class CoroutineYieldFunction : LuaFunction
 {
 {
-    public const string FunctionName = "yield";
+    public static readonly CoroutineYieldFunction Instance = new();
+    public override string Name => "yield";
 
 
-    public override string Name => FunctionName;
-
-    protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
-        await context.State.CurrentThread.Yield(context, cancellationToken);
-        return 0;
+        return context.Thread.Yield(context, buffer, cancellationToken);
     }
     }
 }
 }

+ 14 - 7
src/Lua/Standard/OpenLibExtensions.cs

@@ -50,6 +50,15 @@ public static class OpenLibExtensions
         SelectFunction.Instance,
         SelectFunction.Instance,
     ];
     ];
 
 
+    static readonly LuaFunction[] coroutineFunctions = [
+        CoroutineCreateFunction.Instance,
+        CoroutineResumeFunction.Instance,
+        CoroutineYieldFunction.Instance,
+        CoroutineStatusFunction.Instance,
+        CoroutineRunningFunction.Instance,
+        CoroutineWrapFunction.Instance,
+    ];
+
     static readonly LuaFunction[] mathFunctions = [
     static readonly LuaFunction[] mathFunctions = [
         AbsFunction.Instance,
         AbsFunction.Instance,
         AcosFunction.Instance,
         AcosFunction.Instance,
@@ -156,13 +165,11 @@ public static class OpenLibExtensions
         }
         }
 
 
         // coroutine
         // coroutine
-        var coroutine = new LuaTable(0, 6);
-        coroutine[CoroutineCreateFunction.FunctionName] = new CoroutineCreateFunction();
-        coroutine[CoroutineResumeFunction.FunctionName] = new CoroutineResumeFunction();
-        coroutine[CoroutineYieldFunction.FunctionName] = new CoroutineYieldFunction();
-        coroutine[CoroutineStatusFunction.FunctionName] = new CoroutineStatusFunction();
-        coroutine[CoroutineRunningFunction.FunctionName] = new CoroutineRunningFunction();
-        coroutine[CoroutineWrapFunction.FunctionName] = new CoroutineWrapFunction();
+        var coroutine = new LuaTable(0, coroutineFunctions.Length);
+        foreach (var func in coroutineFunctions)
+        {
+            coroutine[func.Name] = func;
+        }
 
 
         state.Environment["coroutine"] = coroutine;
         state.Environment["coroutine"] = coroutine;
     }
     }

+ 5 - 6
src/Lua/Standard/Table/InsertFunction.cs

@@ -13,21 +13,20 @@ public sealed class InsertFunction : LuaFunction
             ? context.GetArgument(2)
             ? context.GetArgument(2)
             : context.GetArgument(1);
             : context.GetArgument(1);
 
 
-        var pos = context.HasArgument(2)
+        var pos_arg = context.HasArgument(2)
             ? context.GetArgument<double>(1)
             ? context.GetArgument<double>(1)
             : table.ArrayLength + 1;
             : table.ArrayLength + 1;
 
 
-        if (!MathEx.IsInteger(pos))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'insert' (number has no integer representation)");
-        }
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, pos_arg);
+
+        var pos = (int)pos_arg;
 
 
         if (pos <= 0 || pos > table.ArrayLength + 1)
         if (pos <= 0 || pos > table.ArrayLength + 1)
         {
         {
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'insert' (position out of bounds)");
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'insert' (position out of bounds)");
         }
         }
 
 
-        table.Insert((int)pos, value);
+        table.Insert(pos, value);
         return new(0);
         return new(0);
     }
     }
 }
 }

+ 19 - 9
src/Lua/Standard/Table/RemoveFunction.cs

@@ -7,22 +7,32 @@ public sealed class RemoveFunction : LuaFunction
 
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
     {
-        var arg0 = context.GetArgument<LuaTable>(0);
-        var arg1 = context.HasArgument(1)
+        var table = context.GetArgument<LuaTable>(0);
+        var n_arg = context.HasArgument(1)
             ? context.GetArgument<double>(1)
             ? context.GetArgument<double>(1)
-            : arg0.ArrayLength;
+            : table.ArrayLength;
 
 
-        if (!MathEx.IsInteger(arg1))
-        {
-            throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'remove' (number has no integer representation)");
-        }
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, this, 2, n_arg);
+
+        var n = (int)n_arg;
 
 
-        if (arg1 <= 0 || arg1 > arg0.ArrayLength)
+        if (n <= 0 || n > table.GetArraySpan().Length)
         {
         {
+            if (!context.HasArgument(1) && n == 0)
+            {
+                buffer.Span[0] = LuaValue.Nil;
+                return new(1);
+            }
+            
             throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #2 to 'remove' (position out of bounds)");
             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);
+        }
 
 
-        buffer.Span[0] = arg0.RemoveAt((int)arg1);
+        buffer.Span[0] = table.RemoveAt(n);
         return new(1);
         return new(1);
     }
     }
 }
 }

+ 1 - 1
src/Lua/Standard/Table/SortFunction.cs

@@ -63,7 +63,7 @@ public sealed class SortFunction : LuaFunction
             await comparer.InvokeAsync(context with
             await comparer.InvokeAsync(context with
             {
             {
                 ArgumentCount = 2,
                 ArgumentCount = 2,
-                StackPosition = null,
+                FrameBase = null,
             }, methodBuffer.AsMemory(), cancellationToken);
             }, methodBuffer.AsMemory(), cancellationToken);
 
 
             if (methodBuffer[0].ToBoolean())
             if (methodBuffer[0].ToBoolean())

+ 1 - 1
src/Lua/Standard/Text/GSubFunction.cs

@@ -65,7 +65,7 @@ public sealed class GSubFunction : LuaFunction
                 await func.InvokeAsync(context with
                 await func.InvokeAsync(context with
                 {
                 {
                     ArgumentCount = match.Groups.Count,
                     ArgumentCount = match.Groups.Count,
-                    StackPosition = null
+                    FrameBase = null
                 }, methodBuffer.AsMemory(), cancellationToken);
                 }, methodBuffer.AsMemory(), cancellationToken);
 
 
                 result = methodBuffer[0];
                 result = methodBuffer[0];

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