Browse Source

improve: error message (function name)

Akeit0 6 months ago
parent
commit
b480c11ae0

+ 40 - 21
src/Lua/Exceptions.cs

@@ -127,7 +127,7 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         {
             throw new LuaRuntimeException(thread, $"attempt to {op} two {typeA} values");
         }
- 
+
         throw new LuaRuntimeException(thread, $"attempt to {op} a {typeA} value with a {typeB} value");
     }
 
@@ -174,7 +174,7 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         var luaValue = reg < 255 ? thread.Stack[caller.Base + reg] : ((LuaClosure)caller.Function).Proto.Constants[reg - 256];
         var function = caller.Function;
         var t = LuaDebug.GetName(((LuaClosure)function).Proto, lastPc, reg, out string? name);
-        
+
         using var builder = new PooledList<char>(64);
         builder.Clear();
         builder.AddRange("attempt to ");
@@ -186,12 +186,11 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         {
             builder.AddRange($" ({t} '{name}')");
         }
-        {
-            throw new LuaRuntimeException(thread,  builder.AsSpan().ToString());
-        }
+
+        throw new LuaRuntimeException(thread, builder.AsSpan().ToString());
     }
 
-    internal static void AttemptInvalidOperationOnUpValues(LuaThread thread, string op, int lastPc, int reg)
+    internal static void AttemptInvalidOperationOnUpValues(LuaThread thread, string op, int reg)
     {
         var caller = thread.GetCurrentFrame();
         var closure = (LuaClosure)caller.Function;
@@ -204,41 +203,61 @@ public class LuaRuntimeException : Exception, ILuaTracebackBuildable
         throw new LuaRuntimeException(thread, $"attempt to {op} a {luaValue.TypeToString()} value (global '{name}')");
     }
 
-    public static void BadArgument(LuaThread? thread, int argumentId, string functionName)
+    internal static string GetCurrentFunctionName(LuaThread thread)
     {
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' (value expected)");
+        var current = thread.GetCurrentFrame();
+        var pc = current.CallerInstructionIndex;
+        LuaClosure callerClosure;
+        if (current.IsTailCall)
+        {
+            pc = thread.LastPc;
+            callerClosure = (LuaClosure)thread.LastCallerFunction!;
+        }
+        else
+        {
+            var caller = thread.GetCallStackFrames()[^2];
+            callerClosure = (LuaClosure)caller.Function;
+        }
+
+        LuaDebug.GetFuncName(callerClosure.Proto, pc, out var name);
+        return name ?? current.Function.Name;
     }
 
-    public static void BadArgument(LuaThread? thread, int argumentId, string functionName, LuaValueType[] expected)
+    public static void BadArgument(LuaThread thread, int argumentId)
     {
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({string.Join(" or ", expected)} expected)");
+        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{GetCurrentFunctionName(thread)}' (value expected)");
     }
 
-    public static void BadArgument(LuaThread? thread, int argumentId, string functionName, string expected, string actual)
+    public static void BadArgument(LuaThread thread, int argumentId, LuaValueType[] expected)
     {
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({expected} expected, got {actual})");
+        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{GetCurrentFunctionName(thread)}' ({string.Join(" or ", expected)} expected)");
     }
-    
-    public static void BadArgument(LuaThread? thread, int argumentId, string functionName, LuaValueType expected, LuaValueType actual)
+
+    public static void BadArgument(LuaThread thread, int argumentId, string expected, string actual)
+    {
+        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{GetCurrentFunctionName(thread)}' ({expected} expected, got {actual})");
+    }
+
+    public static void BadArgument(LuaThread thread, int argumentId, LuaValueType expected, LuaValueType actual)
     {
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({LuaValue.ToString(expected)} expected, got {LuaValue.ToString(actual)})");
+        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{GetCurrentFunctionName(thread)}' ({LuaValue.ToString(expected)} expected, got {LuaValue.ToString(actual)})");
     }
 
-    public static void BadArgument(LuaThread? thread, int argumentId, string functionName, string message)
+    public static void BadArgument(LuaThread thread, int argumentId, string message)
     {
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({message})");
+        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{GetCurrentFunctionName(thread)}' ({message})");
     }
 
-    public static void BadArgumentNumberIsNotInteger(LuaThread? thread, int argumentId, string functionName)
+    public static void BadArgumentNumberIsNotInteger(LuaThread thread, int argumentId)
     {
-        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' (number has no integer representation)");
+        throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{GetCurrentFunctionName(thread)}' (number has no integer representation)");
     }
 
-    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaThread? thread, string functionName, int argumentId, double value)
+    public static void ThrowBadArgumentIfNumberIsNotInteger(LuaThread thread, int argumentId, double value)
     {
         if (!MathEx.IsInteger(value))
         {
-            BadArgumentNumberIsNotInteger(thread, argumentId, functionName);
+            BadArgumentNumberIsNotInteger(thread, argumentId);
         }
     }
 

+ 8 - 8
src/Lua/LuaFunctionExecutionContext.cs

@@ -59,19 +59,19 @@ public readonly record struct LuaFunctionExecutionContext
             var t = typeof(T);
             if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _))
             {
-                LuaRuntimeException.BadArgumentNumberIsNotInteger(Thread, index + 1, Thread.GetCurrentFrame().Function.Name);
+                LuaRuntimeException.BadArgumentNumberIsNotInteger(Thread, index + 1);
             }
             else if (LuaValue.TryGetLuaValueType(t, out var type))
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, Thread.GetCurrentFrame().Function.Name, type, arg.Type);
+                LuaRuntimeException.BadArgument(Thread, index + 1,type, arg.Type);
             }
             else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData)
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
+                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
             }
             else
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.TypeToString());
+                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.TypeToString());
             }
         }
 
@@ -98,19 +98,19 @@ public readonly record struct LuaFunctionExecutionContext
             var t = typeof(T);
             if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _))
             {
-                LuaRuntimeException.BadArgumentNumberIsNotInteger(Thread, index + 1, Thread.GetCurrentFrame().Function.Name);
+                LuaRuntimeException.BadArgumentNumberIsNotInteger(Thread, index + 1);
             }
             else if (LuaValue.TryGetLuaValueType(t, out var type))
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, Thread.GetCurrentFrame().Function.Name, type, arg.Type);
+                LuaRuntimeException.BadArgument(Thread, index + 1, type, arg.Type);
             }
             else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData)
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
+                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.UnsafeRead<object>()?.GetType().ToString() ?? "userdata: 0");
             }
             else
             {
-                LuaRuntimeException.BadArgument(Thread, index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.TypeToString());
+                LuaRuntimeException.BadArgument(Thread, index + 1, t.Name, arg.TypeToString());
             }
         }
 

+ 1 - 0
src/Lua/LuaThread.cs

@@ -72,6 +72,7 @@ public abstract class LuaThread
 
     internal ILuaTracebackBuildable? CurrentException;
     internal readonly ReversedStack<CallStackFrame> ExceptionTrace = new();
+    internal LuaFunction? LastCallerFunction;
 
     public bool IsRunning => CallStackFrameCount != 0;
     internal LuaFunction? Hook { get; set; }

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

@@ -1298,6 +1298,8 @@ public static partial class LuaVirtualMachine
         newBase = context.FrameBase + variableArgumentCount;
         stack.PopUntil(newBase + argumentCount);
         var lastFrame = thread.GetCurrentFrame();
+        thread.LastPc = context.Pc;
+        thread.LastCallerFunction = lastFrame.Function;
         context.Thread.PopCallStackFrame();
         var newFrame = func.CreateNewTailCallFrame(context, newBase, context.CurrentReturnFrameBase, variableArgumentCount);
 
@@ -1532,7 +1534,7 @@ public static partial class LuaVirtualMachine
             if (op != OpCode.GetTabUp)
                 LuaRuntimeException.AttemptInvalidOperationOnLuaStack(GetThreadWithCurrentPc(context), "index", context.Pc, context.Instruction.B);
             else
-                LuaRuntimeException.AttemptInvalidOperationOnUpValues(GetThreadWithCurrentPc(context), "index", context.Pc, context.Instruction.B);
+                LuaRuntimeException.AttemptInvalidOperationOnUpValues(GetThreadWithCurrentPc(context), "index", context.Instruction.B);
         }
     }
 
@@ -1713,7 +1715,7 @@ public static partial class LuaVirtualMachine
             if (op != OpCode.SetTabUp)
                 LuaRuntimeException.AttemptInvalidOperationOnLuaStack(GetThreadWithCurrentPc(context), "index", context.Pc, context.Instruction.A);
             else
-                LuaRuntimeException.AttemptInvalidOperationOnUpValues(GetThreadWithCurrentPc(context), "index", context.Pc, context.Instruction.A);
+                LuaRuntimeException.AttemptInvalidOperationOnUpValues(GetThreadWithCurrentPc(context), "index", context.Instruction.A);
         }
     }
 

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

@@ -58,7 +58,7 @@ public static class Metamethods
             case OpCode.Call:
                 return (Call, "call");
             case OpCode.Concat:
-                return (Concat, "concat");
+                return (Concat, "concatenate");
             default: return (opCode.ToString(), opCode.ToString());
         }
     }

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

@@ -141,7 +141,7 @@ public class Traceback(LuaState state, ReadOnlySpan<CallStackFrame> stackFrames)
                 if (p.LineDefined == 0)
                 {
                     list.AddRange("main chunk");
-                    list.AddRange("\n");
+                    list.Add('\n');
                     goto Next;
                 }
 

+ 7 - 6
src/Lua/Standard/BasicLibrary.cs

@@ -81,6 +81,7 @@ public sealed class BasicLibrary
 
     public ValueTask<int> CollectGarbage(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
+        if (context.HasArgument(0)) context.GetArgument<string>(0);
         GC.Collect();
         return new(context.Return());
     }
@@ -89,7 +90,7 @@ public sealed class BasicLibrary
     {
         var arg0 = context.GetArgument<string>(0);
         context.Thread.Stack.PopUntil(context.ReturnFrameBase);
-        var closure = await context.State.LoadFileAsync(arg0, "bt",null, cancellationToken);
+        var closure = await context.State.LoadFileAsync(arg0, "bt", null, cancellationToken);
         return await context.Access.RunAsync(closure, cancellationToken);
     }
 
@@ -158,7 +159,7 @@ public sealed class BasicLibrary
         // do not use LuaState.DoFileAsync as it uses the newExecutionContext
         try
         {
-            return context.Return(await context.State.LoadFileAsync(arg0,  mode, arg2,cancellationToken));
+            return context.Return(await context.State.LoadFileAsync(arg0, mode, arg2, cancellationToken));
         }
         catch (Exception ex)
         {
@@ -260,7 +261,7 @@ public sealed class BasicLibrary
                 case LuaCanceledException:
                     throw;
                 case OperationCanceledException:
-                    throw new LuaCanceledException(context.Thread,cancellationToken, ex);
+                    throw new LuaCanceledException(context.Thread, cancellationToken, ex);
                 case LuaRuntimeException luaEx:
                     luaEx.Forget();
                     return context.Return(false, luaEx.ErrorObject);
@@ -312,7 +313,7 @@ public sealed class BasicLibrary
         }
         else
         {
-            LuaRuntimeException.BadArgument(context.Thread, 2, "rawlen", [LuaValueType.String, LuaValueType.Table]);
+            LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.String, LuaValueType.Table]);
             return default;
         }
     }
@@ -350,7 +351,7 @@ public sealed class BasicLibrary
         }
         else
         {
-            LuaRuntimeException.BadArgument(context.Thread, 1, "select", LuaValueType.Number, arg0.Type);
+            LuaRuntimeException.BadArgument(context.Thread, 1, LuaValueType.Number, arg0.Type);
             return default;
         }
     }
@@ -362,7 +363,7 @@ public sealed class BasicLibrary
 
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         {
-            LuaRuntimeException.BadArgument(context.Thread, 2, "setmetatable", [LuaValueType.Nil, LuaValueType.Table]);
+            LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.Nil, LuaValueType.Table]);
         }
 
         if (arg0.Metatable != null && arg0.Metatable.TryGetValue(Metamethods.Metatable, out _))

+ 40 - 39
src/Lua/Standard/BitwiseLibrary.cs

@@ -8,32 +8,33 @@ public sealed class BitwiseLibrary
 
     public BitwiseLibrary()
     {
+        var libraryName = "bit32";
         Functions =
         [
-            new("arshift", ArShift),
-            new("band", BAnd),
-            new("bnot", BNot),
-            new("bor", BOr),
-            new("btest", BTest),
-            new("bxor", BXor),
-            new("extract", Extract),
-            new("lrotate", LRotate),
-            new("lshift", LShift),
-            new("replace", Replace),
-            new("rrotate", RRotate),
-            new("rshift", RShift),
+            new(libraryName, "arshift", ArShift),
+            new(libraryName, "band", BAnd),
+            new(libraryName, "bnot", BNot),
+            new(libraryName, "bor", BOr),
+            new(libraryName, "btest", BTest),
+            new(libraryName, "bxor", BXor),
+            new(libraryName, "extract", Extract),
+            new(libraryName, "lrotate", LRotate),
+            new(libraryName, "lshift", LShift),
+            new(libraryName, "replace", Replace),
+            new(libraryName, "rrotate", RRotate),
+            new(libraryName, "rshift", RShift),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     public ValueTask<int> ArShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "arshift", 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "arshift", 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
 
         var v = Bit32Helper.ToInt32(x);
         var a = (int)disp;
@@ -59,14 +60,14 @@ public sealed class BitwiseLibrary
         }
 
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "band", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
 
         var value = Bit32Helper.ToUInt32(arg0);
 
         for (int i = 1; i < context.ArgumentCount; i++)
         {
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "band", 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
 
             var v = Bit32Helper.ToUInt32(arg);
             value &= v;
@@ -79,7 +80,7 @@ public sealed class BitwiseLibrary
     public ValueTask<int> BNot(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "bnot", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
 
         var value = Bit32Helper.ToUInt32(arg0);
         return new(context.Return(~value));
@@ -93,14 +94,14 @@ public sealed class BitwiseLibrary
         }
 
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "bor", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
 
         var value = Bit32Helper.ToUInt32(arg0);
 
         for (int i = 1; i < context.ArgumentCount; i++)
         {
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "bor", 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
 
             var v = Bit32Helper.ToUInt32(arg);
             value |= v;
@@ -118,14 +119,14 @@ public sealed class BitwiseLibrary
         }
 
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "btest", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
 
         var value = Bit32Helper.ToUInt32(arg0);
 
         for (int i = 1; i < context.ArgumentCount; i++)
         {
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "btest", 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
 
             var v = Bit32Helper.ToUInt32(arg);
             value &= v;
@@ -142,14 +143,14 @@ public sealed class BitwiseLibrary
         }
 
         var arg0 = context.GetArgument<double>(0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "bxor", 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
 
         var value = Bit32Helper.ToUInt32(arg0);
 
         for (int i = 1; i < context.ArgumentCount; i++)
         {
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "bxor", 1 + i, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1 + i, arg);
 
             var v = Bit32Helper.ToUInt32(arg);
             value ^= v;
@@ -166,9 +167,9 @@ public sealed class BitwiseLibrary
             ? context.GetArgument<double>(2)
             : 1;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "extract", 1, arg0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "extract", 2, arg1);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "extract", 3, arg2);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, arg2);
 
         var n = Bit32Helper.ToUInt32(arg0);
         var field = (int)arg1;
@@ -192,8 +193,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "lrotate", 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "lrotate", 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
 
         var v = Bit32Helper.ToUInt32(x);
         var a = ((int)disp) % 32;
@@ -216,8 +217,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "lshift", 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "lshift", 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
 
         var v = Bit32Helper.ToUInt32(x);
         var a = (int)disp;
@@ -247,10 +248,10 @@ public sealed class BitwiseLibrary
             ? context.GetArgument<double>(3)
             : 1;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "replace", 1, arg0);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "replace", 2, arg1);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "replace", 3, arg2);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "replace", 4, arg3);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, arg0);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, arg1);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, arg2);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 4, arg3);
 
         var n = Bit32Helper.ToUInt32(arg0);
         var v = Bit32Helper.ToUInt32(arg1);
@@ -278,8 +279,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "rrotate", 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "rrotate", 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
 
         var v = Bit32Helper.ToUInt32(x);
         var a = ((int)disp) % 32;
@@ -301,8 +302,8 @@ public sealed class BitwiseLibrary
         var x = context.GetArgument<double>(0);
         var disp = context.GetArgument<double>(1);
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "rshift", 1, x);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "rshift", 2, disp);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 1, x);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, disp);
 
         var v = Bit32Helper.ToUInt32(x);
         var a = (int)disp;

+ 8 - 7
src/Lua/Standard/CoroutineLibrary.cs

@@ -8,18 +8,19 @@ public sealed class CoroutineLibrary
 
     public CoroutineLibrary()
     {
+        var libraryName = "coroutine";
         Functions =
         [
-            new("create", Create),
-            new("resume", Resume),
-            new("running", Running),
-            new("status", Status),
-            new("wrap", Wrap),
-            new("yield", Yield),
+            new(libraryName, "create", Create),
+            new(libraryName, "resume", Resume),
+            new(libraryName, "running", Running),
+            new(libraryName, "status", Status),
+            new(libraryName, "wrap", Wrap),
+            new(libraryName, "yield", Yield),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     public ValueTask<int> Create(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {

+ 18 - 17
src/Lua/Standard/DebugLibrary.cs

@@ -10,27 +10,28 @@ public class DebugLibrary
 
     public DebugLibrary()
     {
+        var libraryName = "debug";
         Functions =
         [
-            new("getlocal", GetLocal),
-            new("setlocal", SetLocal),
-            new("getupvalue", GetUpValue),
-            new("setupvalue", SetUpValue),
-            new("getmetatable", GetMetatable),
-            new("setmetatable", SetMetatable),
-            new("getuservalue", GetUserValue),
-            new("setuservalue", SetUserValue),
-            new("traceback", Traceback),
-            new("getregistry", GetRegistry),
-            new("upvalueid", UpValueId),
-            new("upvaluejoin", UpValueJoin),
-            new("gethook", GetHook),
-            new("sethook", SetHook),
-            new("getinfo", GetInfo),
+            new(libraryName,"getlocal", GetLocal),
+            new(libraryName,"setlocal", SetLocal),
+            new(libraryName,"getupvalue", GetUpValue),
+            new(libraryName,"setupvalue", SetUpValue),
+            new(libraryName,"getmetatable", GetMetatable),
+            new(libraryName,"setmetatable", SetMetatable),
+            new(libraryName,"getuservalue", GetUserValue),
+            new(libraryName,"setuservalue", SetUserValue),
+            new(libraryName,"traceback", Traceback),
+            new(libraryName,"getregistry", GetRegistry),
+            new(libraryName,"upvalueid", UpValueId),
+            new(libraryName,"upvaluejoin", UpValueJoin),
+            new(libraryName,"gethook", GetHook),
+            new(libraryName,"sethook", SetHook),
+            new(libraryName,"getinfo", GetInfo),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
 
     static LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset)
@@ -277,7 +278,7 @@ public class DebugLibrary
 
         if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
         {
-            LuaRuntimeException.BadArgument(context.Thread, 2, "setmetatable", [LuaValueType.Nil, LuaValueType.Table]);
+            LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.Nil, LuaValueType.Table]);
         }
 
         context.State.SetMetatable(arg0, arg1.UnsafeRead<LuaTable>());

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

@@ -124,7 +124,7 @@ public class FileHandle : ILuaUserData
         }
     });
 
-    static readonly LuaFunction FlushFunction = new("flush", async (context, cancellationToken) =>
+    static readonly LuaFunction FlushFunction = new("file.flush", async (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
 
@@ -139,7 +139,7 @@ public class FileHandle : ILuaUserData
         }
     });
 
-    static readonly LuaFunction LinesFunction = new("lines", (context, cancellationToken) =>
+    static readonly LuaFunction LinesFunction = new("file.lines", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var format = context.HasArgument(1)
@@ -152,21 +152,21 @@ public class FileHandle : ILuaUserData
             var upValues = context.GetCsClosure()!.UpValues.AsMemory();
             var file = upValues.Span[0].Read<FileHandle>();
             context.Return();
-            var resultCount = await IOHelper.ReadAsync(context.Thread, file, "lines", 0, upValues[1..], true, cancellationToken);
+            var resultCount = await IOHelper.ReadAsync(context.Thread, file, "file.lines", 0, upValues[1..], true, cancellationToken);
             return resultCount;
         })));
     });
 
-    static readonly LuaFunction ReadFunction = new("read", async (context, cancellationToken) =>
+    static readonly LuaFunction ReadFunction = new("file.read", async (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var args = context.Arguments[1..].ToArray();
         context.Return();
-        var resultCount = await IOHelper.ReadAsync(context.Thread, file, "read", 1, args, false, cancellationToken);
+        var resultCount = await IOHelper.ReadAsync(context.Thread, file, "file.read", 1, args, false, cancellationToken);
         return resultCount;
     });
 
-    static readonly LuaFunction SeekFunction = new("seek", (context, cancellationToken) =>
+    static readonly LuaFunction SeekFunction = new("file.seek", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var whence = context.HasArgument(1)
@@ -178,7 +178,7 @@ public class FileHandle : ILuaUserData
 
         if (whence is not ("set" or "cur" or "end"))
         {
-            throw new LuaRuntimeException(context.Thread, $"bad argument #2 to 'seek' (invalid option '{whence}')");
+            throw new LuaRuntimeException(context.Thread, $"bad argument #2 to 'file.seek' (invalid option '{whence}')");
         }
 
         try
@@ -191,7 +191,7 @@ public class FileHandle : ILuaUserData
         }
     });
 
-    static readonly LuaFunction SetVBufFunction = new("setvbuf", (context, cancellationToken) =>
+    static readonly LuaFunction SetVBufFunction = new("file.setvbuf", (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
         var mode = context.GetArgument<string>(1);
@@ -204,10 +204,10 @@ public class FileHandle : ILuaUserData
         return new(context.Return(true));
     });
 
-    static readonly LuaFunction WriteFunction = new("write", async (context, cancellationToken) =>
+    static readonly LuaFunction WriteFunction = new("file.write", async (context, cancellationToken) =>
     {
         var file = context.GetArgument<FileHandle>(0);
-        var resultCount = await IOHelper.WriteAsync(file, "write", context with { ArgumentCount = context.ArgumentCount - 1 }, cancellationToken);
+        var resultCount = await IOHelper.WriteAsync(file, "io.write", context with { ArgumentCount = context.ArgumentCount - 1 }, cancellationToken);
         return resultCount;
     });
 }

+ 16 - 15
src/Lua/Standard/IOLibrary.cs

@@ -10,22 +10,23 @@ public sealed class IOLibrary
 
     public IOLibrary()
     {
+        var libraryName = "io";
         Functions =
         [
-            new("close", Close),
-            new("flush", Flush),
-            new("input", Input),
-            new("lines", Lines),
-            new("open", Open),
-            new("output", Output),
-            new("read", Read),
-            new("type", Type),
-            new("write", Write),
-            new("tmpfile", TmpFile),
+            new(libraryName,"close", Close),
+            new(libraryName,"flush", Flush),
+            new(libraryName,"input", Input),
+            new(libraryName,"lines", Lines),
+            new(libraryName,"open", Open),
+            new(libraryName,"output", Output),
+            new(libraryName,"read", Read),
+            new(libraryName,"type", Type),
+            new(libraryName,"write", Write),
+            new(libraryName,"tmpfile", TmpFile),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     public ValueTask<int> Close(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
@@ -92,7 +93,7 @@ public sealed class IOLibrary
             {
                 var file = context.GetCsClosure()!.UpValues[0].Read<FileHandle>();
                 context.Return();
-                var resultCount = await IOHelper.ReadAsync(context.Thread, file, "lines", 0, Memory<LuaValue>.Empty, true, cancellationToken);
+                var resultCount = await IOHelper.ReadAsync(context.Thread, file, "io.lines", 0, Memory<LuaValue>.Empty, true, cancellationToken);
                 if (resultCount > 0 && context.Thread.Stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 {
                     file.Close();
@@ -121,7 +122,7 @@ public sealed class IOLibrary
                 var formats = upValues.AsMemory(1);
                 var stack = context.Thread.Stack;
                 context.Return();
-                var resultCount = await IOHelper.ReadAsync(context.Thread, file, "lines", 0, formats, true, cancellationToken);
+                var resultCount = await IOHelper.ReadAsync(context.Thread, file, "io.lines", 0, formats, true, cancellationToken);
                 if (resultCount > 0 && stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil)
                 {
                     file.Close();
@@ -180,7 +181,7 @@ public sealed class IOLibrary
         var args = context.Arguments.ToArray();
         context.Return();
 
-        var resultCount = await IOHelper.ReadAsync(context.Thread, file, "read", 0, args, false, cancellationToken);
+        var resultCount = await IOHelper.ReadAsync(context.Thread, file, "io.read", 0, args, false, cancellationToken);
         return resultCount;
     }
 
@@ -201,7 +202,7 @@ public sealed class IOLibrary
     public async ValueTask<int> Write(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
         var file = context.State.Registry["_IO_output"].Read<FileHandle>();
-        var resultCount = await IOHelper.WriteAsync(file, "write", context, cancellationToken);
+        var resultCount = await IOHelper.WriteAsync(file, "io.write", context, cancellationToken);
         return resultCount;
     }
 

+ 8 - 0
src/Lua/Standard/LibraryFunction.cs

@@ -0,0 +1,8 @@
+namespace Lua.Standard;
+
+public readonly record struct LibraryFunction(string Name, LuaFunction Func)
+{
+    public LibraryFunction(string libraryName, string name, Func<LuaFunctionExecutionContext, CancellationToken, ValueTask<int>> function) : this(name, new LuaFunction(libraryName + "." + name, function))
+    {
+    }
+}

+ 29 - 28
src/Lua/Standard/MathematicsLibrary.cs

@@ -7,39 +7,40 @@ public sealed class MathematicsLibrary
 
     public MathematicsLibrary()
     {
+        var libraryName = "math";
         Functions =
         [
-            new("abs", Abs),
-            new("acos", Acos),
-            new("asin", Asin),
-            new("atan2", Atan2),
-            new("atan", Atan),
-            new("ceil", Ceil),
-            new("cos", Cos),
-            new("cosh", Cosh),
-            new("deg", Deg),
-            new("exp", Exp),
-            new("floor", Floor),
-            new("fmod", Fmod),
-            new("frexp", Frexp),
-            new("ldexp", Ldexp),
-            new("log", Log),
-            new("max", Max),
-            new("min", Min),
-            new("modf", Modf),
-            new("pow", Pow),
-            new("rad", Rad),
-            new("random", Random),
-            new("randomseed", RandomSeed),
-            new("sin", Sin),
-            new("sinh", Sinh),
-            new("sqrt", Sqrt),
-            new("tan", Tan),
-            new("tanh", Tanh),
+            new(libraryName, "abs", Abs),
+            new(libraryName, "acos", Acos),
+            new(libraryName, "asin", Asin),
+            new(libraryName, "atan2", Atan2),
+            new(libraryName, "atan", Atan),
+            new(libraryName, "ceil", Ceil),
+            new(libraryName, "cos", Cos),
+            new(libraryName, "cosh", Cosh),
+            new(libraryName, "deg", Deg),
+            new(libraryName, "exp", Exp),
+            new(libraryName, "floor", Floor),
+            new(libraryName, "fmod", Fmod),
+            new(libraryName, "frexp", Frexp),
+            new(libraryName, "ldexp", Ldexp),
+            new(libraryName, "log", Log),
+            new(libraryName, "max", Max),
+            new(libraryName, "min", Min),
+            new(libraryName, "modf", Modf),
+            new(libraryName, "pow", Pow),
+            new(libraryName, "rad", Rad),
+            new(libraryName, "random", Random),
+            new(libraryName, "randomseed", RandomSeed),
+            new(libraryName, "sin", Sin),
+            new(libraryName, "sinh", Sinh),
+            new(libraryName, "sqrt", Sqrt),
+            new(libraryName, "tan", Tan),
+            new(libraryName, "tanh", Tanh),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     public sealed class RandomUserData(Random random) : ILuaUserData
     {

+ 1 - 1
src/Lua/Standard/ModuleLibrary.cs

@@ -11,7 +11,7 @@ public sealed class ModuleLibrary
     public ModuleLibrary()
     {
         RequireFunction = new("require", Require);
-        SearchPathFunction = new("searchpath", SearchPath);
+        SearchPathFunction = new("package.searchpath", SearchPath);
     }
 
     public readonly LuaFunction RequireFunction;

+ 8 - 8
src/Lua/Standard/OpenLibsExtensions.cs

@@ -21,7 +21,7 @@ public static class OpenLibsExtensions
         var bit32 = new LuaTable(0, BitwiseLibrary.Instance.Functions.Length);
         foreach (var func in BitwiseLibrary.Instance.Functions)
         {
-            bit32[func.Name] = func;
+            bit32[func.Name] = func.Func;
         }
 
         state.Environment["bit32"] = bit32;
@@ -33,7 +33,7 @@ public static class OpenLibsExtensions
         var coroutine = new LuaTable(0, CoroutineLibrary.Instance.Functions.Length);
         foreach (var func in CoroutineLibrary.Instance.Functions)
         {
-            coroutine[func.Name] = func;
+            coroutine[func.Name] = func.Func;
         }
 
         state.Environment["coroutine"] = coroutine;
@@ -44,7 +44,7 @@ public static class OpenLibsExtensions
         var io = new LuaTable(0, IOLibrary.Instance.Functions.Length);
         foreach (var func in IOLibrary.Instance.Functions)
         {
-            io[func.Name] = func;
+            io[func.Name] = func.Func;
         }
 
         var registry = state.Registry;
@@ -68,7 +68,7 @@ public static class OpenLibsExtensions
         var math = new LuaTable(0, MathematicsLibrary.Instance.Functions.Length);
         foreach (var func in MathematicsLibrary.Instance.Functions)
         {
-            math[func.Name] = func;
+            math[func.Name] = func.Func;
         }
 
         math["pi"] = Math.PI;
@@ -100,7 +100,7 @@ public static class OpenLibsExtensions
         var os = new LuaTable(0, OperatingSystemLibrary.Instance.Functions.Length);
         foreach (var func in OperatingSystemLibrary.Instance.Functions)
         {
-            os[func.Name] = func;
+            os[func.Name] = func.Func;
         }
 
         state.Environment["os"] = os;
@@ -112,7 +112,7 @@ public static class OpenLibsExtensions
         var @string = new LuaTable(0, StringLibrary.Instance.Functions.Length);
         foreach (var func in StringLibrary.Instance.Functions)
         {
-            @string[func.Name] = func;
+            @string[func.Name] = func.Func;
         }
 
         state.Environment["string"] = @string;
@@ -139,7 +139,7 @@ public static class OpenLibsExtensions
         var table = new LuaTable(0, TableLibrary.Instance.Functions.Length);
         foreach (var func in TableLibrary.Instance.Functions)
         {
-            table[func.Name] = func;
+            table[func.Name] = func.Func;
         }
 
         state.Environment["table"] = table;
@@ -151,7 +151,7 @@ public static class OpenLibsExtensions
         var debug = new LuaTable(0, DebugLibrary.Instance.Functions.Length);
         foreach (var func in DebugLibrary.Instance.Functions)
         {
-            debug[func.Name] = func;
+            debug[func.Name] = func.Func;
         }
 
         state.Environment["debug"] = debug;

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

@@ -11,21 +11,21 @@ public sealed class OperatingSystemLibrary
     {
         Functions =
         [
-            new("clock", Clock),
-            new("date", Date),
-            new("difftime", DiffTime),
-            new("execute", Execute),
-            new("exit", Exit),
-            new("getenv", GetEnv),
-            new("remove", Remove),
-            new("rename", Rename),
-            new("setlocale", SetLocale),
-            new("time", Time),
-            new("tmpname", TmpName),
+            new("os","clock", Clock),
+            new("os","date", Date),
+            new("os","difftime", DiffTime),
+            new("os","execute", Execute),
+            new("os","exit", Exit),
+            new("os","getenv", GetEnv),
+            new("os","remove", Remove),
+            new("os","rename", Rename),
+            new("os","setlocale", SetLocale),
+            new("os","time", Time),
+            new("os","tmpname", TmpName),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     public ValueTask<int> Clock(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
@@ -121,7 +121,7 @@ public sealed class OperatingSystemLibrary
             }
             else
             {
-                LuaRuntimeException.BadArgument(context.Thread, 1, "exit", LuaValueType.Nil, code.Type);
+                LuaRuntimeException.BadArgument(context.Thread, 1, LuaValueType.Nil, code.Type);
             }
         }
         else

+ 26 - 25
src/Lua/Standard/StringLibrary.cs

@@ -12,25 +12,26 @@ public sealed class StringLibrary
 
     public StringLibrary()
     {
+        var libraryName = "string";
         Functions =
         [
-            new("byte", Byte),
-            new("char", Char),
-            new("dump", Dump),
-            new("find", Find),
-            new("format", Format),
-            new("gmatch", GMatch),
-            new("gsub", GSub),
-            new("len", Len),
-            new("lower", Lower),
-            new("rep", Rep),
-            new("reverse", Reverse),
-            new("sub", Sub),
-            new("upper", Upper),
+            new(libraryName,"byte", Byte),
+            new(libraryName,"char", Char),
+            new(libraryName,"dump", Dump),
+            new(libraryName,"find", Find),
+            new(libraryName,"format", Format),
+            new(libraryName,"gmatch", GMatch),
+            new(libraryName,"gsub", GSub),
+            new(libraryName,"len", Len),
+            new(libraryName,"lower", Lower),
+            new(libraryName,"rep", Rep),
+            new(libraryName,"reverse", Reverse),
+            new(libraryName,"sub", Sub),
+            new(libraryName,"upper", Upper),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     public ValueTask<int> Byte(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
     {
@@ -42,8 +43,8 @@ public sealed class StringLibrary
             ? context.GetArgument<double>(2)
             : i;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "byte", 2, i);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "byte", 3, j);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, i);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, j);
 
         var span = StringHelper.Slice(s, (int)i, (int)j);
         var buffer = context.GetReturnBuffer(span.Length);
@@ -66,7 +67,7 @@ public sealed class StringLibrary
         for (int i = 0; i < context.ArgumentCount; i++)
         {
             var arg = context.GetArgument<double>(i);
-            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "char", i + 1, arg);
+            LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, i + 1, arg);
             builder.Append((char)arg);
         }
 
@@ -88,7 +89,7 @@ public sealed class StringLibrary
             : 1;
         var plain = context.HasArgument(3) && context.GetArgument(3).ToBoolean();
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "find", 3, init);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, init);
 
         // init can be negative value
         if (init < 0)
@@ -243,7 +244,7 @@ public sealed class StringLibrary
                     case 'G':
                         if (!parameter.TryRead<double>(out var f))
                         {
-                            LuaRuntimeException.BadArgument(context.Thread, parameterIndex + 1, "format", LuaValueType.Number, parameter.Type);
+                            LuaRuntimeException.BadArgument(context.Thread, parameterIndex + 1, LuaValueType.Number, parameter.Type);
                         }
 
                         switch (specifier)
@@ -324,10 +325,10 @@ public sealed class StringLibrary
                     case 'X':
                         if (!parameter.TryRead<double>(out var x))
                         {
-                            LuaRuntimeException.BadArgument(context.Thread, parameterIndex + 1, "format", LuaValueType.Number, parameter.Type);
+                            LuaRuntimeException.BadArgument(context.Thread, parameterIndex + 1, LuaValueType.Number, parameter.Type);
                         }
 
-                        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "format", parameterIndex + 1, x);
+                        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, parameterIndex + 1, x);
 
                         switch (specifier)
                         {
@@ -469,7 +470,7 @@ public sealed class StringLibrary
             ? context.GetArgument<double>(3)
             : int.MaxValue;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "gsub", 4, n_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 4, n_arg);
 
         var n = (int)n_arg;
         var regex = StringHelper.ToRegex(pattern);
@@ -568,7 +569,7 @@ public sealed class StringLibrary
             ? context.GetArgument<string>(2)
             : null;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "rep", 2, n_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, n_arg);
 
         var n = (int)n_arg;
 
@@ -603,8 +604,8 @@ public sealed class StringLibrary
             ? context.GetArgument<double>(2)
             : -1;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "sub", 2, i);
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "sub", 3, j);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, i);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 3, j);
 
         return new(context.Return(StringHelper.Slice(s, (int)i, (int)j).ToString()));
     }

+ 11 - 10
src/Lua/Standard/TableLibrary.cs

@@ -13,18 +13,19 @@ public sealed class TableLibrary
 
     public TableLibrary()
     {
+        var libraryName = "table";
         Functions =
         [
-            new("concat", Concat),
-            new("insert", Insert),
-            new("pack", Pack),
-            new("remove", Remove),
-            new("sort", Sort),
-            new("unpack", Unpack),
+            new(libraryName, "concat", Concat),
+            new(libraryName, "insert", Insert),
+            new(libraryName, "pack", Pack),
+            new(libraryName, "remove", Remove),
+            new(libraryName, "sort", Sort),
+            new(libraryName, "unpack", Unpack),
         ];
     }
 
-    public readonly LuaFunction[] Functions;
+    public readonly LibraryFunction[] Functions;
 
     // TODO: optimize
     private static readonly Prototype defaultComparer = new Prototype(
@@ -89,7 +90,7 @@ public sealed class TableLibrary
             ? context.GetArgument<double>(1)
             : table.ArrayLength + 1;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "insert", 2, pos_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, pos_arg);
 
         var pos = (int)pos_arg;
 
@@ -124,7 +125,7 @@ public sealed class TableLibrary
             ? context.GetArgument<double>(1)
             : table.ArrayLength;
 
-        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, "remove", 2, n_arg);
+        LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.Thread, 2, n_arg);
 
         var n = (int)n_arg;
 
@@ -189,7 +190,7 @@ public sealed class TableLibrary
             var top = stack.Count;
             stack.Push(memory.Span[j]);
             stack.Push(pivot);
-            await context.Access.RunAsync(comparer, 2,cancellationToken);
+            await context.Access.RunAsync(comparer, 2, cancellationToken);
 
             if (context.Thread.Stack.Get(top).ToBoolean())
             {

+ 3 - 3
tests/Lua.Tests/tests-lua/db.lua

@@ -477,8 +477,8 @@ assert(debug.traceback(print) == print)
 assert(debug.traceback(print, 4) == print)
 assert(string.find(debug.traceback("hi", 4), "^hi\n"))
 assert(string.find(debug.traceback("hi"), "^hi\n"))
-assert(not string.find(debug.traceback("hi"), "'traceback'"))
-assert(string.find(debug.traceback("hi", 0), "'traceback'"))
+assert(not string.find(debug.traceback("hi"), "'debug.traceback'"))
+assert(string.find(debug.traceback("hi", 0), "'debug.traceback'"))
 assert(string.find(debug.traceback(), "^stack traceback:\n"))
 
 
@@ -572,7 +572,7 @@ function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end
 
 co = coroutine.create(function (x) f(x) end)
 a, b = coroutine.resume(co, 3)
-t = {"'yield'", "'f'", "in function <"}
+t = {"'coroutine.yield'", "'f'", "in function <"}
 while coroutine.status(co) == "suspended" do
   checktraceback(co, t)
   a, b = coroutine.resume(co)