Browse Source

Merge pull request #15 from AnnulusGames/fix-bugs

Fix bugs
Annulus Games 1 year ago
parent
commit
fc8af6b220
82 changed files with 410 additions and 230 deletions
  1. 3 11
      src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs
  2. 31 13
      src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs
  3. 26 14
      src/Lua/CodeAnalysis/Syntax/Lexer.cs
  4. 20 3
      src/Lua/CodeAnalysis/Syntax/Parser.cs
  5. 79 0
      src/Lua/Internal/HexConverter.cs
  6. 1 1
      src/Lua/LuaFunction.cs
  7. 8 2
      src/Lua/LuaFunctionExecutionContext.cs
  8. 2 2
      src/Lua/LuaTable.cs
  9. 0 1
      src/Lua/Runtime/Chunk.cs
  10. 8 1
      src/Lua/Runtime/LuaStack.cs
  11. 39 0
      src/Lua/Runtime/LuaValueRuntimeExtensions.cs
  12. 15 21
      src/Lua/Runtime/LuaVirtualMachine.cs
  13. 5 4
      src/Lua/Standard/Basic/AssertFunction.cs
  14. 1 1
      src/Lua/Standard/Basic/DoFileFunction.cs
  15. 1 1
      src/Lua/Standard/Basic/GetMetatableFunction.cs
  16. 10 5
      src/Lua/Standard/Basic/IPairsFunction.cs
  17. 3 3
      src/Lua/Standard/Basic/LoadFileFunction.cs
  18. 5 5
      src/Lua/Standard/Basic/LoadFunction.cs
  19. 2 2
      src/Lua/Standard/Basic/NextFunction.cs
  20. 1 1
      src/Lua/Standard/Basic/PCallFunction.cs
  21. 5 17
      src/Lua/Standard/Basic/PairsFunction.cs
  22. 2 2
      src/Lua/Standard/Basic/RawEqualFunction.cs
  23. 2 2
      src/Lua/Standard/Basic/RawGetFunction.cs
  24. 1 1
      src/Lua/Standard/Basic/RawLenFunction.cs
  25. 3 3
      src/Lua/Standard/Basic/RawSetFunction.cs
  26. 10 2
      src/Lua/Standard/Basic/SelectFunction.cs
  27. 2 2
      src/Lua/Standard/Basic/SetMetatableFunction.cs
  28. 7 6
      src/Lua/Standard/Basic/ToNumberFunction.cs
  29. 1 1
      src/Lua/Standard/Basic/ToStringFunction.cs
  30. 2 2
      src/Lua/Standard/Basic/TypeFunction.cs
  31. 2 2
      src/Lua/Standard/Basic/XPCallFunction.cs
  32. 1 1
      src/Lua/Standard/Coroutines/CoroutineCreateFunction.cs
  33. 1 1
      src/Lua/Standard/Coroutines/CoroutineResumeFunction.cs
  34. 1 1
      src/Lua/Standard/Coroutines/CoroutineStatusFunction.cs
  35. 1 1
      src/Lua/Standard/Coroutines/CoroutineWrapFunction.cs
  36. 2 2
      src/Lua/Standard/IO/CloseFunction.cs
  37. 1 1
      src/Lua/Standard/IO/FileFlushFunction.cs
  38. 2 2
      src/Lua/Standard/IO/FileHandle.cs
  39. 3 3
      src/Lua/Standard/IO/FileLinesFunction.cs
  40. 1 1
      src/Lua/Standard/IO/FileReadFunction.cs
  41. 7 7
      src/Lua/Standard/IO/FileSeekFunction.cs
  42. 4 4
      src/Lua/Standard/IO/FileSetVBufFunction.cs
  43. 1 1
      src/Lua/Standard/IO/FileWriteFunction.cs
  44. 1 1
      src/Lua/Standard/IO/LinesFunction.cs
  45. 3 3
      src/Lua/Standard/IO/OpenFunction.cs
  46. 1 1
      src/Lua/Standard/IO/TypeFunction.cs
  47. 1 1
      src/Lua/Standard/Mathematics/AbsFunction.cs
  48. 1 1
      src/Lua/Standard/Mathematics/AcosFuncion.cs
  49. 1 1
      src/Lua/Standard/Mathematics/AsinFuncion.cs
  50. 2 2
      src/Lua/Standard/Mathematics/Atan2Function.cs
  51. 1 1
      src/Lua/Standard/Mathematics/AtanFuncion.cs
  52. 1 1
      src/Lua/Standard/Mathematics/CeilFuncion.cs
  53. 1 1
      src/Lua/Standard/Mathematics/CosFuncion.cs
  54. 1 1
      src/Lua/Standard/Mathematics/CoshFuncion.cs
  55. 1 1
      src/Lua/Standard/Mathematics/DegFunction.cs
  56. 1 1
      src/Lua/Standard/Mathematics/ExpFuncion.cs
  57. 1 1
      src/Lua/Standard/Mathematics/FloorFuncion.cs
  58. 2 2
      src/Lua/Standard/Mathematics/FmodFunction.cs
  59. 1 1
      src/Lua/Standard/Mathematics/FrexpFunction.cs
  60. 2 2
      src/Lua/Standard/Mathematics/LdexpFunction.cs
  61. 3 3
      src/Lua/Standard/Mathematics/LogFunction.cs
  62. 2 2
      src/Lua/Standard/Mathematics/MaxFunction.cs
  63. 2 2
      src/Lua/Standard/Mathematics/MinFunction.cs
  64. 1 1
      src/Lua/Standard/Mathematics/ModfFunction.cs
  65. 2 2
      src/Lua/Standard/Mathematics/PowFunction.cs
  66. 1 1
      src/Lua/Standard/Mathematics/RadFunction.cs
  67. 3 3
      src/Lua/Standard/Mathematics/RandomFunction.cs
  68. 1 1
      src/Lua/Standard/Mathematics/RandomSeedFunction.cs
  69. 1 1
      src/Lua/Standard/Mathematics/SinFuncion.cs
  70. 1 1
      src/Lua/Standard/Mathematics/SinhFuncion.cs
  71. 1 1
      src/Lua/Standard/Mathematics/SqrtFunction.cs
  72. 1 1
      src/Lua/Standard/Mathematics/TanFunction.cs
  73. 1 1
      src/Lua/Standard/Mathematics/TanhFunction.cs
  74. 1 1
      src/Lua/Standard/Modules/RequireFunction.cs
  75. 6 5
      src/Lua/Standard/OpenLibExtensions.cs
  76. 7 7
      src/Lua/Standard/Table/ConcatFunction.cs
  77. 6 6
      src/Lua/Standard/Table/InsertFunction.cs
  78. 3 3
      src/Lua/Standard/Table/RemoveFunction.cs
  79. 3 4
      src/Lua/Standard/Table/SortFunction.cs
  80. 5 5
      src/Lua/Standard/Table/UnpackFunction.cs
  81. 14 0
      tests/Lua.Tests/HexConverterTests.cs
  82. 1 1
      tests/Lua.Tests/tests-lua/vararg.lua

+ 3 - 11
src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs

@@ -128,16 +128,9 @@ public class FunctionCompilationContext : IDisposable
 
     public void AddOrSetFunctionProto(ReadOnlyMemory<char> name, Chunk chunk, out int index)
     {
-        if (functionMap.TryGetValue(name, out index))
-        {
-            functions.AsSpan()[index] = chunk;
-        }
-        else
-        {
-            index = functions.Length;
-            functionMap.Add(name, functions.Length);
-            functions.Add(chunk);
-        }
+        index = functions.Length;
+        functionMap[name] = functions.Length;
+        functions.Add(chunk);
     }
 
     public void AddFunctionProto(Chunk chunk, out int index)
@@ -278,7 +271,6 @@ public class FunctionCompilationContext : IDisposable
             UpValues = upvalues.AsSpan().ToArray(),
             Functions = functions.AsSpan().ToArray(),
             ParameterCount = ParameterCount,
-            HasVariableArgments = HasVariableArguments,
         };
 
         foreach (var function in functions.AsSpan())

+ 31 - 13
src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs

@@ -402,9 +402,11 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             ? (ushort)0
             : (ushort)(node.Nodes.Length + 1);
 
+        var a = context.StackPosition;
+
         CompileExpressionList(node, node.Nodes, b - 1, context);
 
-        context.PushInstruction(Instruction.Return((byte)(context.StackPosition - node.Nodes.Length), b), node.Position);
+        context.PushInstruction(Instruction.Return(a, b), node.Position);
 
         return true;
     }
@@ -550,7 +552,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     // function declaration
     public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, ScopeCompilationContext context)
     {
-        var funcIndex = CompileFunctionProto(ReadOnlyMemory<char>.Empty, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, false);
+        var funcIndex = CompileFunctionProto(ReadOnlyMemory<char>.Empty, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false);
 
         // push closure instruction
         context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
@@ -567,7 +569,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         });
 
         // compile function
-        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, false);
+        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false);
 
         // push closure instruction
         context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true);
@@ -577,7 +579,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
 
     public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNode node, ScopeCompilationContext context)
     {
-        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, false);
+        var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false);
 
         // add closure
         var index = context.Function.GetConstantIndex(node.Name.ToString());
@@ -594,7 +596,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, ScopeCompilationContext context)
     {
         var funcIdentifier = node.MemberPath[^1];
-        var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.HasVariableArguments, node.HasSelfParameter);
+        var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length + 1, node.HasVariableArguments, node.HasSelfParameter);
 
         // add closure
         var index = context.Function.GetConstantIndex(funcIdentifier.Name.ToString());
@@ -624,11 +626,11 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         return true;
     }
 
-    int CompileFunctionProto(ReadOnlyMemory<char> functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, bool hasVarArg, bool hasSelfParameter)
+    int CompileFunctionProto(ReadOnlyMemory<char> functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, int parameterCount, bool hasVarArg, bool hasSelfParameter)
     {
         using var funcContext = context.CreateChildFunction();
         funcContext.ChunkName = functionName.ToString();
-        funcContext.ParameterCount = parameters.Length;
+        funcContext.ParameterCount = parameterCount;
         funcContext.HasVariableArguments = hasVarArg;
 
         if (hasSelfParameter)
@@ -637,6 +639,8 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             {
                 RegisterIndex = 0,
             });
+
+            funcContext.Scope.StackPosition++;
         }
 
         // add arguments
@@ -647,9 +651,9 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             {
                 RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)),
             });
-        }
 
-        funcContext.Scope.StackPosition = (byte)parameters.Length;
+            funcContext.Scope.StackPosition++;
+        }
 
         foreach (var statement in statements)
         {
@@ -891,7 +895,22 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     {
         // get iterator
         var startPosition = context.StackPosition;
-        node.ExpressionNode.Accept(this, context);
+        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);
+        }
 
         // jump to TFORCALL
         var startJumpIndex = context.Function.Instructions.Length;
@@ -1050,7 +1069,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 case BinaryOperator.LessThanOrEqual:
                     {
                         (var b, var c) = GetBAndC(binaryExpression, context);
-                        context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)1 : (byte)0, b, c), node.Position);
+                        context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
                         return;
                     }
                 case BinaryOperator.GreaterThan:
@@ -1078,9 +1097,8 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         for (int i = 0; i < expressions.Length; i++)
         {
             var expression = expressions[i];
-            var remaining = expressions.Length - i + 1;
             var isLast = i == expressions.Length - 1;
-            var resultCount = isLast ? (minimumCount == -1 ? -1 : remaining) : 1;
+            var resultCount = isLast ? (minimumCount == -1 ? -1 : minimumCount - i) : 1;
 
             if (expression is CallFunctionExpressionNode call)
             {

+ 26 - 14
src/Lua/CodeAnalysis/Syntax/Lexer.cs

@@ -223,13 +223,29 @@ public ref struct Lexer
         }
 
         // numeric literal
-        if (IsNumeric(c1))
+        if (c1 is '.' || IsNumeric(c1))
         {
             if (c1 is '0' && c2 is 'x' or 'X') // hex 0x
             {
                 Advance(1);
+                if (span[offset] is '.') Advance(1);
+
                 ReadDigit(ref span, ref offset, out var readCount);
 
+                if (span.Length > offset && span[offset] is '.')
+                {
+                    Advance(1);
+                    ReadDigit(ref span, ref offset, out _);
+                }
+
+                if (span.Length > offset && span[offset] is 'p' or 'P')
+                {
+                    Advance(1);
+                    if (span[offset] is '-' or '+') Advance(1);
+
+                    ReadDigit(ref span, ref offset, out _);
+                }
+
                 if (readCount == 0)
                 {
                     throw new LuaParseException(ChunkName, this.position, $"error: Illegal hexadecimal number");
@@ -239,22 +255,18 @@ public ref struct Lexer
             {
                 ReadNumber(ref span, ref offset, out _);
 
-                if (span.Length > offset)
+                if (span.Length > offset && span[offset] is '.')
                 {
-                    var c = span[offset];
+                    Advance(1);
+                    ReadNumber(ref span, ref offset, out _);
+                }
 
-                    if (c is '.')
-                    {
-                        Advance(1);
-                        ReadNumber(ref span, ref offset, out _);
-                    }
-                    else if (c is 'e' or 'E')
-                    {
-                        Advance(1);
-                        if (span[offset] is '-' or '+') Advance(1);
+                if (span.Length > offset && span[offset] is 'e' or 'E')
+                {
+                    Advance(1);
+                    if (span[offset] is '-' or '+') Advance(1);
 
-                        ReadNumber(ref span, ref offset, out _);
-                    }
+                    ReadNumber(ref span, ref offset, out _);
                 }
             }
 

+ 20 - 3
src/Lua/CodeAnalysis/Syntax/Parser.cs

@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using Lua.Internal;
 using Lua.CodeAnalysis.Syntax.Nodes;
+using System.Globalization;
 
 namespace Lua.CodeAnalysis.Syntax;
 
@@ -559,7 +560,7 @@ public ref struct Parser
                 SyntaxTokenType.LSquare or SyntaxTokenType.Dot or SyntaxTokenType.Colon => ParseTableAccessExpression(ref enumerator, null),
                 _ => new IdentifierNode(enumerator.Current.Text, enumerator.Current.Position),
             },
-            SyntaxTokenType.Number => new NumericLiteralNode(double.Parse(enumerator.Current.Text.Span), enumerator.Current.Position),
+            SyntaxTokenType.Number => new NumericLiteralNode(ConvertTextToNumber(enumerator.Current.Text.Span), enumerator.Current.Position),
             SyntaxTokenType.String => new StringLiteralNode(enumerator.Current.Text.ToString(), enumerator.Current.Position),
             SyntaxTokenType.True => new BooleanLiteralNode(true, enumerator.Current.Position),
             SyntaxTokenType.False => new BooleanLiteralNode(false, enumerator.Current.Position),
@@ -586,7 +587,7 @@ public ref struct Parser
             result = ParseTableAccessExpression(ref enumerator, result);
             goto RECURSIVE;
         }
-        else if (nextType is SyntaxTokenType.LParen)
+        else if (nextType is SyntaxTokenType.LParen or SyntaxTokenType.String or SyntaxTokenType.LCurly)
         {
             MoveNextWithValidation(ref enumerator);
             result = ParseCallFunctionExpression(ref enumerator, result);
@@ -626,7 +627,7 @@ public ref struct Parser
             enumerator.MoveNext();
             enumerator.SkipEoL();
 
-            return new NumericLiteralNode(-double.Parse(enumerator.Current.Text.Span), token.Position);
+            return new NumericLiteralNode(-ConvertTextToNumber(enumerator.Current.Text.Span), token.Position);
         }
         else
         {
@@ -860,6 +861,10 @@ public ref struct Parser
         {
             return [new StringLiteralNode(enumerator.Current.Text.ToString(), enumerator.Current.Position)];
         }
+        else if (enumerator.Current.Type is SyntaxTokenType.LCurly)
+        {
+            return [ParseTableConstructorExpression(ref enumerator)];
+        }
 
         // check and skip '('
         CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.LParen, out _);
@@ -988,4 +993,16 @@ public ref struct Parser
             _ => OperatorPrecedence.NonOperator,
         };
     }
+
+    static double ConvertTextToNumber(ReadOnlySpan<char> text)
+    {
+        if (text.Length > 2 && text[0] is '0' && text[1] is 'x' or 'X')
+        {
+            return HexConverter.ToDouble(text);
+        }
+        else
+        {
+            return double.Parse(text);
+        }
+    }
 }

+ 79 - 0
src/Lua/Internal/HexConverter.cs

@@ -0,0 +1,79 @@
+using System.Globalization;
+
+namespace Lua.Internal;
+
+public static class HexConverter
+{
+    public static double ToDouble(ReadOnlySpan<char> text)
+    {
+        var sign = 1;
+        if (text[0] == '-')
+        {
+            // Remove the "-0x"
+            sign = -1;
+            text = text[3..];
+        }
+        else
+        {
+            // Remove the "0x"
+            text = text[2..];
+        }
+
+        var dotIndex = text.IndexOf('.');
+        var expIndex = text.IndexOfAny('p', 'P');
+
+        if (dotIndex == -1 && expIndex == -1)
+        {
+            return long.Parse(text, 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)..];
+
+        var value = intPart.Length == 0
+            ? 0
+            : long.Parse(intPart, NumberStyles.AllowHexSpecifier);
+
+        var decimalValue = 0.0;
+        for (int i = 0; i < decimalPart.Length; i++)
+        {
+            decimalValue += ToInt(decimalPart[i]) * Math.Pow(16, -(i + 1));
+        }
+
+        double result = value + decimalValue;
+
+        if (expPart.Length > 0)
+        {
+            result *= Math.Pow(2, int.Parse(expPart));
+        }
+
+        return result * sign;
+    }
+
+    static int ToInt(char c)
+    {
+        return c 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 'd' => 12,
+            'D' or 'e' => 13,
+            'E' or 'e' => 14,
+            'F' or 'f' => 15,
+            _ => 0
+        };
+    }
+}

+ 1 - 1
src/Lua/LuaFunction.cs

@@ -17,7 +17,7 @@ public abstract partial class LuaFunction
             CallPosition = context.SourcePosition,
             ChunkName = context.ChunkName ?? LuaState.DefaultChunkName,
             RootChunkName = context.RootChunkName ?? LuaState.DefaultChunkName,
-            VariableArgumentCount = this is Closure closure ? context.ArgumentCount - closure.Proto.ParameterCount : 0,
+            VariableArgumentCount = this is Closure closure ? Math.Max(context.ArgumentCount - closure.Proto.ParameterCount, 0) : 0,
             Function = this,
         };
 

+ 8 - 2
src/Lua/LuaFunctionExecutionContext.cs

@@ -22,14 +22,20 @@ public readonly record struct LuaFunctionExecutionContext
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public LuaValue ReadArgument(int index)
+    public bool HasArgument(int index)
+    {
+        return ArgumentCount > index && Arguments[index].Type is not LuaValueType.Nil;
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public LuaValue GetArgument(int index)
     {
         ThrowIfArgumentNotExists(index);
         return Arguments[index];
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public T ReadArgument<T>(int index)
+    public T GetArgument<T>(int index)
     {
         ThrowIfArgumentNotExists(index);
 

+ 2 - 2
src/Lua/LuaTable.cs

@@ -56,7 +56,7 @@ public sealed class LuaTable
         {
             if (TryGetInteger(key, out var index))
             {
-                if (0 < index && index <= array.Length * 2)
+                if (0 < index && index <= Math.Max(array.Length * 2, 8))
                 {
                     EnsureArrayCapacity(index);
                     array[index - 1] = value;
@@ -127,7 +127,7 @@ public sealed class LuaTable
 
         if (TryGetInteger(key, out var index))
         {
-            return index > 0 && index <= array.Length && array[index].Type != LuaValueType.Nil;
+            return index > 0 && index <= array.Length && array[index - 1].Type != LuaValueType.Nil;
         }
 
         return dictionary.ContainsKey(key);

+ 0 - 1
src/Lua/Runtime/Chunk.cs

@@ -13,7 +13,6 @@ public sealed class Chunk
     public required UpValueInfo[] UpValues { get; init; }
     public required Chunk[] Functions { get; init; }
     public required int ParameterCount { get; init; }
-    public required bool HasVariableArgments { get; init; }
 
     internal Chunk GetRoot()
     {

+ 8 - 1
src/Lua/Runtime/LuaStack.cs

@@ -52,7 +52,14 @@ public class LuaStack(int initialSize = 256)
     public void PopUntil(int newSize)
     {
         if (newSize >= top) return;
-        array.AsSpan(newSize, top).Clear();
+        if (newSize == 0)
+        {
+            array.AsSpan().Clear();
+        }
+        else
+        {
+            array.AsSpan(newSize - 1).Clear();
+        }
         top = newSize;
     }
 

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

@@ -1,10 +1,49 @@
 using System.Buffers;
 using System.Runtime.CompilerServices;
+using Lua.Internal;
 
 namespace Lua.Runtime;
 
 internal static class LuaRuntimeExtensions
 {
+    public static bool TryGetNumber(this LuaValue value, out double result)
+    {
+        if (value.TryRead(out result)) return true;
+
+        if (value.TryRead<string>(out var str))
+        {
+            var span = str.AsSpan().Trim();
+
+            var sign = 1;
+            if (span.Length > 0 && span[0] == '-')
+            {
+                sign = -1;
+                span = span[1..];
+            }
+
+            if (span.Length > 2 && span[0] is '0' && span[1] is 'x' or 'X')
+            {
+                // TODO: optimize
+                try
+                {
+                    result = HexConverter.ToDouble(span) * sign;
+                    return true;
+                }
+                catch (FormatException)
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                return double.TryParse(str, out result);
+            }
+        }
+
+        result = default;
+        return false;
+    }
+
     public static bool TryGetMetamethod(this LuaValue value, LuaState state, string methodName, out LuaValue result)
     {
         result = default;

+ 15 - 21
src/Lua/Runtime/LuaVirtualMachine.cs

@@ -121,7 +121,7 @@ public static partial class LuaVirtualMachine
                         var vb = RK(stack, chunk, instruction.B, frame.Base);
                         var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                        if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
+                        if (vb.TryGetNumber(out var valueB) && vc.TryGetNumber(out var valueC))
                         {
                             stack.UnsafeGet(RA) = valueB + valueC;
                         }
@@ -160,7 +160,7 @@ public static partial class LuaVirtualMachine
                         var vb = RK(stack, chunk, instruction.B, frame.Base);
                         var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                        if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
+                        if (vb.TryGetNumber(out var valueB) && vc.TryGetNumber(out var valueC))
                         {
                             stack.UnsafeGet(RA) = valueB - valueC;
                         }
@@ -199,7 +199,7 @@ public static partial class LuaVirtualMachine
                         var vb = RK(stack, chunk, instruction.B, frame.Base);
                         var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                        if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
+                        if (vb.TryGetNumber(out var valueB) && vc.TryGetNumber(out var valueC))
                         {
                             stack.UnsafeGet(RA) = valueB * valueC;
                         }
@@ -238,7 +238,7 @@ public static partial class LuaVirtualMachine
                         var vb = RK(stack, chunk, instruction.B, frame.Base);
                         var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                        if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
+                        if (vb.TryGetNumber(out var valueB) && vc.TryGetNumber(out var valueC))
                         {
                             stack.UnsafeGet(RA) = valueB / valueC;
                         }
@@ -277,7 +277,7 @@ public static partial class LuaVirtualMachine
                         var vb = RK(stack, chunk, instruction.B, frame.Base);
                         var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                        if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
+                        if (vb.TryGetNumber(out var valueB) && vc.TryGetNumber(out var valueC))
                         {
                             stack.UnsafeGet(RA) = valueB % valueC;
                         }
@@ -316,7 +316,7 @@ public static partial class LuaVirtualMachine
                         var vb = RK(stack, chunk, instruction.B, frame.Base);
                         var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                        if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
+                        if (vb.TryGetNumber(out var valueB) && vc.TryGetNumber(out var valueC))
                         {
                             stack.UnsafeGet(RA) = Math.Pow(valueB, valueC);
                         }
@@ -354,7 +354,7 @@ public static partial class LuaVirtualMachine
 
                         var vb = stack.UnsafeGet(RB);
 
-                        if (vb.TryRead<double>(out var valueB))
+                        if (vb.TryGetNumber(out var valueB))
                         {
                             stack.UnsafeGet(RA) = -valueB;
                         }
@@ -388,18 +388,7 @@ public static partial class LuaVirtualMachine
                 case OpCode.Not:
                     {
                         stack.EnsureCapacity(RA + 1);
-
-                        var rb = stack.UnsafeGet(RB);
-
-                        if (rb.TryRead<bool>(out var valueB))
-                        {
-                            stack.UnsafeGet(RA) = !valueB;
-                        }
-                        else
-                        {
-                            stack.UnsafeGet(RA) = false;
-                        }
-
+                        stack.UnsafeGet(RA) = !stack.UnsafeGet(RB).ToBoolean();
                         stack.NotifyTop(RA + 1);
                     }
                     break;
@@ -792,6 +781,9 @@ public static partial class LuaVirtualMachine
                         var iterator = stack.UnsafeGet(RA).Read<LuaFunction>();
 
                         var nextBase = RA + 3 + instruction.C;
+                        stack.UnsafeGet(nextBase) = stack.UnsafeGet(RA + 1);
+                        stack.UnsafeGet(nextBase + 1) = stack.UnsafeGet(RA + 2);
+                        stack.NotifyTop(nextBase + 2);
 
                         var resultBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
                         resultBuffer.AsSpan().Clear();
@@ -857,7 +849,9 @@ public static partial class LuaVirtualMachine
                         stack.EnsureCapacity(RA + count);
                         for (int i = 0; i < count; i++)
                         {
-                            stack.UnsafeGet(RA + i) = stack.UnsafeGet(frame.Base - (frame.VariableArgumentCount - i));
+                            stack.UnsafeGet(RA + i) = frame.VariableArgumentCount > i
+                                ? stack.UnsafeGet(frame.Base - (frame.VariableArgumentCount - i))
+                                : LuaValue.Nil;
                         }
                         stack.NotifyTop(RA + count);
                     }
@@ -1000,7 +994,7 @@ public static partial class LuaVirtualMachine
             newBase = currentBase;
         }
 
-        var variableArgumentCount = function is Closure luaClosure && luaClosure.Proto.HasVariableArgments
+        var variableArgumentCount = function is Closure luaClosure
             ? argumentCount - luaClosure.Proto.ParameterCount
             : 0;
 

+ 5 - 4
src/Lua/Standard/Basic/AssertFunction.cs

@@ -7,19 +7,20 @@ public sealed class AssertFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
 
         if (!arg0.ToBoolean())
         {
             var message = "assertion failed!";
-            if (context.ArgumentCount >= 2)
+            if (context.HasArgument(1))
             {
-                message = context.ReadArgument<string>(1);
+                message = context.GetArgument<string>(1);
             }
 
             throw new LuaAssertionException(context.State.GetTraceback(), message);
         }
 
-        return new(0);
+        context.Arguments.CopyTo(buffer.Span);
+        return new(context.ArgumentCount);
     }
 }

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

@@ -10,7 +10,7 @@ public sealed class DoFileFunction : LuaFunction
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<string>(0);
+        var arg0 = context.GetArgument<string>(0);
 
         // do not use LuaState.DoFileAsync as it uses the new LuaFunctionExecutionContext
         var text = await File.ReadAllTextAsync(arg0, cancellationToken);

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

@@ -10,7 +10,7 @@ public sealed class GetMetatableFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
 
         if (arg0.TryRead<LuaTable>(out var table))
         {

+ 10 - 5
src/Lua/Standard/Basic/IPairsFunction.cs

@@ -9,7 +9,7 @@ public sealed class IPairsFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
+        var arg0 = context.GetArgument<LuaTable>(0);
 
         // If table has a metamethod __ipairs, calls it with table as argument and returns the first three results from the call.
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.IPairs, out var metamethod))
@@ -22,16 +22,21 @@ public sealed class IPairsFunction : LuaFunction
             return function.InvokeAsync(context, buffer, cancellationToken);
         }
 
-        buffer.Span[0] = new Iterator(arg0);
-        return new(1);
+        buffer.Span[0] = Iterator.Instance;
+        buffer.Span[1] = arg0;
+        buffer.Span[2] = 0;
+        return new(3);
     }
 
-    class Iterator(LuaTable table) : LuaFunction
+    class Iterator : LuaFunction
     {
-        int i;
+        public static readonly Iterator Instance = new();
 
         protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
         {
+            var table = context.GetArgument<LuaTable>(0);
+            var i = context.GetArgument<double>(1);
+
             i++;
             if (table.TryGetValue(i, out var value))
             {

+ 3 - 3
src/Lua/Standard/Basic/LoadFileFunction.cs

@@ -11,9 +11,9 @@ public sealed class LoadFileFunction : LuaFunction
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
         // Lua-CSharp does not support binary chunks, the mode argument is ignored.
-        var arg0 = context.ReadArgument<string>(0);
-        var arg2 = context.ArgumentCount >= 3
-            ? context.ReadArgument<LuaTable>(2)
+        var arg0 = context.GetArgument<string>(0);
+        var arg2 = context.HasArgument(2)
+            ? context.GetArgument<LuaTable>(2)
             : null;
 
         // do not use LuaState.DoFileAsync as it uses the new LuaFunctionExecutionContext

+ 5 - 5
src/Lua/Standard/Basic/LoadFunction.cs

@@ -11,14 +11,14 @@ public sealed class LoadFunction : LuaFunction
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
         // Lua-CSharp does not support binary chunks, the mode argument is ignored.
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
 
-        var arg1 = context.ArgumentCount >= 2
-            ? context.ReadArgument<string>(1)
+        var arg1 = context.HasArgument(1)
+            ? context.GetArgument<string>(1)
             : null;
 
-        var arg3 = context.ArgumentCount >= 4
-            ? context.ReadArgument<LuaTable>(3)
+        var arg3 = context.HasArgument(3)
+            ? context.GetArgument<LuaTable>(3)
             : null;
 
         // do not use LuaState.DoFileAsync as it uses the new LuaFunctionExecutionContext

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

@@ -7,8 +7,8 @@ public sealed class NextFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ArgumentCount >= 2 ? context.Arguments[1] : LuaValue.Nil;
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil;
 
         var kv = arg0.GetNext(arg1);
         buffer.Span[0] = kv.Key;

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

@@ -9,7 +9,7 @@ public sealed class PCallFunction : LuaFunction
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaFunction>(0);
+        var arg0 = context.GetArgument<LuaFunction>(0);
 
         try
         {

+ 5 - 17
src/Lua/Standard/Basic/PairsFunction.cs

@@ -9,7 +9,7 @@ public sealed class PairsFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
+        var arg0 = context.GetArgument<LuaTable>(0);
 
         // If table has a metamethod __pairs, calls it with table as argument and returns the first three results from the call.
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Pairs, out var metamethod))
@@ -22,21 +22,9 @@ public sealed class PairsFunction : LuaFunction
             return function.InvokeAsync(context, buffer, cancellationToken);
         }
 
-        buffer.Span[0] = new Iterator(arg0);
-        return new(1);
-    }
-
-    class Iterator(LuaTable table) : LuaFunction
-    {
-        LuaValue key;
-
-        protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
-        {
-            var kv = table.GetNext(key);
-            buffer.Span[0] = kv.Key;
-            buffer.Span[1] = kv.Value;
-            key = kv.Key;
-            return new(2);
-        }
+        buffer.Span[0] = NextFunction.Instance;
+        buffer.Span[1] = arg0;
+        buffer.Span[2] = LuaValue.Nil;
+        return new(3);
     }
 }

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

@@ -8,8 +8,8 @@ public sealed class RawEqualFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
-        var arg1 = context.ReadArgument(1);
+        var arg0 = context.GetArgument(0);
+        var arg1 = context.GetArgument(1);
 
         buffer.Span[0] = arg0 == arg1;
         return new(1);

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

@@ -8,8 +8,8 @@ public sealed class RawGetFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ReadArgument(1);
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.GetArgument(1);
 
         buffer.Span[0] = arg0[arg1];
         return new(1);

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

@@ -8,7 +8,7 @@ public sealed class RawLenFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
 
         if (arg0.TryRead<LuaTable>(out var table))
         {

+ 3 - 3
src/Lua/Standard/Basic/RawSetFunction.cs

@@ -8,9 +8,9 @@ public sealed class RawSetFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ReadArgument(1);
-        var arg2 = context.ReadArgument(2);
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.GetArgument(1);
+        var arg2 = context.GetArgument(2);
 
         arg0[arg1] = arg2;
         return new(0);

+ 10 - 2
src/Lua/Standard/Basic/SelectFunction.cs

@@ -7,7 +7,7 @@ public sealed class SelectFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
 
         if (arg0.TryRead<double>(out var d))
         {
@@ -18,7 +18,15 @@ public sealed class SelectFunction : LuaFunction
 
             var index = (int)d;
 
-            var span = context.Arguments[index..];
+            if (Math.Abs(index) > context.ArgumentCount)
+            {
+                throw new LuaRuntimeException(context.State.GetTraceback(), "bad argument #1 to 'select' (index out of range)");
+            }
+
+            var span = index >= 0
+                ? context.Arguments[index..]
+                : context.Arguments[(context.ArgumentCount + index)..];
+
             span.CopyTo(buffer.Span);
 
             return new(span.Length);

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

@@ -10,8 +10,8 @@ public sealed class SetMetatableFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ReadArgument(1);
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.GetArgument(1);
 
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         {

+ 7 - 6
src/Lua/Standard/Basic/ToNumberFunction.cs

@@ -1,4 +1,5 @@
 using System.Globalization;
+using Lua.Runtime;
 
 namespace Lua.Standard.Basic;
 
@@ -9,9 +10,9 @@ public sealed class ToNumberFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
-        var arg1 = context.ArgumentCount >= 2
-            ? (int)context.ReadArgument<double>(1)
+        var arg0 = context.GetArgument(0);
+        var arg1 = context.HasArgument(1)
+            ? (int)context.GetArgument<double>(1)
             : 10;
 
         if (arg1 < 2 || arg1 > 36)
@@ -25,9 +26,9 @@ public sealed class ToNumberFunction : LuaFunction
         }
         else if (arg0.TryRead<string>(out var str))
         {
-            if ((arg1 == 10 || arg1 == 16) && str.Length >= 3 && str[0] == '0' && str[1] == 'x')
+            if (arg1 == 10 || arg1 == 16)
             {
-                if (int.TryParse(str.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
+                if (arg0.TryGetNumber(out var result))
                 {
                     buffer.Span[0] = result;
                 }
@@ -38,7 +39,7 @@ public sealed class ToNumberFunction : LuaFunction
             }
             else if (arg0 == 10)
             {
-                if (double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out double result))
+                if (double.TryParse(str, out double result))
                 {
                     buffer.Span[0] = result;
                 }

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

@@ -7,7 +7,7 @@ public sealed class ToStringFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
         return arg0.CallToStringAsync(context, buffer, cancellationToken);
     }
 }

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

@@ -7,8 +7,8 @@ public sealed class TypeFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
-        
+        var arg0 = context.GetArgument(0);
+
         buffer.Span[0] = arg0.Type switch
         {
             LuaValueType.Nil => "nil",

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

@@ -9,8 +9,8 @@ public sealed class XPCallFunction : LuaFunction
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaFunction>(0);
-        var arg1 = context.ReadArgument<LuaFunction>(1);
+        var arg0 = context.GetArgument<LuaFunction>(0);
+        var arg1 = context.GetArgument<LuaFunction>(1);
 
         using var methodBuffer = new PooledArray<LuaValue>(1024);
         methodBuffer.AsSpan().Clear();

+ 1 - 1
src/Lua/Standard/Coroutines/CoroutineCreateFunction.cs

@@ -9,7 +9,7 @@ public sealed class CoroutineCreateFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaFunction>(0);
+        var arg0 = context.GetArgument<LuaFunction>(0);
         buffer.Span[0] = context.State.CreateThread(arg0, true);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Coroutines/CoroutineResumeFunction.cs

@@ -9,7 +9,7 @@ public sealed class CoroutineResumeFunction : LuaFunction
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var thread = context.ReadArgument<LuaThread>(0);
+        var thread = context.GetArgument<LuaThread>(0);
         return await thread.Resume(context, buffer, cancellationToken);
     }
 }

+ 1 - 1
src/Lua/Standard/Coroutines/CoroutineStatusFunction.cs

@@ -9,7 +9,7 @@ public sealed class CoroutineStatusFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var thread = context.ReadArgument<LuaThread>(0);
+        var thread = context.GetArgument<LuaThread>(0);
         buffer.Span[0] = thread.GetStatus() switch
         {
             LuaThreadStatus.Normal => "normal",

+ 1 - 1
src/Lua/Standard/Coroutines/CoroutineWrapFunction.cs

@@ -9,7 +9,7 @@ public sealed class CoroutineWrapFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaFunction>(0);
+        var arg0 = context.GetArgument<LuaFunction>(0);
         var thread = context.State.CreateThread(arg0, false);
         buffer.Span[0] = new Wrapper(thread);
         return new(1);

+ 2 - 2
src/Lua/Standard/IO/CloseFunction.cs

@@ -7,8 +7,8 @@ public sealed class CloseFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var file = context.ArgumentCount >= 1
-            ? context.ReadArgument<FileHandle>(0)
+        var file = context.HasArgument(0)
+            ? context.GetArgument<FileHandle>(0)
             : context.State.Environment["io"].Read<LuaTable>()["stdout"].Read<FileHandle>();
 
         try

+ 1 - 1
src/Lua/Standard/IO/FileFlushFunction.cs

@@ -7,7 +7,7 @@ public sealed class FileFlushFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var file = context.ReadArgument<FileHandle>(0);
+        var file = context.GetArgument<FileHandle>(0);
 
         try
         {

+ 2 - 2
src/Lua/Standard/IO/FileHandle.cs

@@ -10,8 +10,8 @@ public class FileHandle : LuaUserData
     {
         protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
         {
-            context.ReadArgument<FileHandle>(0);
-            var key = context.ReadArgument(1);
+            context.GetArgument<FileHandle>(0);
+            var key = context.GetArgument(1);
 
             if (key.TryRead<string>(out var name))
             {

+ 3 - 3
src/Lua/Standard/IO/FileLinesFunction.cs

@@ -7,11 +7,11 @@ public sealed class FileLinesFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<FileHandle>(0);
-        var arg1 = context.ArgumentCount >= 2
+        var arg0 = context.GetArgument<FileHandle>(0);
+        var arg1 = context.HasArgument(1)
             ? context.Arguments[1]
             : "*l";
-        
+
         buffer.Span[0] = new Iterator(arg0, arg1);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/IO/FileReadFunction.cs

@@ -11,7 +11,7 @@ public sealed class FileReadFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var file = context.ReadArgument<FileHandle>(0);
+        var file = context.GetArgument<FileHandle>(0);
         var resultCount = IOHelper.Read(context.State, file, Name, 1, context.Arguments[1..], buffer, false);
         return new(resultCount);
     }

+ 7 - 7
src/Lua/Standard/IO/FileSeekFunction.cs

@@ -7,14 +7,14 @@ public sealed class FileSeekFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var file = context.ReadArgument<FileHandle>(0);
-        var whence = context.ArgumentCount >= 2
-            ? context.ReadArgument<string>(1)
+        var file = context.GetArgument<FileHandle>(0);
+        var whence = context.HasArgument(1)
+            ? context.GetArgument<string>(1)
             : "cur";
-        var offset = context.ArgumentCount >= 3
-            ? context.ReadArgument<double>(2)
+        var offset = context.HasArgument(2)
+            ? context.GetArgument<double>(2)
             : 0;
-        
+
         if (whence is not ("set" or "cur" or "end"))
         {
             throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #2 to 'seek' (invalid option '{whence}')");
@@ -24,7 +24,7 @@ public sealed class FileSeekFunction : LuaFunction
         {
             throw new LuaRuntimeException(context.State.GetTraceback(), $"bad argument #3 to 'seek' (number has no integer representation)");
         }
-        
+
         try
         {
             buffer.Span[0] = file.Seek(whence, (long)offset);

+ 4 - 4
src/Lua/Standard/IO/FileSetVBufFunction.cs

@@ -7,10 +7,10 @@ public sealed class FileSetVBufFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var file = context.ReadArgument<FileHandle>(0);
-        var mode = context.ReadArgument<string>(1);
-        var size = context.ArgumentCount >= 3
-            ? context.ReadArgument<double>(2)
+        var file = context.GetArgument<FileHandle>(0);
+        var mode = context.GetArgument<string>(1);
+        var size = context.HasArgument(2)
+            ? context.GetArgument<double>(2)
             : -1;
 
         if (!MathEx.IsInteger(size))

+ 1 - 1
src/Lua/Standard/IO/FileWriteFunction.cs

@@ -7,7 +7,7 @@ public sealed class FileWriteFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var file = context.ReadArgument<FileHandle>(0);
+        var file = context.GetArgument<FileHandle>(0);
         var resultCount = IOHelper.Write(file, Name, context, buffer);
         return new(resultCount);
     }

+ 1 - 1
src/Lua/Standard/IO/LinesFunction.cs

@@ -17,7 +17,7 @@ public sealed class LinesFunction : LuaFunction
         }
         else
         {
-            var fileName = context.ReadArgument<string>(0);
+            var fileName = context.GetArgument<string>(0);
 
             using var methodBuffer = new PooledArray<LuaValue>(32);
             IOHelper.Open(context.State, fileName, "r", methodBuffer.AsMemory(), true);

+ 3 - 3
src/Lua/Standard/IO/OpenFunction.cs

@@ -7,9 +7,9 @@ public sealed class OpenFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var fileName = context.ReadArgument<string>(0);
-        var mode = context.ArgumentCount >= 2
-            ? context.ReadArgument<string>(1)
+        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);

+ 1 - 1
src/Lua/Standard/IO/TypeFunction.cs

@@ -7,7 +7,7 @@ public sealed class TypeFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument(0);
+        var arg0 = context.GetArgument(0);
 
         if (arg0.TryRead<FileHandle>(out var file))
         {

+ 1 - 1
src/Lua/Standard/Mathematics/AbsFunction.cs

@@ -9,7 +9,7 @@ public sealed class AbsFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Abs(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/AcosFuncion.cs

@@ -9,7 +9,7 @@ public sealed class AcosFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Acos(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/AsinFuncion.cs

@@ -9,7 +9,7 @@ public sealed class AsinFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Asin(arg0);
         return new(1);
     }

+ 2 - 2
src/Lua/Standard/Mathematics/Atan2Function.cs

@@ -9,8 +9,8 @@ public sealed class Atan2Function : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
-        var arg1 = context.ReadArgument<double>(1);
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
 
         buffer.Span[0] = Math.Atan2(arg0, arg1);
         return new(1);

+ 1 - 1
src/Lua/Standard/Mathematics/AtanFuncion.cs

@@ -9,7 +9,7 @@ public sealed class AtanFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Atan(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/CeilFuncion.cs

@@ -9,7 +9,7 @@ public sealed class CeilFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Ceiling(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/CosFuncion.cs

@@ -9,7 +9,7 @@ public sealed class CosFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Cos(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/CoshFuncion.cs

@@ -9,7 +9,7 @@ public sealed class CoshFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Cosh(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/DegFunction.cs

@@ -9,7 +9,7 @@ public sealed class DegFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = arg0 * (180.0 / Math.PI);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/ExpFuncion.cs

@@ -9,7 +9,7 @@ public sealed class ExpFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Exp(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/FloorFuncion.cs

@@ -9,7 +9,7 @@ public sealed class FloorFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Floor(arg0);
         return new(1);
     }

+ 2 - 2
src/Lua/Standard/Mathematics/FmodFunction.cs

@@ -9,8 +9,8 @@ public sealed class FmodFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
-        var arg1 = context.ReadArgument<double>(1);
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
         buffer.Span[0] = arg0 % arg1;
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/FrexpFunction.cs

@@ -9,7 +9,7 @@ public sealed class FrexpFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
 
         var (m, e) = MathEx.Frexp(arg0);
         buffer.Span[0] = m;

+ 2 - 2
src/Lua/Standard/Mathematics/LdexpFunction.cs

@@ -9,8 +9,8 @@ public sealed class LdexpFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
-        var arg1 = context.ReadArgument<double>(1);
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
 
         buffer.Span[0] = arg0 * Math.Pow(2, arg1);
         return new(1);

+ 3 - 3
src/Lua/Standard/Mathematics/LogFunction.cs

@@ -9,7 +9,7 @@ public sealed class LogFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
 
         if (context.ArgumentCount == 1)
         {
@@ -17,10 +17,10 @@ public sealed class LogFunction : LuaFunction
         }
         else
         {
-            var arg1 = context.ReadArgument<double>(1);
+            var arg1 = context.GetArgument<double>(1);
             buffer.Span[0] = Math.Log(arg0, arg1);
         }
-        
+
         return new(1);
     }
 }

+ 2 - 2
src/Lua/Standard/Mathematics/MaxFunction.cs

@@ -9,10 +9,10 @@ public sealed class MaxFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var x = context.ReadArgument<double>(0);
+        var x = context.GetArgument<double>(0);
         for (int i = 1; i < context.ArgumentCount; i++)
         {
-            x = Math.Max(x, context.ReadArgument<double>(i));
+            x = Math.Max(x, context.GetArgument<double>(i));
         }
 
         buffer.Span[0] = x;

+ 2 - 2
src/Lua/Standard/Mathematics/MinFunction.cs

@@ -9,10 +9,10 @@ public sealed class MinFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var x = context.ReadArgument<double>(0);
+        var x = context.GetArgument<double>(0);
         for (int i = 1; i < context.ArgumentCount; i++)
         {
-            x = Math.Min(x, context.ReadArgument<double>(i));
+            x = Math.Min(x, context.GetArgument<double>(i));
         }
 
         buffer.Span[0] = x;

+ 1 - 1
src/Lua/Standard/Mathematics/ModfFunction.cs

@@ -9,7 +9,7 @@ public sealed class ModfFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         var (i, f) = MathEx.Modf(arg0);
         buffer.Span[0] = i;
         buffer.Span[1] = f;

+ 2 - 2
src/Lua/Standard/Mathematics/PowFunction.cs

@@ -9,8 +9,8 @@ public sealed class PowFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
-        var arg1 = context.ReadArgument<double>(1);
+        var arg0 = context.GetArgument<double>(0);
+        var arg1 = context.GetArgument<double>(1);
 
         buffer.Span[0] = Math.Pow(arg0, arg1);
         return new(1);

+ 1 - 1
src/Lua/Standard/Mathematics/RadFunction.cs

@@ -9,7 +9,7 @@ public sealed class RadFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = arg0 * (Math.PI / 180.0);
         return new(1);
     }

+ 3 - 3
src/Lua/Standard/Mathematics/RandomFunction.cs

@@ -18,13 +18,13 @@ public sealed class RandomFunction : LuaFunction
         }
         else if (context.ArgumentCount == 1)
         {
-            var arg0 = context.ReadArgument<double>(0);
+            var arg0 = context.GetArgument<double>(0);
             buffer.Span[0] = rand.NextDouble() * (arg0 - 1) + 1;
         }
         else
         {
-            var arg0 = context.ReadArgument<double>(0);
-            var arg1 = context.ReadArgument<double>(1);
+            var arg0 = context.GetArgument<double>(0);
+            var arg1 = context.GetArgument<double>(1);
             buffer.Span[0] = rand.NextDouble() * (arg1 - arg0) + arg0;
         }
 

+ 1 - 1
src/Lua/Standard/Mathematics/RandomSeedFunction.cs

@@ -9,7 +9,7 @@ public sealed class RandomSeedFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         context.State.Environment[RandomFunction.RandomInstanceKey] = new LuaUserData<Random>(new Random((int)BitConverter.DoubleToInt64Bits(arg0)));
         return new(0);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/SinFuncion.cs

@@ -9,7 +9,7 @@ public sealed class SinFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Sin(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/SinhFuncion.cs

@@ -8,7 +8,7 @@ public sealed class SinhFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Sinh(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/SqrtFunction.cs

@@ -9,7 +9,7 @@ public sealed class SqrtFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Sqrt(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/TanFunction.cs

@@ -9,7 +9,7 @@ public sealed class TanFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Tan(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Mathematics/TanhFunction.cs

@@ -9,7 +9,7 @@ public sealed class TanhFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<double>(0);
+        var arg0 = context.GetArgument<double>(0);
         buffer.Span[0] = Math.Tanh(arg0);
         return new(1);
     }

+ 1 - 1
src/Lua/Standard/Modules/RequireFunction.cs

@@ -12,7 +12,7 @@ public sealed class RequireFunction : LuaFunction
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<string>(0);
+        var arg0 = context.GetArgument<string>(0);
         var loaded = context.State.Environment["package"].Read<LuaTable>()["loaded"].Read<LuaTable>();
 
         if (!loaded.TryGetValue(arg0, out var loadedTable))

+ 6 - 5
src/Lua/Standard/OpenLibExtensions.cs

@@ -109,16 +109,17 @@ public static class OpenLibExtensions
     public static void OpenMathLibrary(this LuaState state)
     {
         state.Environment[RandomFunction.RandomInstanceKey] = new LuaUserData<Random>(new Random());
-        state.Environment["pi"] = Math.PI;
-        state.Environment["huge"] = double.PositiveInfinity;
 
-        var table = new LuaTable(0, mathFunctions.Length);
+        var math = new LuaTable(0, mathFunctions.Length);
         foreach (var func in mathFunctions)
         {
-            table[func.Name] = func;
+            math[func.Name] = func;
         }
 
-        state.Environment["math"] = table;
+        math["pi"] = Math.PI;
+        math["huge"] = double.PositiveInfinity;
+
+        state.Environment["math"] = math;
     }
 
     public static void OpenModuleLibrary(this LuaState state)

+ 7 - 7
src/Lua/Standard/Table/ConcatFunction.cs

@@ -9,15 +9,15 @@ public sealed class ConcatFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ArgumentCount >= 2
-            ? context.ReadArgument<string>(1)
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.HasArgument(1)
+            ? context.GetArgument<string>(1)
             : "";
-        var arg2 = context.ArgumentCount >= 3
-            ? (int)context.ReadArgument<double>(2)
+        var arg2 = context.HasArgument(2)
+            ? (int)context.GetArgument<double>(2)
             : 1;
-        var arg3 = context.ArgumentCount >= 4
-            ? (int)context.ReadArgument<double>(3)
+        var arg3 = context.HasArgument(3)
+            ? (int)context.GetArgument<double>(3)
             : arg0.ArrayLength;
 
         var builder = new ValueStringBuilder(512);

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

@@ -7,14 +7,14 @@ public sealed class InsertFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var table = context.ReadArgument<LuaTable>(0);
+        var table = context.GetArgument<LuaTable>(0);
 
-        var value = context.ArgumentCount >= 3
-            ? context.ReadArgument(2)
-            : context.ReadArgument(1);
+        var value = context.HasArgument(2)
+            ? context.GetArgument(2)
+            : context.GetArgument(1);
 
-        var pos = context.ArgumentCount >= 3
-            ? context.ReadArgument<double>(1)
+        var pos = context.HasArgument(2)
+            ? context.GetArgument<double>(1)
             : table.ArrayLength + 1;
 
         if (!MathEx.IsInteger(pos))

+ 3 - 3
src/Lua/Standard/Table/RemoveFunction.cs

@@ -7,9 +7,9 @@ public sealed class RemoveFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ArgumentCount >= 2
-            ? context.ReadArgument<double>(1)
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.HasArgument(1)
+            ? context.GetArgument<double>(1)
             : arg0.ArrayLength;
 
         if (!MathEx.IsInteger(arg1))

+ 3 - 4
src/Lua/Standard/Table/SortFunction.cs

@@ -15,7 +15,6 @@ public sealed class SortFunction : LuaFunction
         Name = "comp",
         Functions = [],
         Constants = [],
-        HasVariableArgments = false,
         Instructions = [
             Instruction.Le(1, 0, 1),
             Instruction.LoadBool(2, 1, 1),
@@ -31,9 +30,9 @@ public sealed class SortFunction : LuaFunction
 
     protected override async ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ArgumentCount >= 2
-            ? context.ReadArgument<LuaFunction>(1)
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.HasArgument(1)
+            ? context.GetArgument<LuaFunction>(1)
             : new Closure(context.State, defaultComparer);
 
         await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken);

+ 5 - 5
src/Lua/Standard/Table/UnpackFunction.cs

@@ -7,12 +7,12 @@ public sealed class UnpackFunction : LuaFunction
 
     protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var arg0 = context.ReadArgument<LuaTable>(0);
-        var arg1 = context.ArgumentCount >= 2
-            ? (int)context.ReadArgument<double>(1)
+        var arg0 = context.GetArgument<LuaTable>(0);
+        var arg1 = context.HasArgument(1)
+            ? (int)context.GetArgument<double>(1)
             : 1;
-        var arg2 = context.ArgumentCount >= 3
-            ? (int)context.ReadArgument<double>(2)
+        var arg2 = context.HasArgument(2)
+            ? (int)context.GetArgument<double>(2)
             : arg0.ArrayLength;
 
         var index = 0;

+ 14 - 0
tests/Lua.Tests/HexConverterTests.cs

@@ -0,0 +1,14 @@
+using Lua.Internal;
+
+namespace Lua.Tests;
+
+public class HexConverterTests
+{
+    [TestCase("0x10", 16)]
+    [TestCase("0x0p12", 0)]
+    [TestCase("-0x1.0p-1", -0.5)]
+    public void Test_ToDouble(string text, double expected)
+    {
+        Assert.That(HexConverter.ToDouble(text), Is.EqualTo(expected));
+    }
+}

+ 1 - 1
tests/Lua.Tests/tests-lua/vararg.lua

@@ -74,7 +74,7 @@ print("+")
 
 function oneless (a, ...) return ... end
 
-function f (n, a, ...)
+function f(n, a, ...)
   local b
   assert(arg == nil)
   if n == 0 then