From 3501dacddd1775ab20833ff643e16debe5dc31ce Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:45:36 +0900 Subject: [PATCH 01/22] Add: debug library, but not full --- sandbox/ConsoleApp1/Program.cs | 26 +- .../CodeAnalysis/Compilation/Descriptions.cs | 1 + .../Compilation/FunctionCompilationContext.cs | 18 +- .../CodeAnalysis/Compilation/LuaCompiler.cs | 47 ++- .../Compilation/ScopeCompilationContext.cs | 9 + src/Lua/Exceptions.cs | 5 + src/Lua/LuaFunctionExecutionContext.cs | 44 +++ src/Lua/LuaState.cs | 2 + src/Lua/Runtime/Chunk.cs | 1 + src/Lua/Runtime/LocalValueInfo.cs | 9 + src/Lua/Runtime/Tracebacks.cs | 21 +- src/Lua/Standard/DebugLibrary.cs | 286 ++++++++++++++++++ src/Lua/Standard/OpenLibsExtensions.cs | 13 + src/Lua/Standard/TableLibrary.cs | 1 + tests/Lua.Tests/LuaTests.cs | 6 + tests/Lua.Tests/tests-lua/db-mini.lua | 66 ++++ 16 files changed, 544 insertions(+), 11 deletions(-) create mode 100644 src/Lua/Runtime/LocalValueInfo.cs create mode 100644 src/Lua/Standard/DebugLibrary.cs create mode 100644 tests/Lua.Tests/tests-lua/db-mini.lua diff --git a/sandbox/ConsoleApp1/Program.cs b/sandbox/ConsoleApp1/Program.cs index 1e986c96..10905080 100644 --- a/sandbox/ConsoleApp1/Program.cs +++ b/sandbox/ConsoleApp1/Program.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Lua.CodeAnalysis.Syntax; using Lua.CodeAnalysis.Compilation; using Lua.Runtime; @@ -11,7 +12,7 @@ try { - var source = File.ReadAllText("test.lua"); + var source = File.ReadAllText(GetAbsolutePath("test.lua")); var syntaxTree = LuaSyntaxTree.Parse(source, "test.lua"); @@ -41,6 +42,15 @@ catch (Exception ex) { Console.WriteLine(ex); + if(ex is LuaRuntimeException { InnerException: not null } luaEx) + { + Console.WriteLine(luaEx.InnerException); + } +} + +static string GetAbsolutePath(string relativePath, [CallerFilePath] string callerFilePath = "") +{ + return Path.Combine(Path.GetDirectoryName(callerFilePath)!, relativePath); } static void DebugChunk(Chunk chunk, int id) @@ -56,14 +66,24 @@ static void DebugChunk(Chunk chunk, int id) index++; } - Console.WriteLine("Constants " + new string('-', 50)); index = 0; + Console.WriteLine("Locals " + new string('-', 50)); + index = 0; + foreach (var local in chunk.Locals.ToArray()) + { + Console.WriteLine($"[{index}]\t{local.Index}\t{local.Name}\t{local.StartPc}\t{local.EndPc}"); + index++; + } + + Console.WriteLine("Constants " + new string('-', 50)); + index = 0; foreach (var constant in chunk.Constants.ToArray()) { Console.WriteLine($"[{index}]\t{constant}"); index++; } - Console.WriteLine("UpValues " + new string('-', 50)); index = 0; + Console.WriteLine("UpValues " + new string('-', 50)); + index = 0; foreach (var upValue in chunk.UpValues.ToArray()) { Console.WriteLine($"[{index}]\t{upValue.Name}\t{(upValue.IsInRegister ? 1 : 0)}\t{upValue.Index}"); diff --git a/src/Lua/CodeAnalysis/Compilation/Descriptions.cs b/src/Lua/CodeAnalysis/Compilation/Descriptions.cs index 5001cbf4..e3313e45 100644 --- a/src/Lua/CodeAnalysis/Compilation/Descriptions.cs +++ b/src/Lua/CodeAnalysis/Compilation/Descriptions.cs @@ -6,6 +6,7 @@ namespace Lua.CodeAnalysis.Compilation public readonly record struct LocalVariableDescription { public required byte RegisterIndex { get; init; } + public required int StartPc { get; init; } } public readonly record struct FunctionDescription diff --git a/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs b/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs index e564f660..63aa35c0 100644 --- a/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs +++ b/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs @@ -60,6 +60,7 @@ internal static FunctionCompilationContext Create(ScopeCompilationContext? paren // upvalues FastListCore upvalues; + FastListCore localVariables; // loop FastListCore breakQueue; @@ -301,6 +302,17 @@ public bool TryGetFunctionProto(ReadOnlyMemory name, [NotNullWhen(true)] o return false; } } + + public void AddLocalVariable(ReadOnlyMemory name, LocalVariableDescription description) + { + localVariables.Add(new LocalValueInfo() + { + Name = name, + Index = description.RegisterIndex, + StartPc = description.StartPc, + EndPc = Instructions.Length, + }); + } public void AddUpValue(UpValueInfo upValue) { @@ -410,7 +422,9 @@ public Chunk ToChunk() // add return instructions.Add(Instruction.Return(0, 1)); instructionPositions.Add(instructionPositions.Length == 0 ? default : instructionPositions[^1]); - + Scope.RegisterLocalsToFunction(); + var locals = localVariables.AsSpan().ToArray(); + Array.Sort(locals, (x, y) => x.Index.CompareTo(y.Index)); var chunk = new Chunk() { Name = ChunkName ?? "chunk", @@ -418,6 +432,7 @@ public Chunk ToChunk() SourcePositions = instructionPositions.AsSpan().ToArray(), Constants = constants.AsSpan().ToArray(), UpValues = upvalues.AsSpan().ToArray(), + Locals = locals, Functions = functions.AsSpan().ToArray(), ParameterCount = ParameterCount, MaxStackPosition = MaxStackPosition, @@ -442,6 +457,7 @@ public void Reset() constantIndexMap.Clear(); constants.Clear(); upvalues.Clear(); + localVariables.Clear(); functionMap.Clear(); functions.Clear(); breakQueue.Clear(); diff --git a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs index 3cd17910..c75e0e6d 100644 --- a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs +++ b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs @@ -466,9 +466,11 @@ public bool VisitLocalAssignmentStatementNode(LocalAssignmentStatementNode node, context.AddLocalVariable(identifier.Name, new() { RegisterIndex = (byte)(context.StackPosition - 1), + StartPc = context.Function.Instructions.Length, }); } } + return true; } @@ -598,6 +600,7 @@ public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationS context.AddLocalVariable(node.Name, new() { RegisterIndex = context.StackPosition, + StartPc = context.Function.Instructions.Length, }); // compile function @@ -678,6 +681,7 @@ int CompileFunctionProto(ReadOnlyMemory functionName, ScopeCompilationCont funcContext.Scope.AddLocalVariable("self".AsMemory(), new() { RegisterIndex = 0, + StartPc = 0, }); funcContext.Scope.StackPosition++; @@ -690,6 +694,7 @@ int CompileFunctionProto(ReadOnlyMemory functionName, ScopeCompilationCont funcContext.Scope.AddLocalVariable(parameter.Name, new() { RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)), + StartPc = 0, }); funcContext.Scope.StackPosition++; @@ -905,10 +910,29 @@ public bool VisitNumericForStatementNode(NumericForStatementNode node, ScopeComp context.Function.LoopLevel++; using var scopeContext = context.CreateChildScope(); { + scopeContext.AddLocalVariable("(for index)".AsMemory(), new() + { + RegisterIndex = startPosition, + StartPc = context.Function.Instructions.Length, + }); + + scopeContext.AddLocalVariable("(for limit)".AsMemory(), new() + { + RegisterIndex = (byte)(startPosition + 1), + StartPc = context.Function.Instructions.Length, + }); + + scopeContext.AddLocalVariable("(for step)".AsMemory(), new() + { + RegisterIndex = (byte)(startPosition + 2), + StartPc = context.Function.Instructions.Length, + }); + // add local variable scopeContext.AddLocalVariable(node.VariableName, new() { - RegisterIndex = startPosition + RegisterIndex = (byte)(startPosition + 3), + StartPc = context.Function.Instructions.Length, }); foreach (var childNode in node.StatementNodes) @@ -949,13 +973,32 @@ public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeComp { scopeContext.StackPosition = (byte)(startPosition + 3 + node.Names.Length); + scopeContext.AddLocalVariable("(for generator)".AsMemory(), new() + { + RegisterIndex = (byte)(startPosition), + StartPc = context.Function.Instructions.Length, + }); + + scopeContext.AddLocalVariable("(for state)".AsMemory(), new() + { + RegisterIndex = (byte)(startPosition + 1), + StartPc = context.Function.Instructions.Length, + }); + + scopeContext.AddLocalVariable("(for control)".AsMemory(), new() + { + RegisterIndex = (byte)(startPosition + 2), + StartPc = context.Function.Instructions.Length, + }); + // add local variables for (int i = 0; i < node.Names.Length; i++) { var name = node.Names[i]; scopeContext.AddLocalVariable(name.Name, new() { - RegisterIndex = (byte)(startPosition + 3 + i) + RegisterIndex = (byte)(startPosition + 3 + i), + StartPc = context.Function.Instructions.Length, }); } diff --git a/src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs b/src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs index 51c67984..3be2b072 100644 --- a/src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs +++ b/src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs @@ -153,6 +153,14 @@ public bool TryGetLabel(ReadOnlyMemory name, out LabelDescription descript return false; } + + public void RegisterLocalsToFunction() + { + foreach (var localVariable in localVariables) + { + Function.AddLocalVariable(localVariable.Key, localVariable.Value); + } + } /// /// Resets the values ​​held in the context. @@ -173,6 +181,7 @@ public void Reset() /// public void Dispose() { + RegisterLocalsToFunction(); Function = null!; Pool.Return(this); } diff --git a/src/Lua/Exceptions.cs b/src/Lua/Exceptions.cs index 8774ff51..0301c18e 100644 --- a/src/Lua/Exceptions.cs +++ b/src/Lua/Exceptions.cs @@ -100,6 +100,11 @@ public static void BadArgument(Traceback traceback, int argumentId, string funct throw new LuaRuntimeException(traceback, $"bad argument #{argumentId} to '{functionName}' ({expected} expected, got {actual})"); } + public static void BadArgument(Traceback traceback, int argumentId, string functionName, string message) + { + throw new LuaRuntimeException(traceback, $"bad argument #{argumentId} to '{functionName}' ({message})"); + } + public static void BadArgumentNumberIsNotInteger(Traceback traceback, int argumentId, string functionName) { throw new LuaRuntimeException(traceback, $"bad argument #{argumentId} to '{functionName}' (number has no integer representation)"); diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index d9a483ad..221bc43b 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -37,6 +37,16 @@ public LuaValue GetArgument(int index) ThrowIfArgumentNotExists(index); return Arguments[index]; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal LuaValue GetArgumentOrDefault(int index, LuaValue defaultValue = default) + { + if (ArgumentCount <= index) + { + return defaultValue; + } + return Arguments[index]; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public T GetArgument(int index) @@ -64,6 +74,40 @@ public T GetArgument(int index) return argValue; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal T GetArgumentOrDefault(int index,T defaultValue =default!) + { + if (ArgumentCount <= index) + { + return defaultValue; + } + + var arg = Arguments[index]; + if (!arg.TryRead(out var argValue)) + { + var t = typeof(T); + if ((t == typeof(int) || t == typeof(long)) && arg.TryReadNumber(out _)) + { + LuaRuntimeException.BadArgumentNumberIsNotInteger(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name); + } + else if (LuaValue.TryGetLuaValueType(t, out var type)) + { + LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString()); + } + else + { + LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.Type.ToString()); + } + } + + return argValue; + } + + internal void ThrowBadArgument(int index, string message) + { + LuaRuntimeException.BadArgument(State.GetTraceback(), index, Thread.GetCurrentFrame().Function.Name, message); + } + void ThrowIfArgumentNotExists(int index) { if (ArgumentCount <= index) diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index e11a22d8..73663f34 100644 --- a/src/Lua/LuaState.cs +++ b/src/Lua/LuaState.cs @@ -16,6 +16,7 @@ public sealed class LuaState FastStackCore threadStack; readonly LuaTable packages = new(); readonly LuaTable environment; + readonly LuaTable registry = new(); readonly UpValue envUpValue; bool isRunning; @@ -24,6 +25,7 @@ public sealed class LuaState internal ref FastListCore OpenUpValues => ref openUpValues; public LuaTable Environment => environment; + public LuaTable Registry => registry; public LuaTable LoadedModules => packages; public LuaMainThread MainThread => mainThread; public LuaThread CurrentThread diff --git a/src/Lua/Runtime/Chunk.cs b/src/Lua/Runtime/Chunk.cs index cfd8c53b..2d83bcf0 100644 --- a/src/Lua/Runtime/Chunk.cs +++ b/src/Lua/Runtime/Chunk.cs @@ -11,6 +11,7 @@ public sealed class Chunk public required SourcePosition[] SourcePositions { get; init; } public required LuaValue[] Constants { get; init; } public required UpValueInfo[] UpValues { get; init; } + public required LocalValueInfo[] Locals { get; init; } public required Chunk[] Functions { get; init; } public required int ParameterCount { get; init; } diff --git a/src/Lua/Runtime/LocalValueInfo.cs b/src/Lua/Runtime/LocalValueInfo.cs new file mode 100644 index 00000000..a138e6ed --- /dev/null +++ b/src/Lua/Runtime/LocalValueInfo.cs @@ -0,0 +1,9 @@ +namespace Lua.Runtime; + +public readonly record struct LocalValueInfo +{ + public required ReadOnlyMemory Name { get; init; } + public required byte Index { get; init; } + public required int StartPc { get; init; } + public required int EndPc { get; init; } +} \ No newline at end of file diff --git a/src/Lua/Runtime/Tracebacks.cs b/src/Lua/Runtime/Tracebacks.cs index 2a97ea19..6eb14277 100644 --- a/src/Lua/Runtime/Tracebacks.cs +++ b/src/Lua/Runtime/Tracebacks.cs @@ -10,7 +10,7 @@ public class Traceback public required Closure RootFunc { get; init; } public required CallStackFrame[] StackFrames { get; init; } - internal string RootChunkName => RootFunc.Proto.Name; //StackFrames.Length == 0 ? "" : StackFrames[^1].Function is Closure closure ? closure.Proto.GetRoot().Name : StackFrames[^2].Function.Name; + internal string RootChunkName => RootFunc.Proto.Name; internal SourcePosition LastPosition { @@ -34,15 +34,25 @@ internal SourcePosition LastPosition public override string ToString() + { + return GetTracebackString(RootFunc, StackFrames, LuaValue.Nil); + } + + internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message) { using var list = new PooledList(64); + if (message.Type is not LuaValueType.Nil) + { + list.AddRange(message.ToString()); + list.AddRange("\n"); + } + list.AddRange("stack traceback:\n"); - var stackFrames = StackFrames.AsSpan(); var intFormatBuffer = (stackalloc char[15]); + for (var index = stackFrames.Length - 1; index >= 0; index--) { - LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : RootFunc; - var frame = stackFrames[index]; + LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : rootFunc; if (lastFunc is not null and not Closure) { list.AddRange("\t[C#]: in function '"); @@ -51,12 +61,13 @@ public override string ToString() } else if (lastFunc is Closure closure) { + var frame = stackFrames[index]; var p = closure.Proto; var root = p.GetRoot(); list.AddRange("\t"); list.AddRange(root.Name); list.AddRange(":"); - p.SourcePositions[frame.CallerInstructionIndex].Line.TryFormat(intFormatBuffer, out var charsWritten,provider:CultureInfo.InvariantCulture); + p.SourcePositions[frame.CallerInstructionIndex].Line.TryFormat(intFormatBuffer, out var charsWritten, provider: CultureInfo.InvariantCulture); list.AddRange(intFormatBuffer[..charsWritten]); list.AddRange(root == p ? ": in '" : ": in function '"); list.AddRange(p.Name); diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs new file mode 100644 index 00000000..23399eae --- /dev/null +++ b/src/Lua/Standard/DebugLibrary.cs @@ -0,0 +1,286 @@ +using System.Runtime.CompilerServices; +using Lua.Runtime; + +namespace Lua.Standard; + +public class DebugLibrary +{ + public static readonly DebugLibrary Instance = new(); + + public DebugLibrary() + { + Functions = + [ + new("getlocal", GetLocal), + new("setlocal", SetLocal), + new("getupvalue", GetUpValue), + new("setupvalue", SetUpValue), + new("getmetatable", GetMetatable), + new("setmetatable", SetMetatable), + new("traceback", Traceback), + new("getregistry", GetRegistry) + ]; + } + + public readonly LuaFunction[] Functions; + + + LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset) + { + if (context.ArgumentCount < 1) + { + argOffset = 0; + return context.Thread; + } + + if (context.GetArgument(0).TryRead(out var thread)) + { + argOffset = 1; + return thread; + } + + argOffset = 0; + return context.Thread; + } + + ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name) + { + if (index == 0) + { + name = null; + return ref Unsafe.NullRef(); + } + + var callStack = thread.GetCallStackFrames(); + var frame = callStack[^(level + 1)]; + if (index < 0) + { + index = -index - 1; + var frameVariableArgumentCount = frame.VariableArgumentCount; + if (frameVariableArgumentCount > 0 && index < frameVariableArgumentCount) + { + name = "(vararg)"; + return ref thread.Stack.Get(frame.Base - frameVariableArgumentCount + index); + } + + name = null; + return ref Unsafe.NullRef(); + } + + index -= 1; + + + var frameBase = frame.Base; + var nextFrameBase = level != 0 ? callStack[^level].Base : thread.Stack.Count; + if (nextFrameBase - frameBase <= index) + { + name = null; + return ref Unsafe.NullRef(); + } + + if (frame.Function is Closure closure) + { + var locals = closure.Proto.Locals; + var currentPc = callStack[^level].CallerInstructionIndex; + foreach (var local in locals) + { + if (local.Index == index && currentPc >= local.StartPc && currentPc < local.EndPc) + { + name = local.Name.ToString(); + return ref thread.Stack.Get(frameBase + local.Index); + } + + if (local.Index >= index) + { + break; + } + } + } + + name = "(*temporary)"; + return ref thread.Stack.Get(frameBase + index); + } + + public ValueTask GetLocal(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + static LuaValue GetParam(LuaFunction function, int index) + { + if (function is Closure closure) + { + var paramCount = closure.Proto.ParameterCount; + if (0 <= index && index < paramCount) + { + return closure.Proto.Locals[index].Name.ToString(); + } + } + + return LuaValue.Nil; + } + + var thread = GetLuaThread(context, out var argOffset); + + var index = context.GetArgument(argOffset + 1); + if (context.GetArgument(argOffset).TryReadFunction(out var f)) + { + buffer.Span[0] = GetParam(f, index - 1); + return new(1); + } + + var level = context.GetArgument(argOffset); + + + if (level < 0 || level >= thread.GetCallStackFrames().Length) + { + context.ThrowBadArgument(1, "level out of range"); + } + + ref var local = ref FindLocal(thread, level, index, out var name); + if (name is null) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + buffer.Span[0] = name; + buffer.Span[1] = local; + return new(2); + } + + public ValueTask SetLocal(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var thread = GetLuaThread(context, out var argOffset); + + var value = context.GetArgument(argOffset + 2); + var index = context.GetArgument(argOffset + 1); + var level = context.GetArgument(argOffset); + + + if (level < 0 || level >= thread.GetCallStackFrames().Length) + { + context.ThrowBadArgument(1, "level out of range"); + } + + ref var local = ref FindLocal(thread, level, index, out var name); + if (name is null) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + buffer.Span[0] = name; + local = value; + return new(1); + } + + public ValueTask GetUpValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var func = context.GetArgument(0); + var index = context.GetArgument(1) - 1; + if (func is not Closure closure) + { + return new(0); + } + + var upValues = closure.UpValues; + var descriptions = closure.Proto.UpValues; + if (index < 0 || index >= descriptions.Length) + { + return new(0); + } + + var description = descriptions[index]; + buffer.Span[0] = description.Name.ToString(); + buffer.Span[1] = upValues[index].GetValue(); + return new(2); + } + + public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var func = context.GetArgument(0); + var index = context.GetArgument(1) - 1; + var value = context.GetArgument(2); + if (func is not Closure closure) + { + return new(0); + } + + var upValues = closure.UpValues; + var descriptions = closure.Proto.UpValues; + if (index < 0 || index >= descriptions.Length) + { + return new(0); + } + + var description = descriptions[index]; + buffer.Span[0] = description.Name.ToString(); + upValues[index].SetValue(value); + return new(1); + } + + public ValueTask GetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var arg0 = context.GetArgument(0); + + if (context.State.TryGetMetatable(arg0, out var table)) + { + buffer.Span[0] = table; + } + else + { + buffer.Span[0] = LuaValue.Nil; + } + + return new(1); + } + + public ValueTask SetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var arg0 = context.GetArgument(0); + var arg1 = context.GetArgument(1); + + if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table)) + { + LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, "setmetatable", [LuaValueType.Nil, LuaValueType.Table]); + } + + context.State.SetMetatable(arg0, arg1.UnsafeRead()); + + buffer.Span[0] = arg0; + return new(1); + } + + public ValueTask Traceback(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var thread = (GetLuaThread(context, out var argOffset)); + + var message = context.GetArgumentOrDefault(argOffset); + var level = context.GetArgumentOrDefault(argOffset + 1, 1); + + + if (message.Type is not (LuaValueType.Nil or LuaValueType.String or LuaValueType.Number)) + { + buffer.Span[0] = message; + return new(1); + } + + if (level < 0) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + thread.PushCallStackFrame(thread.GetCurrentFrame()); + var callStack = thread.GetCallStackFrames(); + var skipCount = Math.Min(level, callStack.Length - 1); + var frames = callStack[1..^skipCount]; + buffer.Span[0] = Runtime.Traceback.GetTracebackString((Closure)callStack[0].Function, frames, message); + thread.PopCallStackFrame(); + return new(1); + } + + public ValueTask GetRegistry(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + buffer.Span[0] = context.State.Registry; + return new(1); + } +} \ No newline at end of file diff --git a/src/Lua/Standard/OpenLibsExtensions.cs b/src/Lua/Standard/OpenLibsExtensions.cs index ed2fb763..c274abb1 100644 --- a/src/Lua/Standard/OpenLibsExtensions.cs +++ b/src/Lua/Standard/OpenLibsExtensions.cs @@ -130,6 +130,18 @@ public static void OpenTableLibrary(this LuaState state) state.Environment["table"] = table; state.LoadedModules["table"] = table; } + + public static void OpenDebugLibrary(this LuaState state) + { + var debug = new LuaTable(0, DebugLibrary.Instance.Functions.Length); + foreach (var func in DebugLibrary.Instance.Functions) + { + debug[func.Name] = func; + } + + state.Environment["debug"] = debug; + state.LoadedModules["debug"] = debug; + } public static void OpenStandardLibraries(this LuaState state) { @@ -142,5 +154,6 @@ public static void OpenStandardLibraries(this LuaState state) state.OpenOperatingSystemLibrary(); state.OpenStringLibrary(); state.OpenTableLibrary(); + state.OpenDebugLibrary(); } } \ No newline at end of file diff --git a/src/Lua/Standard/TableLibrary.cs b/src/Lua/Standard/TableLibrary.cs index c9e1773c..4901f1d3 100644 --- a/src/Lua/Standard/TableLibrary.cs +++ b/src/Lua/Standard/TableLibrary.cs @@ -41,6 +41,7 @@ public TableLibrary() ], ParameterCount = 2, UpValues = [], + Locals = [new LocalValueInfo(){Name = "a".AsMemory(),StartPc = 0,Index = 0,EndPc = 4}, new LocalValueInfo(){Name = "b".AsMemory(),StartPc = 0,Index = 1,EndPc = 4}], MaxStackPosition = 2, }; diff --git a/tests/Lua.Tests/LuaTests.cs b/tests/Lua.Tests/LuaTests.cs index 34aef51e..551ecaad 100644 --- a/tests/Lua.Tests/LuaTests.cs +++ b/tests/Lua.Tests/LuaTests.cs @@ -54,6 +54,12 @@ public async Task Test_Coroutine() { await state.DoFileAsync(FileHelper.GetAbsolutePath("tests-lua/coroutine.lua")); } + + [Test] + public async Task Test_Debug_Mini() + { + await state.DoFileAsync(FileHelper.GetAbsolutePath("tests-lua/db-mini.lua")); + } [Test] public async Task Test_VeryBig() diff --git a/tests/Lua.Tests/tests-lua/db-mini.lua b/tests/Lua.Tests/tests-lua/db-mini.lua new file mode 100644 index 00000000..b4280685 --- /dev/null +++ b/tests/Lua.Tests/tests-lua/db-mini.lua @@ -0,0 +1,66 @@ +-- testing debug library +local a =1 + +local function multi_assert(expected,...) + local arg = {...} + for i = 1, #arg do + assert(arg[i]==expected[i]) + end +end +local function test_locals(x,...) + local b ="local b" + assert(debug.getlocal(test_locals,1) == "x") + multi_assert({"x",1},debug.getlocal(1,1)) + multi_assert({"b","local b"},debug.getlocal(1,2)) + multi_assert({"(vararg)",2},debug.getlocal(1,-1)) + multi_assert({"(vararg)",3},debug.getlocal(1,-2)) + multi_assert({"a",1},debug.getlocal(2,1)) + assert(debug.setlocal(2,1,"new a") == "a") + +end + +test_locals(1,2,3) +assert(a == "new a") + +local function test_upvalues() + local a =3 + local function f(x) + local b = a + x + local function g(y) + local c = b + y + local function h() + return a+b+c + end + multi_assert({"a",3},debug.getupvalue(h,1)) + multi_assert({"b",4},debug.getupvalue(h,2)) + multi_assert({"c",6},debug.getupvalue(h,3)) + multi_assert({"b",4},debug.getupvalue(g,1)) + multi_assert({"a",3},debug.getupvalue(g,2)) + multi_assert({"a",3},debug.getupvalue(f,1)) + debug.setupvalue(h,1,10) + debug.setupvalue(h,2,20) + debug.setupvalue(h,3,30) + assert(h() == 60) + end + g(2) + end + f(1) +end +test_upvalues() +local mt = { + __metatable = "my own metatable", + __index = function (o, k) + return o+k + end +} +debug.setmetatable(10, mt) +assert(debug.getmetatable(10) == mt) +a = 10 +assert( a[3] == 13) + +assert(debug.traceback(print)==print) +assert(debug.traceback(print)==print) + + + +assert(type(debug.getregistry())=="table") \ No newline at end of file From 320a634779f19d65843a69aaba81d9cfa0342233 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:18:00 +0900 Subject: [PATCH 02/22] Add: debug.upvaluejoin --- src/Lua/Runtime/Closure.cs | 1 + src/Lua/Standard/DebugLibrary.cs | 32 ++++++++++++++++++++++++++- tests/Lua.Tests/tests-lua/db-mini.lua | 16 ++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Lua/Runtime/Closure.cs b/src/Lua/Runtime/Closure.cs index 8cb4a5d0..4817b27b 100644 --- a/src/Lua/Runtime/Closure.cs +++ b/src/Lua/Runtime/Closure.cs @@ -24,6 +24,7 @@ public Closure(LuaState state, Chunk proto, LuaTable? environment = null) public Chunk Proto => proto; public ReadOnlySpan UpValues => upValues.AsSpan(); + internal Span GetUpValuesSpan() => upValues.AsSpan(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal LuaValue GetUpValue(int index) diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 23399eae..04b08e9d 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -18,7 +18,8 @@ public DebugLibrary() new("getmetatable", GetMetatable), new("setmetatable", SetMetatable), new("traceback", Traceback), - new("getregistry", GetRegistry) + new("getregistry", GetRegistry), + new("upvaluejoin", UpValueJoin) ]; } @@ -283,4 +284,33 @@ public ValueTask GetRegistry(LuaFunctionExecutionContext context, Memory UpValueJoin(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var n2 = context.GetArgument(3); + var f2 = context.GetArgument(2); + var n1 = context.GetArgument(1); + var f1 = context.GetArgument(0); + + if (f1 is not Closure closure1 || f2 is not Closure closure2) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + var upValues1 = closure1.GetUpValuesSpan(); + var upValues2 = closure2.GetUpValuesSpan(); + if (n1 <= 0 || n1 > upValues1.Length) + { + context.ThrowBadArgument(1, "invalid upvalue index"); + } + + if (n2 < 0 || n2 > upValues2.Length) + { + context.ThrowBadArgument(3, "invalid upvalue index"); + } + + upValues1[n1 - 1] = upValues2[n2 - 1]; + return new(0); + } } \ No newline at end of file diff --git a/tests/Lua.Tests/tests-lua/db-mini.lua b/tests/Lua.Tests/tests-lua/db-mini.lua index b4280685..1142537d 100644 --- a/tests/Lua.Tests/tests-lua/db-mini.lua +++ b/tests/Lua.Tests/tests-lua/db-mini.lua @@ -53,6 +53,22 @@ local mt = { return o+k end } + +local a = 1 +local b = 2 +local function f() + return a +end +local function g() + return b +end + +debug.upvaluejoin(f,1,g,1) + +assert(f() == 2) +b=3 +assert(f() == 3) + debug.setmetatable(10, mt) assert(debug.getmetatable(10) == mt) a = 10 From 8b134a2312c85365defce443955bc853461b6ff9 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Thu, 13 Feb 2025 18:46:15 +0900 Subject: [PATCH 03/22] Add: debug.getinfo --- .../Compilation/FunctionCompilationContext.cs | 30 ++- .../CodeAnalysis/Compilation/LuaCompiler.cs | 24 +- .../FunctionDeclarationExpressionNode.cs | 2 +- .../Nodes/FunctionDeclarationStatementNode.cs | 2 +- .../Syntax/Nodes/GenericForStatementNode.cs | 2 +- .../Nodes/LocalFunctionDeclarationNode.cs | 2 +- .../Syntax/Nodes/NumericForStatementNode.cs | 2 +- .../TableMethodDeclarationStatementNode.cs | 2 +- src/Lua/CodeAnalysis/Syntax/Parser.cs | 60 +++-- src/Lua/LuaFunctionExecutionContext.cs | 6 + src/Lua/LuaState.cs | 3 + src/Lua/Runtime/CallStackFrame.cs | 1 + src/Lua/Runtime/Chunk.cs | 4 +- src/Lua/Runtime/Tracebacks.cs | 32 ++- src/Lua/Standard/DebugLibrary.cs | 109 ++++++++- src/Lua/Standard/TableLibrary.cs | 3 + tests/Lua.Tests/LuaTests.cs | 6 +- tests/Lua.Tests/tests-lua/db-mini.lua | 82 ------- tests/Lua.Tests/tests-lua/db_mini.lua | 227 ++++++++++++++++++ 19 files changed, 458 insertions(+), 141 deletions(-) delete mode 100644 tests/Lua.Tests/tests-lua/db-mini.lua create mode 100644 tests/Lua.Tests/tests-lua/db_mini.lua diff --git a/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs b/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs index 63aa35c0..dfa2d73f 100644 --- a/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs +++ b/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs @@ -91,6 +91,16 @@ internal static FunctionCompilationContext Create(ScopeCompilationContext? paren /// public bool HasVariableArguments { get; set; } + /// + /// Line number where the function is defined + /// + public int LineDefined { get; set; } + + /// + /// Last line number where the function is defined + /// + public int LastLineDefined { get; set; } + /// /// Parent scope context /// @@ -128,6 +138,7 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in instructionPositions.Add(position); return; } + ref var lastInstruction = ref instructions.AsSpan()[^1]; var opcode = instruction.OpCode; switch (opcode) @@ -157,6 +168,7 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in } } } + break; case OpCode.GetTable: { @@ -170,8 +182,8 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in incrementStackPosition = false; return; } - } + break; } case OpCode.SetTable: @@ -198,6 +210,7 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in return; } } + lastInstruction = Instruction.SetTable((byte)(lastB), instruction.B, instruction.C); instructionPositions[^1] = position; incrementStackPosition = false; @@ -218,7 +231,6 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in var last2OpCode = last2Instruction.OpCode; if (last2OpCode is OpCode.LoadK or OpCode.Move) { - var last2A = last2Instruction.A; if (last2A != lastLocal && instruction.C == last2A) { @@ -232,6 +244,7 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in } } } + break; } case OpCode.Unm: @@ -239,11 +252,13 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in case OpCode.Len: if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A && lastInstruction.A == instruction.B) { - lastInstruction = instruction with { B = lastInstruction.B }; ; + lastInstruction = instruction with { B = lastInstruction.B }; + ; instructionPositions[^1] = position; incrementStackPosition = false; return; } + break; case OpCode.Return: if (lastInstruction.OpCode == OpCode.Move && instruction.B == 2 && lastInstruction.B < 256) @@ -253,6 +268,7 @@ public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in incrementStackPosition = false; return; } + break; } @@ -302,7 +318,7 @@ public bool TryGetFunctionProto(ReadOnlyMemory name, [NotNullWhen(true)] o return false; } } - + public void AddLocalVariable(ReadOnlyMemory name, LocalVariableDescription description) { localVariables.Add(new LocalValueInfo() @@ -390,6 +406,7 @@ public void ResolveAllBreaks(byte startPosition, int endPosition, ScopeCompilati { instruction.A = startPosition; } + instruction.SBx = endPosition - description.Index; } @@ -421,7 +438,7 @@ public Chunk ToChunk() { // add return instructions.Add(Instruction.Return(0, 1)); - instructionPositions.Add(instructionPositions.Length == 0 ? default : instructionPositions[^1]); + instructionPositions.Add( new (LastLineDefined, 0)); Scope.RegisterLocalsToFunction(); var locals = localVariables.AsSpan().ToArray(); Array.Sort(locals, (x, y) => x.Index.CompareTo(y.Index)); @@ -435,7 +452,10 @@ public Chunk ToChunk() Locals = locals, Functions = functions.AsSpan().ToArray(), ParameterCount = ParameterCount, + HasVariableArguments = HasVariableArguments, MaxStackPosition = MaxStackPosition, + LineDefined = LineDefined, + LastLineDefined = LastLineDefined, }; foreach (var function in functions.AsSpan()) diff --git a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs index c75e0e6d..6dc80401 100644 --- a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs +++ b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs @@ -20,7 +20,7 @@ public Chunk Compile(string source, string? chunkName = null) public Chunk Compile(LuaSyntaxTree syntaxTree, string? chunkName = null) { using var context = FunctionCompilationContext.Create(null); - + context.HasVariableArguments = true; // set global enviroment upvalue context.AddUpValue(new() { @@ -586,7 +586,7 @@ void CompileCallFunctionExpression(CallFunctionExpressionNode node, ScopeCompila // function declaration public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, ScopeCompilationContext context) { - var funcIndex = CompileFunctionProto(ReadOnlyMemory.Empty, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false); + var funcIndex = CompileFunctionProto(ReadOnlyMemory.Empty, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.LastLineDefined); // push closure instruction context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true); @@ -604,7 +604,7 @@ public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationS }); // compile function - var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false); + var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.LastLineDefined); // push closure instruction context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true); @@ -614,7 +614,7 @@ public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationS public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNode node, ScopeCompilationContext context) { - var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false); + var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.LastLineDefined); // add closure var index = context.Function.GetConstantIndex(node.Name.ToString()); @@ -639,7 +639,7 @@ public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNo public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, ScopeCompilationContext context) { var funcIdentifier = node.MemberPath[^1]; - var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length + 1, node.HasVariableArguments, node.HasSelfParameter); + var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length + 1, node.HasVariableArguments, node.HasSelfParameter, node.LineDefined, node.LastLineDefined); // add closure var index = context.Function.GetConstantIndex(funcIdentifier.Name.ToString()); @@ -669,12 +669,14 @@ public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationState return true; } - int CompileFunctionProto(ReadOnlyMemory functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, int parameterCount, bool hasVarArg, bool hasSelfParameter) + int CompileFunctionProto(ReadOnlyMemory functionName, ScopeCompilationContext context, IdentifierNode[] parameters, SyntaxNode[] statements, int parameterCount, bool hasVarArg, bool hasSelfParameter, int lineDefined, int lastLineDefined) { using var funcContext = context.CreateChildFunction(); funcContext.ChunkName = functionName.ToString(); funcContext.ParameterCount = parameterCount; funcContext.HasVariableArguments = hasVarArg; + funcContext.LineDefined = lineDefined; + funcContext.LastLineDefined = lastLineDefined; if (hasSelfParameter) { @@ -900,11 +902,11 @@ public bool VisitNumericForStatementNode(NumericForStatementNode node, ScopeComp else { var index = context.Function.GetConstantIndex(1); - context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.Position, true); + context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.DoPosition, true); } var prepIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.ForPrep(startPosition, 0), node.Position, true); + context.PushInstruction(Instruction.ForPrep(startPosition, 0), node.DoPosition, true); // compile statements context.Function.LoopLevel++; @@ -965,7 +967,7 @@ public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeComp // jump to TFORCALL var startJumpIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.Jmp(0, 0), node.Position); + context.PushInstruction(Instruction.Jmp(0, 0), node.DoPosition); // compile statements context.Function.LoopLevel++; @@ -1016,7 +1018,7 @@ public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeComp // push OP_TFORCALL and OP_TFORLOOP context.PushInstruction(Instruction.TForCall(startPosition, (ushort)node.Names.Length), node.Position); - context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.Position); + context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.EndPosition); context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext); context.StackPosition = startPosition; @@ -1145,6 +1147,7 @@ static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, { value = stringLiteral.Text.ToString(); } + return true; case UnaryExpressionNode unaryExpression: if (TryGetConstant(unaryExpression.Node, context, out var unaryNodeValue)) @@ -1212,6 +1215,7 @@ static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, break; } } + break; } diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs index e604af75..748863d2 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record FunctionDeclarationExpressionNode(IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position) : ExpressionNode(Position) +public record FunctionDeclarationExpressionNode(IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position, int LineDefined,int LastLineDefined) : ExpressionNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs index 181ee0ff..5ced6e16 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record FunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position) : StatementNode(Position) +public record FunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position,int LineDefined,int LastLineDefined) : StatementNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/GenericForStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/GenericForStatementNode.cs index 74b62479..d4c34afd 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/GenericForStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/GenericForStatementNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record GenericForStatementNode(IdentifierNode[] Names, ExpressionNode[] ExpressionNodes, StatementNode[] StatementNodes, SourcePosition Position) : StatementNode(Position) +public record GenericForStatementNode(IdentifierNode[] Names, ExpressionNode[] ExpressionNodes, StatementNode[] StatementNodes, SourcePosition Position, SourcePosition DoPosition, SourcePosition EndPosition) : StatementNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs index dd31836a..b1aafaa2 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record LocalFunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position) : FunctionDeclarationStatementNode(Name, ParameterNodes, Nodes, HasVariableArguments, Position) +public record LocalFunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position, int LineDefined,int LastLineDefined) : FunctionDeclarationStatementNode(Name, ParameterNodes, Nodes, HasVariableArguments, Position, LineDefined, LastLineDefined) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/NumericForStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/NumericForStatementNode.cs index 43167744..b64fa57b 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/NumericForStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/NumericForStatementNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record NumericForStatementNode(ReadOnlyMemory VariableName, ExpressionNode InitNode, ExpressionNode LimitNode, ExpressionNode? StepNode, StatementNode[] StatementNodes, SourcePosition Position) : StatementNode(Position) +public record NumericForStatementNode(ReadOnlyMemory VariableName, ExpressionNode InitNode, ExpressionNode LimitNode, ExpressionNode? StepNode, StatementNode[] StatementNodes, SourcePosition Position,SourcePosition DoPosition) : StatementNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs index dc4b87d1..81fee012 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record TableMethodDeclarationStatementNode(IdentifierNode[] MemberPath, IdentifierNode[] ParameterNodes, StatementNode[] Nodes, bool HasVariableArguments, bool HasSelfParameter, SourcePosition Position) : StatementNode(Position) +public record TableMethodDeclarationStatementNode(IdentifierNode[] MemberPath, IdentifierNode[] ParameterNodes, StatementNode[] Nodes, bool HasVariableArguments, bool HasSelfParameter, SourcePosition Position, int LineDefined,int LastLineDefined) : StatementNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Parser.cs b/src/Lua/CodeAnalysis/Syntax/Parser.cs index 99984512..0535a371 100644 --- a/src/Lua/CodeAnalysis/Syntax/Parser.cs +++ b/src/Lua/CodeAnalysis/Syntax/Parser.cs @@ -64,6 +64,7 @@ StatementNode ParseStatement(ref SyntaxTokenEnumerator enumerator) return ParseAssignmentStatement(firstExpression, ref enumerator); } + break; } } @@ -168,7 +169,7 @@ DoStatementNode ParseDoStatement(ref SyntaxTokenEnumerator enumerator) var doToken = enumerator.Current; // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out _); return new DoStatementNode(statements, doToken.Position); } @@ -208,6 +209,7 @@ AssignmentStatementNode ParseAssignmentStatement(ExpressionNode firstExpression, enumerator.MovePrevious(); return new AssignmentStatementNode(leftNodes.AsSpan().ToArray(), [], firstExpression.Position); } + MoveNextWithValidation(ref enumerator); // parse expressions @@ -227,6 +229,7 @@ LocalAssignmentStatementNode ParseLocalAssignmentStatement(ref SyntaxTokenEnumer enumerator.MovePrevious(); return new LocalAssignmentStatementNode(identifiers, [], localToken.Position); } + MoveNextWithValidation(ref enumerator); // parse expressions @@ -353,7 +356,7 @@ WhileStatementNode ParseWhileStatement(ref SyntaxTokenEnumerator enumerator) CheckCurrent(ref enumerator, SyntaxTokenType.Do); // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out _); return new WhileStatementNode(condition, statements, whileToken.Position); } @@ -365,7 +368,7 @@ RepeatStatementNode ParseRepeatStatement(ref SyntaxTokenEnumerator enumerator) var repeatToken = enumerator.Current; // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.Until); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.Until, out _); // skip 'until keyword' CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Until, out _); @@ -418,11 +421,12 @@ NumericForStatementNode ParseNumericForStatement(ref SyntaxTokenEnumerator enume // skip 'do' keyword CheckCurrent(ref enumerator, SyntaxTokenType.Do); + var doToken = enumerator.Current; // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out _); - return new NumericForStatementNode(varName, initialValueNode, limitNode, stepNode, statements, forToken.Position); + return new NumericForStatementNode(varName, initialValueNode, limitNode, stepNode, statements, forToken.Position, doToken.Position); } GenericForStatementNode ParseGenericForStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken forToken) @@ -433,33 +437,33 @@ GenericForStatementNode ParseGenericForStatement(ref SyntaxTokenEnumerator enume // skip 'in' keyword CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.In, out _); enumerator.SkipEoL(); - + var iteratorToken = enumerator.Current; var expressions = ParseExpressionList(ref enumerator); MoveNextWithValidation(ref enumerator); enumerator.SkipEoL(); // skip 'do' keyword CheckCurrent(ref enumerator, SyntaxTokenType.Do); - + var doToken = enumerator.Current; // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out var endToken); - return new GenericForStatementNode(identifiers, expressions, statements, forToken.Position); + return new GenericForStatementNode(identifiers, expressions, statements, iteratorToken.Position, doToken.Position, endToken.Position); } FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken) { - var (Name, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, false); - return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position); + var (Name, Identifiers, Statements, HasVariableArgments, LineDefined, LastLineDefined) = ParseFunctionDeclarationCore(ref enumerator, false); + return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, LastLineDefined); } LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken) { - var (Name, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, false); - return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position); + var (Name, Identifiers, Statements, HasVariableArgments, LineDefined, LastLineDefined) = ParseFunctionDeclarationCore(ref enumerator, false); + return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, LastLineDefined); } - (ReadOnlyMemory Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous) + (ReadOnlyMemory Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments, int LineDefined, int LastLineDefined) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous) { ReadOnlyMemory name; @@ -478,7 +482,7 @@ LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref } // skip '(' - CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.LParen, out _); + CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.LParen, out var leftParenToken); enumerator.SkipEoL(); // parse parameters @@ -494,16 +498,16 @@ LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref CheckCurrent(ref enumerator, SyntaxTokenType.RParen); // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out var endToken); - return (name, identifiers, statements, hasVarArg); + return (name, identifiers, statements, hasVarArg, leftParenToken.Position.Line, endToken.Position.Line); } TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken) { using var names = new PooledList(32); var hasSelfParameter = false; - + SyntaxToken leftParenToken; while (true) { CheckCurrent(ref enumerator, SyntaxTokenType.Identifier); @@ -517,6 +521,7 @@ TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref Syn { LuaParseException.UnexpectedToken(ChunkName, enumerator.Current.Position, enumerator.Current); } + hasSelfParameter = enumerator.Current.Type is SyntaxTokenType.Colon; MoveNextWithValidation(ref enumerator); @@ -524,6 +529,7 @@ TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref Syn } else if (enumerator.Current.Type is SyntaxTokenType.LParen) { + leftParenToken = enumerator.Current; // skip '(' MoveNextWithValidation(ref enumerator); enumerator.SkipEoL(); @@ -544,9 +550,9 @@ TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref Syn CheckCurrent(ref enumerator, SyntaxTokenType.RParen); // parse statements - var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End); + var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out var endToken); - return new TableMethodDeclarationStatementNode(names.AsSpan().ToArray(), identifiers, statements, hasVarArg, hasSelfParameter, functionToken.Position); + return new TableMethodDeclarationStatementNode(names.AsSpan().ToArray(), identifiers, statements, hasVarArg, hasSelfParameter, functionToken.Position, leftParenToken.Position.Line, endToken.Position.Line); } bool TryParseExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence precedence, [NotNullWhen(true)] out ExpressionNode? result) @@ -579,7 +585,7 @@ bool TryParseExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence // nested table access & function call RECURSIVE: enumerator.SkipEoL(); - + var nextType = enumerator.GetNext().Type; if (nextType is SyntaxTokenType.LSquare or SyntaxTokenType.Dot or SyntaxTokenType.Colon) { @@ -851,9 +857,9 @@ FunctionDeclarationExpressionNode ParseFunctionDeclarationExpression(ref SyntaxT // skip 'function' keyword CheckCurrentAndSkip(ref enumerator, SyntaxTokenType.Function, out var functionToken); enumerator.SkipEoL(); - - var (_, Identifiers, Statements, HasVariableArgments) = ParseFunctionDeclarationCore(ref enumerator, true); - return new FunctionDeclarationExpressionNode(Identifiers, Statements, HasVariableArgments, functionToken.Position); + + var (_, Identifiers, Statements, HasVariableArgments, LineDefined, LastLineDefined) = ParseFunctionDeclarationCore(ref enumerator, true); + return new FunctionDeclarationExpressionNode(Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, LastLineDefined); } ExpressionNode[] ParseCallFunctionArguments(ref SyntaxTokenEnumerator enumerator) @@ -946,20 +952,22 @@ IdentifierNode[] ParseIdentifierList(ref SyntaxTokenEnumerator enumerator) return buffer.AsSpan().ToArray(); } - StatementNode[] ParseStatementList(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType endToken) + StatementNode[] ParseStatementList(ref SyntaxTokenEnumerator enumerator, SyntaxTokenType endTokenType, out SyntaxToken endToken) { using var statements = new PooledList(64); // parse statements while (enumerator.MoveNext()) { - if (enumerator.Current.Type == endToken) break; + if (enumerator.Current.Type == endTokenType) break; if (enumerator.Current.Type is SyntaxTokenType.EndOfLine or SyntaxTokenType.SemiColon) continue; var node = ParseStatement(ref enumerator); statements.Add(node); } + endToken = enumerator.Current; + return statements.AsSpan().ToArray(); } diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index 221bc43b..970c0de2 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -83,6 +83,12 @@ internal T GetArgumentOrDefault(int index,T defaultValue =default!) } var arg = Arguments[index]; + + if(arg.Type is LuaValueType.Nil) + { + return defaultValue; + } + if (!arg.TryRead(out var argValue)) { var t = typeof(T); diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index 73663f34..19d624b9 100644 --- a/src/Lua/LuaState.cs +++ b/src/Lua/LuaState.cs @@ -19,10 +19,13 @@ public sealed class LuaState readonly LuaTable registry = new(); readonly UpValue envUpValue; bool isRunning; + + FastStackCore debugBufferPool; internal UpValue EnvUpValue => envUpValue; internal ref FastStackCore ThreadStack => ref threadStack; internal ref FastListCore OpenUpValues => ref openUpValues; + internal ref FastStackCore DebugBufferPool => ref debugBufferPool; public LuaTable Environment => environment; public LuaTable Registry => registry; diff --git a/src/Lua/Runtime/CallStackFrame.cs b/src/Lua/Runtime/CallStackFrame.cs index 7b00bc22..8a665e44 100644 --- a/src/Lua/Runtime/CallStackFrame.cs +++ b/src/Lua/Runtime/CallStackFrame.cs @@ -16,4 +16,5 @@ public record struct CallStackFrame public enum CallStackFrameFlags { ReversedLe = 1, + TailCall } \ No newline at end of file diff --git a/src/Lua/Runtime/Chunk.cs b/src/Lua/Runtime/Chunk.cs index 2d83bcf0..350ff7b8 100644 --- a/src/Lua/Runtime/Chunk.cs +++ b/src/Lua/Runtime/Chunk.cs @@ -14,8 +14,10 @@ public sealed class Chunk public required LocalValueInfo[] Locals { get; init; } public required Chunk[] Functions { get; init; } public required int ParameterCount { get; init; } - + public required bool HasVariableArguments { get; init; } public required byte MaxStackPosition { get; init; } + public required int LineDefined { get; init; } + public required int LastLineDefined { get; init; } Chunk? rootCache; diff --git a/src/Lua/Runtime/Tracebacks.cs b/src/Lua/Runtime/Tracebacks.cs index 6eb14277..d8c26b38 100644 --- a/src/Lua/Runtime/Tracebacks.cs +++ b/src/Lua/Runtime/Tracebacks.cs @@ -49,7 +49,7 @@ internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan= 0; index--) { LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : rootFunc; @@ -65,13 +65,37 @@ internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan 1 ? stackFrames[index - 2].Function : rootFunc; + if (index > 0 && caller is Closure callerClosure) + { + var t = LuaDebug.GetFuncName(callerClosure.Proto, stackFrames[index - 1].CallerInstructionIndex, out var name); + if (t is not null and not "global") + { + list.AddRange(t); + list.AddRange(" '"); + list.AddRange(name); + list.AddRange("'\n"); + continue; + } + } + + list.AddRange("function '"); list.AddRange(p.Name); - list.AddRange("'\n"); + list.AddRange("\n"); } } diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 04b08e9d..89064b2a 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; using Lua.Runtime; - +using Lua.Internal; namespace Lua.Standard; public class DebugLibrary @@ -19,7 +19,8 @@ public DebugLibrary() new("setmetatable", SetMetatable), new("traceback", Traceback), new("getregistry", GetRegistry), - new("upvaluejoin", UpValueJoin) + new("upvaluejoin", UpValueJoin), + new("getinfo", GetInfo), ]; } @@ -252,10 +253,10 @@ public ValueTask SetMetatable(LuaFunctionExecutionContext context, Memory Traceback(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) { - var thread = (GetLuaThread(context, out var argOffset)); + var thread = GetLuaThread(context, out var argOffset); var message = context.GetArgumentOrDefault(argOffset); - var level = context.GetArgumentOrDefault(argOffset + 1, 1); + var level = context.GetArgumentOrDefault(argOffset + 1, 0); if (message.Type is not (LuaValueType.Nil or LuaValueType.String or LuaValueType.Number)) @@ -313,4 +314,104 @@ public ValueTask UpValueJoin(LuaFunctionExecutionContext context, Memory GetInfo(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + //return new(0); + var thread = GetLuaThread(context, out var argOffset); + string what = context.GetArgumentOrDefault(argOffset + 1,"flnStu"); + CallStackFrame? previousFrame = null; + CallStackFrame? currentFrame = null; + int pc = 0; + var arg1 = context.GetArgument(argOffset); + + if (arg1.TryReadFunction(out var functionToInspect)) + { + //what = ">" + what; + } + else if (arg1.TryReadNumber(out _)) + { + var level = context.GetArgument(argOffset) + 1; + + var callStack = thread.GetCallStackFrames(); + if (level <= 0 || level > callStack.Length) + { + context.ThrowBadArgument(1, "level out of range"); + } + currentFrame = thread.GetCallStackFrames()[^(level)]; + functionToInspect = currentFrame.Value.Function; + previousFrame = level +1 <= callStack.Length?callStack[^(level + 1)]:null; + if(level!=0) + { + pc = thread.GetCallStackFrames()[^(level - 1)].CallerInstructionIndex; + } + + } + else + { + context.ThrowBadArgument(argOffset, "function or level expected"); + } + + using var debug = LuaDebug.Create(context.State, previousFrame, currentFrame, functionToInspect, pc, what, out var isValid); + if (!isValid) + { + context.ThrowBadArgument(argOffset + 1, "invalid option"); + } + + var table = new LuaTable(0, 1); + if (what.Contains('S')) + { + table["source"] = debug.Source ?? LuaValue.Nil; + table["short_src"] = debug.ShortSource.ToString(); + table["linedefined"] = debug.LineDefined; + table["lastlinedefined"] = debug.LastLineDefined; + table["what"] = debug.What?? LuaValue.Nil;; + } + + if (what.Contains('l')) + { + table["currentline"] = debug.CurrentLine; + } + + if (what.Contains('u')) + { + table["nups"] = debug.UpValueCount; + table["nparams"] = debug.ParameterCount; + table["isvararg"] = debug.IsVarArg; + } + + if (what.Contains('n')) + { + table["name"] = debug.Name?? LuaValue.Nil;; + table["namewhat"] = debug.NameWhat?? LuaValue.Nil;; + } + + if (what.Contains('t')) + { + table["istailcall"] = debug.IsTailCall; + } + + if (what.Contains('f')) + { + table["func"] = functionToInspect; + } + + if (what.Contains('L')) + { + var activeLines = new LuaTable(0, 8); + if (functionToInspect is Closure closure) + { + foreach (var pos in closure.Proto.SourcePositions) + { + activeLines[pos.Line] = true; + } + } + + table["activelines"] = activeLines; + } + + buffer.Span[0] = table; + + return new(1); + } } \ No newline at end of file diff --git a/src/Lua/Standard/TableLibrary.cs b/src/Lua/Standard/TableLibrary.cs index 4901f1d3..fb3896b6 100644 --- a/src/Lua/Standard/TableLibrary.cs +++ b/src/Lua/Standard/TableLibrary.cs @@ -43,6 +43,9 @@ public TableLibrary() UpValues = [], Locals = [new LocalValueInfo(){Name = "a".AsMemory(),StartPc = 0,Index = 0,EndPc = 4}, new LocalValueInfo(){Name = "b".AsMemory(),StartPc = 0,Index = 1,EndPc = 4}], MaxStackPosition = 2, + HasVariableArguments = false, + LineDefined = 0, + LastLineDefined = 0, }; public ValueTask Concat(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) diff --git a/tests/Lua.Tests/LuaTests.cs b/tests/Lua.Tests/LuaTests.cs index 551ecaad..e26f34c4 100644 --- a/tests/Lua.Tests/LuaTests.cs +++ b/tests/Lua.Tests/LuaTests.cs @@ -12,7 +12,7 @@ public void SetUp() state = LuaState.Create(); state.OpenStandardLibraries(); } - + [Test] public async Task Test_Closure() { @@ -54,11 +54,11 @@ public async Task Test_Coroutine() { await state.DoFileAsync(FileHelper.GetAbsolutePath("tests-lua/coroutine.lua")); } - + [Test] public async Task Test_Debug_Mini() { - await state.DoFileAsync(FileHelper.GetAbsolutePath("tests-lua/db-mini.lua")); + await state.DoFileAsync(FileHelper.GetAbsolutePath("tests-lua/db_mini.lua")); } [Test] diff --git a/tests/Lua.Tests/tests-lua/db-mini.lua b/tests/Lua.Tests/tests-lua/db-mini.lua deleted file mode 100644 index 1142537d..00000000 --- a/tests/Lua.Tests/tests-lua/db-mini.lua +++ /dev/null @@ -1,82 +0,0 @@ --- testing debug library -local a =1 - -local function multi_assert(expected,...) - local arg = {...} - for i = 1, #arg do - assert(arg[i]==expected[i]) - end -end -local function test_locals(x,...) - local b ="local b" - assert(debug.getlocal(test_locals,1) == "x") - multi_assert({"x",1},debug.getlocal(1,1)) - multi_assert({"b","local b"},debug.getlocal(1,2)) - multi_assert({"(vararg)",2},debug.getlocal(1,-1)) - multi_assert({"(vararg)",3},debug.getlocal(1,-2)) - multi_assert({"a",1},debug.getlocal(2,1)) - assert(debug.setlocal(2,1,"new a") == "a") - -end - -test_locals(1,2,3) -assert(a == "new a") - -local function test_upvalues() - local a =3 - local function f(x) - local b = a + x - local function g(y) - local c = b + y - local function h() - return a+b+c - end - multi_assert({"a",3},debug.getupvalue(h,1)) - multi_assert({"b",4},debug.getupvalue(h,2)) - multi_assert({"c",6},debug.getupvalue(h,3)) - multi_assert({"b",4},debug.getupvalue(g,1)) - multi_assert({"a",3},debug.getupvalue(g,2)) - multi_assert({"a",3},debug.getupvalue(f,1)) - debug.setupvalue(h,1,10) - debug.setupvalue(h,2,20) - debug.setupvalue(h,3,30) - assert(h() == 60) - end - g(2) - end - f(1) -end -test_upvalues() -local mt = { - __metatable = "my own metatable", - __index = function (o, k) - return o+k - end -} - -local a = 1 -local b = 2 -local function f() - return a -end -local function g() - return b -end - -debug.upvaluejoin(f,1,g,1) - -assert(f() == 2) -b=3 -assert(f() == 3) - -debug.setmetatable(10, mt) -assert(debug.getmetatable(10) == mt) -a = 10 -assert( a[3] == 13) - -assert(debug.traceback(print)==print) -assert(debug.traceback(print)==print) - - - -assert(type(debug.getregistry())=="table") \ No newline at end of file diff --git a/tests/Lua.Tests/tests-lua/db_mini.lua b/tests/Lua.Tests/tests-lua/db_mini.lua new file mode 100644 index 00000000..0040b817 --- /dev/null +++ b/tests/Lua.Tests/tests-lua/db_mini.lua @@ -0,0 +1,227 @@ +-- testing debug library + + + +local a = 1 + +local function multi_assert(expected, ...) + local arg = { ... } + for i = 1, #arg do + assert(arg[i] == expected[i]) + end +end +local function test_locals(x, ...) + local b = "local b" + assert(debug.getlocal(test_locals, 1) == "x") + multi_assert({ "x", 1 }, debug.getlocal(1, 1)) + multi_assert({ "b", "local b" }, debug.getlocal(1, 2)) + multi_assert({ "(vararg)", 2 }, debug.getlocal(1, -1)) + multi_assert({ "(vararg)", 3 }, debug.getlocal(1, -2)) + multi_assert({ "a", 1 }, debug.getlocal(2, 1)) + assert(debug.setlocal(2, 1, "new a") == "a") + +end + +test_locals(1, 2, 3) +assert(a == "new a") + +-- test file and string names truncation +a = "function f () end" +local function dostring (s, x) + return load(s, x)() +end +dostring(a) +assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a)) +dostring(a .. string.format("; %s\n=1", string.rep('p', 400))) +assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) +dostring(a .. string.format("; %s=1", string.rep('p', 400))) +assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) +dostring("\n" .. a) +assert(debug.getinfo(f).short_src == '[string "..."]') +dostring(a, "") +assert(debug.getinfo(f).short_src == '[string ""]') +dostring(a, "@xuxu") +assert(debug.getinfo(f).short_src == "xuxu") +dostring(a, "@" .. string.rep('p', 1000) .. 't') +assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$")) +dostring(a, "=xuxu") +assert(debug.getinfo(f).short_src == "xuxu") +dostring(a, string.format("=%s", string.rep('x', 500))) +assert(string.find(debug.getinfo(f).short_src, "^x*$")) +dostring(a, "=") +assert(debug.getinfo(f).short_src == "") +a = nil; +f = nil; + +repeat + local g = { x = function() + local a = debug.getinfo(2) + assert(a.name == 'f' and a.namewhat == 'local') + a = debug.getinfo(1) + assert(a.name == 'x' and a.namewhat == 'field') + return 'xixi' + end } + local f = function() + return 1 + 1 and (not 1 or g.x()) + end + assert(f() == 'xixi') + g = debug.getinfo(f) + assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name) + + function f (x, name) + -- local! + if not name then + name = 'f' + end + local a = debug.getinfo(1) + print(a.name, a.namewhat, name) + assert(a.name == name and a.namewhat == 'local') + return x + end + + -- breaks in different conditions + if 3 > 4 then + break + end ; + f() + if 3 < 4 then + a = 1 + else + break + end ; + f() + while 1 do + local x = 10; + break + end ; + f() + local b = 1 + if 3 > 4 then + return math.sin(1) + end ; + f() + a = 3 < 4; + f() + a = 3 < 4 or 1; + f() + repeat local x = 20; + if 4 > 3 then + f() + else + break + end ; + f() until 1 + g = {} + f(g).x = f(2) and f(10) + f(9) + assert(g.x == f(19)) + function g(x) + if not x then + return 3 + end + return (x('a', 'x')) + end + assert(g(f) == 'a') +until 1 + +local function test_upvalues() + local a = 3 + local function f(x) + local b = a + x + local function g(y) + local c = b + y + local function h() + return a + b + c + end + multi_assert({ "a", 3 }, debug.getupvalue(h, 1)) + multi_assert({ "b", 4 }, debug.getupvalue(h, 2)) + multi_assert({ "c", 6 }, debug.getupvalue(h, 3)) + multi_assert({ "b", 4 }, debug.getupvalue(g, 1)) + multi_assert({ "a", 3 }, debug.getupvalue(g, 2)) + multi_assert({ "a", 3 }, debug.getupvalue(f, 1)) + debug.setupvalue(h, 1, 10) + debug.setupvalue(h, 2, 20) + debug.setupvalue(h, 3, 30) + assert(h() == 60) + end + g(2) + end + f(1) +end +test_upvalues() +local mt = { + __metatable = "my own metatable", + __index = function(o, k) + return o + k + end +} + +local a = 1 +local b = 2 +local function f() + return a +end +local function g() + return b +end + +debug.upvaluejoin(f, 1, g, 1) + +assert(f() == 2) +b = 3 +assert(f() == 3) + +debug.setmetatable(10, mt) +assert(debug.getmetatable(10) == mt) +a = 10 +assert(a[3] == 13) + +assert(debug.traceback(print) == print) +assert(debug.traceback(print) == print) + +assert(type(debug.getregistry()) == "table") + +-- testing nparams, nups e isvararg +local t = debug.getinfo(print, "u") +assert(t.isvararg == true and t.nparams == 0 and t.nups == 0) + +t = debug.getinfo(function(a, b, c) +end, "u") +assert(t.isvararg == false and t.nparams == 3 and t.nups == 0) + +t = debug.getinfo(function(a, b, ...) + return t[a] +end, "u") +assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) + +t = debug.getinfo(1) -- main +assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and + debug.getupvalue(t.func, 1) == "_ENV") + + +-- testing debugging of coroutines + +local function checktraceback (co, p, level) + local tb = debug.traceback(co, nil, level) + local i = 0 + for l in string.gmatch(tb, "[^\n]+\n?") do + assert(i == 0 or string.find(l, p[i])) + i = i + 1 + end + assert(p[i] == nil) +end + +local function f (n) + if n > 0 then + f(n - 1) + else + coroutine.yield() + end +end + +local co = coroutine.create(f) +coroutine.resume(co, 3) +checktraceback(co, { "yield", "db_mini.lua", "db_mini.lua", "db_mini.lua", "db_mini.lua" }) +checktraceback(co, { "db_mini.lua", "db_mini.lua", "db_mini.lua", "db_mini.lua" }, 1) +checktraceback(co, { "db_mini.lua", "db_mini.lua", "db_mini.lua" }, 2) +checktraceback(co, { "db_mini.lua" }, 4) +checktraceback(co, {}, 40) \ No newline at end of file From 8cef3b4c6505f3be543a9de02c7835a2ad7db76e Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Thu, 13 Feb 2025 20:48:18 +0900 Subject: [PATCH 04/22] Add: LuaDebug --- src/Lua/Internal/LuaDebug.cs | 695 +++++++++++++++++++++++++++++++ src/Lua/Standard/BasicLibrary.cs | 2 +- 2 files changed, 696 insertions(+), 1 deletion(-) create mode 100644 src/Lua/Internal/LuaDebug.cs diff --git a/src/Lua/Internal/LuaDebug.cs b/src/Lua/Internal/LuaDebug.cs new file mode 100644 index 00000000..d578aceb --- /dev/null +++ b/src/Lua/Internal/LuaDebug.cs @@ -0,0 +1,695 @@ +using Lua.Runtime; +using static Lua.Internal.OpMode; +using static Lua.Internal.OpArgMask; + +namespace Lua.Internal; + +internal readonly struct LuaDebug : IDisposable +{ + readonly LuaDebugBuffer buffer; + readonly uint version; + + LuaDebug(LuaDebugBuffer buffer, uint version) + { + this.buffer = buffer; + this.version = version; + } + + public string? Name + { + get + { + CheckVersion(); + return buffer.Name; + } + } + + public string? NameWhat + { + get + { + CheckVersion(); + return buffer.NameWhat; + } + } + + public string? What + { + get + { + CheckVersion(); + return buffer.What; + } + } + + public string? Source + { + get + { + CheckVersion(); + return buffer.Source; + } + } + + public int CurrentLine + { + get + { + CheckVersion(); + return buffer.CurrentLine; + } + } + + public int LineDefined + { + get + { + CheckVersion(); + return buffer.LineDefined; + } + } + + public int LastLineDefined + { + get + { + CheckVersion(); + return buffer.LastLineDefined; + } + } + + public int UpValueCount + { + get + { + CheckVersion(); + return buffer.UpValueCount; + } + } + + public int ParameterCount + { + get + { + CheckVersion(); + return buffer.ParameterCount; + } + } + + public bool IsVarArg + { + get + { + CheckVersion(); + return buffer.IsVarArg; + } + } + + public bool IsTailCall + { + get + { + CheckVersion(); + return buffer.IsTailCall; + } + } + + public ReadOnlySpan ShortSource + { + get + { + CheckVersion(); + return buffer.ShortSource.AsSpan(0, buffer.ShortSourceLength); + } + } + + + public static LuaDebug Create(LuaState state, CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunction function, int pc, ReadOnlySpan what, out bool isValid) + { + if (!state.DebugBufferPool.TryPop(out var buffer)) + { + buffer = new (state); + } + + isValid = buffer.GetInfo(prevFrame, frame, function, pc, what); + + return new (buffer, buffer.version); + } + + public void CheckVersion() + { + if (buffer.version != version) ThrowObjectDisposedException(); + } + + + public void Dispose() + { + if (buffer.version != version) ThrowObjectDisposedException(); + buffer.Return(version); + } + + void ThrowObjectDisposedException() + { + throw new ObjectDisposedException("This has been disposed"); + } + + + internal class LuaDebugBuffer(LuaState state) + { + internal uint version; + LuaState state = state; + public string? Name; + public string? NameWhat; + public string? What; + public string? Source; + public int CurrentLine; + public int LineDefined; + public int LastLineDefined; + public int UpValueCount; + public int ParameterCount; + public bool IsVarArg; + public bool IsTailCall; + public readonly char[] ShortSource = new char[59]; + public int ShortSourceLength; + + internal void Return(uint version) + { + if (this.version != version) throw new ObjectDisposedException("Buffer has been modified"); + + Name = null; + NameWhat = null; + What = null; + Source = null; + CurrentLine = 0; + LineDefined = 0; + LastLineDefined = 0; + UpValueCount = 0; + ParameterCount = 0; + IsVarArg = false; + IsTailCall = false; + + if (version < uint.MaxValue) + { + this.version++; + state.DebugBufferPool.Push(this); + } + } + + + internal bool GetInfo(CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunction function, int pc, ReadOnlySpan what) + { + Closure? closure = function as Closure; + int status = 1; + foreach (var c in what) + { + switch (c) + { + case 'S': + { + GetFuncInfo(function); + break; + } + case 'l': + { + CurrentLine = (pc >= 0 && closure is not null) ? closure.Proto.SourcePositions[pc].Line : -1; + break; + } + case 'u': + { + UpValueCount = (closure is null) ? 0 : closure.UpValues.Length; + if (closure is null) + { + IsVarArg = true; + ParameterCount = 0; + } + else + { + IsVarArg = closure.Proto.HasVariableArguments; + ParameterCount = closure.Proto.ParameterCount; + } + + break; + } + case 't': + { + IsTailCall = frame.HasValue && (frame.Value.Flags | CallStackFrameFlags.TailCall) == frame.Value.Flags; + break; + } + case 'n': + { + /* calling function is a known Lua function? */ + if (prevFrame is { Function: Closure prevFrameClosure }) + NameWhat = GetFuncName(prevFrameClosure.Proto, frame?.CallerInstructionIndex ?? 0, out Name); + else + NameWhat = null; + if (NameWhat is null) + { + NameWhat = ""; /* not found */ + Name = null; + } + else if (NameWhat != null && Name is "?") + { + Name = function.Name; + } + + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: + status = 0; /* invalid option */ + break; + } + } + + return status == 1; + } + + void GetFuncInfo(LuaFunction f) + { + if (f is not Closure cl) + { + Source = "=[C#]"; + LineDefined = -1; + LastLineDefined = -1; + What = "C#"; + } + else + { + var p = cl.Proto; + Source = p.GetRoot().Name; + LineDefined = p.LineDefined; + LastLineDefined = p.LastLineDefined; + What = (LineDefined == 0) ? "main" : "Lua"; + } + + ShortSourceLength = WriteShortSource(Source, ShortSource); + } + } + + + internal static string? GetLocalName(Chunk chunk, int register, int pc) + { + var locals = chunk.Locals; + foreach (var local in locals) + { + if (local.Index == register && pc >= local.StartPc && pc < local.EndPc) + { + return local.Name.ToString(); + } + + if (local.Index > register) + { + break; + } + } + + return null; + } + + static int FilterPc(int pc, int jmpTarget) + { + if (pc < jmpTarget) /* is code conditional (inside a jump)? */ + return -1; /* cannot know who sets that register */ + else return pc; /* current position sets that register */ + } + + internal static int FindSetRegister(Chunk chunk, int lastPc, int reg) + { + int pc; + int setReg = -1; /* keep last instruction that changed 'reg' */ + int jmpTarget = 0; /* any code before this address is conditional */ + var instructions = chunk.Instructions; + for (pc = 0; pc < lastPc; pc++) + { + Instruction i = instructions[pc]; + OpCode op = i.OpCode; + int a = i.A; + switch (op) + { + case OpCode.LoadNil: + { + int b = i.B; + if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */ + setReg = FilterPc(pc, jmpTarget); + break; + } + case OpCode.TForCall: + { + if (reg >= a + 2) /* affect all regs above its base */ + setReg = FilterPc(pc, jmpTarget); + break; + } + case OpCode.Call: + case OpCode.TailCall: + { + if (reg >= a) /* affect all registers above base */ + setReg = FilterPc(pc, jmpTarget); + break; + } + case OpCode.Jmp: + { + int b = i.SBx; + int dest = pc + 1 + b; + /* jump is forward and do not skip `lastpc'? */ + if (pc < dest && dest <= lastPc) + { + if (dest > jmpTarget) + jmpTarget = dest; /* update 'jmptarget' */ + } + + break; + } + case OpCode.Test: + { + if (reg == a) /* jumped code can change 'a' */ + setReg = FilterPc(pc, jmpTarget); + break; + } + default: + if (TestAMode(op) && reg == a) /* any instruction that set A */ + setReg = FilterPc(pc, jmpTarget); + break; + } + } + + return setReg; + } + + static void GetConstantName(Chunk p, int pc, int c, out string name) + { + if (c > 256) + { + /* is 'c' a constant? */ + ref var kvalue = ref p.Constants[c - 256]; + if (kvalue.TryReadString(out name)) + { + /* literal constant? */ + /* it is its own name */ + return; + } + /* else no reasonable name found */ + } + else + { + /* 'c' is a register */ + var what = GetName(p, pc, c, out name!); /* search for 'c' */ + if (what != null && what[0] == 'c') + { + /* found a constant name? */ + return; /* 'name' already filled */ + } + /* else no reasonable name found */ + } + + name = "?"; /* no reasonable name found */ + } + + + internal static string? GetName(Chunk chunk, int lastPc, int reg, out string? name) + { + name = GetLocalName(chunk, reg, lastPc); + if (name != null) + { + return "local"; + } + + var pc = FindSetRegister(chunk, lastPc, reg); + if (pc != -1) + { + /* could find instruction? */ + Instruction i = chunk.Instructions[pc]; + OpCode op = i.OpCode; + switch (op) + { + case OpCode.Move: + { + int b = i.B; /* move from 'b' to 'a' */ + if (b < i.A) + return GetName(chunk, pc, b, out name); /* get name for 'b' */ + break; + } + case OpCode.GetTabUp: + case OpCode.GetTable: + { + int k = i.C; /* key index */ + int t = i.B; /* table index */ + var vn = (op == OpCode.GetTable) /* name of indexed variable */ + ? GetLocalName(chunk, t + 1, pc) + : chunk.UpValues[t].Name.ToString(); + GetConstantName(chunk, pc, k, out name); + return vn is "_ENV" ? "global" : "field"; + } + case OpCode.GetUpVal: + { + name = chunk.UpValues[i.B].Name.ToString(); + return "upvalue"; + } + case OpCode.LoadK: + case OpCode.LoadKX: + { + uint b = (op == OpCode.LoadKX) + ? i.Bx + : (chunk.Instructions[pc + 1].Ax); + if (chunk.Constants[b].TryReadString(out name)) + { + return "constant"; + } + + break; + } + case OpCode.Self: + { + int k = i.C; /* key index */ + GetConstantName(chunk, pc, k, out name); + return "method"; + } + default: break; /* go through to return NULL */ + } + } + + return null; /* could not find reasonable name */ + } + + internal static string? GetFuncName(Chunk chunk, int pc, out string? name) + { + Instruction i = chunk.Instructions[pc]; /* calling instruction */ + switch (i.OpCode) + { + case OpCode.Call: + case OpCode.TailCall: /* get function name */ + return GetName(chunk, pc, i.A, out name); + case OpCode.TForCall: + { + /* for iterator */ + name = "for iterator"; + return "for iterator"; + } + case OpCode.Self: + case OpCode.GetTabUp: + case OpCode.GetTable: + name = "index"; + break; + case OpCode.SetTabUp: + case OpCode.SetTable: + name = "newindex"; + break; + case OpCode.Add: + name = "add"; + break; + case OpCode.Sub: + name = "sub"; + break; + case OpCode.Mul: + name = "mul"; + break; + case OpCode.Div: + name = "div"; + break; + case OpCode.Mod: + name = "mod"; + break; + case OpCode.Pow: + name = "pow"; + break; + case OpCode.Unm: + name = "unm"; + break; + case OpCode.Len: + name = "len"; + break; + case OpCode.Concat: + name = "concat"; + break; + case OpCode.Eq: + name = "eq"; + break; + case OpCode.Lt: + name = "lt"; + break; + case OpCode.Le: + name = "le"; + break; + default: + name = null; + return null; + } + + return "metamethod"; + } + + internal static int WriteShortSource(ReadOnlySpan source, Span dest) + { + const string PRE = "[string \""; + const int PRE_LEN = 9; + const string POS = "\"]"; + const int POS_LEN = 2; + const string RETS = "..."; + const int RETS_LEN = 3; + const string PREPOS = "[string \"\"]"; + + const int BUFFER_LEN = 59; + if (dest.Length != BUFFER_LEN) throw new ArgumentException("dest must be 60 chars long"); + + if (source.Length == 0) + { + PREPOS.AsSpan().CopyTo(dest); + return PREPOS.Length; + } + + if (source[0] == '=') + { + source = source[1..]; /* skip the '=' */ + /* 'literal' source */ + if (source.Length < BUFFER_LEN) /* small enough? */ + { + source.CopyTo(dest); + return source.Length; + } + else + { + /* truncate it */ + source[..BUFFER_LEN].CopyTo(dest); + return BUFFER_LEN; + } + } + else if (source[0] == '@') + { + /* file name */ + source = source[1..]; /* skip the '@' */ + if (source.Length <= BUFFER_LEN) /* small enough? */ + { + source.CopyTo(dest); + return source.Length; + } + else + { + /* add '...' before rest of name */ + RETS.AsSpan().CopyTo(dest); + source[^(BUFFER_LEN - RETS_LEN )..].CopyTo(dest[RETS_LEN..]); + + return BUFFER_LEN; + } + } + else + { + /* string; format as [string "source"] */ + + + PRE.AsSpan().CopyTo(dest); + int newLine = source.IndexOf('\n'); + if (newLine == -1 && source.Length < BUFFER_LEN - (PRE_LEN + RETS_LEN + POS_LEN)) + { + source.CopyTo(dest[PRE_LEN..]); + POS.AsSpan().CopyTo(dest[(PRE_LEN + source.Length)..]); + return PRE_LEN + source.Length + POS_LEN; + } + if (newLine != -1) + { + source = source[..newLine]; /* stop at first newline */ + } + + if (BUFFER_LEN - (PRE_LEN + RETS_LEN + POS_LEN) < source.Length) + { + source = source[..(BUFFER_LEN - PRE_LEN - RETS_LEN - POS_LEN)]; + } + + /* add '...' before rest of name */ + source.CopyTo(dest[PRE_LEN..]); + RETS.AsSpan().CopyTo(dest[(PRE_LEN+source.Length)..]); + POS.AsSpan().CopyTo(dest[(PRE_LEN+source.Length +RETS_LEN)..]); + return PRE_LEN+source.Length +RETS_LEN +POS_LEN; + + } + } + + static int GetOpMode(byte t, byte a, OpArgMask b, OpArgMask c, OpMode m) => (((t) << 7) | ((a) << 6) | (((byte)b) << 4) | (((byte)c) << 2) | ((byte)m)); + + + static readonly int[] OpModes = + [ + GetOpMode(0, 1, OpArgR, OpArgN, iABC), /* OP_MOVE */ + GetOpMode(0, 1, OpArgK, OpArgN, iABx), /* OP_LOADK */ + GetOpMode(0, 1, OpArgN, OpArgN, iABx), /* OP_LOADKX */ + GetOpMode(0, 1, OpArgU, OpArgU, iABC), /* OP_LOADBOOL */ + GetOpMode(0, 1, OpArgU, OpArgN, iABC), /* OP_LOADNIL */ + GetOpMode(0, 1, OpArgU, OpArgN, iABC), /* OP_GETUPVAL */ + GetOpMode(0, 1, OpArgU, OpArgK, iABC), /* OP_GETTABUP */ + GetOpMode(0, 1, OpArgR, OpArgK, iABC), /* OP_GETTABLE */ + GetOpMode(0, 0, OpArgK, OpArgK, iABC), /* OP_SETTABUP */ + GetOpMode(0, 0, OpArgU, OpArgN, iABC), /* OP_SETUPVAL */ + GetOpMode(0, 0, OpArgK, OpArgK, iABC), /* OP_SETTABLE */ + GetOpMode(0, 1, OpArgU, OpArgU, iABC), /* OP_NEWTABLE */ + GetOpMode(0, 1, OpArgR, OpArgK, iABC), /* OP_SELF */ + GetOpMode(0, 1, OpArgK, OpArgK, iABC), /* OP_ADD */ + GetOpMode(0, 1, OpArgK, OpArgK, iABC), /* OP_SUB */ + GetOpMode(0, 1, OpArgK, OpArgK, iABC), /* OP_MUL */ + GetOpMode(0, 1, OpArgK, OpArgK, iABC), /* OP_DIV */ + GetOpMode(0, 1, OpArgK, OpArgK, iABC), /* OP_MOD */ + GetOpMode(0, 1, OpArgK, OpArgK, iABC), /* OP_POW */ + GetOpMode(0, 1, OpArgR, OpArgN, iABC), /* OP_UNM */ + GetOpMode(0, 1, OpArgR, OpArgN, iABC), /* OP_NOT */ + GetOpMode(0, 1, OpArgR, OpArgN, iABC), /* OP_LEN */ + GetOpMode(0, 1, OpArgR, OpArgR, iABC), /* OP_CONCAT */ + GetOpMode(0, 0, OpArgR, OpArgN, iAsBx), /* OP_JMP */ + GetOpMode(1, 0, OpArgK, OpArgK, iABC), /* OP_EQ */ + GetOpMode(1, 0, OpArgK, OpArgK, iABC), /* OP_LT */ + GetOpMode(1, 0, OpArgK, OpArgK, iABC), /* OP_LE */ + GetOpMode(1, 0, OpArgN, OpArgU, iABC), /* OP_TEST */ + GetOpMode(1, 1, OpArgR, OpArgU, iABC), /* OP_TESTSET */ + GetOpMode(0, 1, OpArgU, OpArgU, iABC), /* OP_CALL */ + GetOpMode(0, 1, OpArgU, OpArgU, iABC), /* OP_TAILCALL */ + GetOpMode(0, 0, OpArgU, OpArgN, iABC), /* OP_RETURN */ + GetOpMode(0, 1, OpArgR, OpArgN, iAsBx), /* OP_FORLOOP */ + GetOpMode(0, 1, OpArgR, OpArgN, iAsBx), /* OP_FORPREP */ + GetOpMode(0, 0, OpArgN, OpArgU, iABC), /* OP_TFORCALL */ + GetOpMode(0, 1, OpArgR, OpArgN, iAsBx), /* OP_TFORLOOP */ + GetOpMode(0, 0, OpArgU, OpArgU, iABC), /* OP_SETLIST */ + GetOpMode(0, 1, OpArgU, OpArgN, iABx), /* OP_CLOSURE */ + GetOpMode(0, 1, OpArgU, OpArgN, iABC), /* OP_VARARG */ + GetOpMode(0, 0, OpArgU, OpArgU, iAx), /* OP_EXTRAARG */ + ]; + + internal static OpMode GetOpMode(OpCode m) => (OpMode)(OpModes[(int)m] & 3); + internal static OpArgMask GetBMode(OpCode m) => (OpArgMask)((OpModes[(int)m] >> 4) & 3); + internal static OpArgMask GetCMode(OpCode m) => (OpArgMask)((OpModes[(int)m] >> 2) & 3); + internal static bool TestAMode(OpCode m) => (OpModes[(int)m] & (1 << 6)) != 0; + internal static bool TestTMode(OpCode m) => (OpModes[(int)m] & (1 << 7)) != 0; +} + +internal enum OpMode : byte +{ + iABC, + iABx, + iAsBx, + iAx +} + +internal enum OpArgMask : byte +{ + OpArgN, /* argument is not used */ + OpArgU, /* argument is used */ + OpArgR, /* argument is a register or a jump offset */ + OpArgK /* argument is a constant or register/constant */ +} \ No newline at end of file diff --git a/src/Lua/Standard/BasicLibrary.cs b/src/Lua/Standard/BasicLibrary.cs index cc72b997..47b3492b 100644 --- a/src/Lua/Standard/BasicLibrary.cs +++ b/src/Lua/Standard/BasicLibrary.cs @@ -200,7 +200,7 @@ public ValueTask Load(LuaFunctionExecutionContext context, Memory { if (arg0.TryRead(out var str)) { - var chunk = LuaCompiler.Default.Compile(str, arg1 ?? "chunk"); + var chunk = LuaCompiler.Default.Compile(str, arg1 ?? str); buffer.Span[0] = new Closure(context.State, chunk, arg3); return new(1); } From 3064beed0fb864a2b02dd14bcc36d7ca8ad98597 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Thu, 13 Feb 2025 23:57:26 +0900 Subject: [PATCH 05/22] Fix: wrong function name with debug --- src/Lua/Internal/LuaDebug.cs | 3 ++- src/Lua/Runtime/Tracebacks.cs | 40 +++++++++++++++++++++++++------- src/Lua/Standard/DebugLibrary.cs | 28 ++++++++++++---------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/Lua/Internal/LuaDebug.cs b/src/Lua/Internal/LuaDebug.cs index d578aceb..0f489335 100644 --- a/src/Lua/Internal/LuaDebug.cs +++ b/src/Lua/Internal/LuaDebug.cs @@ -379,7 +379,7 @@ internal static int FindSetRegister(Chunk chunk, int lastPc, int reg) static void GetConstantName(Chunk p, int pc, int c, out string name) { - if (c > 256) + if (c >= 256) { /* is 'c' a constant? */ ref var kvalue = ref p.Constants[c - 256]; @@ -435,6 +435,7 @@ static void GetConstantName(Chunk p, int pc, int c, out string name) { int k = i.C; /* key index */ int t = i.B; /* table index */ + var vn = (op == OpCode.GetTable) /* name of indexed variable */ ? GetLocalName(chunk, t + 1, pc) : chunk.UpValues[t].Name.ToString(); diff --git a/src/Lua/Runtime/Tracebacks.cs b/src/Lua/Runtime/Tracebacks.cs index d8c26b38..eb64dc20 100644 --- a/src/Lua/Runtime/Tracebacks.cs +++ b/src/Lua/Runtime/Tracebacks.cs @@ -83,22 +83,44 @@ internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan 0 && caller is Closure callerClosure) { var t = LuaDebug.GetFuncName(callerClosure.Proto, stackFrames[index - 1].CallerInstructionIndex, out var name); - if (t is not null and not "global") + if (t is not null) { - list.AddRange(t); - list.AddRange(" '"); - list.AddRange(name); - list.AddRange("'\n"); + if (t is "global") + { + list.AddRange("function '"); + list.AddRange(name); + list.AddRange("'\n"); + } + else + { + list.AddRange(t); + list.AddRange(" '"); + list.AddRange(name); + list.AddRange("'\n"); + } + continue; } } - list.AddRange("function '"); - list.AddRange(p.Name); - list.AddRange("\n"); + if (p.Name != "") + { + list.AddRange("function '"); + list.AddRange(p.Name); + list.AddRange("'\n"); + } + else + { + list.AddRange("function <"); + list.AddRange(shortSourceBuffer[..len]); + list.AddRange(":"); + p.LineDefined.TryFormat(intFormatBuffer, out charsWritten, provider: CultureInfo.InvariantCulture); + list.AddRange(intFormatBuffer[..charsWritten]); + list.AddRange(">\n"); + } } } - return list.AsSpan().ToString(); + return list.AsSpan()[..^1].ToString(); } } \ No newline at end of file diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 89064b2a..764ba611 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; using Lua.Runtime; using Lua.Internal; + namespace Lua.Standard; public class DebugLibrary @@ -256,8 +257,7 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Memory(argOffset + 1, 0); - + var level = context.GetArgumentOrDefault(argOffset + 1, argOffset == 0 ? 1 : 0); if (message.Type is not (LuaValueType.Nil or LuaValueType.String or LuaValueType.Number)) { @@ -270,8 +270,9 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Memory0? thread.GetCallStackFrames()[^1]:default; + thread.PushCallStackFrame(currentFrame); var callStack = thread.GetCallStackFrames(); var skipCount = Math.Min(level, callStack.Length - 1); var frames = callStack[1..^skipCount]; @@ -319,7 +320,7 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory(argOffset + 1,"flnStu"); + string what = context.GetArgumentOrDefault(argOffset + 1, "flnStu"); CallStackFrame? previousFrame = null; CallStackFrame? currentFrame = null; int pc = 0; @@ -332,20 +333,20 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory(argOffset) + 1; - + var callStack = thread.GetCallStackFrames(); if (level <= 0 || level > callStack.Length) { context.ThrowBadArgument(1, "level out of range"); } + currentFrame = thread.GetCallStackFrames()[^(level)]; functionToInspect = currentFrame.Value.Function; - previousFrame = level +1 <= callStack.Length?callStack[^(level + 1)]:null; - if(level!=0) + previousFrame = level + 1 <= callStack.Length ? callStack[^(level + 1)] : null; + if (level != 0) { pc = thread.GetCallStackFrames()[^(level - 1)].CallerInstructionIndex; } - } else { @@ -365,7 +366,8 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory GetInfo(LuaFunctionExecutionContext context, Memory Date: Fri, 14 Feb 2025 10:52:52 +0900 Subject: [PATCH 06/22] Fix: find function name from global fields for traceback --- src/Lua/LuaState.cs | 4 +-- src/Lua/LuaTable.cs | 2 ++ src/Lua/Runtime/Tracebacks.cs | 44 +++++++++++++++++++------------- src/Lua/Standard/DebugLibrary.cs | 2 +- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index 19d624b9..9f5debda 100644 --- a/src/Lua/LuaState.cs +++ b/src/Lua/LuaState.cs @@ -95,7 +95,7 @@ public Traceback GetTraceback() { if (threadStack.Count == 0) { - return new() + return new(this) { RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function, StackFrames = MainThread.GetCallStackFrames()[1..] @@ -116,7 +116,7 @@ public Traceback GetTraceback() list.Add(frame); } } - return new() + return new(this) { RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function, StackFrames = list.AsSpan().ToArray() diff --git a/src/Lua/LuaTable.cs b/src/Lua/LuaTable.cs index dd4fcc41..6fc1f70a 100644 --- a/src/Lua/LuaTable.cs +++ b/src/Lua/LuaTable.cs @@ -19,6 +19,8 @@ public LuaTable(int arrayCapacity, int dictionaryCapacity) readonly LuaValueDictionary dictionary; LuaTable? metatable; + internal LuaValueDictionary Dictionary => dictionary; + public LuaValue this[LuaValue key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Lua/Runtime/Tracebacks.cs b/src/Lua/Runtime/Tracebacks.cs index eb64dc20..4b906c3b 100644 --- a/src/Lua/Runtime/Tracebacks.cs +++ b/src/Lua/Runtime/Tracebacks.cs @@ -5,8 +5,9 @@ namespace Lua.Runtime; -public class Traceback +public class Traceback(LuaState state) { + public LuaState State => state; public required Closure RootFunc { get; init; } public required CallStackFrame[] StackFrames { get; init; } @@ -35,10 +36,10 @@ internal SourcePosition LastPosition public override string ToString() { - return GetTracebackString(RootFunc, StackFrames, LuaValue.Nil); + return GetTracebackString(State,RootFunc, StackFrames, LuaValue.Nil); } - internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message) + internal static string GetTracebackString(LuaState state,Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message) { using var list = new PooledList(64); if (message.Type is not LuaValueType.Nil) @@ -79,6 +80,19 @@ internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan 1 ? stackFrames[index - 2].Function : rootFunc; if (index > 0 && caller is Closure callerClosure) { @@ -103,21 +117,15 @@ internal static string GetTracebackString(Closure rootFunc, ReadOnlySpan\n"); - } + + list.AddRange("function <"); + list.AddRange(shortSourceBuffer[..len]); + list.AddRange(":"); + p.LineDefined.TryFormat(intFormatBuffer, out charsWritten, provider: CultureInfo.InvariantCulture); + list.AddRange(intFormatBuffer[..charsWritten]); + list.AddRange(">\n"); + + Next: ; } } diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 764ba611..9fc6642b 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -276,7 +276,7 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Memory Date: Wed, 26 Feb 2025 14:38:27 +0900 Subject: [PATCH 07/22] Add: ILuaUserData.UserValues property --- src/Lua/LuaUserData.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Lua/LuaUserData.cs b/src/Lua/LuaUserData.cs index 98b9a3cf..19671373 100644 --- a/src/Lua/LuaUserData.cs +++ b/src/Lua/LuaUserData.cs @@ -1,6 +1,22 @@ namespace Lua; +internal sealed class LuaUserData : ILuaUserData +{ + public LuaTable? Metatable { get; set; } + readonly LuaValue[] userValues = new LuaValue[1]; + public Span UserValues => userValues; + + public LuaUserData(LuaValue value, LuaTable? metatable) + { + userValues[0] = value; + Metatable = metatable; + } +} + public interface ILuaUserData { LuaTable? Metatable { get; set; } + + //We use span for compatibility with lua5.4. + Span UserValues => default; } \ No newline at end of file From 9f59f05634aed557aa3eba7d1f7ebd01a95ca30b Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Wed, 26 Feb 2025 14:38:52 +0900 Subject: [PATCH 08/22] Add: LightUserData --- src/Lua/LuaValue.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Lua/LuaValue.cs b/src/Lua/LuaValue.cs index 924e309f..c4ed3561 100644 --- a/src/Lua/LuaValue.cs +++ b/src/Lua/LuaValue.cs @@ -14,6 +14,7 @@ public enum LuaValueType : byte Number, Function, Thread, + LightUserData, UserData, Table, } @@ -136,6 +137,16 @@ public bool TryRead(out T result) } else { + break; + } + case LuaValueType.LightUserData: + { + if (referenceValue is T tValue) + { + result = tValue; + return true; + } + break; } case LuaValueType.UserData: @@ -360,6 +371,7 @@ internal T UnsafeRead() case LuaValueType.Thread: case LuaValueType.Function: case LuaValueType.Table: + case LuaValueType.LightUserData: case LuaValueType.UserData: { var v = referenceValue!; @@ -378,6 +390,13 @@ public bool ToBoolean() return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LuaValue(object obj) + { + Type = LuaValueType.LightUserData; + referenceValue = obj; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LuaValue(bool value) { @@ -517,6 +536,7 @@ public override string ToString() LuaValueType.Function => $"function: {referenceValue!.GetHashCode()}", LuaValueType.Thread => $"thread: {referenceValue!.GetHashCode()}", LuaValueType.Table => $"table: {referenceValue!.GetHashCode()}", + LuaValueType.LightUserData => $"userdata: {referenceValue!.GetHashCode()}", LuaValueType.UserData => $"userdata: {referenceValue!.GetHashCode()}", _ => "", }; @@ -554,6 +574,11 @@ public static bool TryGetLuaValueType(Type type, out LuaValueType result) result = LuaValueType.Thread; return true; } + else if (type == typeof(ILuaUserData) || type.IsAssignableFrom(typeof(ILuaUserData))) + { + result = LuaValueType.UserData; + return true; + } result = default; return false; From 9f927240a842081c3a0184a5ebb23e7e6ac56ffc Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Wed, 26 Feb 2025 14:45:37 +0900 Subject: [PATCH 09/22] Add: debug.getuservalue, setuservalue and upvalueid --- src/Lua/LuaFunctionExecutionContext.cs | 8 +++ src/Lua/Standard/DebugLibrary.cs | 72 ++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index 970c0de2..70630fa7 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -65,6 +65,10 @@ public T GetArgument(int index) { LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString()); } + else if(arg.Type is LuaValueType.UserData or LuaValueType.LightUserData) + { + LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead()?.GetType().ToString()??"userdata: 0"); + } else { LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.Type.ToString()); @@ -100,6 +104,10 @@ internal T GetArgumentOrDefault(int index,T defaultValue =default!) { LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString()); } + else if(arg.Type is LuaValueType.UserData or LuaValueType.LightUserData) + { + LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead()?.GetType().ToString()??"userdata: 0"); + } else { LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.Type.ToString()); diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 9fc6642b..3ec2cf5b 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -18,8 +18,11 @@ public DebugLibrary() 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("getinfo", GetInfo), ]; @@ -252,6 +255,47 @@ public ValueTask SetMetatable(LuaFunctionExecutionContext context, Memory GetUserValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + if (!context.GetArgumentOrDefault(0).TryRead(out var iUserData)) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + var index = 1; // context.GetArgument(1); //for lua 5.4 + var userValues = iUserData.UserValues; + if (index > userValues.Length + //index < 1 || // for lua 5.4 + ) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + buffer.Span[0] = userValues[index - 1]; + return new(1); + } + + public ValueTask SetUserValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var iUserData = context.GetArgument(0); + var value = context.GetArgument(1); + var index = 1; // context.GetArgument(2);// for lua 5.4 + var userValues = iUserData.UserValues; + if (index > userValues.Length + //|| index < 1 // for lua 5.4 + ) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + userValues[index - 1] = value; + buffer.Span[0] = new LuaValue(iUserData); + return new(1); + } + public ValueTask Traceback(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) { var thread = GetLuaThread(context, out var argOffset); @@ -270,13 +314,13 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Memory0? thread.GetCallStackFrames()[^1]:default; + + var currentFrame = thread.GetCallStackFrames().Length > 0 ? thread.GetCallStackFrames()[^1] : default; thread.PushCallStackFrame(currentFrame); var callStack = thread.GetCallStackFrames(); var skipCount = Math.Min(level, callStack.Length - 1); var frames = callStack[1..^skipCount]; - buffer.Span[0] = Runtime.Traceback.GetTracebackString(context.State,(Closure)callStack[0].Function, frames, message); + buffer.Span[0] = Runtime.Traceback.GetTracebackString(context.State, (Closure)callStack[0].Function, frames, message); thread.PopCallStackFrame(); return new(1); } @@ -287,6 +331,28 @@ public ValueTask GetRegistry(LuaFunctionExecutionContext context, Memory UpValueId(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var n1 = context.GetArgument(1); + var f1 = context.GetArgument(0); + + if (f1 is not Closure closure) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + var upValues = closure.GetUpValuesSpan(); + if (n1 <= 0 || n1 > upValues.Length) + { + buffer.Span[0] = LuaValue.Nil; + return new(1); + } + + buffer.Span[0] = new LuaValue(upValues[n1 - 1]); + return new(1); + } + public ValueTask UpValueJoin(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) { var n2 = context.GetArgument(3); From dee257d1542f599da5737ff73b61540cc3b1aaf9 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:41:37 +0900 Subject: [PATCH 10/22] Add: CsClosure --- src/Lua/LuaFunctionExecutionContext.cs | 33 +++++++------ src/Lua/Runtime/CsClosure.cs | 6 +++ src/Lua/Standard/CoroutineLibrary.cs | 7 ++- src/Lua/Standard/DebugLibrary.cs | 64 ++++++++++++++++++-------- src/Lua/Standard/FileHandle.cs | 7 +-- src/Lua/Standard/IOLibrary.cs | 13 ++++-- src/Lua/Standard/StringLibrary.cs | 10 ++-- 7 files changed, 97 insertions(+), 43 deletions(-) create mode 100644 src/Lua/Runtime/CsClosure.cs diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index 70630fa7..87df5f6a 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Lua.CodeAnalysis; +using Lua.Runtime; namespace Lua; @@ -19,10 +20,7 @@ public readonly record struct LuaFunctionExecutionContext public ReadOnlySpan Arguments { - get - { - return Thread.GetStackValues().Slice(FrameBase, ArgumentCount); - } + get { return Thread.GetStackValues().Slice(FrameBase, ArgumentCount); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -37,7 +35,7 @@ public LuaValue GetArgument(int index) ThrowIfArgumentNotExists(index); return Arguments[index]; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal LuaValue GetArgumentOrDefault(int index, LuaValue defaultValue = default) { @@ -45,6 +43,7 @@ internal LuaValue GetArgumentOrDefault(int index, LuaValue defaultValue = defaul { return defaultValue; } + return Arguments[index]; } @@ -65,9 +64,9 @@ public T GetArgument(int index) { LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString()); } - else if(arg.Type is LuaValueType.UserData or LuaValueType.LightUserData) + else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData) { - LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead()?.GetType().ToString()??"userdata: 0"); + LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead()?.GetType().ToString() ?? "userdata: 0"); } else { @@ -79,7 +78,7 @@ public T GetArgument(int index) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal T GetArgumentOrDefault(int index,T defaultValue =default!) + internal T GetArgumentOrDefault(int index, T defaultValue = default!) { if (ArgumentCount <= index) { @@ -87,12 +86,12 @@ internal T GetArgumentOrDefault(int index,T defaultValue =default!) } var arg = Arguments[index]; - - if(arg.Type is LuaValueType.Nil) + + if (arg.Type is LuaValueType.Nil) { return defaultValue; } - + if (!arg.TryRead(out var argValue)) { var t = typeof(T); @@ -104,9 +103,9 @@ internal T GetArgumentOrDefault(int index,T defaultValue =default!) { LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, type.ToString(), arg.Type.ToString()); } - else if(arg.Type is LuaValueType.UserData or LuaValueType.LightUserData) + else if (arg.Type is LuaValueType.UserData or LuaValueType.LightUserData) { - LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead()?.GetType().ToString()??"userdata: 0"); + LuaRuntimeException.BadArgument(State.GetTraceback(), index + 1, Thread.GetCurrentFrame().Function.Name, t.Name, arg.UnsafeRead()?.GetType().ToString() ?? "userdata: 0"); } else { @@ -116,7 +115,13 @@ internal T GetArgumentOrDefault(int index,T defaultValue =default!) return argValue; } - + + public CsClosure? GetCsClosure() + { + var stack = Thread.CallStack[^1]; + return stack.Function as CsClosure; + } + internal void ThrowBadArgument(int index, string message) { LuaRuntimeException.BadArgument(State.GetTraceback(), index, Thread.GetCurrentFrame().Function.Name, message); diff --git a/src/Lua/Runtime/CsClosure.cs b/src/Lua/Runtime/CsClosure.cs new file mode 100644 index 00000000..99f92d35 --- /dev/null +++ b/src/Lua/Runtime/CsClosure.cs @@ -0,0 +1,6 @@ +namespace Lua.Runtime; + +public sealed class CsClosure(string name,LuaValue[] upValues,Func, CancellationToken, ValueTask> func) : LuaFunction(name, func) +{ + public readonly LuaValue[] UpValues = upValues; +} \ No newline at end of file diff --git a/src/Lua/Standard/CoroutineLibrary.cs b/src/Lua/Standard/CoroutineLibrary.cs index 93b96a7f..6516c5be 100644 --- a/src/Lua/Standard/CoroutineLibrary.cs +++ b/src/Lua/Standard/CoroutineLibrary.cs @@ -1,3 +1,5 @@ +using Lua.Runtime; + namespace Lua.Standard; public sealed class CoroutineLibrary @@ -57,8 +59,9 @@ public ValueTask Wrap(LuaFunctionExecutionContext context, Memory var arg0 = context.GetArgument(0); var thread = new LuaCoroutine(arg0, false); - buffer.Span[0] = new LuaFunction("wrap", async (context, buffer, cancellationToken) => + buffer.Span[0] = new CsClosure("wrap", [thread],static async (context, buffer, cancellationToken) => { + var thread = context.GetCsClosure()!.UpValues[0].Read(); var stack = context.Thread.Stack; var frameBase = stack.Count; @@ -68,7 +71,7 @@ public ValueTask Wrap(LuaFunctionExecutionContext context, Memory { Base = frameBase, VariableArgumentCount = 0, - Function = arg0, + Function = thread.Function, }); try { diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 3ec2cf5b..007ec445 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -184,20 +184,35 @@ public ValueTask GetUpValue(LuaFunctionExecutionContext context, Memory(1) - 1; if (func is not Closure closure) { + if (func is CsClosure csClosure) + { + var upValues = csClosure.UpValues; + if (index < 0 || index >= upValues.Length) + { + return new(0); + } + + buffer.Span[0] = ""; + buffer.Span[1] = upValues[index]; + return new(1); + } + return new(0); } - var upValues = closure.UpValues; - var descriptions = closure.Proto.UpValues; - if (index < 0 || index >= descriptions.Length) { - return new(0); - } + var upValues = closure.UpValues; + var descriptions = closure.Proto.UpValues; + if (index < 0 || index >= descriptions.Length) + { + return new(0); + } - var description = descriptions[index]; - buffer.Span[0] = description.Name.ToString(); - buffer.Span[1] = upValues[index].GetValue(); - return new(2); + var description = descriptions[index]; + buffer.Span[0] = description.Name.ToString(); + buffer.Span[1] = upValues[index].GetValue(); + return new(2); + } } public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) @@ -207,20 +222,33 @@ public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory= upValues.Length) + { + upValues[index - 1] = value; + buffer.Span[0] = ""; + return new(0); + } + } + return new(0); } - var upValues = closure.UpValues; - var descriptions = closure.Proto.UpValues; - if (index < 0 || index >= descriptions.Length) { - return new(0); - } + var upValues = closure.UpValues; + var descriptions = closure.Proto.UpValues; + if (index < 0 || index >= descriptions.Length) + { + return new(0); + } - var description = descriptions[index]; - buffer.Span[0] = description.Name.ToString(); - upValues[index].SetValue(value); - return new(1); + var description = descriptions[index]; + buffer.Span[0] = description.Name.ToString(); + upValues[index].SetValue(value); + return new(1); + } } public ValueTask GetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) diff --git a/src/Lua/Standard/FileHandle.cs b/src/Lua/Standard/FileHandle.cs index ba1a5dc6..f18adbeb 100644 --- a/src/Lua/Standard/FileHandle.cs +++ b/src/Lua/Standard/FileHandle.cs @@ -176,11 +176,12 @@ public void Close() ? context.Arguments[1] : "*l"; - LuaValue[] formats = [format]; - buffer.Span[0] = new LuaFunction("iterator", (context, buffer, cancellationToken) => + buffer.Span[0] = new CsClosure("iterator", [new (file),format],static (context, buffer, cancellationToken) => { - var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, buffer, true); + var upValues = context.GetCsClosure()!.UpValues.AsSpan(); + var file = upValues[0].Read(); + var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], buffer, true); return new(resultCount); }); diff --git a/src/Lua/Standard/IOLibrary.cs b/src/Lua/Standard/IOLibrary.cs index 4a95d220..755f702b 100644 --- a/src/Lua/Standard/IOLibrary.cs +++ b/src/Lua/Standard/IOLibrary.cs @@ -1,4 +1,5 @@ using Lua.Internal; +using Lua.Runtime; using Lua.Standard.Internal; namespace Lua.Standard; @@ -96,8 +97,9 @@ public ValueTask Lines(LuaFunctionExecutionContext context, Memory()["stdio"].Read(); - buffer.Span[0] = new LuaFunction("iterator", (context, buffer, ct) => + buffer.Span[0] = new CsClosure("iterator",[new (file)] ,static (context, buffer, ct) => { + var file = context.GetCsClosure()!.UpValues[0].Read(); var resultCount = IOHelper.Read(context.State, file, "lines", 0, [], buffer, true); if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil) { @@ -115,10 +117,15 @@ public ValueTask Lines(LuaFunctionExecutionContext context, Memory(); - var formats = context.Arguments[1..].ToArray(); + var upValues = new LuaValue[context.Arguments.Length]; + upValues[0] = new(file); + context.Arguments[1..].CopyTo(upValues[1..]); - buffer.Span[0] = new LuaFunction("iterator", (context, buffer, ct) => + buffer.Span[0] = new CsClosure("iterator", upValues, static (context, buffer, ct) => { + var upValues = context.GetCsClosure()!.UpValues; + var file = upValues[0].Read(); + var formats = upValues.AsSpan(1); var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, buffer, true); if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil) { diff --git a/src/Lua/Standard/StringLibrary.cs b/src/Lua/Standard/StringLibrary.cs index 9eeb1bb1..ab011cc5 100644 --- a/src/Lua/Standard/StringLibrary.cs +++ b/src/Lua/Standard/StringLibrary.cs @@ -1,5 +1,7 @@ using System.Text; +using System.Text.RegularExpressions; using Lua.Internal; +using Lua.Runtime; namespace Lua.Standard; @@ -429,17 +431,19 @@ public ValueTask GMatch(LuaFunctionExecutionContext context, Memory + buffer.Span[0] = new CsClosure("iterator",[new LuaValue(matches),0],static (context, buffer, cancellationToken) => { + var upValues = context.GetCsClosure()!.UpValues; + var matches = upValues[0].Read(); + var i = upValues[1].Read(); if (matches.Count > i) { var match = matches[i]; var groups = match.Groups; i++; - + upValues[1] = i; if (groups.Count == 1) { buffer.Span[0] = match.Value; From 4c0317351c7c8a5c854f5af0a272ab40639fa537 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:52:09 +0900 Subject: [PATCH 11/22] Fix: improve supports for source position tracking --- .../CodeAnalysis/Compilation/LuaCompiler.cs | 45 +++++++++++-------- src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs | 2 +- .../FunctionDeclarationExpressionNode.cs | 2 +- .../Nodes/FunctionDeclarationStatementNode.cs | 2 +- .../Syntax/Nodes/IfStatementNode.cs | 1 + .../Nodes/LocalFunctionDeclarationNode.cs | 2 +- .../TableMethodDeclarationStatementNode.cs | 2 +- src/Lua/CodeAnalysis/Syntax/Parser.cs | 32 +++++++++---- tests/Lua.Tests/ParserTests.cs | 4 +- 9 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs index 6dc80401..f6696f9e 100644 --- a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs +++ b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs @@ -21,6 +21,8 @@ public Chunk Compile(LuaSyntaxTree syntaxTree, string? chunkName = null) { using var context = FunctionCompilationContext.Create(null); context.HasVariableArguments = true; + context.LineDefined = syntaxTree.Position.Line; + context.LastLineDefined = syntaxTree.Position.Line; // set global enviroment upvalue context.AddUpValue(new() { @@ -586,10 +588,10 @@ void CompileCallFunctionExpression(CallFunctionExpressionNode node, ScopeCompila // function declaration public bool VisitFunctionDeclarationExpressionNode(FunctionDeclarationExpressionNode node, ScopeCompilationContext context) { - var funcIndex = CompileFunctionProto(ReadOnlyMemory.Empty, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.LastLineDefined); + var funcIndex = CompileFunctionProto(ReadOnlyMemory.Empty, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.EndPosition.Line); // push closure instruction - context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true); + context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.EndPosition, true); return true; } @@ -604,23 +606,23 @@ public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationS }); // compile function - var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.LastLineDefined); + var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.EndPosition.Line); // push closure instruction - context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true); + context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.EndPosition, true); return true; } public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNode node, ScopeCompilationContext context) { - var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.LastLineDefined); + var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.EndPosition.Line); // add closure var index = context.Function.GetConstantIndex(node.Name.ToString()); // push closure instruction - context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.Position, true); + context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.EndPosition, true); if (context.TryGetLocalVariableInThisScope(node.Name, out var variable)) { @@ -639,7 +641,7 @@ public bool VisitFunctionDeclarationStatementNode(FunctionDeclarationStatementNo public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationStatementNode node, ScopeCompilationContext context) { var funcIdentifier = node.MemberPath[^1]; - var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length + 1, node.HasVariableArguments, node.HasSelfParameter, node.LineDefined, node.LastLineDefined); + var funcIndex = CompileFunctionProto(funcIdentifier.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length + 1, node.HasVariableArguments, node.HasSelfParameter, node.LineDefined, node.EndPosition.Line); // add closure var index = context.Function.GetConstantIndex(funcIdentifier.Name.ToString()); @@ -660,7 +662,7 @@ public bool VisitTableMethodDeclarationStatementNode(TableMethodDeclarationState // push closure instruction var closureIndex = context.StackPosition; - context.PushInstruction(Instruction.Closure(closureIndex, funcIndex), node.Position, true); + context.PushInstruction(Instruction.Closure(closureIndex, funcIndex), node.EndPosition, true); // set table context.PushInstruction(Instruction.SetTable(tableIndex, (ushort)(index + 256), closureIndex), funcIdentifier.Position); @@ -758,10 +760,10 @@ public bool VisitIfStatementNode(IfStatementNode node, ScopeCompilationContext c // if using (var scopeContext = context.CreateChildScope()) { - CompileConditionNode(node.IfNode.ConditionNode, scopeContext, true); + CompileConditionNode(node.IfNode.ConditionNode, scopeContext, true, node.IfNode.Position); var ifPosition = scopeContext.Function.Instructions.Length; - scopeContext.PushInstruction(Instruction.Jmp(0, 0), node.Position); + scopeContext.PushInstruction(Instruction.Jmp(0, 0), node.IfNode.Position); foreach (var childNode in node.IfNode.ThenNodes) { @@ -772,7 +774,7 @@ public bool VisitIfStatementNode(IfStatementNode node, ScopeCompilationContext c if (hasElse) { endJumpIndexList.Add(scopeContext.Function.Instructions.Length); - scopeContext.PushInstruction(Instruction.Jmp(stackPositionToClose, 0), node.Position, true); + scopeContext.PushInstruction(Instruction.Jmp(stackPositionToClose, 0), node.IfNode.ThenNodes[^1].Position, true); } else { @@ -790,7 +792,7 @@ public bool VisitIfStatementNode(IfStatementNode node, ScopeCompilationContext c CompileConditionNode(elseIf.ConditionNode, scopeContext, true); var elseifPosition = scopeContext.Function.Instructions.Length; - scopeContext.PushInstruction(Instruction.Jmp(0, 0), node.Position); + scopeContext.PushInstruction(Instruction.Jmp(0, 0), elseIf.Position); foreach (var childNode in elseIf.ThenNodes) { @@ -802,11 +804,11 @@ public bool VisitIfStatementNode(IfStatementNode node, ScopeCompilationContext c if (hasElse) { endJumpIndexList.Add(scopeContext.Function.Instructions.Length); - scopeContext.PushInstruction(Instruction.Jmp(stackPositionToClose, 0), node.Position); + scopeContext.PushInstruction(Instruction.Jmp(stackPositionToClose, 0), elseIf.Position); } else { - scopeContext.TryPushCloseUpValue(stackPositionToClose, node.Position); + scopeContext.TryPushCloseUpValue(stackPositionToClose, elseIf.Position); } scopeContext.Function.Instructions[elseifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - elseifPosition; @@ -847,8 +849,9 @@ public bool VisitRepeatStatementNode(RepeatStatementNode node, ScopeCompilationC CompileConditionNode(node.ConditionNode, scopeContext, true); var a = scopeContext.HasCapturedLocalVariables ? (byte)(stackPosition + 1) : (byte)0; - scopeContext.PushInstruction(Instruction.Jmp(a, startIndex - scopeContext.Function.Instructions.Length - 1), node.Position); - scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position); + var untilPosition = node.ConditionNode.Position; + scopeContext.PushInstruction(Instruction.Jmp(a, startIndex - scopeContext.Function.Instructions.Length - 1), untilPosition); + scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, untilPosition); context.Function.LoopLevel--; @@ -1018,7 +1021,7 @@ public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeComp // push OP_TFORCALL and OP_TFORLOOP context.PushInstruction(Instruction.TForCall(startPosition, (ushort)node.Names.Length), node.Position); - context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.EndPosition); + context.PushInstruction(Instruction.TForLoop((byte)(startPosition + 2), startJumpIndex - context.Function.Instructions.Length), node.Position); context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext); context.StackPosition = startPosition; @@ -1160,6 +1163,7 @@ static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, value = -d1; return true; } + break; case UnaryOperator.Not: if (unaryNodeValue.TryRead(out var b)) @@ -1167,9 +1171,11 @@ static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, value = !b; return true; } + break; } } + break; case BinaryExpressionNode binaryExpression: if (TryGetConstant(binaryExpression.LeftNode, context, out var leftValue) && @@ -1234,7 +1240,8 @@ static bool IsFixedNumberOfReturnValues(ExpressionNode node) /// Condition node /// Context /// If true, generates an instruction sequence that skips the next instruction if the condition is false. - void CompileConditionNode(ExpressionNode node, ScopeCompilationContext context, bool falseIsSkip) + /// Position of the test instruction + void CompileConditionNode(ExpressionNode node, ScopeCompilationContext context, bool falseIsSkip, SourcePosition? testPosition = null) { if (node is BinaryExpressionNode binaryExpression) { @@ -1286,7 +1293,7 @@ void CompileConditionNode(ExpressionNode node, ScopeCompilationContext context, } node.Accept(this, context); - context.PushInstruction(Instruction.Test((byte)(context.StackPosition - 1), falseIsSkip ? (byte)0 : (byte)1), node.Position); + context.PushInstruction(Instruction.Test((byte)(context.StackPosition - 1), falseIsSkip ? (byte)0 : (byte)1), testPosition ?? node.Position); } void CompileExpressionList(SyntaxNode rootNode, ExpressionNode[] expressions, int minimumCount, ScopeCompilationContext context) diff --git a/src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs b/src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs index 5404489a..f0f18172 100644 --- a/src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs +++ b/src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax; -public record LuaSyntaxTree(SyntaxNode[] Nodes) : SyntaxNode(new SourcePosition(0, 0)) +public record LuaSyntaxTree(SyntaxNode[] Nodes,SourcePosition Position) : SyntaxNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs index 748863d2..c1ce41d1 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record FunctionDeclarationExpressionNode(IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position, int LineDefined,int LastLineDefined) : ExpressionNode(Position) +public record FunctionDeclarationExpressionNode(IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position, int LineDefined,SourcePosition EndPosition) : ExpressionNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs index 5ced6e16..08858c47 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record FunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position,int LineDefined,int LastLineDefined) : StatementNode(Position) +public record FunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position,int LineDefined,SourcePosition EndPosition) : StatementNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/IfStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/IfStatementNode.cs index 79aa885a..37853641 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/IfStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/IfStatementNode.cs @@ -4,6 +4,7 @@ public record IfStatementNode(IfStatementNode.ConditionAndThenNodes IfNode, IfSt { public record ConditionAndThenNodes { + public SourcePosition Position; public required ExpressionNode ConditionNode; public required StatementNode[] ThenNodes; } diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs index b1aafaa2..34396555 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record LocalFunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position, int LineDefined,int LastLineDefined) : FunctionDeclarationStatementNode(Name, ParameterNodes, Nodes, HasVariableArguments, Position, LineDefined, LastLineDefined) +public record LocalFunctionDeclarationStatementNode(ReadOnlyMemory Name, IdentifierNode[] ParameterNodes, SyntaxNode[] Nodes, bool HasVariableArguments, SourcePosition Position, int LineDefined,SourcePosition EndPosition) : FunctionDeclarationStatementNode(Name, ParameterNodes, Nodes, HasVariableArguments, Position, LineDefined, EndPosition) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs index 81fee012..cb159633 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs @@ -1,6 +1,6 @@ namespace Lua.CodeAnalysis.Syntax.Nodes; -public record TableMethodDeclarationStatementNode(IdentifierNode[] MemberPath, IdentifierNode[] ParameterNodes, StatementNode[] Nodes, bool HasVariableArguments, bool HasSelfParameter, SourcePosition Position, int LineDefined,int LastLineDefined) : StatementNode(Position) +public record TableMethodDeclarationStatementNode(IdentifierNode[] MemberPath, IdentifierNode[] ParameterNodes, StatementNode[] Nodes, bool HasVariableArguments, bool HasSelfParameter, SourcePosition Position, int LineDefined,SourcePosition EndPosition) : StatementNode(Position) { public override TResult Accept(ISyntaxNodeVisitor visitor, TContext context) { diff --git a/src/Lua/CodeAnalysis/Syntax/Parser.cs b/src/Lua/CodeAnalysis/Syntax/Parser.cs index 0535a371..4ac87574 100644 --- a/src/Lua/CodeAnalysis/Syntax/Parser.cs +++ b/src/Lua/CodeAnalysis/Syntax/Parser.cs @@ -33,8 +33,19 @@ public LuaSyntaxTree Parse() var node = ParseStatement(ref enumerator); root.Add(node); } + var tokensSpan = tokens.AsSpan(); + var lastToken = tokensSpan[0]; + for (int i = tokensSpan.Length-1; 0(64); using var elseIfBuilder = new PooledList(64); @@ -283,6 +295,7 @@ IfStatementNode ParseIfStatement(ref SyntaxTokenEnumerator enumerator) case 0: ifNodes = new() { + Position = thenToken.Position, ConditionNode = condition, ThenNodes = builder.AsSpan().ToArray(), }; @@ -291,6 +304,7 @@ IfStatementNode ParseIfStatement(ref SyntaxTokenEnumerator enumerator) case 1: elseIfBuilder.Add(new() { + Position = thenToken.Position, ConditionNode = condition, ThenNodes = builder.AsSpan().ToArray(), }); @@ -314,7 +328,7 @@ IfStatementNode ParseIfStatement(ref SyntaxTokenEnumerator enumerator) // check 'then' keyword CheckCurrent(ref enumerator, SyntaxTokenType.Then); - + thenToken = enumerator.Current; // set elseif state state = 1; @@ -453,17 +467,17 @@ GenericForStatementNode ParseGenericForStatement(ref SyntaxTokenEnumerator enume FunctionDeclarationStatementNode ParseFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken) { - var (Name, Identifiers, Statements, HasVariableArgments, LineDefined, LastLineDefined) = ParseFunctionDeclarationCore(ref enumerator, false); - return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, LastLineDefined); + var (Name, Identifiers, Statements, HasVariableArgments, LineDefined, EndPosition) = ParseFunctionDeclarationCore(ref enumerator, false); + return new FunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, EndPosition); } LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken) { - var (Name, Identifiers, Statements, HasVariableArgments, LineDefined, LastLineDefined) = ParseFunctionDeclarationCore(ref enumerator, false); - return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, LastLineDefined); + var (Name, Identifiers, Statements, HasVariableArgments, LineDefined, EndPosition) = ParseFunctionDeclarationCore(ref enumerator, false); + return new LocalFunctionDeclarationStatementNode(Name, Identifiers, Statements, HasVariableArgments, functionToken.Position, LineDefined, EndPosition); } - (ReadOnlyMemory Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments, int LineDefined, int LastLineDefined) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous) + (ReadOnlyMemory Name, IdentifierNode[] Identifiers, StatementNode[] Statements, bool HasVariableArgments, int LineDefined, SourcePosition EndPosition) ParseFunctionDeclarationCore(ref SyntaxTokenEnumerator enumerator, bool isAnonymous) { ReadOnlyMemory name; @@ -500,7 +514,7 @@ LocalFunctionDeclarationStatementNode ParseLocalFunctionDeclarationStatement(ref // parse statements var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out var endToken); - return (name, identifiers, statements, hasVarArg, leftParenToken.Position.Line, endToken.Position.Line); + return (name, identifiers, statements, hasVarArg, leftParenToken.Position.Line, endToken.Position); } TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref SyntaxTokenEnumerator enumerator, SyntaxToken functionToken) @@ -552,7 +566,7 @@ TableMethodDeclarationStatementNode ParseTableMethodDeclarationStatement(ref Syn // parse statements var statements = ParseStatementList(ref enumerator, SyntaxTokenType.End, out var endToken); - return new TableMethodDeclarationStatementNode(names.AsSpan().ToArray(), identifiers, statements, hasVarArg, hasSelfParameter, functionToken.Position, leftParenToken.Position.Line, endToken.Position.Line); + return new TableMethodDeclarationStatementNode(names.AsSpan().ToArray(), identifiers, statements, hasVarArg, hasSelfParameter, functionToken.Position, leftParenToken.Position.Line, endToken.Position); } bool TryParseExpression(ref SyntaxTokenEnumerator enumerator, OperatorPrecedence precedence, [NotNullWhen(true)] out ExpressionNode? result) diff --git a/tests/Lua.Tests/ParserTests.cs b/tests/Lua.Tests/ParserTests.cs index 1dcda7eb..d132eb05 100644 --- a/tests/Lua.Tests/ParserTests.cs +++ b/tests/Lua.Tests/ParserTests.cs @@ -17,8 +17,8 @@ elseif true then end"; var actual = LuaSyntaxTree.Parse(source).Nodes[0]; var expected = new IfStatementNode( - new() { ConditionNode = new BooleanLiteralNode(true, new(1, 3)), ThenNodes = [] }, - [new() { ConditionNode = new BooleanLiteralNode(true, new(2, 7)), ThenNodes = [] }], + new() { Position = new(1,8),ConditionNode = new BooleanLiteralNode(true, new(1, 3)), ThenNodes = [] }, + [new() {Position = new(2,13), ConditionNode = new BooleanLiteralNode(true, new(2, 7)), ThenNodes = [] }], [], new(1, 0)); From f9655ad9b768d1ba7f30c7c2849168ca8f12c012 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:58:29 +0900 Subject: [PATCH 12/22] Change: enhance traceback handling for TailCall --- src/Lua/Runtime/CallStackFrame.cs | 7 +- src/Lua/Runtime/LuaVirtualMachine.cs | 132 ++++++++++++++------------- src/Lua/Runtime/Tracebacks.cs | 72 +++++++++++---- 3 files changed, 130 insertions(+), 81 deletions(-) diff --git a/src/Lua/Runtime/CallStackFrame.cs b/src/Lua/Runtime/CallStackFrame.cs index 8a665e44..febd4cc9 100644 --- a/src/Lua/Runtime/CallStackFrame.cs +++ b/src/Lua/Runtime/CallStackFrame.cs @@ -10,11 +10,14 @@ public record struct CallStackFrame public required int VariableArgumentCount; public int CallerInstructionIndex; internal CallStackFrameFlags Flags; + internal bool IsTailCall => (Flags & CallStackFrameFlags.TailCall) ==CallStackFrameFlags.TailCall; } [Flags] public enum CallStackFrameFlags { - ReversedLe = 1, - TailCall + //None = 0, + ReversedLe = 1, + TailCall = 2, + InHook = 4, } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index d0ff6ae3..2286993a 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -39,6 +39,8 @@ struct VirtualMachineExecutionContext( readonly int BaseCallStackCount = thread.CallStack.Count; + public PostOperationType PostOperation; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Pop(Instruction instruction, int frameBase) { @@ -186,49 +188,60 @@ public void ClearResultsBuffer(int count) ResultsBuffer.AsSpan(0, count).Clear(); } + public int? ExecutePostOperation(PostOperationType postOperation) + { + switch (postOperation) + { + case PostOperationType.Nop: break; + case PostOperationType.SetResult: + var RA = Instruction.A + FrameBase; + Stack.Get(RA) = TaskResult == 0 ? LuaValue.Nil : ResultsBuffer[0]; + Stack.NotifyTop(RA + 1); + ClearResultsBuffer(); + break; + case PostOperationType.TForCall: + TForCallPostOperation(ref this); + break; + case PostOperationType.Call: + CallPostOperation(ref this); + break; + case PostOperationType.TailCall: + var resultsSpan = ResultsBuffer.AsSpan(0, TaskResult); + if (!PopFromBuffer(resultsSpan)) + { + ResultCount = TaskResult; + resultsSpan.CopyTo(Buffer.Span); + resultsSpan.Clear(); + LuaValueArrayPool.Return1024(ResultsBuffer); + return TaskResult; + } + + resultsSpan.Clear(); + break; + case PostOperationType.Self: + SelfPostOperation(ref this); + break; + case PostOperationType.Compare: + ComparePostOperation(ref this); + break; + } + + return null; + } + public async ValueTask ExecuteClosureAsyncImpl() { - while (MoveNext(ref this, out var postOperation)) + while (MoveNext(ref this)) { TaskResult = await Task; Task = default; - - Thread.PopCallStackFrame(); - switch (postOperation) + if (PostOperation != PostOperationType.TailCall) { - case PostOperationType.Nop: break; - case PostOperationType.SetResult: - var RA = Instruction.A + FrameBase; - Stack.Get(RA) = TaskResult == 0 ? LuaValue.Nil : ResultsBuffer[0]; - Stack.NotifyTop(RA + 1); - ClearResultsBuffer(); - break; - case PostOperationType.TForCall: - TForCallPostOperation(ref this); - break; - case PostOperationType.Call: - CallPostOperation(ref this); - break; - case PostOperationType.TailCall: - var resultsSpan = ResultsBuffer.AsSpan(0, TaskResult); - if (!PopFromBuffer(resultsSpan)) - { - ResultCount = TaskResult; - resultsSpan.CopyTo(Buffer.Span); - resultsSpan.Clear(); - LuaValueArrayPool.Return1024(ResultsBuffer); - return TaskResult; - } - - resultsSpan.Clear(); - break; - case PostOperationType.Self: - SelfPostOperation(ref this); - break; - case PostOperationType.Compare: - ComparePostOperation(ref this); - break; + Thread.PopCallStackFrame(); } + + var r = ExecutePostOperation(PostOperation); + if (r.HasValue) return r.Value; } return ResultCount; @@ -259,20 +272,17 @@ internal static ValueTask ExecuteClosureAsync(LuaState luaState, Memory 0 ? stackFrames[index - 1].Function : RootFunc; var frame = stackFrames[index]; - if (lastFunc is Closure closure) + if (!frame.IsTailCall && lastFunc is Closure closure) { var p = closure.Proto; + if (frame.CallerInstructionIndex < 0 || p.SourcePositions.Length <= frame.CallerInstructionIndex) + { + Console.WriteLine($"Trace back error"); + return default; + } + return p.SourcePositions[frame.CallerInstructionIndex]; } } + return default; } } - public override string ToString() { - return GetTracebackString(State,RootFunc, StackFrames, LuaValue.Nil); + return GetTracebackString(State, RootFunc, StackFrames, LuaValue.Nil); } - internal static string GetTracebackString(LuaState state,Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message) + internal static string GetTracebackString(LuaState state, Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message, bool skipFirstCsharpCall = false) { using var list = new PooledList(64); if (message.Type is not LuaValueType.Nil) @@ -51,6 +57,15 @@ internal static string GetTracebackString(LuaState state,Closure rootFunc, ReadO list.AddRange("stack traceback:\n"); var intFormatBuffer = (stackalloc char[15]); var shortSourceBuffer = (stackalloc char[59]); + { + if (0 < stackFrames.Length && !skipFirstCsharpCall && stackFrames[^1].Function is { } f and not Closure) + { + list.AddRange("\t[C#]: in function '"); + list.AddRange(f.Name); + list.AddRange("'\n"); + } + } + for (var index = stackFrames.Length - 1; index >= 0; index--) { LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : rootFunc; @@ -63,27 +78,50 @@ internal static string GetTracebackString(LuaState state,Closure rootFunc, ReadO else if (lastFunc is Closure closure) { var frame = stackFrames[index]; + + if (frame.IsTailCall) + { + list.AddRange("\t(...tail calls...)\n"); + } + var p = closure.Proto; var root = p.GetRoot(); list.AddRange("\t"); var len = LuaDebug.WriteShortSource(root.Name, shortSourceBuffer); list.AddRange(shortSourceBuffer[..len]); list.AddRange(":"); - p.SourcePositions[frame.CallerInstructionIndex].Line.TryFormat(intFormatBuffer, out var charsWritten, provider: CultureInfo.InvariantCulture); - list.AddRange(intFormatBuffer[..charsWritten]); + if (p.SourcePositions.Length <= frame.CallerInstructionIndex) + { + list.AddRange("Trace back error"); + } + else + { + p.SourcePositions[frame.CallerInstructionIndex].Line.TryFormat(intFormatBuffer, out var charsWritten, provider: CultureInfo.InvariantCulture); + list.AddRange(intFormatBuffer[..charsWritten]); + } + list.AddRange(": in "); if (root == p) { list.AddRange("main chunk"); list.AddRange("\n"); - continue; + goto Next; + } + + if (0 < index && stackFrames[index - 1].Flags.HasFlag(CallStackFrameFlags.InHook)) + { + list.AddRange("hook"); + list.AddRange(" '"); + list.AddRange("?"); + list.AddRange("'\n"); + goto Next; } foreach (var pair in state.Environment.Dictionary) { - if (pair.Key.TryReadString(out var name) - && pair.Value.TryReadFunction(out var result) && + if (pair.Key.TryReadString(out var name) + && pair.Value.TryReadFunction(out var result) && result == closure) { list.AddRange("function '"); @@ -113,19 +151,21 @@ internal static string GetTracebackString(LuaState state,Closure rootFunc, ReadO list.AddRange("'\n"); } - continue; + goto Next; } } - + list.AddRange("function <"); list.AddRange(shortSourceBuffer[..len]); list.AddRange(":"); - p.LineDefined.TryFormat(intFormatBuffer, out charsWritten, provider: CultureInfo.InvariantCulture); - list.AddRange(intFormatBuffer[..charsWritten]); - list.AddRange(">\n"); - - Next: ; + { + p.LineDefined.TryFormat(intFormatBuffer, out var charsWritten, provider: CultureInfo.InvariantCulture); + list.AddRange(intFormatBuffer[..charsWritten]); + list.AddRange(">\n"); + } + + Next: ; } } From 7051b6a5bc171fda3dda07b0c52d8c0b0672c0d5 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:45:53 +0900 Subject: [PATCH 13/22] Add: debug.gethook/sethook --- src/Lua/Internal/LuaDebug.cs | 24 +-- src/Lua/LuaCoroutine.cs | 6 +- src/Lua/LuaState.cs | 30 ++- src/Lua/LuaStateExtensions.cs | 2 +- src/Lua/LuaThread.cs | 43 +++- src/Lua/Runtime/LuaValueRuntimeExtensions.cs | 4 +- src/Lua/Runtime/LuaVirtualMachine.Debug.cs | 211 +++++++++++++++++++ src/Lua/Runtime/LuaVirtualMachine.cs | 49 ++++- src/Lua/Runtime/Tracebacks.cs | 9 + src/Lua/Standard/BasicLibrary.cs | 15 +- src/Lua/Standard/CoroutineLibrary.cs | 8 +- src/Lua/Standard/DebugLibrary.cs | 185 +++++++++++++--- tests/Lua.Tests/LuaTests.cs | 2 +- tests/Lua.Tests/tests-lua/coroutine.lua | 64 +++--- tests/Lua.Tests/tests-lua/db.lua | 36 ++-- 15 files changed, 588 insertions(+), 100 deletions(-) create mode 100644 src/Lua/Runtime/LuaVirtualMachine.Debug.cs diff --git a/src/Lua/Internal/LuaDebug.cs b/src/Lua/Internal/LuaDebug.cs index 0f489335..fa1ecac9 100644 --- a/src/Lua/Internal/LuaDebug.cs +++ b/src/Lua/Internal/LuaDebug.cs @@ -128,12 +128,12 @@ public static LuaDebug Create(LuaState state, CallStackFrame? prevFrame, CallSta { if (!state.DebugBufferPool.TryPop(out var buffer)) { - buffer = new (state); + buffer = new(state); } isValid = buffer.GetInfo(prevFrame, frame, function, pc, what); - return new (buffer, buffer.version); + return new(buffer, buffer.version); } public void CheckVersion() @@ -281,7 +281,7 @@ void GetFuncInfo(LuaFunction f) Source = p.GetRoot().Name; LineDefined = p.LineDefined; LastLineDefined = p.LastLineDefined; - What = (LineDefined == 0) ? "main" : "Lua"; + What = (p.GetRoot() == p) ? "main" : "Lua"; } ShortSourceLength = WriteShortSource(Source, ShortSource); @@ -435,7 +435,7 @@ static void GetConstantName(Chunk p, int pc, int c, out string name) { int k = i.C; /* key index */ int t = i.B; /* table index */ - + var vn = (op == OpCode.GetTable) /* name of indexed variable */ ? GetLocalName(chunk, t + 1, pc) : chunk.UpValues[t].Name.ToString(); @@ -588,15 +588,15 @@ internal static int WriteShortSource(ReadOnlySpan source, Span dest) { /* add '...' before rest of name */ RETS.AsSpan().CopyTo(dest); - source[^(BUFFER_LEN - RETS_LEN )..].CopyTo(dest[RETS_LEN..]); - + source[^(BUFFER_LEN - RETS_LEN)..].CopyTo(dest[RETS_LEN..]); + return BUFFER_LEN; } } else { /* string; format as [string "source"] */ - + PRE.AsSpan().CopyTo(dest); int newLine = source.IndexOf('\n'); @@ -606,6 +606,7 @@ internal static int WriteShortSource(ReadOnlySpan source, Span dest) POS.AsSpan().CopyTo(dest[(PRE_LEN + source.Length)..]); return PRE_LEN + source.Length + POS_LEN; } + if (newLine != -1) { source = source[..newLine]; /* stop at first newline */ @@ -615,13 +616,12 @@ internal static int WriteShortSource(ReadOnlySpan source, Span dest) { source = source[..(BUFFER_LEN - PRE_LEN - RETS_LEN - POS_LEN)]; } - + /* add '...' before rest of name */ source.CopyTo(dest[PRE_LEN..]); - RETS.AsSpan().CopyTo(dest[(PRE_LEN+source.Length)..]); - POS.AsSpan().CopyTo(dest[(PRE_LEN+source.Length +RETS_LEN)..]); - return PRE_LEN+source.Length +RETS_LEN +POS_LEN; - + RETS.AsSpan().CopyTo(dest[(PRE_LEN + source.Length)..]); + POS.AsSpan().CopyTo(dest[(PRE_LEN + source.Length + RETS_LEN)..]); + return PRE_LEN + source.Length + RETS_LEN + POS_LEN; } } diff --git a/src/Lua/LuaCoroutine.cs b/src/Lua/LuaCoroutine.cs index 3e39e303..dcf8c5d5 100644 --- a/src/Lua/LuaCoroutine.cs +++ b/src/Lua/LuaCoroutine.cs @@ -24,6 +24,7 @@ struct ResumeContext ManualResetValueTaskSourceCore resume; ManualResetValueTaskSourceCore yield; + Traceback? traceback; public LuaCoroutine(LuaFunction function, bool isProtectedMode) { @@ -43,6 +44,9 @@ public override void UnsafeSetStatus(LuaThreadStatus status) public bool IsProtectedMode { get; } public LuaFunction Function { get; } + + + internal Traceback? LuaTraceback => traceback; public override async ValueTask ResumeAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default) { @@ -179,7 +183,7 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con if (IsProtectedMode) { ArrayPool.Shared.Return(this.buffer); - + traceback = (ex as LuaRuntimeException)?.LuaTraceback; Volatile.Write(ref status, (byte)LuaThreadStatus.Dead); buffer.Span[0] = false; buffer.Span[1] = ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message; diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index 9f5debda..b329961e 100644 --- a/src/Lua/LuaState.cs +++ b/src/Lua/LuaState.cs @@ -19,7 +19,7 @@ public sealed class LuaState readonly LuaTable registry = new(); readonly UpValue envUpValue; bool isRunning; - + FastStackCore debugBufferPool; internal UpValue EnvUpValue => envUpValue; @@ -108,6 +108,7 @@ public Traceback GetTraceback() { list.Add(frame); } + foreach (var thread in threadStack.AsSpan()) { if (thread.CallStack.Count == 0) continue; @@ -116,6 +117,7 @@ public Traceback GetTraceback() list.Add(frame); } } + return new(this) { RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function, @@ -123,6 +125,30 @@ public Traceback GetTraceback() }; } + internal Traceback GetTraceback(LuaThread thread) + { + using var list = new PooledList(8); + foreach (var frame in thread.GetCallStackFrames()[1..]) + { + list.Add(frame); + } + Closure rootFunc; + if (thread.GetCallStackFrames()[0].Function is Closure closure) + { + rootFunc = closure; + } + else + { + rootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function; + } + + return new(this) + { + RootFunc = rootFunc, + StackFrames = list.AsSpan().ToArray() + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result) { @@ -212,4 +238,4 @@ void ThrowIfRunning() throw new InvalidOperationException("the lua state is currently running"); } } -} +} \ No newline at end of file diff --git a/src/Lua/LuaStateExtensions.cs b/src/Lua/LuaStateExtensions.cs index 01e74962..2710517a 100644 --- a/src/Lua/LuaStateExtensions.cs +++ b/src/Lua/LuaStateExtensions.cs @@ -30,7 +30,7 @@ public static async ValueTask DoStringAsync(this LuaState state, str public static async ValueTask DoFileAsync(this LuaState state, string path, Memory buffer, CancellationToken cancellationToken = default) { var text = await File.ReadAllTextAsync(path, cancellationToken); - var fileName = Path.GetFileName(path); + var fileName = "@"+Path.GetFileName(path); var syntaxTree = LuaSyntaxTree.Parse(text, fileName); var chunk = LuaCompiler.Default.Compile(syntaxTree, fileName); return await state.RunAsync(chunk, buffer, cancellationToken); diff --git a/src/Lua/LuaThread.cs b/src/Lua/LuaThread.cs index 4171c6c9..255c4a4e 100644 --- a/src/Lua/LuaThread.cs +++ b/src/Lua/LuaThread.cs @@ -17,6 +17,47 @@ public abstract class LuaThread internal LuaStack Stack => stack; internal ref FastStackCore CallStack => ref callStack; + internal bool IsLineHookEnabled + { + get => (LineAndCountHookMask & 1) != 0; + set + { + if (value) + { + LineAndCountHookMask |= 1; + } + else + { + LineAndCountHookMask &= 0b1111_1110; + } + } + } + + internal bool IsCountHookEnabled + { + get => (LineAndCountHookMask & 2) != 0; + set + { + if (value) + { + LineAndCountHookMask |= 2; + } + else + { + LineAndCountHookMask &= 0b1111_1101; + } + } + } + internal bool IsCallHookEnabled; + internal byte LineAndCountHookMask; + internal bool IsReturnHookEnabled; + internal bool CallOrReturnHookEnabled => IsCallHookEnabled || IsReturnHookEnabled; + internal bool IsInHook; + internal int HookCount; + internal int BaseHookCount; + internal int LastPc; + internal LuaFunction? Hook { get; set; } + public CallStackFrame GetCurrentFrame() { return callStack.Peek(); @@ -81,6 +122,6 @@ internal void DumpStackValues() Console.WriteLine($"LuaStack [{i}]\t{span[i]}"); } } - + static void ThrowForEmptyStack() => throw new InvalidOperationException("Empty stack"); } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaValueRuntimeExtensions.cs b/src/Lua/Runtime/LuaValueRuntimeExtensions.cs index e65c69ba..e02ee65b 100644 --- a/src/Lua/Runtime/LuaValueRuntimeExtensions.cs +++ b/src/Lua/Runtime/LuaValueRuntimeExtensions.cs @@ -15,8 +15,8 @@ public static bool TryGetMetamethod(this LuaValue value, LuaState state, string [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetVariableArgumentCount(this LuaFunction function, int argumentCount) { - return function is Closure luaClosure - ? argumentCount - luaClosure.Proto.ParameterCount + return function is Closure { Proto.HasVariableArguments: true } luaClosure + ?argumentCount - luaClosure.Proto.ParameterCount : 0; } } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs new file mode 100644 index 00000000..dd4058ad --- /dev/null +++ b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs @@ -0,0 +1,211 @@ +using System.Runtime.CompilerServices; + +namespace Lua.Runtime; + +public static partial class LuaVirtualMachine +{ + [MethodImpl(MethodImplOptions.NoInlining)] + static bool ExecutePerInstructionHook(ref VirtualMachineExecutionContext context) + { + var r = Impl(context); + if (r.IsCompleted) + { + if (r.Result == 0) + { + context.Thread.PopCallStackFrame(); + } + + return false; + } + + context.Task = r; + context.Pc--; + return true; + + static async ValueTask Impl(VirtualMachineExecutionContext context) + { + bool countHookIsDone = false; + if (context.Thread.IsCountHookEnabled && --context.Thread.HookCount == 0) + { + context.Thread.HookCount = context.Thread.BaseHookCount; + + var hook = context.Thread.Hook!; + var stack = context.Thread.Stack; + stack.Push("count"); + stack.Push(LuaValue.Nil); + var funcContext = new LuaFunctionExecutionContext + { + State = context.State, + Thread = context.Thread, + ArgumentCount = 2, + FrameBase = context.Thread.Stack.Count - 2, + }; + var frame = new CallStackFrame + { + Base = funcContext.FrameBase, + VariableArgumentCount = hook is Closure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + Function = hook, + CallerInstructionIndex = context.Pc, + }; + frame.Flags |= CallStackFrameFlags.InHook; + context.Thread.IsInHook = true; + context.Thread.PushCallStackFrame(frame); + await hook.Func(funcContext, Memory.Empty, context.CancellationToken); + context.Thread.IsInHook = false; + + + countHookIsDone = true; + } + + + if (context.Thread.IsLineHookEnabled) + { + var pc = context.Pc; + var sourcePositions = context.Chunk.SourcePositions; + var line = sourcePositions[pc].Line; + + if (countHookIsDone || pc == 0 || context.Thread.LastPc < 0 || pc <= context.Thread.LastPc || sourcePositions[context.Thread.LastPc].Line != line) + { + if (countHookIsDone) + { + context.Thread.PopCallStackFrame(); + } + + + var hook = context.Thread.Hook!; + var stack = context.Thread.Stack; + stack.Push("line"); + stack.Push(line); + var funcContext = new LuaFunctionExecutionContext + { + State = context.State, + Thread = context.Thread, + ArgumentCount = 2, + FrameBase = context.Thread.Stack.Count - 2, + }; + var frame = new CallStackFrame + { + Base = funcContext.FrameBase, + VariableArgumentCount = hook is Closure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + Function = hook, + CallerInstructionIndex = pc, + }; + frame.Flags |= CallStackFrameFlags.InHook; + context.Thread.IsInHook = true; + context.Thread.PushCallStackFrame(frame); + await hook.Func(funcContext, Memory.Empty, context.CancellationToken); + context.Thread.IsInHook = false; + context.Pc--; + context.Thread.LastPc = pc; + return 0; + } + + context.Thread.LastPc = pc; + } + + if (countHookIsDone) + { + context.Pc--; + return 0; + } + + return -1; + } + } + + static void ExecuteCallHook(ref VirtualMachineExecutionContext context, int argCount, bool isTailCall = false) + { + context.Task = Impl(context, argCount, isTailCall); + + static async ValueTask Impl(VirtualMachineExecutionContext context, int argCount, bool isTailCall) + { + var topFrame = context.Thread.GetCurrentFrame(); + var hook = context.Thread.Hook!; + var stack = context.Thread.Stack; + CallStackFrame frame; + if (context.Thread.IsCallHookEnabled) + { + stack.Push((isTailCall ? "tail call" : "call")); + + stack.Push(LuaValue.Nil); + var funcContext = new LuaFunctionExecutionContext + { + State = context.State, + Thread = context.Thread, + ArgumentCount = 2, + FrameBase = context.Thread.Stack.Count - 2, + }; + frame = new() + { + Base = funcContext.FrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(2), + Function = hook, + CallerInstructionIndex = 0, + }; + frame.Flags |= CallStackFrameFlags.InHook; + + context.Thread.PushCallStackFrame(frame); + try + { + context.Thread.IsInHook = true; + await hook.Func(funcContext, Memory.Empty, context.CancellationToken); + } + finally + { + context.Thread.IsInHook = false; + context.Thread.PopCallStackFrame(); + } + } + + frame = topFrame; + context.Push(frame); + + var task = frame.Function.Func(new () + { + State = context.State, + Thread = context.Thread, + ArgumentCount = argCount, + FrameBase = frame.Base, + }, context.ResultsBuffer, context.CancellationToken); + if (isTailCall || !context.Thread.IsReturnHookEnabled) + { + return await task; + } + + { + var result = await task; + stack.Push("return"); + stack.Push(LuaValue.Nil); + var funcContext = new LuaFunctionExecutionContext + { + State = context.State, + Thread = context.Thread, + ArgumentCount = 2, + FrameBase = context.Thread.Stack.Count - 2, + }; + frame = new() + { + Base = funcContext.FrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(2), + Function = hook, + CallerInstructionIndex = 0 + }; + frame.Flags |= CallStackFrameFlags.InHook; + + context.Thread.PushCallStackFrame(frame); + try + { + context.Thread.IsInHook = true; + await hook.Func(funcContext, Memory.Empty, context.CancellationToken); + } + finally + { + context.Thread.IsInHook = false; + } + + context.Thread.PopCallStackFrame(); + return result; + } + } + } +} \ No newline at end of file diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index 2286993a..a76871e2 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -35,6 +35,7 @@ struct VirtualMachineExecutionContext( public int ResultCount; public int TaskResult; public ValueTask Task; + public int LastHookPc = -1; public bool IsTopLevel => BaseCallStackCount == Thread.CallStack.Count; readonly int BaseCallStackCount = thread.CallStack.Count; @@ -60,6 +61,7 @@ public bool PopFromBuffer(Span result) if (frames.Length == BaseCallStackCount) return false; ref readonly var frame = ref frames[^1]; Pc = frame.CallerInstructionIndex; + Thread.LastPc = Pc; ref readonly var lastFrame = ref frames[^2]; Closure = Unsafe.As(lastFrame.Function); var callInstruction = Chunk.Instructions[Pc]; @@ -283,10 +285,35 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) var stack = context.Stack; stack.EnsureCapacity(frameBase + context.Chunk.MaxStackPosition); ref var constHead = ref MemoryMarshalEx.UnsafeElementAt(context.Chunk.Constants, 0); + ref byte lineAndCountHookMask = ref context.Thread.LineAndCountHookMask; + goto Loop; + LineHook: + + { + context.LastHookPc = context.Pc; + if (!context.Thread.IsInHook && ExecutePerInstructionHook(ref context)) + { + { + context.PostOperation = PostOperationType.Nop; + return true; + } + } + + --context.Pc; + } + + + Loop: while (true) { var instructionRef = Unsafe.Add(ref instructionsHead, ++context.Pc); context.Instruction = instructionRef; + if (lineAndCountHookMask != 0 && (context.Pc != context.LastHookPc)) + { + goto LineHook; + } + + context.LastHookPc = -1; switch (instructionRef.OpCode) { case OpCode.Move: @@ -924,16 +951,17 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } catch (Exception e) { - context.PopOnTopCallStackFrames(); context.State.CloseUpValues(context.Thread, context.FrameBase); LuaValueArrayPool.Return1024(context.ResultsBuffer, true); if (e is not LuaRuntimeException) { - var newException = new LuaRuntimeException(GetTracebacks(ref context), e); + var newException = new LuaRuntimeException(context.State.GetTraceback(), e); + context.PopOnTopCallStackFrames(); context = default; throw newException; } + context.PopOnTopCallStackFrames(); throw; } } @@ -1024,7 +1052,14 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount); thread.PushCallStackFrame(newFrame); - + if (thread.CallOrReturnHookEnabled && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.Call; + ExecuteCallHook(ref context, argumentCount); + doRestart = false; + return false; + } + if (func is Closure) { context.Push(newFrame); @@ -1152,6 +1187,14 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest newFrame.CallerInstructionIndex = lastPc; thread.PushCallStackFrame(newFrame); + if (thread.CallOrReturnHookEnabled && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.TailCall; + ExecuteCallHook(ref context, argumentCount, true); + doRestart = false; + return false; + } + context.Push(newFrame); if (func is Closure) { diff --git a/src/Lua/Runtime/Tracebacks.cs b/src/Lua/Runtime/Tracebacks.cs index 79a218db..19a2d6ee 100644 --- a/src/Lua/Runtime/Tracebacks.cs +++ b/src/Lua/Runtime/Tracebacks.cs @@ -44,6 +44,15 @@ public override string ToString() { return GetTracebackString(State, RootFunc, StackFrames, LuaValue.Nil); } + + public string ToString(int skipFrames) + { + if(skipFrames < 0 || skipFrames >= StackFrames.Length) + { + return "stack traceback:\n"; + } + return GetTracebackString(State, RootFunc, StackFrames[..^skipFrames], LuaValue.Nil); + } internal static string GetTracebackString(LuaState state, Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message, bool skipFirstCsharpCall = false) { diff --git a/src/Lua/Standard/BasicLibrary.cs b/src/Lua/Standard/BasicLibrary.cs index 47b3492b..da4eed4b 100644 --- a/src/Lua/Standard/BasicLibrary.cs +++ b/src/Lua/Standard/BasicLibrary.cs @@ -95,7 +95,7 @@ public async ValueTask DoFile(LuaFunctionExecutionContext context, Memory Error(LuaFunctionExecutionContext context, Memory GetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) diff --git a/src/Lua/Standard/CoroutineLibrary.cs b/src/Lua/Standard/CoroutineLibrary.cs index 6516c5be..5b25bb4a 100644 --- a/src/Lua/Standard/CoroutineLibrary.cs +++ b/src/Lua/Standard/CoroutineLibrary.cs @@ -61,7 +61,11 @@ public ValueTask Wrap(LuaFunctionExecutionContext context, Memory buffer.Span[0] = new CsClosure("wrap", [thread],static async (context, buffer, cancellationToken) => { - var thread = context.GetCsClosure()!.UpValues[0].Read(); + var thread = context.GetCsClosure()!.UpValues[0].Read(); + if (thread is not LuaCoroutine coroutine) + { + return await thread.ResumeAsync(context, buffer, cancellationToken); + } var stack = context.Thread.Stack; var frameBase = stack.Count; @@ -71,7 +75,7 @@ public ValueTask Wrap(LuaFunctionExecutionContext context, Memory { Base = frameBase, VariableArgumentCount = 0, - Function = thread.Function, + Function = coroutine.Function, }); try { diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index 007ec445..ef83ae63 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -24,6 +24,8 @@ public DebugLibrary() new("getregistry", GetRegistry), new("upvalueid", UpValueId), new("upvaluejoin", UpValueJoin), + new("gethook", GetHook), + new("sethook", SetHook), new("getinfo", GetInfo), ]; } @@ -31,7 +33,7 @@ public DebugLibrary() public readonly LuaFunction[] Functions; - LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset) + static LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset) { if (context.ArgumentCount < 1) { @@ -49,7 +51,8 @@ LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset return context.Thread; } - ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name) + + static ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name) { if (index == 0) { @@ -65,7 +68,7 @@ ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name) var frameVariableArgumentCount = frame.VariableArgumentCount; if (frameVariableArgumentCount > 0 && index < frameVariableArgumentCount) { - name = "(vararg)"; + name = "(*vararg)"; return ref thread.Stack.Get(frame.Base - frameVariableArgumentCount + index); } @@ -77,17 +80,21 @@ ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name) var frameBase = frame.Base; - var nextFrameBase = level != 0 ? callStack[^level].Base : thread.Stack.Count; - if (nextFrameBase - frameBase <= index) - { - name = null; - return ref Unsafe.NullRef(); - } + if (frame.Function is Closure closure) { var locals = closure.Proto.Locals; - var currentPc = callStack[^level].CallerInstructionIndex; + var nextFrame = callStack[^level]; + var currentPc = nextFrame.CallerInstructionIndex; + { + int nextFrameBase = (closure.Proto.Instructions[currentPc].OpCode is OpCode.Call or OpCode.TailCall) ? nextFrame.Base - 1 : nextFrame.Base; + if (nextFrameBase - 1 < frameBase + index) + { + name = null; + return ref Unsafe.NullRef(); + } + } foreach (var local in locals) { if (local.Index == index && currentPc >= local.StartPc && currentPc < local.EndPc) @@ -96,12 +103,22 @@ ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name) return ref thread.Stack.Get(frameBase + local.Index); } - if (local.Index >= index) + if (local.Index > index) { break; } } } + else + { + int nextFrameBase = level != 0 ? callStack[^level].Base : thread.Stack.Count; + + if (nextFrameBase - 1 < frameBase + index) + { + name = null; + return ref Unsafe.NullRef(); + } + } name = "(*temporary)"; return ref thread.Stack.Get(frameBase + index); @@ -225,12 +242,14 @@ public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory= upValues.Length) + if (index >= 0 && index < upValues.Length) { - upValues[index - 1] = value; buffer.Span[0] = ""; + upValues[index] = value; return new(0); } + + return new(0); } return new(0); @@ -343,13 +362,25 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Memory 0 ? thread.GetCallStackFrames()[^1] : default; - thread.PushCallStackFrame(currentFrame); + if (thread is LuaCoroutine coroutine) + { + if (coroutine.LuaTraceback is not null) + { + buffer.Span[0] = coroutine.LuaTraceback.ToString(level); + return new(1); + } + } + var callStack = thread.GetCallStackFrames(); - var skipCount = Math.Min(level, callStack.Length - 1); + if (callStack.Length == 0) + { + buffer.Span[0] = "stack traceback:"; + return new(1); + } + + var skipCount = Math.Min(Math.Max(level - 1, 0), callStack.Length - 1); var frames = callStack[1..^skipCount]; - buffer.Span[0] = Runtime.Traceback.GetTracebackString(context.State, (Closure)callStack[0].Function, frames, message); - thread.PopCallStackFrame(); + buffer.Span[0] = Runtime.Traceback.GetTracebackString(context.State, (Closure)callStack[0].Function, frames, message, level == 1); return new(1); } @@ -410,6 +441,108 @@ public ValueTask UpValueJoin(LuaFunctionExecutionContext context, Memory SetHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var thread = GetLuaThread(context, out var argOffset); + LuaFunction? hook = context.GetArgumentOrDefault(argOffset); + if (hook is null) + { + thread.HookCount = -1; + thread.BaseHookCount = 0; + thread.IsCountHookEnabled = false; + thread.Hook = null; + thread.IsLineHookEnabled = false; + thread.IsCallHookEnabled = false; + thread.IsReturnHookEnabled = false; + return 0; + } + + var mask = context.GetArgument(argOffset + 1); + if (context.HasArgument(argOffset + 2)) + { + var count = context.GetArgument(argOffset + 2); + thread.BaseHookCount = count; + thread.HookCount = count; + if (count > 0) + { + thread.IsCountHookEnabled = true; + } + } + else + { + thread.HookCount = 0; + thread.BaseHookCount = 0; + thread.IsCountHookEnabled = false; + } + + thread.IsLineHookEnabled = (mask.Contains('l')); + thread.IsCallHookEnabled = (mask.Contains('c')); + thread.IsReturnHookEnabled = (mask.Contains('r')); + + if (thread.IsLineHookEnabled) + { + thread.LastPc = thread.CallStack.Count > 0 ? thread.GetCurrentFrame().CallerInstructionIndex : -1; + } + + thread.Hook = hook; + if (thread.IsReturnHookEnabled && context.Thread == thread) + { + var stack = thread.Stack; + stack.Push("return"); + stack.Push(LuaValue.Nil); + var funcContext = new LuaFunctionExecutionContext + { + State = context.State, + Thread = context.Thread, + ArgumentCount = 2, + FrameBase = stack.Count - 2, + }; + var frame = new CallStackFrame + { + Base = funcContext.FrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(2), + Function = hook, + }; + frame.Flags |= CallStackFrameFlags.InHook; + thread.PushCallStackFrame(frame); + try + { + thread.IsInHook = true; + await hook.Func(funcContext, Memory.Empty, cancellationToken); + } + finally + { + thread.IsInHook = false; + } + + thread.PopCallStackFrame(); + } + + return 0; + } + + + public ValueTask GetHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + { + var thread = GetLuaThread(context, out var argOffset); + if (thread.Hook is null) + { + buffer.Span[0] = LuaValue.Nil; + buffer.Span[1] = LuaValue.Nil; + buffer.Span[2] = LuaValue.Nil; + return new(3); + } + + buffer.Span[0] = thread.Hook; + buffer.Span[1] = ( + (thread.IsCallHookEnabled ? "c" : "") + + (thread.IsReturnHookEnabled ? "r" : "") + + (thread.IsLineHookEnabled ? "l" : "") + ); + buffer.Span[2] = thread.BaseHookCount; + return new(3); + } + public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) { //return new(0); @@ -429,18 +562,22 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory(argOffset) + 1; var callStack = thread.GetCallStackFrames(); + if (level <= 0 || level > callStack.Length) { - context.ThrowBadArgument(1, "level out of range"); + buffer.Span[0] = LuaValue.Nil; + return new(1); } + currentFrame = thread.GetCallStackFrames()[^(level)]; - functionToInspect = currentFrame.Value.Function; previousFrame = level + 1 <= callStack.Length ? callStack[^(level + 1)] : null; - if (level != 0) + if (level != 1) { pc = thread.GetCallStackFrames()[^(level - 1)].CallerInstructionIndex; } + + functionToInspect = currentFrame.Value.Function; } else { @@ -496,16 +633,16 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory=b and a.op == "__le") -assert (a>b and a.op == "__lt") +assert(a[3] == "index" and a^3 == "pow" and a..a == "concat") +assert(a/3 == "div" and 3%a == "mod") +assert (a==b and a.op == "eq") +assert (a>=b and a.op == "le") +assert (a>b and a.op == "lt") print"OK" - From a54abf5b9d4b1f0bf59f677afadd2da2e70a6c8b Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 3 Mar 2025 18:24:40 +0900 Subject: [PATCH 14/22] Add: BitFlags --- src/Lua/Internal/BitFlags.cs | 38 ++++++++++++++++++++++++++++ src/Lua/LuaThread.cs | 37 +++++++-------------------- src/Lua/Runtime/LuaVirtualMachine.cs | 8 +++--- 3 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 src/Lua/Internal/BitFlags.cs diff --git a/src/Lua/Internal/BitFlags.cs b/src/Lua/Internal/BitFlags.cs new file mode 100644 index 00000000..2f5a2649 --- /dev/null +++ b/src/Lua/Internal/BitFlags.cs @@ -0,0 +1,38 @@ +namespace Lua.Internal; + +internal struct BitFlags2 +{ + public byte Value; + + public bool Flag0 + { + get => (Value & 1) == 1; + set + { + if (value) + { + Value |= 1; + } + else + { + Value = (byte)(Value & ~1); + } + } + } + + public bool Flag1 + { + get => (Value & 2) == 2; + set + { + if (value) + { + Value |= 2; + } + else + { + Value = (byte)(Value & ~2); + } + } + } +} \ No newline at end of file diff --git a/src/Lua/LuaThread.cs b/src/Lua/LuaThread.cs index 255c4a4e..6047d04e 100644 --- a/src/Lua/LuaThread.cs +++ b/src/Lua/LuaThread.cs @@ -19,39 +19,20 @@ public abstract class LuaThread internal bool IsLineHookEnabled { - get => (LineAndCountHookMask & 1) != 0; - set - { - if (value) - { - LineAndCountHookMask |= 1; - } - else - { - LineAndCountHookMask &= 0b1111_1110; - } - } + get => LineAndCountHookMask.Flag0; + set => LineAndCountHookMask.Flag0 = value; } internal bool IsCountHookEnabled { - get => (LineAndCountHookMask & 2) != 0; - set - { - if (value) - { - LineAndCountHookMask |= 2; - } - else - { - LineAndCountHookMask &= 0b1111_1101; - } - } + get => LineAndCountHookMask.Flag1; + set => LineAndCountHookMask.Flag1 = value; } - internal bool IsCallHookEnabled; - internal byte LineAndCountHookMask; - internal bool IsReturnHookEnabled; - internal bool CallOrReturnHookEnabled => IsCallHookEnabled || IsReturnHookEnabled; + + internal BitFlags2 LineAndCountHookMask; + internal bool IsCallHookEnabled { get => CallOrReturnHookMask.Flag0; set => CallOrReturnHookMask.Flag0 = value; } + internal bool IsReturnHookEnabled { get => CallOrReturnHookMask.Flag1; set => CallOrReturnHookMask.Flag1 = value; } + internal BitFlags2 CallOrReturnHookMask; internal bool IsInHook; internal int HookCount; internal int BaseHookCount; diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index a76871e2..321c50df 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -285,7 +285,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) var stack = context.Stack; stack.EnsureCapacity(frameBase + context.Chunk.MaxStackPosition); ref var constHead = ref MemoryMarshalEx.UnsafeElementAt(context.Chunk.Constants, 0); - ref byte lineAndCountHookMask = ref context.Thread.LineAndCountHookMask; + ref var lineAndCountHookMask = ref context.Thread.LineAndCountHookMask; goto Loop; LineHook: @@ -308,7 +308,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) { var instructionRef = Unsafe.Add(ref instructionsHead, ++context.Pc); context.Instruction = instructionRef; - if (lineAndCountHookMask != 0 && (context.Pc != context.LastHookPc)) + if (lineAndCountHookMask.Value!=0 && (context.Pc != context.LastHookPc)) { goto LineHook; } @@ -1052,7 +1052,7 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount); thread.PushCallStackFrame(newFrame); - if (thread.CallOrReturnHookEnabled && !context.Thread.IsInHook) + if (thread.CallOrReturnHookMask.Value!=0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.Call; ExecuteCallHook(ref context, argumentCount); @@ -1187,7 +1187,7 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest newFrame.CallerInstructionIndex = lastPc; thread.PushCallStackFrame(newFrame); - if (thread.CallOrReturnHookEnabled && !context.Thread.IsInHook) + if (thread.CallOrReturnHookMask.Value!=0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.TailCall; ExecuteCallHook(ref context, argumentCount, true); From 3c2be936ff419301842b027356227a719a5cb0ef Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 3 Mar 2025 19:20:09 +0900 Subject: [PATCH 15/22] Fix: hook metamethods --- src/Lua/LuaFunction.cs | 7 + src/Lua/Runtime/LuaVirtualMachine.Debug.cs | 166 +++++++++++---------- src/Lua/Runtime/LuaVirtualMachine.cs | 49 +++++- tests/Lua.Tests/MetatableTests.cs | 18 ++- 4 files changed, 154 insertions(+), 86 deletions(-) diff --git a/src/Lua/LuaFunction.cs b/src/Lua/LuaFunction.cs index 2c1fae5a..766ed40e 100644 --- a/src/Lua/LuaFunction.cs +++ b/src/Lua/LuaFunction.cs @@ -22,8 +22,15 @@ public async ValueTask InvokeAsync(LuaFunctionExecutionContext context, Mem }; context.Thread.PushCallStackFrame(frame); + + try { + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + return await LuaVirtualMachine.ExecuteCallHook(context, buffer, cancellationToken); + } + return await Func(context, buffer, cancellationToken); } finally diff --git a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs index dd4058ad..e6419833 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs @@ -113,99 +113,107 @@ static async ValueTask Impl(VirtualMachineExecutionContext context) } } - static void ExecuteCallHook(ref VirtualMachineExecutionContext context, int argCount, bool isTailCall = false) + [MethodImpl(MethodImplOptions.NoInlining)] + static ValueTask ExecuteCallHook(ref VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments, bool isTailCall = false) { - context.Task = Impl(context, argCount, isTailCall); - - static async ValueTask Impl(VirtualMachineExecutionContext context, int argCount, bool isTailCall) + return ExecuteCallHook(new() { - var topFrame = context.Thread.GetCurrentFrame(); - var hook = context.Thread.Hook!; - var stack = context.Thread.Stack; - CallStackFrame frame; - if (context.Thread.IsCallHookEnabled) - { - stack.Push((isTailCall ? "tail call" : "call")); + State = context.State, + Thread = context.Thread, + ArgumentCount = arguments, + FrameBase = frame.Base, + CallerInstructionIndex = frame.CallerInstructionIndex, + }, context.ResultsBuffer, context.CancellationToken, isTailCall); + } - stack.Push(LuaValue.Nil); - var funcContext = new LuaFunctionExecutionContext - { - State = context.State, - Thread = context.Thread, - ArgumentCount = 2, - FrameBase = context.Thread.Stack.Count - 2, - }; - frame = new() - { - Base = funcContext.FrameBase, - VariableArgumentCount = hook.GetVariableArgumentCount(2), - Function = hook, - CallerInstructionIndex = 0, - }; - frame.Flags |= CallStackFrameFlags.InHook; + internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken, bool isTailCall = false) + { + var topFrame = context.Thread.GetCurrentFrame(); + var argCount = context.ArgumentCount; + var hook = context.Thread.Hook!; + var stack = context.Thread.Stack; + CallStackFrame frame; + if (context.Thread.IsCallHookEnabled) + { + stack.Push((isTailCall ? "tail call" : "call")); - context.Thread.PushCallStackFrame(frame); - try - { - context.Thread.IsInHook = true; - await hook.Func(funcContext, Memory.Empty, context.CancellationToken); - } - finally - { - context.Thread.IsInHook = false; - context.Thread.PopCallStackFrame(); - } + stack.Push(LuaValue.Nil); + var funcContext = new LuaFunctionExecutionContext + { + State = context.State, + Thread = context.Thread, + ArgumentCount = 2, + FrameBase = context.Thread.Stack.Count - 2, + }; + frame = new() + { + Base = funcContext.FrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(2), + Function = hook, + CallerInstructionIndex = 0, + }; + frame.Flags |= CallStackFrameFlags.InHook; + + context.Thread.PushCallStackFrame(frame); + try + { + context.Thread.IsInHook = true; + await hook.Func(funcContext, Memory.Empty, cancellationToken); + } + finally + { + context.Thread.IsInHook = false; + context.Thread.PopCallStackFrame(); } + } - frame = topFrame; - context.Push(frame); + frame = topFrame; - var task = frame.Function.Func(new () + var task = frame.Function.Func(new() + { + State = context.State, + Thread = context.Thread, + ArgumentCount = argCount, + FrameBase = frame.Base, + }, buffer, cancellationToken); + if (isTailCall || !context.Thread.IsReturnHookEnabled) + { + return await task; + } + + { + var result = await task; + stack.Push("return"); + stack.Push(LuaValue.Nil); + var funcContext = new LuaFunctionExecutionContext { State = context.State, Thread = context.Thread, - ArgumentCount = argCount, - FrameBase = frame.Base, - }, context.ResultsBuffer, context.CancellationToken); - if (isTailCall || !context.Thread.IsReturnHookEnabled) + ArgumentCount = 2, + FrameBase = context.Thread.Stack.Count - 2, + }; + frame = new() { - return await task; + Base = funcContext.FrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(2), + Function = hook, + CallerInstructionIndex = 0 + }; + frame.Flags |= CallStackFrameFlags.InHook; + + context.Thread.PushCallStackFrame(frame); + try + { + context.Thread.IsInHook = true; + await hook.Func(funcContext, Memory.Empty, cancellationToken); } - + finally { - var result = await task; - stack.Push("return"); - stack.Push(LuaValue.Nil); - var funcContext = new LuaFunctionExecutionContext - { - State = context.State, - Thread = context.Thread, - ArgumentCount = 2, - FrameBase = context.Thread.Stack.Count - 2, - }; - frame = new() - { - Base = funcContext.FrameBase, - VariableArgumentCount = hook.GetVariableArgumentCount(2), - Function = hook, - CallerInstructionIndex = 0 - }; - frame.Flags |= CallStackFrameFlags.InHook; - - context.Thread.PushCallStackFrame(frame); - try - { - context.Thread.IsInHook = true; - await hook.Func(funcContext, Memory.Empty, context.CancellationToken); - } - finally - { - context.Thread.IsInHook = false; - } - - context.Thread.PopCallStackFrame(); - return result; + context.Thread.IsInHook = false; } + + context.Thread.PopCallStackFrame(); + return result; } } } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index 321c50df..e1b0759e 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -308,7 +308,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) { var instructionRef = Unsafe.Add(ref instructionsHead, ++context.Pc); context.Instruction = instructionRef; - if (lineAndCountHookMask.Value!=0 && (context.Pc != context.LastHookPc)) + if (lineAndCountHookMask.Value != 0 && (context.Pc != context.LastHookPc)) { goto LineHook; } @@ -1052,10 +1052,10 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount); thread.PushCallStackFrame(newFrame); - if (thread.CallOrReturnHookMask.Value!=0 && !context.Thread.IsInHook) + if (thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.Call; - ExecuteCallHook(ref context, argumentCount); + context.Task=ExecuteCallHook(ref context, newFrame,argumentCount); doRestart = false; return false; } @@ -1187,10 +1187,10 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest newFrame.CallerInstructionIndex = lastPc; thread.PushCallStackFrame(newFrame); - if (thread.CallOrReturnHookMask.Value!=0 && !context.Thread.IsInHook) + if (thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.TailCall; - ExecuteCallHook(ref context, argumentCount, true); + context.Task=ExecuteCallHook(ref context, newFrame,argumentCount,true); doRestart = false; return false; } @@ -1390,6 +1390,14 @@ static bool CallGetTableFunc(LuaValue table, LuaFunction indexTable, LuaValue ke var newFrame = indexTable.CreateNewFrame(ref context, stack.Count - 2); context.Thread.PushCallStackFrame(newFrame); + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + context.PostOperation = context.Instruction.OpCode == OpCode.GetTable ? PostOperationType.SetResult : PostOperationType.Self; + context.Task=ExecuteCallHook(ref context, newFrame,2); + doRestart = false; + result = default; + return false; + } if (indexTable is Closure) { @@ -1483,6 +1491,13 @@ static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaVa var newFrame = newIndexFunction.CreateNewFrame(ref context, stack.Count - 3); context.Thread.PushCallStackFrame(newFrame); + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.Nop; + context.Task=ExecuteCallHook(ref context, newFrame,3); + doRestart = false; + return false; + } if (newIndexFunction is Closure) { @@ -1494,6 +1509,7 @@ static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaVa var task = newIndexFunction.Invoke(ref context, newFrame, 3); if (!task.IsCompleted) { + context.PostOperation = PostOperationType.Nop; context.Task = task; return false; } @@ -1529,6 +1545,13 @@ static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc, var newFrame = func.CreateNewFrame(ref context, stack.Count - 2); context.Thread.PushCallStackFrame(newFrame); + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.SetResult; + context.Task=ExecuteCallHook(ref context, newFrame,2); + doRestart = false; + return false; + } if (func is Closure) { @@ -1576,6 +1599,13 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu var newFrame = func.CreateNewFrame(ref context, stack.Count - 1); context.Thread.PushCallStackFrame(newFrame); + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.SetResult; + context.Task=ExecuteCallHook(ref context, newFrame,1); + doRestart = false; + return false; + } if (func is Closure) { @@ -1589,6 +1619,7 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu if (!task.IsCompleted) { + context.PostOperation = PostOperationType.SetResult; context.Task = task; return false; } @@ -1633,7 +1664,13 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, var newFrame = func.CreateNewFrame(ref context, stack.Count - 2); if (reverseLe) newFrame.Flags |= CallStackFrameFlags.ReversedLe; context.Thread.PushCallStackFrame(newFrame); - + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.Compare; + context.Task=ExecuteCallHook(ref context, newFrame,2); + doRestart = false; + return false; + } if (func is Closure) { context.Push(newFrame); diff --git a/tests/Lua.Tests/MetatableTests.cs b/tests/Lua.Tests/MetatableTests.cs index cc0d3b58..d82c83a6 100644 --- a/tests/Lua.Tests/MetatableTests.cs +++ b/tests/Lua.Tests/MetatableTests.cs @@ -10,7 +10,7 @@ public class MetatableTests public void SetUp() { state = LuaState.Create(); - state.OpenBasicLibrary(); + state.OpenStandardLibraries(); } [Test] @@ -86,4 +86,20 @@ public async Task Test_Metamethod_NewIndex() "; await state.DoStringAsync(source); } + + [Test] + public async Task Test_Hook_Metamethods() + { + var source = """ + local t = {} + local a =setmetatable({},{__add =function (a,b) return a end}) + + debug.sethook(function () table.insert(t,debug.traceback()) end,"c") + a =a+a + return t + """; + var r = await state.DoStringAsync(source); + Assert.That(r, Has.Length.EqualTo(1)); + Assert.That(r[0].Read()[1].Read(), Does.Contain("stack traceback:")); + } } \ No newline at end of file From cc6845964713535db0f56fd5eed98be7c67fe1a9 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:20:48 +0900 Subject: [PATCH 16/22] Optimize: return LuaThread.GetCurrentFrame by ref readonly --- src/Lua/Internal/FastStackCore.cs | 15 +++++++- src/Lua/LuaFunctionExecutionContext.cs | 3 +- src/Lua/LuaThread.cs | 21 ++++++++--- src/Lua/Runtime/Closure.cs | 4 +- src/Lua/Runtime/LuaVirtualMachine.Debug.cs | 44 ++++++++++------------ src/Lua/Runtime/LuaVirtualMachine.cs | 2 +- 6 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/Lua/Internal/FastStackCore.cs b/src/Lua/Internal/FastStackCore.cs index 2879dbf1..485daae9 100644 --- a/src/Lua/Internal/FastStackCore.cs +++ b/src/Lua/Internal/FastStackCore.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -100,6 +101,17 @@ public T Peek() return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref T PeekRef() + { + if (tail == 0) + { + ThrowForEmptyStack(); + } + + return ref array[tail - 1]!; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EnsureCapacity(int capacity) @@ -130,7 +142,8 @@ public void Clear() array.AsSpan(0, tail).Clear(); tail = 0; } - + + [DoesNotReturn] void ThrowForEmptyStack() { throw new InvalidOperationException("Empty stack"); diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index 87df5f6a..c857110f 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -118,8 +118,7 @@ internal T GetArgumentOrDefault(int index, T defaultValue = default!) public CsClosure? GetCsClosure() { - var stack = Thread.CallStack[^1]; - return stack.Function as CsClosure; + return Thread.GetCurrentFrame().Function as CsClosure; } internal void ThrowBadArgument(int index, string message) diff --git a/src/Lua/LuaThread.cs b/src/Lua/LuaThread.cs index 6047d04e..f559098b 100644 --- a/src/Lua/LuaThread.cs +++ b/src/Lua/LuaThread.cs @@ -28,10 +28,21 @@ internal bool IsCountHookEnabled get => LineAndCountHookMask.Flag1; set => LineAndCountHookMask.Flag1 = value; } - + internal BitFlags2 LineAndCountHookMask; - internal bool IsCallHookEnabled { get => CallOrReturnHookMask.Flag0; set => CallOrReturnHookMask.Flag0 = value; } - internal bool IsReturnHookEnabled { get => CallOrReturnHookMask.Flag1; set => CallOrReturnHookMask.Flag1 = value; } + + internal bool IsCallHookEnabled + { + get => CallOrReturnHookMask.Flag0; + set => CallOrReturnHookMask.Flag0 = value; + } + + internal bool IsReturnHookEnabled + { + get => CallOrReturnHookMask.Flag1; + set => CallOrReturnHookMask.Flag1 = value; + } + internal BitFlags2 CallOrReturnHookMask; internal bool IsInHook; internal int HookCount; @@ -39,9 +50,9 @@ internal bool IsCountHookEnabled internal int LastPc; internal LuaFunction? Hook { get; set; } - public CallStackFrame GetCurrentFrame() + public ref readonly CallStackFrame GetCurrentFrame() { - return callStack.Peek(); + return ref callStack.PeekRef(); } public ReadOnlySpan GetStackValues() diff --git a/src/Lua/Runtime/Closure.cs b/src/Lua/Runtime/Closure.cs index 4817b27b..1db8b1b8 100644 --- a/src/Lua/Runtime/Closure.cs +++ b/src/Lua/Runtime/Closure.cs @@ -48,7 +48,7 @@ static UpValue GetUpValueFromDescription(LuaState state, LuaThread thread, UpVal { if (description.IsInRegister) { - return state.GetOrAddUpValue(thread, thread.GetCallStackFrames()[^1].Base + description.Index); + return state.GetOrAddUpValue(thread, thread.GetCurrentFrame().Base + description.Index); } if (description.Index == -1) // -1 is global environment @@ -56,7 +56,7 @@ static UpValue GetUpValueFromDescription(LuaState state, LuaThread thread, UpVal return envUpValue; } - if (thread.GetCallStackFrames()[^1].Function is Closure parentClosure) + if (thread.GetCurrentFrame().Function is Closure parentClosure) { return parentClosure.UpValues[description.Index]; } diff --git a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs index e6419833..22a8bb06 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs @@ -128,11 +128,9 @@ static ValueTask ExecuteCallHook(ref VirtualMachineExecutionContext context internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken, bool isTailCall = false) { - var topFrame = context.Thread.GetCurrentFrame(); var argCount = context.ArgumentCount; var hook = context.Thread.Hook!; var stack = context.Thread.Stack; - CallStackFrame frame; if (context.Thread.IsCallHookEnabled) { stack.Push((isTailCall ? "tail call" : "call")); @@ -145,14 +143,14 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext ArgumentCount = 2, FrameBase = context.Thread.Stack.Count - 2, }; - frame = new() + CallStackFrame frame = new() { Base = funcContext.FrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook, CallerInstructionIndex = 0, + Flags = CallStackFrameFlags.InHook }; - frame.Flags |= CallStackFrameFlags.InHook; context.Thread.PushCallStackFrame(frame); try @@ -167,21 +165,19 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext } } - frame = topFrame; - - var task = frame.Function.Func(new() - { - State = context.State, - Thread = context.Thread, - ArgumentCount = argCount, - FrameBase = frame.Base, - }, buffer, cancellationToken); - if (isTailCall || !context.Thread.IsReturnHookEnabled) - { - return await task; - } - { + var frame = context.Thread.GetCurrentFrame(); + var task = frame.Function.Func(new() + { + State = context.State, + Thread = context.Thread, + ArgumentCount = argCount, + FrameBase = frame.Base, + }, buffer, cancellationToken); + if (isTailCall || !context.Thread.IsReturnHookEnabled) + { + return await task; + } var result = await task; stack.Push("return"); stack.Push(LuaValue.Nil); @@ -192,16 +188,16 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext ArgumentCount = 2, FrameBase = context.Thread.Stack.Count - 2, }; - frame = new() + + + context.Thread.PushCallStackFrame( new() { Base = funcContext.FrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook, - CallerInstructionIndex = 0 - }; - frame.Flags |= CallStackFrameFlags.InHook; - - context.Thread.PushCallStackFrame(frame); + CallerInstructionIndex = 0, + Flags = CallStackFrameFlags.InHook + }); try { context.Thread.IsInHook = true; diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index e1b0759e..bb86262d 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -265,7 +265,7 @@ enum PostOperationType internal static ValueTask ExecuteClosureAsync(LuaState luaState, Memory buffer, CancellationToken cancellationToken) { var thread = luaState.CurrentThread; - ref readonly var frame = ref thread.GetCallStackFrames()[^1]; + ref readonly var frame = ref thread.GetCurrentFrame(); var resultBuffer = LuaValueArrayPool.Rent1024(); var context = new VirtualMachineExecutionContext(luaState, thread.Stack, resultBuffer, buffer, thread, in frame, From f887bfbf15516ed98137cc6493f5fe03bacb1b73 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Thu, 6 Mar 2025 23:25:18 +0900 Subject: [PATCH 17/22] Change: Return the result of LuaFunction to LuaStack. --- sandbox/Benchmark/AddBenchmark.cs | 7 +- sandbox/Benchmark/InterpreterSteps.cs | 4 +- sandbox/ConsoleApp1/Program.cs | 13 +- .../LuaObjectGenerator.Emit.cs | 27 +- src/Lua/Internal/LuaValueArrayPool.cs | 73 ---- src/Lua/LuaCoroutine.cs | 57 +-- src/Lua/LuaFunction.cs | 19 +- src/Lua/LuaFunctionExecutionContext.cs | 62 +++ src/Lua/LuaFunctionExtensions.cs | 9 +- src/Lua/LuaMainThread.cs | 8 +- src/Lua/LuaResult.cs | 26 ++ src/Lua/LuaState.cs | 51 +-- src/Lua/LuaStateExtensions.cs | 43 +- src/Lua/LuaThread.cs | 10 +- src/Lua/LuaThreadExtensions.cs | 26 +- src/Lua/LuaValue.cs | 14 +- src/Lua/Runtime/CallStackFrame.cs | 5 +- src/Lua/Runtime/Closure.cs | 2 +- src/Lua/Runtime/CsClosure.cs | 2 +- src/Lua/Runtime/LuaStack.cs | 12 + src/Lua/Runtime/LuaVirtualMachine.Debug.cs | 49 ++- src/Lua/Runtime/LuaVirtualMachine.cs | 396 +++++++++--------- src/Lua/Standard/BasicLibrary.cs | 236 +++++------ src/Lua/Standard/BitwiseLibrary.cs | 82 ++-- src/Lua/Standard/CoroutineLibrary.cs | 102 +++-- src/Lua/Standard/DebugLibrary.cs | 160 +++---- src/Lua/Standard/FileHandle.cs | 69 ++- src/Lua/Standard/IOLibrary.cs | 104 +++-- src/Lua/Standard/Internal/IOHelper.cs | 44 +- src/Lua/Standard/MathematicsLibrary.cs | 156 +++---- src/Lua/Standard/ModuleLibrary.cs | 11 +- src/Lua/Standard/OpenLibsExtensions.cs | 6 +- src/Lua/Standard/OperatingSystemLibrary.cs | 69 ++- src/Lua/Standard/StringLibrary.cs | 129 +++--- src/Lua/Standard/TableLibrary.cs | 54 +-- tests/Lua.Tests/LuaTests.cs | 2 +- tests/Lua.Tests/MetatableTests.cs | 16 + 37 files changed, 995 insertions(+), 1160 deletions(-) delete mode 100644 src/Lua/Internal/LuaValueArrayPool.cs create mode 100644 src/Lua/LuaResult.cs diff --git a/sandbox/Benchmark/AddBenchmark.cs b/sandbox/Benchmark/AddBenchmark.cs index 0c0a9221..99af7b39 100644 --- a/sandbox/Benchmark/AddBenchmark.cs +++ b/sandbox/Benchmark/AddBenchmark.cs @@ -22,10 +22,11 @@ public void Setup() core.Setup("add.lua"); core.LuaCSharpState.OpenStandardLibraries(); - core.LuaCSharpState.Environment["add"] = new LuaFunction("add", (context, buffer, ct) => + core.LuaCSharpState.Environment["add"] = new LuaFunction("add", (context, ct) => { - buffer.Span[0] = context.GetArgument(0) + context.GetArgument(1); - return new(1); + var a = context.GetArgument(0); + var b = context.GetArgument(1); + return new(context.Return(a + b)); }); core.MoonSharpState.Globals["add"] = (Func)Add; core.NLuaState.RegisterFunction("add", typeof(AddBenchmark).GetMethod(nameof(Add), BindingFlags.Static | BindingFlags.Public)); diff --git a/sandbox/Benchmark/InterpreterSteps.cs b/sandbox/Benchmark/InterpreterSteps.cs index 59ed0bb4..70f2de8e 100644 --- a/sandbox/Benchmark/InterpreterSteps.cs +++ b/sandbox/Benchmark/InterpreterSteps.cs @@ -92,6 +92,8 @@ public Chunk Compile() [Benchmark] public async ValueTask RunAsync() { - await state.RunAsync(chunk, results); + using (await state.RunAsync(chunk)) + { + } } } \ No newline at end of file diff --git a/sandbox/ConsoleApp1/Program.cs b/sandbox/ConsoleApp1/Program.cs index 10905080..fc81e61f 100644 --- a/sandbox/ConsoleApp1/Program.cs +++ b/sandbox/ConsoleApp1/Program.cs @@ -27,12 +27,11 @@ Console.WriteLine("Output " + new string('-', 50)); - var results = new LuaValue[64]; - var resultCount = await state.RunAsync(chunk, results); + using var results = await state.RunAsync(chunk); Console.WriteLine("Result " + new string('-', 50)); - for (int i = 0; i < resultCount; i++) + for (int i = 0; i < results.Count; i++) { Console.WriteLine(results[i]); } @@ -42,9 +41,13 @@ catch (Exception ex) { Console.WriteLine(ex); - if(ex is LuaRuntimeException { InnerException: not null } luaEx) + if (ex is LuaRuntimeException luaRuntimeException) { - Console.WriteLine(luaEx.InnerException); + Console.WriteLine(luaRuntimeException.LuaTraceback); + if (ex is { InnerException: not null } luaEx) + { + Console.WriteLine(luaEx.InnerException); + } } } diff --git a/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs b/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs index 4772b3ef..4d78eb92 100644 --- a/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs +++ b/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs @@ -201,7 +201,7 @@ static bool ValidateMembers(TypeMetadata typeMetadata, Compilation compilation, static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context) { - builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_index = new global::Lua.LuaFunction(""index"", (context, buffer, ct) =>"); + builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_index = new global::Lua.LuaFunction(""index"", (context, ct) =>"); using (builder.BeginBlockScope()) { @@ -233,8 +233,7 @@ static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builde } builder.AppendLine(";"); - builder.AppendLine("buffer.Span[0] = result;"); - builder.AppendLine("return new(1);"); + builder.AppendLine( "return new global::System.Threading.Tasks.ValueTask(context.Return(result));"); } builder.AppendLine(");"); @@ -244,7 +243,7 @@ static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builde static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, in SourceProductionContext context) { - builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_newindex = new global::Lua.LuaFunction(""newindex"", (context, buffer, ct) =>"); + builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_newindex = new global::Lua.LuaFunction(""newindex"", (context, ct) =>"); using (builder.BeginBlockScope()) { @@ -295,8 +294,7 @@ static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder bui builder.AppendLine(@$"throw new global::Lua.LuaRuntimeException(context.State.GetTraceback(), $""'{{key}}' not found."");"); } } - - builder.AppendLine("return new(0);"); + builder.AppendLine( "return new global::System.Threading.Tasks.ValueTask(context.Return());"); } builder.AppendLine(");"); @@ -348,7 +346,7 @@ static bool TryEmitMethods(TypeMetadata typeMetadata, CodeBuilder builder, Symbo static void EmitMethodFunction(string functionName, string chunkName, TypeMetadata typeMetadata, MethodMetadata methodMetadata, CodeBuilder builder, SymbolReferences references) { - builder.AppendLine($@"static readonly global::Lua.LuaFunction {functionName} = new global::Lua.LuaFunction(""{chunkName}"", {(methodMetadata.IsAsync ? "async" : "")} (context, buffer, ct) =>"); + builder.AppendLine($@"static readonly global::Lua.LuaFunction {functionName} = new global::Lua.LuaFunction(""{chunkName}"", {(methodMetadata.IsAsync ? "async" : "")} (context, ct) =>"); using (builder.BeginBlockScope()) { @@ -413,23 +411,28 @@ static void EmitMethodFunction(string functionName, string chunkName, TypeMetada builder.Append(string.Join(",", Enumerable.Range(1, index - 1).Select(x => $"arg{x}")), false); builder.AppendLine(");", false); } - + + builder.Append("return "); if (methodMetadata.HasReturnValue) { if (SymbolEqualityComparer.Default.Equals(methodMetadata.Symbol.ReturnType, references.LuaValue)) { - builder.AppendLine("buffer.Span[0] = result;"); + + builder.AppendLine(methodMetadata.IsAsync ? + "context.Return(result));" : + "new global::System.Threading.Tasks.ValueTask(context.Return(result));"); } else { - builder.AppendLine("buffer.Span[0] = new global::Lua.LuaValue(result);"); + builder.AppendLine(methodMetadata.IsAsync ? + "context.Return(new global::Lua.LuaValue(result))));" : + "new global::System.Threading.Tasks.ValueTask(context.Return(new global::Lua.LuaValue(result)));"); } - builder.AppendLine($"return {(methodMetadata.IsAsync ? "1" : "new(1)")};"); } else { - builder.AppendLine($"return {(methodMetadata.IsAsync ? "0" : "new(0)")};"); + builder.AppendLine(methodMetadata.IsAsync ? "context.Return();" : "new global::System.Threading.Tasks.ValueTask(context.Return());"); } } builder.AppendLine(");"); diff --git a/src/Lua/Internal/LuaValueArrayPool.cs b/src/Lua/Internal/LuaValueArrayPool.cs deleted file mode 100644 index 8c35deba..00000000 --- a/src/Lua/Internal/LuaValueArrayPool.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Lua.Internal; - -internal static class LuaValueArrayPool -{ - static FastStackCore poolOf1024; - static FastStackCore poolOf1; - - static readonly object lockObject = new(); - - - public static LuaValue[] Rent1024() - { - lock (lockObject) - { - if (poolOf1024.Count > 0) - { - return poolOf1024.Pop(); - } - - return new LuaValue[1024]; - } - } - - public static LuaValue[] Rent1() - { - lock (lockObject) - { - if (poolOf1.Count > 0) - { - return poolOf1.Pop(); - } - - return new LuaValue[1]; - } - } - - public static void Return1024(LuaValue[] array, bool clear = false) - { - if (array.Length != 1024) - { - ThrowInvalidArraySize(array.Length, 1024); - } - - if (clear) - { - array.AsSpan().Clear(); - } - lock (lockObject) - { - poolOf1024.Push(array); - } - } - - - public static void Return1(LuaValue[] array) - { - if (array.Length != 1) - { - ThrowInvalidArraySize(array.Length, 1); - } - - array[0] = LuaValue.Nil; - lock (lockObject) - { - poolOf1.Push(array); - } - } - - static void ThrowInvalidArraySize(int size, int expectedSize) - { - throw new InvalidOperationException($"Invalid array size: {size}, expected: {expectedSize}"); - } -} \ No newline at end of file diff --git a/src/Lua/LuaCoroutine.cs b/src/Lua/LuaCoroutine.cs index dcf8c5d5..90541c8b 100644 --- a/src/Lua/LuaCoroutine.cs +++ b/src/Lua/LuaCoroutine.cs @@ -20,19 +20,17 @@ struct ResumeContext byte status; bool isFirstCall = true; ValueTask functionTask; - LuaValue[] buffer; + int returnFrameBase; ManualResetValueTaskSourceCore resume; ManualResetValueTaskSourceCore yield; Traceback? traceback; + internal int ReturnFrameBase => returnFrameBase; public LuaCoroutine(LuaFunction function, bool isProtectedMode) { IsProtectedMode = isProtectedMode; Function = function; - - buffer = ArrayPool.Shared.Rent(1024); - buffer.AsSpan().Clear(); } public override LuaThreadStatus GetStatus() => (LuaThreadStatus)status; @@ -44,11 +42,9 @@ public override void UnsafeSetStatus(LuaThreadStatus status) public bool IsProtectedMode { get; } public LuaFunction Function { get; } - - internal Traceback? LuaTraceback => traceback; - public override async ValueTask ResumeAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default) + public override async ValueTask ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default) { var baseThread = context.Thread; baseThread.UnsafeSetStatus(LuaThreadStatus.Normal); @@ -84,9 +80,7 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con case LuaThreadStatus.Running: if (IsProtectedMode) { - buffer.Span[0] = false; - buffer.Span[1] = "cannot resume non-suspended coroutine"; - return 2; + return context.Return(false, "cannot resume non-suspended coroutine"); } else { @@ -95,9 +89,7 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con case LuaThreadStatus.Dead: if (IsProtectedMode) { - buffer.Span[0] = false; - buffer.Span[1] = "cannot resume dead coroutine"; - return 2; + return context.Return(false, "cannot resume dead coroutine"); } else { @@ -121,6 +113,7 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con { if (isFirstCall) { + returnFrameBase = Stack.Count; int frameBase; var variableArgumentCount = Function.GetVariableArgumentCount(context.ArgumentCount - 1); @@ -128,7 +121,6 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con { var fixedArgumentCount = context.ArgumentCount - 1 - variableArgumentCount; var args = context.Arguments; - Stack.PushRange(args.Slice(1 + fixedArgumentCount, variableArgumentCount)); frameBase = Stack.Count; @@ -147,47 +139,35 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con State = context.State, Thread = this, ArgumentCount = context.ArgumentCount - 1, - FrameBase = frameBase - }, this.buffer, cancellationToken).Preserve(); + FrameBase = frameBase, + ReturnFrameBase = returnFrameBase + }, cancellationToken).Preserve(); Volatile.Write(ref isFirstCall, false); } var (index, result0, result1) = await ValueTaskEx.WhenAny(resumeTask, functionTask!); - var bufferSpan = buffer.Span; if (index == 0) { var results = result0.Results; - - bufferSpan[0] = true; - results.CopyTo(bufferSpan[1..]); - - return results.Length + 1; + return context.Return(true, results.AsSpan()); } else { - var resultCount = functionTask!.Result; - Volatile.Write(ref status, (byte)LuaThreadStatus.Dead); - bufferSpan[0] = true; - this.buffer.AsSpan()[..resultCount].CopyTo(bufferSpan[1..]); - - ArrayPool.Shared.Return(this.buffer); - - return 1 + resultCount; + var count = context.Return(true, Stack.AsSpan()[returnFrameBase..]); + Stack.PopUntil(returnFrameBase); + return count; } } catch (Exception ex) when (ex is not OperationCanceledException) { if (IsProtectedMode) { - ArrayPool.Shared.Return(this.buffer); traceback = (ex as LuaRuntimeException)?.LuaTraceback; Volatile.Write(ref status, (byte)LuaThreadStatus.Dead); - buffer.Span[0] = false; - buffer.Span[1] = ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message; - return 2; + return context.Return(false, ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message); } else { @@ -207,7 +187,7 @@ public override async ValueTask ResumeAsync(LuaFunctionExecutionContext con } } - public override async ValueTask YieldAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default) + public override async ValueTask YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default) { if (Volatile.Read(ref status) != (byte)LuaThreadStatus.Running) { @@ -240,12 +220,7 @@ public override async ValueTask YieldAsync(LuaFunctionExecutionContext cont try { var result = await new ValueTask(this, yield.Version); - for (int i = 0; i < result.Results.Length; i++) - { - buffer.Span[i] = result.Results[i]; - } - - return result.Results.Length; + return (context.Return(result.Results)); } catch (Exception ex) when (ex is not OperationCanceledException) { diff --git a/src/Lua/LuaFunction.cs b/src/Lua/LuaFunction.cs index 766ed40e..855ca7ea 100644 --- a/src/Lua/LuaFunction.cs +++ b/src/Lua/LuaFunction.cs @@ -1,37 +1,34 @@ -using System.Runtime.CompilerServices; using Lua.Runtime; namespace Lua; -public class LuaFunction(string name, Func, CancellationToken, ValueTask> func) +public class LuaFunction(string name, Func> func) { public string Name { get; } = name; - internal Func, CancellationToken, ValueTask> Func { get; } = func; + internal Func> Func { get; } = func; - public LuaFunction(Func, CancellationToken, ValueTask> func) : this("anonymous", func) + public LuaFunction(Func> func) : this("anonymous", func) { } - public async ValueTask InvokeAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask InvokeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var frame = new CallStackFrame { Base = context.FrameBase, - VariableArgumentCount = this is Closure closure ? Math.Max(context.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + VariableArgumentCount = this.GetVariableArgumentCount(context.ArgumentCount), Function = this, + ReturnBase = context.ReturnFrameBase }; - context.Thread.PushCallStackFrame(frame); - - try { if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { - return await LuaVirtualMachine.ExecuteCallHook(context, buffer, cancellationToken); + return await LuaVirtualMachine.ExecuteCallHook(context, cancellationToken); } - return await Func(context, buffer, cancellationToken); + return await Func(context, cancellationToken); } finally { diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index c857110f..408d3dd2 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -12,6 +12,7 @@ public readonly record struct LuaFunctionExecutionContext public required LuaThread Thread { get; init; } public required int ArgumentCount { get; init; } public required int FrameBase { get; init; } + public required int ReturnFrameBase { get; init; } public SourcePosition? SourcePosition { get; init; } public string? RootChunkName { get; init; } public string? ChunkName { get; init; } @@ -116,6 +117,67 @@ internal T GetArgumentOrDefault(int index, T defaultValue = default!) return argValue; } + public int Return() + { + Thread.Stack.PopUntil(ReturnFrameBase); + return 0; + } + + public int Return(LuaValue result) + { + var stack = Thread.Stack; + stack.SetTop(ReturnFrameBase + 1); + stack.FastGet(ReturnFrameBase) = result; + return 1; + } + + public int Return(LuaValue result0, LuaValue result1) + { + var stack = Thread.Stack; + stack.SetTop(ReturnFrameBase + 2); + stack.FastGet(ReturnFrameBase) = result0; + stack.FastGet(ReturnFrameBase + 1) = result1; + return 2; + } + + public int Return(LuaValue result0, LuaValue result1, LuaValue result2) + { + var stack = Thread.Stack; + stack.SetTop(ReturnFrameBase + 3); + stack.FastGet(ReturnFrameBase) = result0; + stack.FastGet(ReturnFrameBase + 1) = result1; + stack.FastGet(ReturnFrameBase + 2) = result2; + return 3; + } + + public int Return(ReadOnlySpan results) + { + var stack = Thread.Stack; + stack.EnsureCapacity(ReturnFrameBase + results.Length); + results.CopyTo(stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + results.Length)]); + stack.SetTop(ReturnFrameBase + results.Length); + return results.Length; + } + + internal int Return(LuaValue result0, ReadOnlySpan results) + { + var stack = Thread.Stack; + stack.EnsureCapacity(ReturnFrameBase + results.Length); + stack.SetTop(ReturnFrameBase + results.Length + 1); + var buffer = stack.GetBuffer(); + buffer[ReturnFrameBase] = result0; + results.CopyTo(buffer[(ReturnFrameBase + 1)..(ReturnFrameBase + results.Length + 1)]); + return results.Length + 1; + } + + public Span GetReturnBuffer(int count) + { + var stack = Thread.Stack; + stack.SetTop(ReturnFrameBase + count); + var buffer = stack.GetBuffer()[ReturnFrameBase..(ReturnFrameBase + count)]; + return buffer; + } + public CsClosure? GetCsClosure() { return Thread.GetCurrentFrame().Function as CsClosure; diff --git a/src/Lua/LuaFunctionExtensions.cs b/src/Lua/LuaFunctionExtensions.cs index 7027164f..dc043d7e 100644 --- a/src/Lua/LuaFunctionExtensions.cs +++ b/src/Lua/LuaFunctionExtensions.cs @@ -6,7 +6,6 @@ public static class LuaFunctionExtensions { public static async ValueTask InvokeAsync(this LuaFunction function, LuaState state, LuaValue[] arguments, CancellationToken cancellationToken = default) { - using var buffer = new PooledArray(1024); var thread = state.CurrentThread; var frameBase = thread.Stack.Count; @@ -22,8 +21,10 @@ public static async ValueTask InvokeAsync(this LuaFunction function, Thread = thread, ArgumentCount = arguments.Length, FrameBase = frameBase, - }, buffer.AsMemory(), cancellationToken); - - return buffer.AsSpan()[0..resultCount].ToArray(); + ReturnFrameBase = frameBase, + }, cancellationToken); + var r =thread.Stack.GetBuffer()[frameBase..(frameBase + resultCount)].ToArray(); + thread.Stack.PopUntil(frameBase); + return r; } } \ No newline at end of file diff --git a/src/Lua/LuaMainThread.cs b/src/Lua/LuaMainThread.cs index 12e1070a..fabe6d94 100644 --- a/src/Lua/LuaMainThread.cs +++ b/src/Lua/LuaMainThread.cs @@ -12,14 +12,12 @@ public override void UnsafeSetStatus(LuaThreadStatus status) // Do nothing } - public override ValueTask ResumeAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default) + public override ValueTask ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default) { - buffer.Span[0] = false; - buffer.Span[1] = "cannot resume non-suspended coroutine"; - return new(2); + return new(context.Return(false,"cannot resume non-suspended coroutine")); } - public override ValueTask YieldAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default) + public override ValueTask YieldAsync(LuaFunctionExecutionContext context,CancellationToken cancellationToken = default) { throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield from outside a coroutine"); } diff --git a/src/Lua/LuaResult.cs b/src/Lua/LuaResult.cs new file mode 100644 index 00000000..6f37f591 --- /dev/null +++ b/src/Lua/LuaResult.cs @@ -0,0 +1,26 @@ +using Lua.Runtime; + +namespace Lua; + +public readonly struct LuaResult : IDisposable +{ + readonly LuaStack stack; + readonly int returnBase; + + internal LuaResult(LuaStack stack, int returnBase) + { + this.stack = stack; + this.returnBase =returnBase; + } + + public int Count => stack.Count - returnBase; + public int Length => stack.Count - returnBase; + public ReadOnlySpan AsSpan() => stack.AsSpan()[returnBase..]; + + public LuaValue this[int index] => AsSpan()[index]; + + public void Dispose() + { + stack.PopUntil(returnBase); + } +} \ No newline at end of file diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index b329961e..91adde37 100644 --- a/src/Lua/LuaState.cs +++ b/src/Lua/LuaState.cs @@ -31,6 +31,7 @@ public sealed class LuaState public LuaTable Registry => registry; public LuaTable LoadedModules => packages; public LuaMainThread MainThread => mainThread; + public LuaThread CurrentThread { get @@ -61,24 +62,28 @@ public static LuaState Create() envUpValue = UpValue.Closed(environment); } - public async ValueTask RunAsync(Chunk chunk, Memory buffer, CancellationToken cancellationToken = default) + public async ValueTask RunAsync(Chunk chunk, CancellationToken cancellationToken = default) { + ThrowIfResultNotDisposed(); ThrowIfRunning(); Volatile.Write(ref isRunning, true); try { var closure = new Closure(this, chunk); - return await closure.InvokeAsync(new() + await closure.InvokeAsync(new() { State = this, Thread = CurrentThread, ArgumentCount = 0, FrameBase = 0, + ReturnFrameBase = 0, SourcePosition = null, RootChunkName = chunk.Name, ChunkName = chunk.Name, - }, buffer, cancellationToken); + }, cancellationToken); + + return new LuaResult(CurrentThread.Stack, 0); } finally { @@ -93,36 +98,7 @@ public void Push(LuaValue value) public Traceback GetTraceback() { - if (threadStack.Count == 0) - { - return new(this) - { - RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function, - StackFrames = MainThread.GetCallStackFrames()[1..] - .ToArray() - }; - } - - using var list = new PooledList(8); - foreach (var frame in MainThread.GetCallStackFrames()[1..]) - { - list.Add(frame); - } - - foreach (var thread in threadStack.AsSpan()) - { - if (thread.CallStack.Count == 0) continue; - foreach (var frame in thread.GetCallStackFrames()[1..]) - { - list.Add(frame); - } - } - - return new(this) - { - RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function, - StackFrames = list.AsSpan().ToArray() - }; + return GetTraceback(CurrentThread); } internal Traceback GetTraceback(LuaThread thread) @@ -132,6 +108,7 @@ internal Traceback GetTraceback(LuaThread thread) { list.Add(frame); } + Closure rootFunc; if (thread.GetCallStackFrames()[0].Function is Closure closure) { @@ -231,6 +208,14 @@ internal void CloseUpValues(LuaThread thread, int frameBase) } } + void ThrowIfResultNotDisposed() + { + if (MainThread.Stack.Count != 0) + { + throw new InvalidOperationException("LuaResult is not disposed"); + } + } + void ThrowIfRunning() { if (Volatile.Read(ref isRunning)) diff --git a/src/Lua/LuaStateExtensions.cs b/src/Lua/LuaStateExtensions.cs index 2710517a..b40e67ce 100644 --- a/src/Lua/LuaStateExtensions.cs +++ b/src/Lua/LuaStateExtensions.cs @@ -1,4 +1,3 @@ -using System.Buffers; using Lua.CodeAnalysis.Compilation; using Lua.CodeAnalysis.Syntax; @@ -6,47 +5,41 @@ namespace Lua; public static class LuaStateExtensions { - public static ValueTask DoStringAsync(this LuaState state, string source, Memory buffer, string? chunkName = null, CancellationToken cancellationToken = default) + public static async ValueTask DoStringAsync(this LuaState state, string source, Memory buffer, string? chunkName = null, CancellationToken cancellationToken = default) { var syntaxTree = LuaSyntaxTree.Parse(source, chunkName); var chunk = LuaCompiler.Default.Compile(syntaxTree, chunkName); - return state.RunAsync(chunk, buffer, cancellationToken); + using var result = await state.RunAsync(chunk, cancellationToken); + result.AsSpan().CopyTo(buffer.Span); + return result.Count; } public static async ValueTask DoStringAsync(this LuaState state, string source, string? chunkName = null, CancellationToken cancellationToken = default) { - var buffer = ArrayPool.Shared.Rent(1024); - try - { - var resultCount = await DoStringAsync(state, source, buffer, chunkName, cancellationToken); - return buffer.AsSpan(0, resultCount).ToArray(); - } - finally - { - ArrayPool.Shared.Return(buffer); - } + var syntaxTree = LuaSyntaxTree.Parse(source, chunkName); + var chunk = LuaCompiler.Default.Compile(syntaxTree, chunkName); + using var result = await state.RunAsync(chunk, cancellationToken); + return result.AsSpan().ToArray(); } public static async ValueTask DoFileAsync(this LuaState state, string path, Memory buffer, CancellationToken cancellationToken = default) { var text = await File.ReadAllTextAsync(path, cancellationToken); - var fileName = "@"+Path.GetFileName(path); + var fileName = "@" + Path.GetFileName(path); var syntaxTree = LuaSyntaxTree.Parse(text, fileName); var chunk = LuaCompiler.Default.Compile(syntaxTree, fileName); - return await state.RunAsync(chunk, buffer, cancellationToken); + using var result = await state.RunAsync(chunk, cancellationToken); + result.AsSpan().CopyTo(buffer.Span); + return result.Count; } public static async ValueTask DoFileAsync(this LuaState state, string path, CancellationToken cancellationToken = default) { - var buffer = ArrayPool.Shared.Rent(1024); - try - { - var resultCount = await DoFileAsync(state, path, buffer, cancellationToken); - return buffer.AsSpan(0, resultCount).ToArray(); - } - finally - { - ArrayPool.Shared.Return(buffer); - } + var text = await File.ReadAllTextAsync(path, cancellationToken); + var fileName = "@" + Path.GetFileName(path); + var syntaxTree = LuaSyntaxTree.Parse(text, fileName); + var chunk = LuaCompiler.Default.Compile(syntaxTree, fileName); + using var result = await state.RunAsync(chunk, cancellationToken); + return result.AsSpan().ToArray(); } } \ No newline at end of file diff --git a/src/Lua/LuaThread.cs b/src/Lua/LuaThread.cs index f559098b..0fb47f2d 100644 --- a/src/Lua/LuaThread.cs +++ b/src/Lua/LuaThread.cs @@ -8,8 +8,8 @@ public abstract class LuaThread { public abstract LuaThreadStatus GetStatus(); public abstract void UnsafeSetStatus(LuaThreadStatus status); - public abstract ValueTask ResumeAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default); - public abstract ValueTask YieldAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken = default); + public abstract ValueTask ResumeAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default); + public abstract ValueTask YieldAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken = default); LuaStack stack = new(); FastStackCore callStack; @@ -72,7 +72,7 @@ internal void PushCallStackFrame(in CallStackFrame frame) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void PopCallStackFrame() + internal void PopCallStackFrameWithStackPop() { if (callStack.TryPop(out var frame)) { @@ -85,7 +85,7 @@ internal void PopCallStackFrame() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void PopCallStackFrameUnsafe(int frameBase) + internal void PopCallStackFrameWithStackPop(int frameBase) { if (callStack.TryPop()) { @@ -98,7 +98,7 @@ internal void PopCallStackFrameUnsafe(int frameBase) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void PopCallStackFrameUnsafe() + internal void PopCallStackFrame() { if (!callStack.TryPop()) { diff --git a/src/Lua/LuaThreadExtensions.cs b/src/Lua/LuaThreadExtensions.cs index ad7e2666..7a3405f2 100644 --- a/src/Lua/LuaThreadExtensions.cs +++ b/src/Lua/LuaThreadExtensions.cs @@ -6,26 +6,25 @@ public static class LuaThreadExtensions { public static async ValueTask ResumeAsync(this LuaThread thread, LuaState state, CancellationToken cancellationToken = default) { - using var buffer = new PooledArray(1024); - var frameBase = thread.Stack.Count; thread.Stack.Push(thread); - var resultCount = await thread.ResumeAsync(new() + await thread.ResumeAsync(new() { State = state, Thread = state.CurrentThread, ArgumentCount = 1, FrameBase = frameBase, - }, buffer.AsMemory(), cancellationToken); - - return buffer.AsSpan()[0..resultCount].ToArray(); + ReturnFrameBase = frameBase, + }, cancellationToken); + var returnBase = ((LuaCoroutine)thread).ReturnFrameBase; + var results = thread.Stack.AsSpan()[returnBase..].ToArray(); + thread.Stack.PopUntil(returnBase); + return results; } public static async ValueTask ResumeAsync(this LuaThread thread, LuaState state, LuaValue[] arguments, CancellationToken cancellationToken = default) { - using var buffer = new PooledArray(1024); - var frameBase = thread.Stack.Count; thread.Stack.Push(thread); for (int i = 0; i < arguments.Length; i++) @@ -33,14 +32,17 @@ public static async ValueTask ResumeAsync(this LuaThread thread, Lua thread.Stack.Push(arguments[i]); } - var resultCount = await thread.ResumeAsync(new() + await thread.ResumeAsync(new() { State = state, Thread = state.CurrentThread, ArgumentCount = 1 + arguments.Length, FrameBase = frameBase, - }, buffer.AsMemory(), cancellationToken); - - return buffer.AsSpan()[0..resultCount].ToArray(); + ReturnFrameBase = frameBase, + }, cancellationToken); + var returnBase = ((LuaCoroutine)thread).ReturnFrameBase; + var results = thread.Stack.AsSpan()[returnBase..].ToArray(); + thread.Stack.PopUntil(returnBase); + return results; } } \ No newline at end of file diff --git a/src/Lua/LuaValue.cs b/src/Lua/LuaValue.cs index c4ed3561..267c4402 100644 --- a/src/Lua/LuaValue.cs +++ b/src/Lua/LuaValue.cs @@ -584,7 +584,7 @@ public static bool TryGetLuaValueType(Type type, out LuaValueType result) return false; } - internal ValueTask CallToStringAsync(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + internal ValueTask CallToStringAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (this.TryGetMetamethod(context.State, Metamethods.ToString, out var metamethod)) { @@ -593,18 +593,20 @@ internal ValueTask CallToStringAsync(LuaFunctionExecutionContext context, M LuaRuntimeException.AttemptInvalidOperation(context.State.GetTraceback(), "call", metamethod); } - context.State.Push(this); + var stack = context.Thread.Stack; + stack.Push(this); return func.InvokeAsync(context with { ArgumentCount = 1, - FrameBase = context.Thread.Stack.Count - 1, - }, buffer, cancellationToken); + FrameBase = stack.Count - 1, + ReturnFrameBase = stack.Count-1, + },cancellationToken); } else { - buffer.Span[0] = ToString(); - return new(1); + context.Thread.Stack.Push(ToString()); + return default; } } } \ No newline at end of file diff --git a/src/Lua/Runtime/CallStackFrame.cs b/src/Lua/Runtime/CallStackFrame.cs index febd4cc9..6a134c02 100644 --- a/src/Lua/Runtime/CallStackFrame.cs +++ b/src/Lua/Runtime/CallStackFrame.cs @@ -6,18 +6,19 @@ namespace Lua.Runtime; public record struct CallStackFrame { public required int Base; + public required int ReturnBase; public required LuaFunction Function; public required int VariableArgumentCount; public int CallerInstructionIndex; internal CallStackFrameFlags Flags; - internal bool IsTailCall => (Flags & CallStackFrameFlags.TailCall) ==CallStackFrameFlags.TailCall; + internal bool IsTailCall => (Flags & CallStackFrameFlags.TailCall) == CallStackFrameFlags.TailCall; } [Flags] public enum CallStackFrameFlags { //None = 0, - ReversedLe = 1, + ReversedLe = 1, TailCall = 2, InHook = 4, } \ No newline at end of file diff --git a/src/Lua/Runtime/Closure.cs b/src/Lua/Runtime/Closure.cs index 1db8b1b8..22329c81 100644 --- a/src/Lua/Runtime/Closure.cs +++ b/src/Lua/Runtime/Closure.cs @@ -9,7 +9,7 @@ public sealed class Closure : LuaFunction FastListCore upValues; public Closure(LuaState state, Chunk proto, LuaTable? environment = null) - : base(proto.Name, (context, buffer, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, buffer, ct)) + : base(proto.Name, (context, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, ct)) { this.proto = proto; diff --git a/src/Lua/Runtime/CsClosure.cs b/src/Lua/Runtime/CsClosure.cs index 99f92d35..12e29747 100644 --- a/src/Lua/Runtime/CsClosure.cs +++ b/src/Lua/Runtime/CsClosure.cs @@ -1,6 +1,6 @@ namespace Lua.Runtime; -public sealed class CsClosure(string name,LuaValue[] upValues,Func, CancellationToken, ValueTask> func) : LuaFunction(name, func) +public sealed class CsClosure(string name,LuaValue[] upValues,Func> func) : LuaFunction(name, func) { public readonly LuaValue[] UpValues = upValues; } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaStack.cs b/src/Lua/Runtime/LuaStack.cs index 8ac48ae7..6d0900b7 100644 --- a/src/Lua/Runtime/LuaStack.cs +++ b/src/Lua/Runtime/LuaStack.cs @@ -27,6 +27,11 @@ static void Resize(ref LuaValue[] array, int newSize) size *= 2; } + if (1000000 < size) + { + throw new LuaException("Lua Stack overflow"); + } + Array.Resize(ref array, size); } } @@ -134,4 +139,11 @@ static void ThrowEmptyStack() { throw new InvalidOperationException("Empty stack"); } + + internal void SetTop( int top) + { + EnsureCapacity(top); + NotifyTop(top); + PopUntil(top); + } } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs index 22a8bb06..937d5ee0 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.Debug.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.Debug.cs @@ -12,7 +12,7 @@ static bool ExecutePerInstructionHook(ref VirtualMachineExecutionContext context { if (r.Result == 0) { - context.Thread.PopCallStackFrame(); + context.Thread.PopCallStackFrameWithStackPop(); } return false; @@ -39,18 +39,20 @@ static async ValueTask Impl(VirtualMachineExecutionContext context) Thread = context.Thread, ArgumentCount = 2, FrameBase = context.Thread.Stack.Count - 2, + ReturnFrameBase = context.Thread.Stack.Count - 2, }; var frame = new CallStackFrame { Base = funcContext.FrameBase, - VariableArgumentCount = hook is Closure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + ReturnBase = funcContext.ReturnFrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount), Function = hook, CallerInstructionIndex = context.Pc, }; frame.Flags |= CallStackFrameFlags.InHook; context.Thread.IsInHook = true; context.Thread.PushCallStackFrame(frame); - await hook.Func(funcContext, Memory.Empty, context.CancellationToken); + await hook.Func(funcContext, context.CancellationToken); context.Thread.IsInHook = false; @@ -68,7 +70,7 @@ static async ValueTask Impl(VirtualMachineExecutionContext context) { if (countHookIsDone) { - context.Thread.PopCallStackFrame(); + context.Thread.PopCallStackFrameWithStackPop(); } @@ -82,18 +84,20 @@ static async ValueTask Impl(VirtualMachineExecutionContext context) Thread = context.Thread, ArgumentCount = 2, FrameBase = context.Thread.Stack.Count - 2, + ReturnFrameBase = context.Thread.Stack.Count - 2, }; var frame = new CallStackFrame { Base = funcContext.FrameBase, - VariableArgumentCount = hook is Closure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + ReturnBase = funcContext.ReturnFrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount), Function = hook, CallerInstructionIndex = pc, }; frame.Flags |= CallStackFrameFlags.InHook; context.Thread.IsInHook = true; context.Thread.PushCallStackFrame(frame); - await hook.Func(funcContext, Memory.Empty, context.CancellationToken); + await hook.Func(funcContext, context.CancellationToken); context.Thread.IsInHook = false; context.Pc--; context.Thread.LastPc = pc; @@ -122,11 +126,12 @@ static ValueTask ExecuteCallHook(ref VirtualMachineExecutionContext context Thread = context.Thread, ArgumentCount = arguments, FrameBase = frame.Base, + ReturnFrameBase = frame.ReturnBase, CallerInstructionIndex = frame.CallerInstructionIndex, - }, context.ResultsBuffer, context.CancellationToken, isTailCall); + }, context.CancellationToken, isTailCall); } - internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken, bool isTailCall = false) + internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken, bool isTailCall = false) { var argCount = context.ArgumentCount; var hook = context.Thread.Hook!; @@ -142,10 +147,12 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext Thread = context.Thread, ArgumentCount = 2, FrameBase = context.Thread.Stack.Count - 2, + ReturnFrameBase = context.Thread.Stack.Count - 2, }; CallStackFrame frame = new() { Base = funcContext.FrameBase, + ReturnBase = funcContext.ReturnFrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook, CallerInstructionIndex = 0, @@ -156,29 +163,31 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext try { context.Thread.IsInHook = true; - await hook.Func(funcContext, Memory.Empty, cancellationToken); + await hook.Func(funcContext, cancellationToken); } finally { context.Thread.IsInHook = false; - context.Thread.PopCallStackFrame(); + context.Thread.PopCallStackFrameWithStackPop(); } } { - var frame = context.Thread.GetCurrentFrame(); + ref readonly var frame = ref context.Thread.GetCurrentFrame(); var task = frame.Function.Func(new() { State = context.State, Thread = context.Thread, ArgumentCount = argCount, FrameBase = frame.Base, - }, buffer, cancellationToken); + ReturnFrameBase = frame.ReturnBase, + }, cancellationToken); + var r = await task; if (isTailCall || !context.Thread.IsReturnHookEnabled) { - return await task; + return r; } - var result = await task; + stack.Push("return"); stack.Push(LuaValue.Nil); var funcContext = new LuaFunctionExecutionContext @@ -187,12 +196,14 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext Thread = context.Thread, ArgumentCount = 2, FrameBase = context.Thread.Stack.Count - 2, + ReturnFrameBase = context.Thread.Stack.Count - 2, }; - - context.Thread.PushCallStackFrame( new() + + context.Thread.PushCallStackFrame(new() { Base = funcContext.FrameBase, + ReturnBase = funcContext.ReturnFrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook, CallerInstructionIndex = 0, @@ -201,15 +212,15 @@ internal static async ValueTask ExecuteCallHook(LuaFunctionExecutionContext try { context.Thread.IsInHook = true; - await hook.Func(funcContext, Memory.Empty, cancellationToken); + await hook.Func(funcContext, cancellationToken); } finally { context.Thread.IsInHook = false; } - context.Thread.PopCallStackFrame(); - return result; + context.Thread.PopCallStackFrameWithStackPop(); + return r; } } } \ No newline at end of file diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index bb86262d..e661df2e 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -14,8 +14,6 @@ public static partial class LuaVirtualMachine struct VirtualMachineExecutionContext( LuaState state, LuaStack stack, - LuaValue[] resultsBuffer, - Memory buffer, LuaThread thread, in CallStackFrame frame, CancellationToken cancellationToken) @@ -23,8 +21,6 @@ struct VirtualMachineExecutionContext( public readonly LuaState State = state; public readonly LuaStack Stack = stack; public Closure Closure = (Closure)frame.Function; - public readonly LuaValue[] ResultsBuffer = resultsBuffer; - public readonly Memory Buffer = buffer; public readonly LuaThread Thread = thread; public Chunk Chunk => Closure.Proto; public int FrameBase = frame.Base; @@ -32,51 +28,64 @@ struct VirtualMachineExecutionContext( public readonly CancellationToken CancellationToken = cancellationToken; public int Pc = -1; public Instruction Instruction; - public int ResultCount; - public int TaskResult; + public int CurrentReturnFrameBase = frame.ReturnBase; public ValueTask Task; public int LastHookPc = -1; public bool IsTopLevel => BaseCallStackCount == Thread.CallStack.Count; - readonly int BaseCallStackCount = thread.CallStack.Count; + public readonly int BaseCallStackCount = thread.CallStack.Count; public PostOperationType PostOperation; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Pop(Instruction instruction, int frameBase) { - if (BaseCallStackCount == Thread.CallStack.Count) return false; var count = instruction.B - 1; var src = instruction.A + frameBase; if (count == -1) count = Stack.Count - src; - return PopFromBuffer(Stack.GetBuffer().Slice(src, count)); + return PopFromBuffer(src, count); } [MethodImpl(MethodImplOptions.NoInlining)] - public bool PopFromBuffer(Span result) + public bool PopFromBuffer(int src, int srcCount) { + var result = Stack.GetBuffer().Slice(src, srcCount); ref var callStack = ref Thread.CallStack; Re: var frames = callStack.AsSpan(); - if (frames.Length == BaseCallStackCount) return false; + if (frames.Length == BaseCallStackCount) + { + var returnBase = frames[^1].ReturnBase; + if (src != returnBase) + { + result.CopyTo(Stack.GetBuffer()[returnBase..]); + } + + Stack.PopUntil(returnBase + srcCount); + return false; + } + ref readonly var frame = ref frames[^1]; Pc = frame.CallerInstructionIndex; Thread.LastPc = Pc; ref readonly var lastFrame = ref frames[^2]; Closure = Unsafe.As(lastFrame.Function); + CurrentReturnFrameBase = frame.ReturnBase; var callInstruction = Chunk.Instructions[Pc]; - FrameBase = lastFrame.Base; - VariableArgumentCount = lastFrame.VariableArgumentCount; if (callInstruction.OpCode == OpCode.TailCall) { - Thread.PopCallStackFrameUnsafe(); + Thread.PopCallStackFrame(); goto Re; } + + FrameBase = lastFrame.Base; + VariableArgumentCount = lastFrame.VariableArgumentCount; + var opCode = callInstruction.OpCode; if (opCode is OpCode.Eq or OpCode.Lt or OpCode.Le) { - var compareResult = result.Length > 0 && result[0].ToBoolean(); + var compareResult = srcCount > 0 && result[0].ToBoolean(); if ((frame.Flags & CallStackFrameFlags.ReversedLe) != 0) { compareResult = !compareResult; @@ -87,7 +96,7 @@ public bool PopFromBuffer(Span result) Pc++; } - Thread.PopCallStackFrameUnsafe(frame.Base); + Thread.PopCallStackFrameWithStackPop(); return true; } @@ -111,34 +120,32 @@ public bool PopFromBuffer(Span result) break; case OpCode.Self: Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0]; - Thread.PopCallStackFrameUnsafe(target + 2); + Thread.PopCallStackFrameWithStackPop(target + 2); return true; case OpCode.SetTable or OpCode.SetTabUp: targetCount = 0; break; // Other opcodes has one result default: - targetCount = 1; - break; + Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0]; + Thread.PopCallStackFrameWithStackPop(target + 1); + return true; } - var count = Math.Min(result.Length, targetCount); Stack.EnsureCapacity(target + targetCount); - - - var stackBuffer = Stack.GetBuffer(); - if (count > 0) + if (0 < targetCount && src != target) { - result[..count].CopyTo(stackBuffer.Slice(target, count)); - } + if (targetCount < result.Length) + { + result = result.Slice(0, targetCount); + } - if (targetCount > count) - { - stackBuffer.Slice(target + count, targetCount - count).Clear(); + result.CopyTo(Stack.GetBuffer().Slice(target, targetCount)); } + Stack.PopUntil(target + Math.Min(targetCount, srcCount)); Stack.NotifyTop(target + targetCount); - Thread.PopCallStackFrameUnsafe(target + targetCount); + Thread.PopCallStackFrame(); return true; } @@ -146,9 +153,12 @@ public bool PopFromBuffer(Span result) public void Push(in CallStackFrame frame) { Pc = -1; - Closure = (frame.Function as Closure)!; + Closure = (Closure)(frame.Function); FrameBase = frame.Base; + CurrentReturnFrameBase = frame.ReturnBase; VariableArgumentCount = frame.VariableArgumentCount; + + //Console.WriteLine($"Push {frame}"); } public void PopOnTopCallStackFrames() @@ -167,39 +177,42 @@ public void PopOnTopCallStackFrames() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ClearResultsBuffer() { - if (TaskResult == 0) return; - if (TaskResult == 1) - { - ResultsBuffer[0] = default; - return; - } - - ResultsBuffer.AsSpan(0, TaskResult).Clear(); + //if (TaskResult == 0) return; + // if (TaskResult == 1) + // { + // ResultsBuffer[0] = default; + // return; + // } + // + // ResultsBuffer.AsSpan(0, TaskResult).Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ClearResultsBuffer(int count) { if (count == 0) return; - if (count == 1) - { - ResultsBuffer[0] = default; - return; - } - - ResultsBuffer.AsSpan(0, count).Clear(); + // if (count == 1) + // { + // ResultsBuffer[0] = default; + // return; + // } + // + // ResultsBuffer.AsSpan(0, count).Clear(); } - public int? ExecutePostOperation(PostOperationType postOperation) + + bool ExecutePostOperation(PostOperationType postOperation) { + var stackCount = Stack.Count; + var resultsSpan = Stack.GetBuffer()[CurrentReturnFrameBase..]; switch (postOperation) { case PostOperationType.Nop: break; case PostOperationType.SetResult: var RA = Instruction.A + FrameBase; - Stack.Get(RA) = TaskResult == 0 ? LuaValue.Nil : ResultsBuffer[0]; + Stack.Get(RA) = stackCount > CurrentReturnFrameBase ? Stack.Get(CurrentReturnFrameBase) : LuaValue.Nil; Stack.NotifyTop(RA + 1); - ClearResultsBuffer(); + Stack.PopUntil(RA + 1); break; case PostOperationType.TForCall: TForCallPostOperation(ref this); @@ -208,45 +221,43 @@ public void ClearResultsBuffer(int count) CallPostOperation(ref this); break; case PostOperationType.TailCall: - var resultsSpan = ResultsBuffer.AsSpan(0, TaskResult); - if (!PopFromBuffer(resultsSpan)) + if (!PopFromBuffer(CurrentReturnFrameBase, Stack.Count - CurrentReturnFrameBase)) { - ResultCount = TaskResult; - resultsSpan.CopyTo(Buffer.Span); - resultsSpan.Clear(); - LuaValueArrayPool.Return1024(ResultsBuffer); - return TaskResult; + return false; } - resultsSpan.Clear(); break; case PostOperationType.Self: - SelfPostOperation(ref this); + SelfPostOperation(ref this, resultsSpan); break; case PostOperationType.Compare: - ComparePostOperation(ref this); + ComparePostOperation(ref this, resultsSpan); break; } - return null; + return true; } public async ValueTask ExecuteClosureAsyncImpl() { + var returnFrameBase = CurrentReturnFrameBase; + while (MoveNext(ref this)) { - TaskResult = await Task; + await Task; Task = default; if (PostOperation != PostOperationType.TailCall) { Thread.PopCallStackFrame(); } - var r = ExecutePostOperation(PostOperation); - if (r.HasValue) return r.Value; + if (!ExecutePostOperation(PostOperation)) + { + break; + } } - return ResultCount; + return Thread.Stack.Count - returnFrameBase; } } @@ -262,13 +273,12 @@ enum PostOperationType Compare, } - internal static ValueTask ExecuteClosureAsync(LuaState luaState, Memory buffer, CancellationToken cancellationToken) + internal static ValueTask ExecuteClosureAsync(LuaState luaState, CancellationToken cancellationToken) { var thread = luaState.CurrentThread; ref readonly var frame = ref thread.GetCurrentFrame(); - var resultBuffer = LuaValueArrayPool.Rent1024(); - var context = new VirtualMachineExecutionContext(luaState, thread.Stack, resultBuffer, buffer, thread, in frame, + var context = new VirtualMachineExecutionContext(luaState, thread.Stack, thread, in frame, cancellationToken); return context.ExecuteClosureAsyncImpl(); @@ -586,6 +596,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } Unsafe.Add(ref stackHead, iA) = mod; + stack.NotifyTop(iA + frameBase + 1); continue; } @@ -808,7 +819,12 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) case OpCode.Call: if (Call(ref context, out doRestart)) { - if (doRestart) goto Restart; + if (doRestart) + { + //Console.WriteLine("Restarting"); + goto Restart; + } + continue; } @@ -824,23 +840,12 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Return: instruction = instructionRef; - iA = instruction.A; - ra1 = iA + frameBase + 1; context.State.CloseUpValues(context.Thread, frameBase); - if (context.Pop(instruction, frameBase)) goto Restart; - var retCount = instruction.B - 1; - - if (retCount == -1) + if (context.Pop(instruction, frameBase)) { - retCount = stack.Count - (ra1 - 1); + goto Restart; } - if (0 < retCount) - { - stack.GetBuffer().Slice(ra1 - 1, retCount).CopyTo(context.Buffer.Span); - } - - context.ResultCount = retCount; goto End; case OpCode.ForLoop: ref var indexRef = ref stack.Get(instructionRef.A + frameBase); @@ -946,13 +951,11 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) End: context.PostOperation = PostOperationType.None; - LuaValueArrayPool.Return1024(context.ResultsBuffer); return false; } catch (Exception e) { context.State.CloseUpValues(context.Thread, context.FrameBase); - LuaValueArrayPool.Return1024(context.ResultsBuffer, true); if (e is not LuaRuntimeException) { var newException = new LuaRuntimeException(context.State.GetTraceback(), e); @@ -978,7 +981,7 @@ static void ThrowLuaNotImplementedException(ref VirtualMachineExecutionContext c } - static void SelfPostOperation(ref VirtualMachineExecutionContext context) + static void SelfPostOperation(ref VirtualMachineExecutionContext context, Span results) { var stack = context.Stack; var instruction = context.Instruction; @@ -987,9 +990,8 @@ static void SelfPostOperation(ref VirtualMachineExecutionContext context) ref var stackHead = ref stack.Get(0); var table = Unsafe.Add(ref stackHead, RB); Unsafe.Add(ref stackHead, RA + 1) = table; - Unsafe.Add(ref stackHead, RA) = context.TaskResult == 0 ? LuaValue.Nil : context.ResultsBuffer[0]; + Unsafe.Add(ref stackHead, RA) = results.Length == 0 ? LuaValue.Nil : results[0]; stack.NotifyTop(RA + 2); - context.ClearResultsBuffer(); } static bool Concat(ref VirtualMachineExecutionContext context, out bool doRestart) @@ -1049,17 +1051,18 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) var thread = context.Thread; var (newBase, argumentCount, variableArgumentCount) = PrepareForFunctionCall(thread, func, instruction, RA); - var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount); + var newFrame = func.CreateNewFrame(ref context, newBase, RA, variableArgumentCount); thread.PushCallStackFrame(newFrame); if (thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.Call; - context.Task=ExecuteCallHook(ref context, newFrame,argumentCount); + context.Task = ExecuteCallHook(ref context, newFrame, argumentCount); doRestart = false; return false; } + //Console.WriteLine("Call ReturnBase:" + " " + context.Thread.GetHashCode() + " " + newFrame.ReturnBase + " " + context.BaseCallStackCount + " " + context.Thread.CallStack.Count); if (func is Closure) { context.Push(newFrame); @@ -1076,46 +1079,29 @@ static bool FuncCall(ref VirtualMachineExecutionContext context, in CallStackFra if (!task.IsCompleted) { - context.PostOperation = PostOperationType.Call; context.Task = task; return false; } var awaiter = task.GetAwaiter(); - context.Thread.PopCallStackFrameUnsafe(newBase); - context.TaskResult = awaiter.GetResult(); + + awaiter.GetResult(); var instruction = context.Instruction; - var rawResultCount = context.TaskResult; - var resultCount = rawResultCount; var ic = instruction.C; if (ic != 0) { - resultCount = ic - 1; - } - - if (resultCount == 0) - { - context.Stack.Pop(); - } - else - { + var resultCount = ic - 1; var stack = context.Stack; - var RA = instruction.A + context.FrameBase; - stack.EnsureCapacity(RA + resultCount); - ref var stackHead = ref stack.Get(RA); - var results = context.ResultsBuffer.AsSpan(0, rawResultCount); - for (int i = 0; i < resultCount; i++) - { - Unsafe.Add(ref stackHead, i) = i >= rawResultCount - ? default - : results[i]; - } - - stack.NotifyTop(RA + resultCount); - results.Clear(); + var top = instruction.A + context.FrameBase + resultCount; + stack.EnsureCapacity(top); + stack.PopUntil(top); + stack.NotifyTop(top); } + //Console.WriteLine(($" FuncCall Pop {context.Thread.CallStack.Count}")); + + context.Thread.PopCallStackFrame(); return true; } } @@ -1123,35 +1109,16 @@ static bool FuncCall(ref VirtualMachineExecutionContext context, in CallStackFra static void CallPostOperation(ref VirtualMachineExecutionContext context) { var instruction = context.Instruction; - var rawResultCount = context.TaskResult; - var resultCount = rawResultCount; var ic = instruction.C; if (ic != 0) { - resultCount = ic - 1; - } - - if (resultCount == 0) - { - context.Stack.Pop(); - } - else - { + var resultCount = ic - 1; var stack = context.Stack; - var RA = instruction.A + context.FrameBase; - stack.EnsureCapacity(RA + resultCount); - ref var stackHead = ref stack.Get(RA); - var results = context.ResultsBuffer.AsSpan(0, rawResultCount); - for (int i = 0; i < resultCount; i++) - { - Unsafe.Add(ref stackHead, i) = i >= rawResultCount - ? default - : results[i]; - } - - stack.NotifyTop(RA + resultCount); - results.Clear(); + var top = instruction.A + context.FrameBase + resultCount; + stack.EnsureCapacity(top); + stack.PopUntil(top); + stack.NotifyTop(top); } } @@ -1178,26 +1145,25 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest var (newBase, argumentCount, variableArgumentCount) = PrepareForFunctionTailCall(thread, func, instruction, RA); - var lastPc = thread.CallStack.AsSpan()[^1].CallerInstructionIndex; - context.Thread.PopCallStackFrameUnsafe(); - - var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount); + var lastPc = thread.GetCurrentFrame().CallerInstructionIndex; + context.Thread.PopCallStackFrame(); + var newFrame = func.CreateNewTailCallFrame(ref context, newBase, context.CurrentReturnFrameBase, variableArgumentCount); - newFrame.Flags |= CallStackFrameFlags.TailCall; newFrame.CallerInstructionIndex = lastPc; thread.PushCallStackFrame(newFrame); if (thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.TailCall; - context.Task=ExecuteCallHook(ref context, newFrame,argumentCount,true); + context.Task = ExecuteCallHook(ref context, newFrame, argumentCount, true); doRestart = false; return false; } - context.Push(newFrame); + if (func is Closure) { + context.Push(newFrame); doRestart = true; return true; } @@ -1212,18 +1178,14 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest return false; } - doRestart = true; - var awaiter = task.GetAwaiter(); - var resultCount = awaiter.GetResult(); - var resultsSpan = context.ResultsBuffer.AsSpan(0, resultCount); - if (!context.PopFromBuffer(resultsSpan)) + + task.GetAwaiter().GetResult(); + if (!context.PopFromBuffer(context.CurrentReturnFrameBase, context.Stack.Count - context.CurrentReturnFrameBase)) { - doRestart = false; - context.ResultCount = resultCount; - resultsSpan.CopyTo(context.Buffer.Span); + return true; } - resultsSpan.Clear(); + doRestart = true; return true; } @@ -1244,8 +1206,16 @@ static bool TForCall(ref VirtualMachineExecutionContext context, out bool doRest stack.Get(newBase) = stack.Get(RA + 1); stack.Get(newBase + 1) = stack.Get(RA + 2); stack.NotifyTop(newBase + 2); - var newFrame = iterator.CreateNewFrame(ref context, newBase); + var newFrame = iterator.CreateNewFrame(ref context, newBase, RA + 3, iterator.GetVariableArgumentCount(2)); context.Thread.PushCallStackFrame(newFrame); + if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) + { + context.PostOperation = PostOperationType.TForCall; + context.Task = ExecuteCallHook(ref context, newFrame, 2); + doRestart = false; + return false; + } + if (iterator is Closure) { context.Push(newFrame); @@ -1254,7 +1224,6 @@ static bool TForCall(ref VirtualMachineExecutionContext context, out bool doRest } var task = iterator.Invoke(ref context, newFrame, 2); - if (!task.IsCompleted) { context.PostOperation = PostOperationType.TForCall; @@ -1264,7 +1233,7 @@ static bool TForCall(ref VirtualMachineExecutionContext context, out bool doRest } var awaiter = task.GetAwaiter(); - context.TaskResult = awaiter.GetResult(); + awaiter.GetResult(); context.Thread.PopCallStackFrame(); TForCallPostOperation(ref context); return true; @@ -1275,19 +1244,7 @@ static void TForCallPostOperation(ref VirtualMachineExecutionContext context) var stack = context.Stack; var instruction = context.Instruction; var RA = instruction.A + context.FrameBase; - var resultBuffer = context.ResultsBuffer; - var resultCount = context.TaskResult; - stack.EnsureCapacity(RA + instruction.C + 3); - for (int i = 1; i <= instruction.C; i++) - { - var index = i - 1; - stack.Get(RA + 2 + i) = index >= resultCount - ? LuaValue.Nil - : resultBuffer[i - 1]; - } - - stack.NotifyTop(RA + instruction.C + 3); - context.ClearResultsBuffer(resultCount); + stack.SetTop(RA + instruction.C + 3); } static void SetList(ref VirtualMachineExecutionContext context) @@ -1311,9 +1268,9 @@ static void SetList(ref VirtualMachineExecutionContext context) stack.PopUntil(RA + 1); } - static void ComparePostOperation(ref VirtualMachineExecutionContext context) + static void ComparePostOperation(ref VirtualMachineExecutionContext context, Span results) { - var compareResult = context.TaskResult != 0 && context.ResultsBuffer[0].ToBoolean(); + var compareResult = results.Length != 0 && results[0].ToBoolean(); if (compareResult != (context.Instruction.A == 1)) { context.Pc++; @@ -1393,7 +1350,7 @@ static bool CallGetTableFunc(LuaValue table, LuaFunction indexTable, LuaValue ke if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = context.Instruction.OpCode == OpCode.GetTable ? PostOperationType.SetResult : PostOperationType.Self; - context.Task=ExecuteCallHook(ref context, newFrame,2); + context.Task = ExecuteCallHook(ref context, newFrame, 2); doRestart = false; result = default; return false; @@ -1418,10 +1375,10 @@ static bool CallGetTableFunc(LuaValue table, LuaFunction indexTable, LuaValue ke } var awaiter = task.GetAwaiter(); - context.Thread.PopCallStackFrame(); - var resultCount = awaiter.GetResult(); - result = resultCount == 0 ? default : context.ResultsBuffer[0]; - context.ClearResultsBuffer(resultCount); + awaiter.GetResult(); + var results = stack.GetBuffer()[newFrame.Base..]; + result = results.Length == 0 ? default : results[0]; + context.Thread.PopCallStackFrameWithStackPop(); return true; } @@ -1494,7 +1451,7 @@ static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaVa if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.Nop; - context.Task=ExecuteCallHook(ref context, newFrame,3); + context.Task = ExecuteCallHook(ref context, newFrame, 3); doRestart = false; return false; } @@ -1514,13 +1471,8 @@ static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaVa return false; } - var resultCount = task.GetAwaiter().GetResult(); - if (0 < resultCount) - { - context.ClearResultsBuffer(resultCount); - } - - thread.PopCallStackFrame(); + task.GetAwaiter().GetResult(); + thread.PopCallStackFrameWithStackPop(); return true; } @@ -1542,13 +1494,13 @@ static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc, stack.Push(vb); stack.Push(vc); - var newFrame = func.CreateNewFrame(ref context, stack.Count - 2); + var newFrame = func.CreateNewFrame(ref context, stack.Count - 2, context.FrameBase + context.Instruction.A, func.GetVariableArgumentCount(2)); context.Thread.PushCallStackFrame(newFrame); if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.SetResult; - context.Task=ExecuteCallHook(ref context, newFrame,2); + context.Task = ExecuteCallHook(ref context, newFrame, 2); doRestart = false; return false; } @@ -1570,11 +1522,14 @@ static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc, return false; } - var resultCount = task.GetAwaiter().GetResult(); - context.Thread.PopCallStackFrame(); + task.GetAwaiter().GetResult(); + var RA = context.Instruction.A + context.FrameBase; - stack.Get(RA) = resultCount == 0 ? LuaValue.Nil : context.ResultsBuffer[0]; - context.ClearResultsBuffer(resultCount); + + var results = stack.GetBuffer()[newFrame.Base..]; + stack.Get(RA) = results.Length == 0 ? default : results[0]; + results.Clear(); + context.Thread.PopCallStackFrameWithStackPop(); return true; } @@ -1602,7 +1557,7 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.SetResult; - context.Task=ExecuteCallHook(ref context, newFrame,1); + context.Task = ExecuteCallHook(ref context, newFrame, 1); doRestart = false; return false; } @@ -1624,11 +1579,11 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu return false; } - context.Thread.PopCallStackFrame(); var RA = context.Instruction.A + context.FrameBase; - var resultCount = task.GetAwaiter().GetResult(); - stack.Get(RA) = resultCount == 0 ? LuaValue.Nil : context.ResultsBuffer[0]; - context.ClearResultsBuffer(resultCount); + var results = stack.GetBuffer()[newFrame.Base..]; + stack.Get(RA) = results.Length == 0 ? default : results[0]; + results.Clear(); + context.Thread.PopCallStackFrameWithStackPop(); return true; } @@ -1667,10 +1622,11 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { context.PostOperation = PostOperationType.Compare; - context.Task=ExecuteCallHook(ref context, newFrame,2); + context.Task = ExecuteCallHook(ref context, newFrame, 2); doRestart = false; return false; } + if (func is Closure) { context.Push(newFrame); @@ -1687,16 +1643,16 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, return false; } - context.Thread.PopCallStackFrame(); - var resultCount = task.GetAwaiter().GetResult(); - var compareResult = resultCount != 0 && context.ResultsBuffer[0].ToBoolean(); + var results = stack.GetBuffer()[newFrame.Base..]; + var compareResult = results.Length == 0 && results[0].ToBoolean(); compareResult = reverseLe ? !compareResult : compareResult; if (compareResult != (context.Instruction.A == 1)) { context.Pc++; } - context.ClearResultsBuffer(resultCount); + results.Clear(); + context.Thread.PopCallStackFrameWithStackPop(); return true; } @@ -1823,22 +1779,51 @@ static Traceback GetTracebacks(LuaState state, int pc) CallerInstructionIndex = pc }); var tracebacks = state.GetTraceback(); - state.CurrentThread.PopCallStackFrame(); + state.CurrentThread.PopCallStackFrameWithStackPop(); return tracebacks; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - static CallStackFrame CreateNewFrame(this LuaFunction function, ref VirtualMachineExecutionContext context, int newBase, int variableArgumentCount = 0) + static CallStackFrame CreateNewFrame(this LuaFunction function, ref VirtualMachineExecutionContext context, int newBase) { return new() { Base = newBase, + ReturnBase = newBase, + Function = function, + VariableArgumentCount = 0, + CallerInstructionIndex = context.Pc, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static CallStackFrame CreateNewFrame(this LuaFunction function, ref VirtualMachineExecutionContext context, int newBase, int returnBase, int variableArgumentCount) + { + return new() + { + Base = newBase, + ReturnBase = returnBase, Function = function, VariableArgumentCount = variableArgumentCount, CallerInstructionIndex = context.Pc, }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static CallStackFrame CreateNewTailCallFrame(this LuaFunction function, ref VirtualMachineExecutionContext context, int newBase, int returnBase, int variableArgumentCount) + { + return new() + { + Base = newBase, + ReturnBase = returnBase, + Function = function, + VariableArgumentCount = variableArgumentCount, + CallerInstructionIndex = context.Pc, + Flags = CallStackFrameFlags.TailCall + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] static ValueTask Invoke(this LuaFunction function, ref VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments) { @@ -1848,7 +1833,8 @@ static ValueTask Invoke(this LuaFunction function, ref VirtualMachineExecut Thread = context.Thread, ArgumentCount = arguments, FrameBase = frame.Base, + ReturnFrameBase = frame.ReturnBase, CallerInstructionIndex = frame.CallerInstructionIndex, - }, context.ResultsBuffer, context.CancellationToken); + }, context.CancellationToken); } } \ No newline at end of file diff --git a/src/Lua/Standard/BasicLibrary.cs b/src/Lua/Standard/BasicLibrary.cs index da4eed4b..2d139b67 100644 --- a/src/Lua/Standard/BasicLibrary.cs +++ b/src/Lua/Standard/BasicLibrary.cs @@ -11,7 +11,8 @@ public sealed class BasicLibrary public BasicLibrary() { - Functions = [ + Functions = + [ new("assert", Assert), new("collectgarbage", CollectGarbage), new("dofile", DoFile), @@ -36,7 +37,7 @@ public BasicLibrary() new("xpcall", XPCall), ]; - IPairsIterator = new("iterator", (context, buffer, cancellationToken) => + IPairsIterator = new("iterator", (context, cancellationToken) => { var table = context.GetArgument(0); var i = context.GetArgument(1); @@ -44,16 +45,12 @@ public BasicLibrary() i++; if (table.TryGetValue(i, out var value)) { - buffer.Span[0] = i; - buffer.Span[1] = value; + return new(context.Return(i, value)); } else { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = LuaValue.Nil; + return new(context.Return(LuaValue.Nil, LuaValue.Nil)); } - - return new(2); }); PairsIterator = new("iterator", Next); @@ -63,7 +60,7 @@ public BasicLibrary() readonly LuaFunction IPairsIterator; readonly LuaFunction PairsIterator; - public ValueTask Assert(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Assert(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); @@ -78,49 +75,43 @@ public ValueTask Assert(LuaFunctionExecutionContext context, Memory CollectGarbage(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask CollectGarbage(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { GC.Collect(); - return new(0); + return new(context.Return()); } - public async ValueTask DoFile(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask DoFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); // do not use LuaState.DoFileAsync as it uses the newExecutionContext var text = await File.ReadAllTextAsync(arg0, cancellationToken); - var fileName = Path.GetFileName(arg0); - var chunk = LuaCompiler.Default.Compile(text, "@"+fileName); + var fileName = "@" + Path.GetFileName(arg0); + var chunk = LuaCompiler.Default.Compile(text, fileName); - return await new Closure(context.State, chunk).InvokeAsync(context, buffer, cancellationToken); + return await new Closure(context.State, chunk).InvokeAsync(context, cancellationToken); } - public ValueTask Error(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Error(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - var value = context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil - ? "(error object is a nil value)" + var value = context.ArgumentCount == 0 + ? LuaValue.Nil : context.Arguments[0]; - Traceback t; - try - { - t = context.State.GetTraceback(context.Thread); - - } - catch (Exception e) + var traceback = context.State.GetTraceback(); + if (value.TryReadString(out var str)) { - Console.WriteLine(e); - throw; + value = $"{traceback.RootChunkName}:{traceback.LastPosition.Line}: {str}"; } - throw new LuaRuntimeException(t, value); + + throw new LuaRuntimeException(traceback, value); } - public ValueTask GetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); @@ -128,26 +119,26 @@ public ValueTask GetMetatable(LuaFunctionExecutionContext context, Memory IPairs(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask IPairs(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); @@ -159,16 +150,13 @@ public ValueTask IPairs(LuaFunctionExecutionContext context, Memory LoadFile(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask LoadFile(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { // Lua-CSharp does not support binary chunks, the mode argument is ignored. var arg0 = context.GetArgument(0); @@ -180,20 +168,17 @@ public async ValueTask LoadFile(LuaFunctionExecutionContext context, Memory try { var text = await File.ReadAllTextAsync(arg0, cancellationToken); - var fileName = Path.GetFileName(arg0); + var fileName = "@" + Path.GetFileName(arg0); var chunk = LuaCompiler.Default.Compile(text, fileName); - buffer.Span[0] = new Closure(context.State, chunk, arg2); - return 1; + return context.Return(new Closure(context.State, chunk, arg2)); } catch (Exception ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - return 2; + return context.Return(LuaValue.Nil, ex.Message); } } - public ValueTask Load(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Load(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { // Lua-CSharp does not support binary chunks, the mode argument is ignored. var arg0 = context.GetArgument(0); @@ -212,8 +197,7 @@ public ValueTask Load(LuaFunctionExecutionContext context, Memory if (arg0.TryRead(out var str)) { var chunk = LuaCompiler.Default.Compile(str, arg1 ?? str); - buffer.Span[0] = new Closure(context.State, chunk, arg3); - return new(1); + return new(context.Return(new Closure(context.State, chunk, arg3))); } else if (arg0.TryRead(out var function)) { @@ -228,31 +212,26 @@ public ValueTask Load(LuaFunctionExecutionContext context, Memory } catch (Exception ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - return new(2); + return new(context.Return(LuaValue.Nil, ex.Message)); } } - public ValueTask Next(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Next(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.HasArgument(1) ? context.Arguments[1] : LuaValue.Nil; if (arg0.TryGetNext(arg1, out var kv)) { - buffer.Span[0] = kv.Key; - buffer.Span[1] = kv.Value; - return new(2); + return new(context.Return(kv.Key, kv.Value)); } else { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } } - public ValueTask Pairs(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Pairs(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); @@ -264,115 +243,100 @@ public ValueTask Pairs(LuaFunctionExecutionContext context, Memory PCall(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask PCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - try { - using var methodBuffer = new PooledArray(1024); - - var resultCount = await arg0.InvokeAsync(context with + var count = await arg0.InvokeAsync(context with { State = context.State, ArgumentCount = context.ArgumentCount - 1, FrameBase = context.FrameBase + 1, - }, methodBuffer.AsMemory(), cancellationToken); - - buffer.Span[0] = true; - methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]); + ReturnFrameBase = context.ReturnFrameBase + 1 + }, cancellationToken); - return resultCount + 1; + context.Thread.Stack.Get(context.ReturnFrameBase) = true; + return count + 1; } catch (Exception ex) { - buffer.Span[0] = false; if (ex is LuaRuntimeException { ErrorObject: not null } luaEx) { - buffer.Span[1] = luaEx.ErrorObject.Value; + return context.Return(false, luaEx.ErrorObject.Value); } else { - buffer.Span[1] = ex.Message; + return context.Return(false, ex.Message); } - - return 2; } } - public async ValueTask Print(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask Print(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - using var methodBuffer = new PooledArray(1); - for (int i = 0; i < context.ArgumentCount; i++) { - await context.Arguments[i].CallToStringAsync(context, methodBuffer.AsMemory(), cancellationToken); - Console.Write(methodBuffer[0]); + var top = context.Thread.Stack.Count; + await context.Arguments[i].CallToStringAsync(context, cancellationToken); + Console.Write(context.Thread.Stack.Get(top).ToString()); Console.Write('\t'); } Console.WriteLine(); - return 0; + return context.Return(); } - public ValueTask RawEqual(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RawEqual(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - buffer.Span[0] = arg0 == arg1; - return new(1); + return new(context.Return(arg0 == arg1)); } - public ValueTask RawGet(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RawGet(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - - buffer.Span[0] = arg0[arg1]; - return new(1); + return new(context.Return(arg0[arg1])); } - public ValueTask RawLen(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RawLen(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); if (arg0.TryRead(out var table)) { - buffer.Span[0] = table.ArrayLength; + return new(context.Return(table.ArrayLength)); } else if (arg0.TryRead(out var str)) { - buffer.Span[0] = str.Length; + return new(context.Return(str.Length)); } else { LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, "rawlen", [LuaValueType.String, LuaValueType.Table]); + return default; } - - return new(1); } - public ValueTask RawSet(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RawSet(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); var arg2 = context.GetArgument(2); arg0[arg1] = arg2; - return new(0); + return new(context.Return()); } - public ValueTask Select(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Select(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); @@ -387,14 +351,11 @@ public ValueTask Select(LuaFunctionExecutionContext context, Memory(out var str) && str == "#") { - buffer.Span[0] = context.ArgumentCount - 1; - return new(1); + return new(context.Return(context.ArgumentCount - 1)); } else { @@ -403,7 +364,7 @@ public ValueTask Select(LuaFunctionExecutionContext context, Memory SetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask SetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); @@ -426,11 +387,11 @@ public ValueTask SetMetatable(LuaFunctionExecutionContext context, Memory(); } - buffer.Span[0] = arg0; - return new(1); + + return new(context.Return(arg0)); } - public ValueTask ToNumber(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask ToNumber(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var e = context.GetArgument(0); int? toBase = context.HasArgument(1) @@ -477,6 +438,7 @@ public ValueTask ToNumber(LuaFunctionExecutionContext context, Memory 2 && span[0] is '0' && span[1] is 'x' or 'X') @@ -500,13 +462,13 @@ public ValueTask ToNumber(LuaFunctionExecutionContext context, Memory text, int toBase) @@ -566,17 +528,18 @@ static double StringToDouble(ReadOnlySpan text, int toBase) return value; } - public ValueTask ToString(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask ToString(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - return arg0.CallToStringAsync(context, buffer, cancellationToken); + context.Return(); + return arg0.CallToStringAsync(context, cancellationToken); } - public ValueTask Type(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Type(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = arg0.Type switch + return new(context.Return(arg0.Type switch { LuaValueType.Nil => "nil", LuaValueType.Boolean => "boolean", @@ -584,56 +547,47 @@ public ValueTask Type(LuaFunctionExecutionContext context, Memory LuaValueType.Number => "number", LuaValueType.Function => "function", LuaValueType.Thread => "thread", + LuaValueType.LightUserData => "userdata", LuaValueType.UserData => "userdata", LuaValueType.Table => "table", _ => throw new NotImplementedException(), - }; - - return new(1); + })); } - public async ValueTask XPCall(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask XPCall(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - using var methodBuffer = new PooledArray(1024); - methodBuffer.AsSpan().Clear(); - try { - var resultCount = await arg0.InvokeAsync(context with + var count = await arg0.InvokeAsync(context with { State = context.State, ArgumentCount = context.ArgumentCount - 2, FrameBase = context.FrameBase + 2, - }, methodBuffer.AsMemory(), cancellationToken); - - buffer.Span[0] = true; - methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]); + ReturnFrameBase = context.ReturnFrameBase + 1 + }, cancellationToken); - return resultCount + 1; + context.Thread.Stack.Get(context.ReturnFrameBase) = true; + return count + 1; } catch (Exception ex) { - methodBuffer.AsSpan().Clear(); var error = ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message; context.State.Push(error); // invoke error handler - await arg1.InvokeAsync(context with + var count = await arg1.InvokeAsync(context with { State = context.State, ArgumentCount = 1, FrameBase = context.Thread.Stack.Count - 1, - }, methodBuffer.AsMemory(), cancellationToken); - - buffer.Span[0] = false; - buffer.Span[1] = methodBuffer[0]; - - - return 2; + ReturnFrameBase = context.ReturnFrameBase + 1 + }, cancellationToken); + context.Thread.Stack.Get(context.ReturnFrameBase) = false; + return count + 1; } } } \ No newline at end of file diff --git a/src/Lua/Standard/BitwiseLibrary.cs b/src/Lua/Standard/BitwiseLibrary.cs index d4ad2d08..e508f6c3 100644 --- a/src/Lua/Standard/BitwiseLibrary.cs +++ b/src/Lua/Standard/BitwiseLibrary.cs @@ -8,7 +8,8 @@ public sealed class BitwiseLibrary public BitwiseLibrary() { - Functions = [ + Functions = + [ new("arshift", ArShift), new("band", BAnd), new("bnot", BNot), @@ -26,7 +27,7 @@ public BitwiseLibrary() public readonly LuaFunction[] Functions; - public ValueTask ArShift(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask ArShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); var disp = context.GetArgument(1); @@ -46,16 +47,15 @@ public ValueTask ArShift(LuaFunctionExecutionContext context, Memory>= a; } - buffer.Span[0] = (uint)v; - return new(1); + return new(context.Return((uint)v)); } - public ValueTask BAnd(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask BAnd(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.ArgumentCount == 0) { - buffer.Span[0] = uint.MaxValue; - return new(1); + context.Return(uint.MaxValue); + return default; } var arg0 = context.GetArgument(0); @@ -72,26 +72,24 @@ public ValueTask BAnd(LuaFunctionExecutionContext context, Memory value &= v; } - buffer.Span[0] = value; - return new(1); + + return new(context.Return(value)); } - public ValueTask BNot(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask BNot(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "bnot", 1, arg0); var value = Bit32Helper.ToUInt32(arg0); - buffer.Span[0] = ~value; - return new(1); + return new(context.Return(~value)); } - public ValueTask BOr(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask BOr(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.ArgumentCount == 0) { - buffer.Span[0] = 0; - return new(1); + return new(context.Return(0)); } var arg0 = context.GetArgument(0); @@ -108,16 +106,15 @@ public ValueTask BOr(LuaFunctionExecutionContext context, Memory value |= v; } - buffer.Span[0] = value; - return new(1); + return new(context.Return(value)); } - public ValueTask BTest(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask BTest(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.ArgumentCount == 0) { - buffer.Span[0] = true; - return new(1); + ; + return new(context.Return(true)); } var arg0 = context.GetArgument(0); @@ -134,16 +131,14 @@ public ValueTask BTest(LuaFunctionExecutionContext context, Memory BXor(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask BXor(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.ArgumentCount == 0) { - buffer.Span[0] = 0; - return new(1); + return new(context.Return(0)); } var arg0 = context.GetArgument(0); @@ -160,11 +155,10 @@ public ValueTask BXor(LuaFunctionExecutionContext context, Memory value ^= v; } - buffer.Span[0] = value; - return new(1); + return new(context.Return(value)); } - public ValueTask Extract(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Extract(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); @@ -184,18 +178,16 @@ public ValueTask Extract(LuaFunctionExecutionContext context, Memory> field) & mask; + return new(context.Return((n >> field) & mask)); } - - return new(1); } - public ValueTask LRotate(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask LRotate(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); var disp = context.GetArgument(1); @@ -215,11 +207,11 @@ public ValueTask LRotate(LuaFunctionExecutionContext context, Memory> (32 - a)); } - buffer.Span[0] = v; - return new(1); + ; + return new(context.Return(v)); } - public ValueTask LShift(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask LShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); var disp = context.GetArgument(1); @@ -243,11 +235,10 @@ public ValueTask LShift(LuaFunctionExecutionContext context, Memory Replace(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Replace(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); @@ -279,11 +270,10 @@ public ValueTask Replace(LuaFunctionExecutionContext context, Memory RRotate(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RRotate(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); var disp = context.GetArgument(1); @@ -303,11 +293,10 @@ public ValueTask RRotate(LuaFunctionExecutionContext context, Memory> a) | (v << (32 - a)); } - buffer.Span[0] = v; - return new(1); + return new(context.Return(v)); } - public ValueTask RShift(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RShift(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); var disp = context.GetArgument(1); @@ -331,7 +320,6 @@ public ValueTask RShift(LuaFunctionExecutionContext context, Memory>= a; } - buffer.Span[0] = v; - return new(1); + return new(context.Return(v)); } } \ No newline at end of file diff --git a/src/Lua/Standard/CoroutineLibrary.cs b/src/Lua/Standard/CoroutineLibrary.cs index 5b25bb4a..0d027a2c 100644 --- a/src/Lua/Standard/CoroutineLibrary.cs +++ b/src/Lua/Standard/CoroutineLibrary.cs @@ -8,7 +8,8 @@ public sealed class CoroutineLibrary public CoroutineLibrary() { - Functions = [ + Functions = + [ new("create", Create), new("resume", Resume), new("running", Running), @@ -20,87 +21,84 @@ public CoroutineLibrary() public readonly LuaFunction[] Functions; - public ValueTask Create(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Create(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = new LuaCoroutine(arg0, true); - return new(1); + return new(context.Return(new LuaCoroutine(arg0, true))); } - public ValueTask Resume(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Resume(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var thread = context.GetArgument(0); - return thread.ResumeAsync(context, buffer, cancellationToken); + return thread.ResumeAsync(context, cancellationToken); } - public ValueTask Running(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Running(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - buffer.Span[0] = context.Thread; - buffer.Span[1] = context.Thread == context.State.MainThread; - return new(2); + return new(context.Return(context.Thread, context.Thread == context.State.MainThread)); } - public ValueTask Status(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Status(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var thread = context.GetArgument(0); - buffer.Span[0] = thread.GetStatus() switch + return new(context.Return(thread.GetStatus() switch { LuaThreadStatus.Normal => "normal", LuaThreadStatus.Suspended => "suspended", LuaThreadStatus.Running => "running", LuaThreadStatus.Dead => "dead", _ => "", - }; - return new(1); + })); } - public ValueTask Wrap(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + + public ValueTask Wrap(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var thread = new LuaCoroutine(arg0, false); - - buffer.Span[0] = new CsClosure("wrap", [thread],static async (context, buffer, cancellationToken) => - { - var thread = context.GetCsClosure()!.UpValues[0].Read(); - if (thread is not LuaCoroutine coroutine) - { - return await thread.ResumeAsync(context, buffer, cancellationToken); - } - var stack = context.Thread.Stack; - var frameBase = stack.Count; - - stack.Push(thread); - stack.PushRange(context.Arguments); - context.Thread.PushCallStackFrame(new() - { - Base = frameBase, - VariableArgumentCount = 0, - Function = coroutine.Function, - }); - try + return new(context.Return(new CsClosure("wrap", [thread], + static async (context, cancellationToken) => { - var resultCount = await thread.ResumeAsync(context with + var thread = context.GetCsClosure()!.UpValues[0].Read(); + if (thread is not LuaCoroutine coroutine) { - ArgumentCount = context.ArgumentCount + 1, - FrameBase = frameBase, - }, buffer, cancellationToken); + return await thread.ResumeAsync(context, cancellationToken); + } - buffer.Span[1..].CopyTo(buffer.Span[0..]); - return resultCount - 1; - } - finally - { - context.Thread.PopCallStackFrame(); - } - - - }); + var stack = context.Thread.Stack; + var frameBase = stack.Count; - return new(1); + stack.Push(thread); + stack.PushRange(context.Arguments); + context.Thread.PushCallStackFrame(new() + { + Base = frameBase, + ReturnBase = context.ReturnFrameBase, + VariableArgumentCount = 0, + Function = coroutine.Function + }); + try + { + await thread.ResumeAsync(context with + { + ArgumentCount = context.ArgumentCount + 1, + FrameBase = frameBase, + ReturnFrameBase = context.ReturnFrameBase, + }, cancellationToken); + var result = context.GetReturnBuffer(context.Thread.Stack.Count - context.ReturnFrameBase); + result[1..].CopyTo(result); + context.Thread.Stack.Pop(); + return result.Length - 1; + } + finally + { + context.Thread.PopCallStackFrame(); + } + }))); } - public ValueTask Yield(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Yield(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - return context.Thread.YieldAsync(context, buffer, cancellationToken); + return context.Thread.YieldAsync(context, cancellationToken); } } \ No newline at end of file diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index ef83ae63..ba23ac84 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -124,7 +124,7 @@ static ref LuaValue FindLocal(LuaThread thread, int level, int index, out string return ref thread.Stack.Get(frameBase + index); } - public ValueTask GetLocal(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { static LuaValue GetParam(LuaFunction function, int index) { @@ -145,8 +145,7 @@ static LuaValue GetParam(LuaFunction function, int index) var index = context.GetArgument(argOffset + 1); if (context.GetArgument(argOffset).TryReadFunction(out var f)) { - buffer.Span[0] = GetParam(f, index - 1); - return new(1); + return new(context.Return(GetParam(f, index - 1))); } var level = context.GetArgument(argOffset); @@ -160,16 +159,13 @@ static LuaValue GetParam(LuaFunction function, int index) ref var local = ref FindLocal(thread, level, index, out var name); if (name is null) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } - buffer.Span[0] = name; - buffer.Span[1] = local; - return new(2); + return new(context.Return(name, local)); } - public ValueTask SetLocal(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask SetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var thread = GetLuaThread(context, out var argOffset); @@ -186,16 +182,14 @@ public ValueTask SetLocal(LuaFunctionExecutionContext context, Memory GetUpValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetUpValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var func = context.GetArgument(0); var index = context.GetArgument(1) - 1; @@ -206,15 +200,13 @@ public ValueTask GetUpValue(LuaFunctionExecutionContext context, Memory= upValues.Length) { - return new(0); + return new(context.Return()); } - buffer.Span[0] = ""; - buffer.Span[1] = upValues[index]; - return new(1); + return new(context.Return("", upValues[index])); } - return new(0); + return new(context.Return()); } { @@ -222,17 +214,15 @@ public ValueTask GetUpValue(LuaFunctionExecutionContext context, Memory= descriptions.Length) { - return new(0); + return new(context.Return()); } var description = descriptions[index]; - buffer.Span[0] = description.Name.ToString(); - buffer.Span[1] = upValues[index].GetValue(); - return new(2); + return new(context.Return(description.Name.ToString(), upValues[index].GetValue())); } } - public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask SetUpValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var func = context.GetArgument(0); var index = context.GetArgument(1) - 1; @@ -244,15 +234,13 @@ public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory= 0 && index < upValues.Length) { - buffer.Span[0] = ""; upValues[index] = value; - return new(0); + return new(context.Return("")); } - return new(0); } - return new(0); + return new(context.Return()); } { @@ -260,33 +248,30 @@ public ValueTask SetUpValue(LuaFunctionExecutionContext context, Memory= descriptions.Length) { - return new(0); + return new(context.Return()); } var description = descriptions[index]; - buffer.Span[0] = description.Name.ToString(); upValues[index].SetValue(value); - return new(1); + return new(context.Return(description.Name.ToString())); } } - public ValueTask GetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); if (context.State.TryGetMetatable(arg0, out var table)) { - buffer.Span[0] = table; + return new(context.Return(table)); } else { - buffer.Span[0] = LuaValue.Nil; + return new(context.Return(LuaValue.Nil)); } - - return new(1); } - public ValueTask SetMetatable(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask SetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); @@ -298,16 +283,14 @@ public ValueTask SetMetatable(LuaFunctionExecutionContext context, Memory()); - buffer.Span[0] = arg0; - return new(1); + return new(context.Return(arg0)); } - public ValueTask GetUserValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetUserValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (!context.GetArgumentOrDefault(0).TryRead(out var iUserData)) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } var index = 1; // context.GetArgument(1); //for lua 5.4 @@ -316,15 +299,13 @@ public ValueTask GetUserValue(LuaFunctionExecutionContext context, Memory SetUserValue(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask SetUserValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var iUserData = context.GetArgument(0); var value = context.GetArgument(1); @@ -334,16 +315,14 @@ public ValueTask SetUserValue(LuaFunctionExecutionContext context, Memory Traceback(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Traceback(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var thread = GetLuaThread(context, out var argOffset); @@ -352,67 +331,58 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Memory GetRegistry(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetRegistry(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - buffer.Span[0] = context.State.Registry; - return new(1); + return new(context.Return(context.State.Registry)); } - public ValueTask UpValueId(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask UpValueId(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var n1 = context.GetArgument(1); var f1 = context.GetArgument(0); if (f1 is not Closure closure) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } var upValues = closure.GetUpValuesSpan(); if (n1 <= 0 || n1 > upValues.Length) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } - buffer.Span[0] = new LuaValue(upValues[n1 - 1]); - return new(1); + return new(context.Return(new LuaValue(upValues[n1 - 1]))); } - public ValueTask UpValueJoin(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask UpValueJoin(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var n2 = context.GetArgument(3); var f2 = context.GetArgument(2); @@ -421,8 +391,7 @@ public ValueTask UpValueJoin(LuaFunctionExecutionContext context, Memory UpValueJoin(LuaFunctionExecutionContext context, Memory SetHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask SetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var thread = GetLuaThread(context, out var argOffset); LuaFunction? hook = context.GetArgumentOrDefault(argOffset); @@ -496,10 +465,12 @@ public async ValueTask SetHook(LuaFunctionExecutionContext context, Memory< Thread = context.Thread, ArgumentCount = 2, FrameBase = stack.Count - 2, + ReturnFrameBase = stack.Count - 2, }; var frame = new CallStackFrame { Base = funcContext.FrameBase, + ReturnBase = funcContext.ReturnFrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook, }; @@ -508,42 +479,38 @@ public async ValueTask SetHook(LuaFunctionExecutionContext context, Memory< try { thread.IsInHook = true; - await hook.Func(funcContext, Memory.Empty, cancellationToken); + await hook.Func(funcContext, cancellationToken); } finally { thread.IsInHook = false; } - thread.PopCallStackFrame(); + thread.PopCallStackFrameWithStackPop(); } return 0; } - public ValueTask GetHook(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var thread = GetLuaThread(context, out var argOffset); if (thread.Hook is null) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = LuaValue.Nil; - buffer.Span[2] = LuaValue.Nil; - return new(3); + return new(context.Return(LuaValue.Nil, LuaValue.Nil, LuaValue.Nil)); } - buffer.Span[0] = thread.Hook; - buffer.Span[1] = ( - (thread.IsCallHookEnabled ? "c" : "") + - (thread.IsReturnHookEnabled ? "r" : "") + - (thread.IsLineHookEnabled ? "l" : "") - ); - buffer.Span[2] = thread.BaseHookCount; - return new(3); + return new(context.Return(thread.Hook, + ( + (thread.IsCallHookEnabled ? "c" : "") + + (thread.IsReturnHookEnabled ? "r" : "") + + (thread.IsLineHookEnabled ? "l" : "") + ) + , thread.BaseHookCount)); } - public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetInfo(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { //return new(0); var thread = GetLuaThread(context, out var argOffset); @@ -565,8 +532,7 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory callStack.Length) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } @@ -616,9 +582,7 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, Memory GetInfo(LuaFunctionExecutionContext context, Memory + public static readonly LuaFunction IndexMetamethod = new("index", (context, ct) => { context.GetArgument(0); var key = context.GetArgument(1); if (key.TryRead(out var name)) { - buffer.Span[0] = name switch + return new(context.Return(name switch { "close" => CloseFunction!, "flush" => FlushFunction!, @@ -24,14 +24,13 @@ public class FileHandle : ILuaUserData "setvbuf" => SetVBufFunction!, "write" => WriteFunction!, _ => LuaValue.Nil, - }; + })); } else { - buffer.Span[0] = LuaValue.Nil; + return new(context.Return( LuaValue.Nil)); } - return new(1); }); Stream stream; @@ -131,45 +130,37 @@ public void Close() } } - static readonly LuaFunction CloseFunction = new("close", (context, buffer, cancellationToken) => + static readonly LuaFunction CloseFunction = new("close", (context, cancellationToken) => { var file = context.GetArgument(0); try { file.Close(); - buffer.Span[0] = true; - return new(1); + return new (context.Return(true)); } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; - return new(3); + return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult)); } }); - static readonly LuaFunction FlushFunction = new("flush", (context, buffer, cancellationToken) => + static readonly LuaFunction FlushFunction = new("flush", (context, cancellationToken) => { var file = context.GetArgument(0); try { file.Flush(); - buffer.Span[0] = true; - return new(1); + return new(context.Return(true)); } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; - return new(3); + return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult)); } }); - static readonly LuaFunction LinesFunction = new("lines", (context, buffer, cancellationToken) => + static readonly LuaFunction LinesFunction = new("lines", (context, cancellationToken) => { var file = context.GetArgument(0); var format = context.HasArgument(1) @@ -177,25 +168,25 @@ public void Close() : "*l"; - buffer.Span[0] = new CsClosure("iterator", [new (file),format],static (context, buffer, cancellationToken) => + return new (context.Return(new CsClosure("iterator", [new (file),format],static (context, cancellationToken) => { var upValues = context.GetCsClosure()!.UpValues.AsSpan(); var file = upValues[0].Read(); - var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], buffer, true); - return new(resultCount); - }); - - return new(1); + context.Return(); + var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], context.Thread.Stack, true); + return new (resultCount); + }))); }); - static readonly LuaFunction ReadFunction = new("read", (context, buffer, cancellationToken) => + static readonly LuaFunction ReadFunction = new("read", (context, cancellationToken) => { var file = context.GetArgument(0); - var resultCount = IOHelper.Read(context.State, file, "read", 1, context.Arguments[1..], buffer, false); + context.Return(); + var resultCount = IOHelper.Read(context.State, file, "read", 1, context.Arguments[1..], context.Thread.Stack, false); return new(resultCount); }); - static readonly LuaFunction SeekFunction = new("seek", (context, buffer, cancellationToken) => + static readonly LuaFunction SeekFunction = new("seek", (context, cancellationToken) => { var file = context.GetArgument(0); var whence = context.HasArgument(1) @@ -212,19 +203,15 @@ public void Close() try { - buffer.Span[0] = file.Seek(whence, (long)offset); - return new(1); + return new(context.Return(file.Seek(whence, (long)offset))); } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; - return new(3); + return new (context.Return(LuaValue.Nil, ex.Message, ex.HResult)); } }); - static readonly LuaFunction SetVBufFunction = new("setvbuf", (context, buffer, cancellationToken) => + static readonly LuaFunction SetVBufFunction = new("setvbuf", (context, cancellationToken) => { var file = context.GetArgument(0); var mode = context.GetArgument(1); @@ -233,15 +220,15 @@ public void Close() : -1; file.SetVBuf(mode, size); - - buffer.Span[0] = true; - return new(1); + + return new(context.Return(true)); }); - static readonly LuaFunction WriteFunction = new("write", (context, buffer, cancellationToken) => + static readonly LuaFunction WriteFunction = new("write", (context, cancellationToken) => { var file = context.GetArgument(0); - var resultCount = IOHelper.Write(file, "write", context, buffer); + context.Return(); + var resultCount = IOHelper.Write(file, "write", context); return new(resultCount); }); } \ No newline at end of file diff --git a/src/Lua/Standard/IOLibrary.cs b/src/Lua/Standard/IOLibrary.cs index 755f702b..7e24f022 100644 --- a/src/Lua/Standard/IOLibrary.cs +++ b/src/Lua/Standard/IOLibrary.cs @@ -10,7 +10,8 @@ public sealed class IOLibrary public IOLibrary() { - Functions = [ + Functions = + [ new("close", Close), new("flush", Flush), new("input", Input), @@ -25,7 +26,7 @@ public IOLibrary() public readonly LuaFunction[] Functions; - public ValueTask Close(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Close(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var file = context.HasArgument(0) ? context.GetArgument(0) @@ -34,175 +35,166 @@ public ValueTask Close(LuaFunctionExecutionContext context, Memory Flush(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Flush(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var file = context.State.Environment["io"].Read()["stdout"].Read(); try { file.Flush(); - buffer.Span[0] = true; - return new(1); + return new(context.Return(true)); } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; - return new(3); + return new(context.Return(LuaValue.Nil, ex.Message, ex.HResult)); } } - public ValueTask Input(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Input(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var io = context.State.Environment["io"].Read(); if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil) { - buffer.Span[0] = io["stdio"]; - return new(1); + return new(context.Return(io["stdio"])); } var arg = context.Arguments[0]; if (arg.TryRead(out var file)) { io["stdio"] = new(file); - buffer.Span[0] = new(file); - return new(1); + return new(context.Return(new LuaValue(file))); } else { var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite); var handle = new FileHandle(stream); io["stdio"] = new(handle); - buffer.Span[0] = new(handle); - return new(1); + return new(context.Return(new LuaValue(handle))); } } - public ValueTask Lines(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Lines(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.ArgumentCount == 0) { var file = context.State.Environment["io"].Read()["stdio"].Read(); - buffer.Span[0] = new CsClosure("iterator",[new (file)] ,static (context, buffer, ct) => + return new(context.Return(new CsClosure("iterator", [new(file)], static (context, ct) => { var file = context.GetCsClosure()!.UpValues[0].Read(); - var resultCount = IOHelper.Read(context.State, file, "lines", 0, [], buffer, true); - if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil) + context.Return(); + var resultCount = IOHelper.Read(context.State, file, "lines", 0, [], context.Thread.Stack, true); + if (resultCount > 0 && context.Thread.Stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil) { file.Close(); } + return new(resultCount); - }); - return new(1); + }))); } else { var fileName = context.GetArgument(0); + var stack = context.Thread.Stack; + context.Return(); - using var methodBuffer = new PooledArray(32); - IOHelper.Open(context.State, fileName, "r", methodBuffer.AsMemory(), true); + IOHelper.Open(context.State, fileName, "r", stack, true); - var file = methodBuffer[0].Read(); + var file = stack.Get(context.ReturnFrameBase).Read(); var upValues = new LuaValue[context.Arguments.Length]; upValues[0] = new(file); context.Arguments[1..].CopyTo(upValues[1..]); - buffer.Span[0] = new CsClosure("iterator", upValues, static (context, buffer, ct) => + return new(context.Return(new CsClosure("iterator", upValues, static (context, ct) => { var upValues = context.GetCsClosure()!.UpValues; var file = upValues[0].Read(); var formats = upValues.AsSpan(1); - var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, buffer, true); - if (resultCount > 0 && buffer.Span[0].Type is LuaValueType.Nil) + var stack = context.Thread.Stack; + context.Return(); + var resultCount = IOHelper.Read(context.State, file, "lines", 0, formats, stack, true); + if (resultCount > 0 && stack.Get(context.ReturnFrameBase).Type is LuaValueType.Nil) { file.Close(); } - return new(resultCount); - }); - return new(1); + return new(resultCount); + }))); } } - public ValueTask Open(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Open(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var fileName = context.GetArgument(0); var mode = context.HasArgument(1) ? context.GetArgument(1) : "r"; - - var resultCount = IOHelper.Open(context.State, fileName, mode, buffer, false); + context.Return(); + var resultCount = IOHelper.Open(context.State, fileName, mode, context.Thread.Stack, false); return new(resultCount); } - public ValueTask Output(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Output(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var io = context.State.Environment["io"].Read(); if (context.ArgumentCount == 0 || context.Arguments[0].Type is LuaValueType.Nil) { - buffer.Span[0] = io["stdout"]; - return new(1); + return new(context.Return(io["stdout"])); } var arg = context.Arguments[0]; if (arg.TryRead(out var file)) { io["stdout"] = new(file); - buffer.Span[0] = new(file); - return new(1); + return new(context.Return(new LuaValue(file))); } else { var stream = File.Open(arg.ToString()!, FileMode.Open, FileAccess.ReadWrite); var handle = new FileHandle(stream); io["stdout"] = new(handle); - buffer.Span[0] = new(handle); - return new(1); + return new(context.Return(new LuaValue(handle))); } } - public ValueTask Read(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Read(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var file = context.State.Environment["io"].Read()["stdio"].Read(); - var resultCount = IOHelper.Read(context.State, file, "read", 0, context.Arguments, buffer, false); + context.Return(); + var stack = context.Thread.Stack; + + var resultCount = IOHelper.Read(context.State, file, "read", 0, context.Arguments, stack, false); return new(resultCount); } - public ValueTask Type(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Type(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); if (arg0.TryRead(out var file)) { - buffer.Span[0] = file.IsClosed ? "closed file" : "file"; + return new(context.Return(file.IsClosed ? "closed file" : "file")); } else { - buffer.Span[0] = LuaValue.Nil; + return new(context.Return(LuaValue.Nil)); } - - return new(1); } - public ValueTask Write(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Write(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var file = context.State.Environment["io"].Read()["stdout"].Read(); - var resultCount = IOHelper.Write(file, "write", context, buffer); + context.Return(); + var resultCount = IOHelper.Write(file, "write", context); return new(resultCount); } } \ No newline at end of file diff --git a/src/Lua/Standard/Internal/IOHelper.cs b/src/Lua/Standard/Internal/IOHelper.cs index c1ee59db..4016c27f 100644 --- a/src/Lua/Standard/Internal/IOHelper.cs +++ b/src/Lua/Standard/Internal/IOHelper.cs @@ -1,11 +1,12 @@ using System.Text; using Lua.Internal; +using Lua.Runtime; namespace Lua.Standard.Internal; internal static class IOHelper { - public static int Open(LuaState state, string fileName, string mode, Memory buffer, bool throwError) + public static int Open(LuaState state, string fileName, string mode, LuaStack stack, bool throwError) { var fileMode = mode switch { @@ -25,7 +26,7 @@ public static int Open(LuaState state, string fileName, string mode, Memory, async) - public static int Write(FileHandle file, string name, LuaFunctionExecutionContext context, Memory buffer) + public static int Write(FileHandle file, string name, LuaFunctionExecutionContext context) { try { @@ -70,25 +71,28 @@ public static int Write(FileHandle file, string name, LuaFunctionExecutionContex } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; + var stack = context.Thread.Stack; + stack.Push(LuaValue.Nil); + stack.Push(ex.Message); + stack.Push(ex.HResult); return 3; } - buffer.Span[0] = new(file); + context.Thread.Stack.Push(new(file)); return 1; } static readonly LuaValue[] defaultReadFormat = ["*l"]; - public static int Read(LuaState state, FileHandle file, string name, int startArgumentIndex, ReadOnlySpan formats, Memory buffer, bool throwError) + public static int Read(LuaState state, FileHandle file, string name, int startArgumentIndex, ReadOnlySpan formats, LuaStack stack, bool throwError) { if (formats.Length == 0) { formats = defaultReadFormat; } + var top = stack.Count; + try { for (int i = 0; i < formats.Length; i++) @@ -104,16 +108,16 @@ public static int Read(LuaState state, FileHandle file, string name, int startAr throw new NotImplementedException(); case "*a": case "*all": - buffer.Span[i] = file.ReadToEnd(); + stack.Push(file.ReadToEnd()); break; case "*l": case "*line": - buffer.Span[i] = file.ReadLine() ?? LuaValue.Nil; + stack.Push(file.ReadLine() ?? LuaValue.Nil); break; case "L": case "*L": var text = file.ReadLine(); - buffer.Span[i] = text == null ? LuaValue.Nil : text + Environment.NewLine; + stack.Push(text == null ? LuaValue.Nil : text + Environment.NewLine); break; } } @@ -126,14 +130,15 @@ public static int Read(LuaState state, FileHandle file, string name, int startAr var b = file.ReadByte(); if (b == -1) { - buffer.Span[0] = LuaValue.Nil; + stack.PopUntil(top); + stack.Push(LuaValue.Nil); return 1; } byteBuffer[j] = (byte)b; } - buffer.Span[i] = Encoding.UTF8.GetString(byteBuffer.AsSpan()); + stack.Push(Encoding.UTF8.GetString(byteBuffer.AsSpan())); } else { @@ -150,9 +155,10 @@ public static int Read(LuaState state, FileHandle file, string name, int startAr throw; } - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; + stack.PopUntil(top); + stack.Push(LuaValue.Nil); + stack.Push(ex.Message); + stack.Push(ex.HResult); return 3; } } diff --git a/src/Lua/Standard/MathematicsLibrary.cs b/src/Lua/Standard/MathematicsLibrary.cs index d72e453a..3d303c01 100644 --- a/src/Lua/Standard/MathematicsLibrary.cs +++ b/src/Lua/Standard/MathematicsLibrary.cs @@ -7,7 +7,8 @@ public sealed class MathematicsLibrary public MathematicsLibrary() { - Functions = [ + Functions = + [ new("abs", Abs), new("acos", Acos), new("asin", Asin), @@ -43,134 +44,123 @@ public MathematicsLibrary() public sealed class RandomUserData(Random random) : ILuaUserData { LuaTable? SharedMetatable; - public LuaTable? Metatable { get => SharedMetatable; set => SharedMetatable = value; } + + public LuaTable? Metatable + { + get => SharedMetatable; + set => SharedMetatable = value; + } + public Random Random { get; } = random; } - public ValueTask Abs(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Abs(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Abs(arg0); - return new(1); + return new (context.Return(Math.Abs(arg0))); } - public ValueTask Acos(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Acos(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Acos(arg0); - return new(1); + return new (context.Return(Math.Acos(arg0))); } - public ValueTask Asin(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Asin(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Asin(arg0); - return new(1); + return new (context.Return(Math.Asin(arg0))); } - public ValueTask Atan2(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Atan2(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - buffer.Span[0] = Math.Atan2(arg0, arg1); - return new(1); + return new (context.Return(Math.Atan2(arg0, arg1))); } - public ValueTask Atan(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Atan(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Atan(arg0); - return new(1); + return new (context.Return(Math.Atan(arg0))); } - public ValueTask Ceil(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Ceil(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Ceiling(arg0); - return new(1); + return new (context.Return(Math.Ceiling(arg0))); } - public ValueTask Cos(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Cos(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Cos(arg0); - return new(1); + return new (context.Return(Math.Cos(arg0))); } - public ValueTask Cosh(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Cosh(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Cosh(arg0); - return new(1); + return new (context.Return(Math.Cosh(arg0))); } - public ValueTask Deg(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Deg(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = arg0 * (180.0 / Math.PI); - return new(1); + return new (context.Return(arg0 * (180.0 / Math.PI))); } - public ValueTask Exp(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Exp(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Exp(arg0); - return new(1); + return new (context.Return(Math.Exp(arg0))); } - public ValueTask Floor(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Floor(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Floor(arg0); - return new(1); + return new (context.Return(Math.Floor(arg0))); } - public ValueTask Fmod(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Fmod(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - buffer.Span[0] = arg0 % arg1; - return new(1); + return new (context.Return(arg0 % arg1)); } - public ValueTask Frexp(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Frexp(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var (m, e) = MathEx.Frexp(arg0); - buffer.Span[0] = m; - buffer.Span[1] = e; - return new(2); + return new (context.Return(m,e)); } - public ValueTask Ldexp(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Ldexp(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - buffer.Span[0] = arg0 * Math.Pow(2, arg1); - return new(1); + return new (context.Return(arg0 * Math.Pow(2, arg1))); } - public ValueTask Log(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Log(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); if (context.ArgumentCount == 1) { - buffer.Span[0] = Math.Log(arg0); + return new (context.Return(Math.Log(arg0))); } else { var arg1 = context.GetArgument(1); - buffer.Span[0] = Math.Log(arg0, arg1); + return new (context.Return(Math.Log(arg0, arg1))); } - - return new(1); } - public ValueTask Max(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Max(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); for (int i = 1; i < context.ArgumentCount; i++) @@ -178,12 +168,10 @@ public ValueTask Max(LuaFunctionExecutionContext context, Memory x = Math.Max(x, context.GetArgument(i)); } - buffer.Span[0] = x; - - return new(1); + return new (context.Return(x)); } - public ValueTask Min(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Min(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var x = context.GetArgument(0); for (int i = 1; i < context.ArgumentCount; i++) @@ -191,98 +179,86 @@ public ValueTask Min(LuaFunctionExecutionContext context, Memory x = Math.Min(x, context.GetArgument(i)); } - buffer.Span[0] = x; - - return new(1); + return new (context.Return(x)); } - public ValueTask Modf(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Modf(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var (i, f) = MathEx.Modf(arg0); - buffer.Span[0] = i; - buffer.Span[1] = f; - return new(2); + return new (context.Return(i,f)); } - public ValueTask Pow(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Pow(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - buffer.Span[0] = Math.Pow(arg0, arg1); - return new(1); + return new (context.Return(Math.Pow(arg0, arg1))); } - public ValueTask Rad(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Rad(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = arg0 * (Math.PI / 180.0); - return new(1); + return new (context.Return(arg0 * (Math.PI / 180.0))); } - public ValueTask Random(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Random(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var rand = context.State.Environment[RandomInstanceKey].Read().Random; if (context.ArgumentCount == 0) { - buffer.Span[0] = rand.NextDouble(); + return new (context.Return(rand.NextDouble())); } else if (context.ArgumentCount == 1) { var arg0 = context.GetArgument(0); - buffer.Span[0] = rand.NextDouble() * (arg0 - 1) + 1; + return new (context.Return(rand.NextDouble() * (arg0 - 1) + 1)); } else { var arg0 = context.GetArgument(0); var arg1 = context.GetArgument(1); - buffer.Span[0] = rand.NextDouble() * (arg1 - arg0) + arg0; + return new (context.Return(rand.NextDouble() * (arg1 - arg0) + arg0)); } - return new(1); } - public ValueTask RandomSeed(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask RandomSeed(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); context.State.Environment[RandomInstanceKey] = new(new RandomUserData(new Random((int)BitConverter.DoubleToInt64Bits(arg0)))); - return new(0); + return new (context.Return()); } - public ValueTask Sin(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Sin(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Sin(arg0); - return new(1); + return new (context.Return(Math.Sin(arg0))); } - public ValueTask Sinh(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Sinh(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Sinh(arg0); - return new(1); + return new (context.Return(Math.Sinh(arg0))); } - public ValueTask Sqrt(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Sqrt(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Sqrt(arg0); - return new(1); + return new (context.Return(Math.Sqrt(arg0))); } - public ValueTask Tan(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Tan(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Tan(arg0); - return new(1); + return new (context.Return(Math.Tan(arg0))); } - public ValueTask Tanh(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Tanh(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); - buffer.Span[0] = Math.Tanh(arg0); - return new(1); + return new (context.Return(Math.Tanh(arg0))); } } \ No newline at end of file diff --git a/src/Lua/Standard/ModuleLibrary.cs b/src/Lua/Standard/ModuleLibrary.cs index 562a7114..b46e753b 100644 --- a/src/Lua/Standard/ModuleLibrary.cs +++ b/src/Lua/Standard/ModuleLibrary.cs @@ -15,7 +15,7 @@ public ModuleLibrary() public readonly LuaFunction RequireFunction; - public async ValueTask Require(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask Require(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var loaded = context.State.LoadedModules; @@ -24,15 +24,12 @@ public async ValueTask Require(LuaFunctionExecutionContext context, Memory< { var module = await context.State.ModuleLoader.LoadAsync(arg0, cancellationToken); var chunk = LuaCompiler.Default.Compile(module.ReadText(), module.Name); + await new Closure(context.State, chunk).InvokeAsync(context, cancellationToken); - using var methodBuffer = new PooledArray(1); - await new Closure(context.State, chunk).InvokeAsync(context, methodBuffer.AsMemory(), cancellationToken); - - loadedTable = methodBuffer[0]; + loadedTable = context.Thread.Stack.Get(context.ReturnFrameBase); loaded[arg0] = loadedTable; } - buffer.Span[0] = loadedTable; - return 1; + return context.Return(loadedTable); } } \ No newline at end of file diff --git a/src/Lua/Standard/OpenLibsExtensions.cs b/src/Lua/Standard/OpenLibsExtensions.cs index c274abb1..e9ec6a25 100644 --- a/src/Lua/Standard/OpenLibsExtensions.cs +++ b/src/Lua/Standard/OpenLibsExtensions.cs @@ -109,13 +109,11 @@ public static void OpenStringLibrary(this LuaState state) state.SetMetatable(key, metatable); } - metatable[Metamethods.Index] = new LuaFunction("index", (context, buffer, cancellationToken) => + metatable[Metamethods.Index] = new LuaFunction("index", (context, cancellationToken) => { context.GetArgument(0); var key = context.GetArgument(1); - - buffer.Span[0] = @string[key]; - return new(1); + return new(context.Return(@string[key])); }); } diff --git a/src/Lua/Standard/OperatingSystemLibrary.cs b/src/Lua/Standard/OperatingSystemLibrary.cs index 11dcb8dc..7d55148e 100644 --- a/src/Lua/Standard/OperatingSystemLibrary.cs +++ b/src/Lua/Standard/OperatingSystemLibrary.cs @@ -26,13 +26,12 @@ public OperatingSystemLibrary() public readonly LuaFunction[] Functions; - public ValueTask Clock(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Clock(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - buffer.Span[0] = DateTimeHelper.GetUnixTime(DateTime.UtcNow, Process.GetCurrentProcess().StartTime); - return new(1); + return new(context.Return(DateTimeHelper.GetUnixTime(DateTime.UtcNow, Process.GetCurrentProcess().StartTime))); } - public ValueTask Date(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Date(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var format = context.HasArgument(0) ? context.GetArgument(0).AsSpan() @@ -74,25 +73,23 @@ public ValueTask Date(LuaFunctionExecutionContext context, Memory table["yday"] = now.DayOfYear; table["isdst"] = isDst; - buffer.Span[0] = table; + return new (context.Return(table)); } else { - buffer.Span[0] = DateTimeHelper.StrFTime(context.State, format, now); + return new (context.Return(DateTimeHelper.StrFTime(context.State, format, now))); } - return new(1); } - public ValueTask DiffTime(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask DiffTime(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var t2 = context.GetArgument(0); var t1 = context.GetArgument(1); - buffer.Span[0] = t2 - t1; - return new(1); + return new (context.Return(t2 - t1)); } - public ValueTask Execute(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Execute(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { // os.execute(command) is not supported @@ -102,12 +99,11 @@ public ValueTask Execute(LuaFunctionExecutionContext context, Memory Exit(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Exit(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { // Ignore 'close' parameter @@ -133,80 +129,67 @@ public ValueTask Exit(LuaFunctionExecutionContext context, Memory Environment.Exit(0); } - return new(0); + return new(context.Return()); } - public ValueTask GetEnv(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GetEnv(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var variable = context.GetArgument(0); - buffer.Span[0] = Environment.GetEnvironmentVariable(variable) ?? LuaValue.Nil; - return new(1); + return new (context.Return(Environment.GetEnvironmentVariable(variable) ?? LuaValue.Nil)); } - public ValueTask Remove(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Remove(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var fileName = context.GetArgument(0); try { File.Delete(fileName); - buffer.Span[0] = true; - return new(1); + return new (context.Return(true)); } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; - return new(3); + return new (context.Return(LuaValue.Nil, ex.Message, ex.HResult)); } } - public ValueTask Rename(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Rename(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var oldName = context.GetArgument(0); var newName = context.GetArgument(1); try { File.Move(oldName, newName); - buffer.Span[0] = true; - return new(1); + return new (context.Return(true)); } catch (IOException ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - buffer.Span[2] = ex.HResult; - return new(3); + return new (context.Return(LuaValue.Nil, ex.Message, ex.HResult)); } } - public ValueTask SetLocale(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask SetLocale(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { // os.setlocale is not supported (always return nil) - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new (context.Return(LuaValue.Nil)); } - public ValueTask Time(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Time(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.HasArgument(0)) { var table = context.GetArgument(0); var date = DateTimeHelper.ParseTimeTable(context.State, table); - buffer.Span[0] = DateTimeHelper.GetUnixTime(date); - return new(1); + return new (context.Return(DateTimeHelper.GetUnixTime(date))); } else { - buffer.Span[0] = DateTimeHelper.GetUnixTime(DateTime.UtcNow); - return new(1); + return new (context.Return(DateTimeHelper.GetUnixTime(DateTime.UtcNow))); } } - public ValueTask TmpName(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask TmpName(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { - buffer.Span[0] = Path.GetTempFileName(); - return new(1); + return new (context.Return(Path.GetTempFileName())); } } \ No newline at end of file diff --git a/src/Lua/Standard/StringLibrary.cs b/src/Lua/Standard/StringLibrary.cs index ab011cc5..10d3996f 100644 --- a/src/Lua/Standard/StringLibrary.cs +++ b/src/Lua/Standard/StringLibrary.cs @@ -11,7 +11,8 @@ public sealed class StringLibrary public StringLibrary() { - Functions = [ + Functions = + [ new("byte", Byte), new("char", Char), new("dump", Dump), @@ -30,7 +31,7 @@ public StringLibrary() public readonly LuaFunction[] Functions; - public ValueTask Byte(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Byte(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); var i = context.HasArgument(1) @@ -44,20 +45,20 @@ public ValueTask Byte(LuaFunctionExecutionContext context, Memory LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "byte", 3, j); var span = StringHelper.Slice(s, (int)i, (int)j); + var buffer = context.GetReturnBuffer(span.Length); for (int k = 0; k < span.Length; k++) { - buffer.Span[k] = span[k]; + buffer[k] = span[k]; } return new(span.Length); } - public ValueTask Char(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Char(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { if (context.ArgumentCount == 0) { - buffer.Span[0] = ""; - return new(1); + return new(context.Return("")); } var builder = new ValueStringBuilder(context.ArgumentCount); @@ -68,17 +69,16 @@ public ValueTask Char(LuaFunctionExecutionContext context, Memory builder.Append((char)arg); } - buffer.Span[0] = builder.ToString(); - return new(1); + return new(context.Return(builder.ToString())); } - public ValueTask Dump(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Dump(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { // stirng.dump is not supported (throw exception) throw new NotSupportedException("stirng.dump is not supported"); } - public ValueTask Find(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Find(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); var pattern = context.GetArgument(1); @@ -100,16 +100,13 @@ public ValueTask Find(LuaFunctionExecutionContext context, Memory // out of range if (init != 1 && (init < 1 || init > s.Length)) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } // empty pattern if (pattern.Length == 0) { - buffer.Span[0] = 1; - buffer.Span[1] = 1; - return new(2); + return new(context.Return(1, 1)); } var source = s.AsSpan()[(int)(init - 1)..]; @@ -119,14 +116,11 @@ public ValueTask Find(LuaFunctionExecutionContext context, Memory var start = source.IndexOf(pattern); if (start == -1) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } // 1-based - buffer.Span[0] = start + 1; - buffer.Span[1] = start + pattern.Length; - return new(2); + return new(context.Return(start + 1, start + pattern.Length)); } else { @@ -136,22 +130,19 @@ public ValueTask Find(LuaFunctionExecutionContext context, Memory if (match.Success) { // 1-based - buffer.Span[0] = init + match.Index; - buffer.Span[1] = init + match.Index + match.Length - 1; - return new(2); + return new(context.Return(init + match.Index, init + match.Index + match.Length - 1)); } else { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } } } - public async ValueTask Format(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask Format(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var format = context.GetArgument(0); - + var stack = context.Thread.Stack; // TODO: pooling StringBuilder var builder = new StringBuilder(format.Length * 2); var parameterIndex = 1; @@ -240,6 +231,7 @@ public async ValueTask Format(LuaFunctionExecutionContext context, Memory Format(LuaFunctionExecutionContext context, Memory(1)) { - await parameter.CallToStringAsync(context, strBuffer.AsMemory(), cancellationToken); - formattedValue = strBuffer[0].Read(); + var top = stack.Count; + stack.Push(default); + await parameter.CallToStringAsync(context with { ReturnFrameBase = top }, cancellationToken); + formattedValue = stack.Pop().Read(); } if (specifier is 's' && precision > 0 && precision <= formattedValue.Length) { formattedValue = formattedValue[..precision]; } + break; case 'q': switch (parameter.Type) @@ -313,13 +308,16 @@ public async ValueTask Format(LuaFunctionExecutionContext context, Memory().ToString(); break; default: - using (var strBuffer = new PooledArray(1)) + { - await parameter.CallToStringAsync(context, strBuffer.AsMemory(), cancellationToken); - formattedValue = strBuffer[0].Read(); + var top = stack.Count; + stack.Push(default); + await parameter.CallToStringAsync(context with { ReturnFrameBase = top }, cancellationToken); + formattedValue = stack.Pop().Read(); } break; } + break; case 'i': case 'd': @@ -384,6 +382,7 @@ public async ValueTask Format(LuaFunctionExecutionContext context, Memory Format(LuaFunctionExecutionContext context, Memory GMatch(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask GMatch(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); var pattern = context.GetArgument(1); @@ -432,7 +429,7 @@ public ValueTask GMatch(LuaFunctionExecutionContext context, Memory + return new (context.Return(new CsClosure("iterator", [new LuaValue(matches), 0], static (context, cancellationToken) => { var upValues = context.GetCsClosure()!.UpValues; var matches = upValues[0].Read(); @@ -443,32 +440,31 @@ public ValueTask GMatch(LuaFunctionExecutionContext context, Memory GSub(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask GSub(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); var pattern = context.GetArgument(1); @@ -520,14 +516,14 @@ public async ValueTask GSub(LuaFunctionExecutionContext context, Memory(1024); + await func.InvokeAsync(context with { ArgumentCount = match.Groups.Count, FrameBase = context.Thread.Stack.Count - context.ArgumentCount, - }, methodBuffer.AsMemory(), cancellationToken); + }, cancellationToken); - result = methodBuffer[0]; + result = context.Thread.Stack.Get(context.ReturnFrameBase); } else { @@ -557,25 +553,22 @@ await func.InvokeAsync(context with builder.Append(s.AsSpan()[lastIndex..s.Length]); - buffer.Span[0] = builder.ToString(); - return 1; + return context.Return(builder.ToString()); } - public ValueTask Len(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Len(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); - buffer.Span[0] = s.Length; - return new(1); + return new (context.Return(s.Length)); } - public ValueTask Lower(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Lower(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); - buffer.Span[0] = s.ToLower(); - return new(1); + return new (context.Return(s.ToLower())); } - public ValueTask Rep(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Rep(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); var n_arg = context.GetArgument(1); @@ -597,22 +590,20 @@ public ValueTask Rep(LuaFunctionExecutionContext context, Memory } } - buffer.Span[0] = builder.ToString(); - return new(1); + return new (context.Return(builder.ToString())); } - public ValueTask Reverse(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Reverse(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); using var strBuffer = new PooledArray(s.Length); var span = strBuffer.AsSpan()[..s.Length]; s.AsSpan().CopyTo(span); span.Reverse(); - buffer.Span[0] = span.ToString(); - return new(1); + return new (context.Return(span.ToString())); } - public ValueTask Sub(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Sub(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); var i = context.GetArgument(1); @@ -623,14 +614,12 @@ public ValueTask Sub(LuaFunctionExecutionContext context, Memory LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "sub", 2, i); LuaRuntimeException.ThrowBadArgumentIfNumberIsNotInteger(context.State, "sub", 3, j); - buffer.Span[0] = StringHelper.Slice(s, (int)i, (int)j).ToString(); - return new(1); + return new (context.Return(StringHelper.Slice(s, (int)i, (int)j).ToString())); } - public ValueTask Upper(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Upper(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var s = context.GetArgument(0); - buffer.Span[0] = s.ToUpper(); - return new(1); + return new (context.Return(s.ToUpper())); } } \ No newline at end of file diff --git a/src/Lua/Standard/TableLibrary.cs b/src/Lua/Standard/TableLibrary.cs index fb3896b6..77f975a0 100644 --- a/src/Lua/Standard/TableLibrary.cs +++ b/src/Lua/Standard/TableLibrary.cs @@ -48,7 +48,7 @@ public TableLibrary() LastLineDefined = 0, }; - public ValueTask Concat(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Concat(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.HasArgument(1) @@ -83,11 +83,10 @@ public ValueTask Concat(LuaFunctionExecutionContext context, Memory Insert(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Insert(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var table = context.GetArgument(0); @@ -109,10 +108,10 @@ public ValueTask Insert(LuaFunctionExecutionContext context, Memory Pack(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Pack(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var table = new LuaTable(context.ArgumentCount, 1); @@ -123,11 +122,10 @@ public ValueTask Pack(LuaFunctionExecutionContext context, Memory } table["n"] = span.Length; - buffer.Span[0] = table; - return new(1); + return new (context.Return(table)); } - public ValueTask Remove(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Remove(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var table = context.GetArgument(0); var n_arg = context.HasArgument(1) @@ -142,23 +140,20 @@ public ValueTask Remove(LuaFunctionExecutionContext context, Memory table.ArrayLength) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new (context.Return(LuaValue.Nil)); } - buffer.Span[0] = table.RemoveAt(n); - return new(1); + return new (context.Return(table.RemoveAt(n))); } - public async ValueTask Sort(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public async ValueTask Sort(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.HasArgument(1) @@ -168,17 +163,18 @@ public async ValueTask Sort(LuaFunctionExecutionContext context, Memory PartitionAsync(LuaFunctionExecutionContext context, Memory memory, int low, int high, LuaFunction comparer, CancellationToken cancellationToken) { - using var methodBuffer = new PooledArray(1); + var pivot = memory.Span[high]; int i = low - 1; for (int j = low; j < high; j++) { - context.State.Push(memory.Span[j]); - context.State.Push(pivot); + var stack = context.Thread.Stack; + var top = stack.Count; + stack.Push(memory.Span[j]); + stack.Push(pivot); await comparer.InvokeAsync(context with { ArgumentCount = 2, - FrameBase = context.Thread.Stack.Count - context.ArgumentCount, - }, methodBuffer.AsMemory(), cancellationToken); + FrameBase = stack.Count - context.ArgumentCount, + ReturnFrameBase = top + }, cancellationToken); - if (methodBuffer[0].ToBoolean()) + if (context.Thread.Stack.Get(top).ToBoolean()) { i++; Swap(memory.Span, i, j); } + context.Thread.Stack.PopUntil(top); } Swap(memory.Span, i + 1, high); @@ -226,7 +226,7 @@ void Swap(Span span, int i, int j) (span[i], span[j]) = (span[j], span[i]); } - public ValueTask Unpack(LuaFunctionExecutionContext context, Memory buffer, CancellationToken cancellationToken) + public ValueTask Unpack(LuaFunctionExecutionContext context, CancellationToken cancellationToken) { var arg0 = context.GetArgument(0); var arg1 = context.HasArgument(1) @@ -237,9 +237,11 @@ public ValueTask Unpack(LuaFunctionExecutionContext context, Memory Date: Fri, 7 Mar 2025 11:15:12 +0900 Subject: [PATCH 18/22] Refactor: remove unused methods --- src/Lua/Runtime/LuaVirtualMachine.cs | 29 +--------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index e661df2e..bd4633a9 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -174,33 +174,6 @@ public void PopOnTopCallStackFrames() Thread.PopCallStackFrame(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearResultsBuffer() - { - //if (TaskResult == 0) return; - // if (TaskResult == 1) - // { - // ResultsBuffer[0] = default; - // return; - // } - // - // ResultsBuffer.AsSpan(0, TaskResult).Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearResultsBuffer(int count) - { - if (count == 0) return; - // if (count == 1) - // { - // ResultsBuffer[0] = default; - // return; - // } - // - // ResultsBuffer.AsSpan(0, count).Clear(); - } - - bool ExecutePostOperation(PostOperationType postOperation) { var stackCount = Stack.Count; @@ -1276,7 +1249,7 @@ static void ComparePostOperation(ref VirtualMachineExecutionContext context, Spa context.Pc++; } - context.ClearResultsBuffer(); + results.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] From ed17281db30de4b2f28f7eeff866dedcc4c2e3bc Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Fri, 7 Mar 2025 12:20:50 +0900 Subject: [PATCH 19/22] Fix: Remove temporary test --- tests/Lua.Tests/MetatableTests.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/Lua.Tests/MetatableTests.cs b/tests/Lua.Tests/MetatableTests.cs index 8063ae46..719329f3 100644 --- a/tests/Lua.Tests/MetatableTests.cs +++ b/tests/Lua.Tests/MetatableTests.cs @@ -62,21 +62,6 @@ public async Task Test_Metamethod_Index() assert(a.x == nil) metatable.__index= function(a,b) return b end assert(a.x == 'x') -"; - await state.DoStringAsync(source); - } - - [Test] - public async Task Test_Metamethod_Index_Self() - { - var source = @" -metatable = { - __index = function(a,b) return function () return a[1]..b; end,1 end -} - -local a = {x=1} -setmetatable(a, metatable) -print(a:some() "; await state.DoStringAsync(source); } From c40be1754565e2bae000f4baf7d74f36be495584 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:57:02 +0900 Subject: [PATCH 20/22] Refactor: remove duplicates in VM --- src/Lua/Runtime/LuaVirtualMachine.cs | 389 +++++++-------------------- src/Lua/Runtime/Metamethods.cs | 41 +++ 2 files changed, 136 insertions(+), 294 deletions(-) diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index bd4633a9..36728570 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -285,50 +285,44 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) --context.Pc; } - Loop: while (true) { - var instructionRef = Unsafe.Add(ref instructionsHead, ++context.Pc); - context.Instruction = instructionRef; + var instruction = Unsafe.Add(ref instructionsHead, ++context.Pc); + context.Instruction = instruction; if (lineAndCountHookMask.Value != 0 && (context.Pc != context.LastHookPc)) { goto LineHook; } context.LastHookPc = -1; - switch (instructionRef.OpCode) + var iA = instruction.A; + var opCode = instruction.OpCode; + switch (opCode) { case OpCode.Move: - var instruction = instructionRef; ref var stackHead = ref stack.FastGet(frameBase); - var iA = instruction.A; Unsafe.Add(ref stackHead, iA) = Unsafe.Add(ref stackHead, instruction.UIntB); stack.NotifyTop(iA + frameBase + 1); continue; case OpCode.LoadK: - instruction = instructionRef; - stack.GetWithNotifyTop(instruction.A + frameBase) = Unsafe.Add(ref constHead, instruction.Bx); + stack.GetWithNotifyTop(iA + frameBase) = Unsafe.Add(ref constHead, instruction.Bx); continue; case OpCode.LoadBool: - instruction = instructionRef; - stack.GetWithNotifyTop(instruction.A + frameBase) = instruction.B != 0; + stack.GetWithNotifyTop(iA + frameBase) = instruction.B != 0; if (instruction.C != 0) context.Pc++; continue; case OpCode.LoadNil: - instruction = instructionRef; - var ra1 = instruction.A + frameBase + 1; + var ra1 = iA + frameBase + 1; var iB = instruction.B; stack.GetBuffer().Slice(ra1 - 1, iB + 1).Clear(); stack.NotifyTop(ra1 + iB); continue; case OpCode.GetUpVal: - instruction = instructionRef; - stack.GetWithNotifyTop(instruction.A + frameBase) = context.Closure.GetUpValue(instruction.B); + stack.GetWithNotifyTop(iA + frameBase) = context.Closure.GetUpValue(instruction.B); continue; case OpCode.GetTabUp: case OpCode.GetTable: - instruction = instructionRef; stackHead = ref stack.FastGet(frameBase); ref readonly var vc = ref RKC(ref stackHead, ref constHead, instruction); ref readonly var vb = ref (instruction.OpCode == OpCode.GetTable ? ref Unsafe.Add(ref stackHead, instruction.UIntB) : ref context.Closure.GetUpValueRef(instruction.B)); @@ -342,7 +336,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.SetTabUp: - instruction = instructionRef; + case OpCode.SetTable: stackHead = ref stack.FastGet(frameBase); vb = ref RKB(ref stackHead, ref constHead, instruction); if (vb.TryReadNumber(out var numB)) @@ -354,7 +348,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } } - var table = context.Closure.GetUpValue(instruction.A); + var table = opCode == OpCode.SetTabUp ? context.Closure.GetUpValue(iA) : Unsafe.Add(ref stackHead, iA); if (table.TryReadTable(out luaTable)) { @@ -374,52 +368,14 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } return true; - case OpCode.SetUpVal: - instruction = instructionRef; - context.Closure.SetUpValue(instruction.B, stack.FastGet(instruction.A + frameBase)); + context.Closure.SetUpValue(instruction.B, stack.FastGet(iA + frameBase)); continue; - case OpCode.SetTable: - instruction = instructionRef; - stackHead = ref stack.FastGet(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - if (vb.TryReadNumber(out numB)) - { - if (double.IsNaN(numB)) - { - ThrowLuaRuntimeException(ref context, " table index is NaN"); - - return true; - } - } - - table = Unsafe.Add(ref stackHead, instruction.A); - - if (table.TryReadTable(out luaTable)) - { - ref var valueRef = ref luaTable.FindValue(vb); - if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil) - { - valueRef = RKC(ref stackHead, ref constHead, instruction); - continue; - } - } - - vc = ref RKC(ref stackHead, ref constHead, instruction); - if (SetTableValueSlowPath(table, vb, vc, ref context, out doRestart)) - { - if (doRestart) goto Restart; - continue; - } - - return true; case OpCode.NewTable: - instruction = instructionRef; - stack.GetWithNotifyTop(instruction.A + frameBase) = new LuaTable(instruction.B, instruction.C); + + stack.GetWithNotifyTop(iA + frameBase) = new LuaTable(instruction.B, instruction.C); continue; case OpCode.Self: - instruction = instructionRef; - iA = instruction.A; stackHead = ref stack.FastGet(frameBase); vc = ref RKC(ref stackHead, ref constHead, instruction); table = Unsafe.Add(ref stackHead, instruction.UIntB); @@ -436,165 +392,57 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Add: - instruction = instructionRef; - iA = instruction.A; - stackHead = ref stack.FastGet(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - vc = ref RKC(ref stackHead, ref constHead, instruction); - if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number) - { - Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() + vc.UnsafeReadDouble(); - stack.NotifyTop(iA + frameBase + 1); - continue; - } - - if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out var numC)) - { - Unsafe.Add(ref stackHead, iA) = numB + numC; - stack.NotifyTop(iA + frameBase + 1); - continue; - } - - if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Add, "add", out doRestart)) - { - if (doRestart) goto Restart; - continue; - } - - return true; case OpCode.Sub: - instruction = instructionRef; - iA = instruction.A; - stackHead = ref stack.FastGet(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - vc = ref RKC(ref stackHead, ref constHead, instruction); - - if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number) - { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() - vc.UnsafeReadDouble(); - stack.NotifyTop(ra1); - continue; - } - - if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC)) - { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = numB - numC; - stack.NotifyTop(ra1); - continue; - } - - if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Sub, "sub", out doRestart)) - { - if (doRestart) goto Restart; - continue; - } - - return true; - case OpCode.Mul: - instruction = instructionRef; - iA = instruction.A; - stackHead = ref stack.FastGet(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - vc = ref RKC(ref stackHead, ref constHead, instruction); - - if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number) - { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() * vc.UnsafeReadDouble(); - stack.NotifyTop(ra1); - continue; - } - - if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC)) - { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = numB * numC; - stack.NotifyTop(ra1); - continue; - } - - if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Mul, "mul", out doRestart)) - { - if (doRestart) goto Restart; - continue; - } - - return true; - case OpCode.Div: - instruction = instructionRef; - iA = instruction.A; + case OpCode.Mod: + case OpCode.Pow: stackHead = ref stack.FastGet(frameBase); vb = ref RKB(ref stackHead, ref constHead, instruction); vc = ref RKC(ref stackHead, ref constHead, instruction); - if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number) - { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() / vc.UnsafeReadDouble(); - stack.NotifyTop(ra1); - continue; - } - - if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC)) + [MethodImpl(MethodImplOptions.NoInlining)] + static double Mod(double a, double b) { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = numB / numC; - stack.NotifyTop(ra1); - continue; - } + var mod = a % b; + if ((b > 0 && mod < 0) || (b < 0 && mod > 0)) + { + mod += b; + } - if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Div, "div", out doRestart)) - { - if (doRestart) goto Restart; - continue; + return mod; } - return true; - case OpCode.Mod: - instruction = instructionRef; - iA = instruction.A; - stackHead = ref stack.FastGet(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - vc = ref RKC(ref stackHead, ref constHead, instruction); - if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC)) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static double ArithmeticOperation(OpCode code, double a, double b) { - var mod = numB % numC; - if ((numC > 0 && mod < 0) || (numC < 0 && mod > 0)) + return code switch { - mod += numC; - } - - Unsafe.Add(ref stackHead, iA) = mod; - stack.NotifyTop(iA + frameBase + 1); - continue; + OpCode.Add => a + b, + OpCode.Sub => a - b, + OpCode.Mul => a * b, + OpCode.Div => a / b, + OpCode.Mod => Mod(a, b), + OpCode.Pow => Math.Pow(a, b), + _ => 0 + }; } - if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Mod, "mod", out doRestart)) + if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number) { - if (doRestart) goto Restart; + Unsafe.Add(ref stackHead, iA) = ArithmeticOperation(opCode, vb.UnsafeReadDouble(), vc.UnsafeReadDouble()); + stack.NotifyTop(iA + frameBase + 1); continue; } - return true; - case OpCode.Pow: - instruction = instructionRef; - iA = instruction.A; - stackHead = ref stack.FastGet(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - vc = ref RKC(ref stackHead, ref constHead, instruction); - if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC)) + if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out var numC)) { - ra1 = iA + frameBase + 1; - Unsafe.Add(ref stackHead, iA) = Math.Pow(numB, numC); - stack.NotifyTop(ra1); + Unsafe.Add(ref stackHead, iA) = ArithmeticOperation(opCode, numB, numC); + stack.NotifyTop(iA + frameBase + 1); continue; } - if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Pow, "pow", out doRestart)) + if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, opCode, out doRestart)) { if (doRestart) goto Restart; continue; @@ -602,8 +450,6 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Unm: - instruction = instructionRef; - iA = instruction.A; stackHead = ref stack.FastGet(frameBase); vb = ref Unsafe.Add(ref stackHead, instruction.UIntB); @@ -615,7 +461,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) continue; } - if (ExecuteUnaryOperationMetaMethod(vb, ref context, Metamethods.Unm, "unm", false, out doRestart)) + if (ExecuteUnaryOperationMetaMethod(vb, ref context, OpCode.Unm, out doRestart)) { if (doRestart) goto Restart; continue; @@ -623,30 +469,24 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Not: - instruction = instructionRef; - iA = instruction.A; - ra1 = iA + frameBase + 1; stackHead = ref stack.FastGet(frameBase); Unsafe.Add(ref stackHead, iA) = !Unsafe.Add(ref stackHead, instruction.UIntB).ToBoolean(); - stack.NotifyTop(ra1); + stack.NotifyTop(iA + frameBase + 1); continue; case OpCode.Len: - instruction = instructionRef; stackHead = ref stack.FastGet(frameBase); - vb = ref Unsafe.Add(ref stackHead, instruction.UIntB); if (vb.TryReadString(out var str)) { - iA = instruction.A; ra1 = iA + frameBase + 1; Unsafe.Add(ref stackHead, iA) = str.Length; stack.NotifyTop(ra1); continue; } - if (ExecuteUnaryOperationMetaMethod(vb, ref context, Metamethods.Len, "get length of", true, out doRestart)) + if (ExecuteUnaryOperationMetaMethod(vb, ref context, OpCode.Len, out doRestart)) { if (doRestart) goto Restart; continue; @@ -662,9 +502,8 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Jmp: - instruction = instructionRef; context.Pc += instruction.SBx; - iA = instruction.A; + if (iA != 0) { context.State.CloseUpValues(context.Thread, frameBase + iA - 1); @@ -672,8 +511,6 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) continue; case OpCode.Eq: - instruction = instructionRef; - iA = instruction.A; stackHead = ref stack.Get(frameBase); vb = ref RKB(ref stackHead, ref constHead, instruction); vc = ref RKC(ref stackHead, ref constHead, instruction); @@ -687,7 +524,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) continue; } - if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, Metamethods.Eq, null, out doRestart)) + if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, OpCode.Eq, out doRestart)) { if (doRestart) goto Restart; continue; @@ -695,52 +532,14 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Lt: - instruction = instructionRef; - iA = instruction.A; - stackHead = ref stack.Get(frameBase); - vb = ref RKB(ref stackHead, ref constHead, instruction); - vc = ref RKC(ref stackHead, ref constHead, instruction); - - if (vb.TryReadNumber(out numB) && vc.TryReadNumber(out numC)) - { - var compareResult = numB < numC; - if (compareResult != (iA == 1)) - { - context.Pc++; - } - - continue; - } - - - if (vb.TryReadString(out var strB) && vc.TryReadString(out var strC)) - { - var compareResult = StringComparer.Ordinal.Compare(strB, strC) < 0; - if (compareResult != (iA == 1)) - { - context.Pc++; - } - - continue; - } - - if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, Metamethods.Lt, "less than", out doRestart)) - { - if (doRestart) goto Restart; - continue; - } - - return true; case OpCode.Le: - instruction = instructionRef; - iA = instruction.A; stackHead = ref stack.Get(frameBase); vb = ref RKB(ref stackHead, ref constHead, instruction); vc = ref RKC(ref stackHead, ref constHead, instruction); if (vb.TryReadNumber(out numB) && vc.TryReadNumber(out numC)) { - var compareResult = numB <= numC; + var compareResult = opCode == OpCode.Lt ? numB < numC : numB <= numC; if (compareResult != (iA == 1)) { context.Pc++; @@ -749,9 +548,10 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) continue; } - if (vb.TryReadString(out strB) && vc.TryReadString(out strC)) + if (vb.TryReadString(out var strB) && vc.TryReadString(out var strC)) { - var compareResult = StringComparer.Ordinal.Compare(strB, strC) <= 0; + var c = StringComparer.Ordinal.Compare(strB, strC); + var compareResult = opCode == OpCode.Lt ? c < 0 : c <= 0; if (compareResult != (iA == 1)) { context.Pc++; @@ -760,7 +560,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) continue; } - if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, Metamethods.Le, "less than or equals", out doRestart)) + if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, opCode, out doRestart)) { if (doRestart) goto Restart; continue; @@ -768,15 +568,13 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Test: - instruction = instructionRef; - if (stack.Get(instruction.A + frameBase).ToBoolean() != (instruction.C == 1)) + if (stack.Get(iA + frameBase).ToBoolean() != (instruction.C == 1)) { context.Pc++; } continue; case OpCode.TestSet: - instruction = instructionRef; vb = ref stack.Get(instruction.B + frameBase); if (vb.ToBoolean() != (instruction.C == 1)) { @@ -784,7 +582,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } else { - stack.GetWithNotifyTop(instruction.A + frameBase) = vb; + stack.GetWithNotifyTop(iA + frameBase) = vb; } continue; @@ -794,7 +592,6 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) { if (doRestart) { - //Console.WriteLine("Restarting"); goto Restart; } @@ -812,7 +609,6 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.Return: - instruction = instructionRef; context.State.CloseUpValues(context.Thread, frameBase); if (context.Pop(instruction, frameBase)) { @@ -821,24 +617,24 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) goto End; case OpCode.ForLoop: - ref var indexRef = ref stack.Get(instructionRef.A + frameBase); + ref var indexRef = ref stack.Get(iA + frameBase); var limit = Unsafe.Add(ref indexRef, 1).UnsafeReadDouble(); var step = Unsafe.Add(ref indexRef, 2).UnsafeReadDouble(); var index = indexRef.UnsafeReadDouble() + step; if (step >= 0 ? index <= limit : limit <= index) { - context.Pc += instructionRef.SBx; + context.Pc += instruction.SBx; indexRef = index; Unsafe.Add(ref indexRef, 3) = index; - stack.NotifyTop(instructionRef.A + frameBase + 4); + stack.NotifyTop(iA + frameBase + 4); continue; } - stack.NotifyTop(instructionRef.A + frameBase + 1); + stack.NotifyTop(iA + frameBase + 1); continue; case OpCode.ForPrep: - indexRef = ref stack.Get(instructionRef.A + frameBase); + indexRef = ref stack.Get(iA + frameBase); if (!indexRef.TryReadDouble(out var init)) { @@ -859,8 +655,8 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } indexRef = init - step; - stack.NotifyTop(instructionRef.A + frameBase + 1); - context.Pc += instructionRef.SBx; + stack.NotifyTop(iA + frameBase + 1); + context.Pc += instruction.SBx; continue; case OpCode.TForCall: if (TForCall(ref context, out doRestart)) @@ -869,13 +665,9 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) continue; } - return true; case OpCode.TForLoop: - instruction = instructionRef; - iA = instruction.A; - ra1 = iA + frameBase + 1; - ref var forState = ref stack.Get(ra1); + ref var forState = ref stack.Get(iA + frameBase + 1); if (forState.Type is not LuaValueType.Nil) { @@ -888,32 +680,38 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) SetList(ref context); continue; case OpCode.Closure: - instruction = instructionRef; - iA = instruction.A; ra1 = iA + frameBase + 1; stack.EnsureCapacity(ra1); stack.Get(ra1 - 1) = new Closure(context.State, context.Chunk.Functions[instruction.SBx]); stack.NotifyTop(ra1); continue; case OpCode.VarArg: - instruction = instructionRef; - iA = instruction.A; - ra1 = iA + frameBase + 1; - var frameVariableArgumentCount = context.VariableArgumentCount; - var count = instruction.B == 0 - ? frameVariableArgumentCount - : instruction.B - 1; - var ra = ra1 - 1; - stack.EnsureCapacity(ra + count); - stackHead = ref stack.Get(0); - for (int i = 0; i < count; i++) + VarArg(ref context); + + static void VarArg(ref VirtualMachineExecutionContext context) { - Unsafe.Add(ref stackHead, ra + i) = frameVariableArgumentCount > i - ? Unsafe.Add(ref stackHead, frameBase - (frameVariableArgumentCount - i)) - : default; + + var instruction = context.Instruction; + var iA = instruction.A; + var frameBase = context.FrameBase; + var frameVariableArgumentCount = context.VariableArgumentCount; + var count = instruction.B == 0 + ? frameVariableArgumentCount + : instruction.B - 1; + var ra = iA + frameBase; + var stack = context.Stack; + stack.EnsureCapacity(ra + count); + ref var stackHead = ref stack.Get(0); + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref stackHead, ra + i) = frameVariableArgumentCount > i + ? Unsafe.Add(ref stackHead, frameBase - (frameVariableArgumentCount - i)) + : default; + } + + stack.NotifyTop(ra + count); } - stack.NotifyTop(ra + count); continue; case OpCode.ExtraArg: default: @@ -1001,7 +799,7 @@ static bool Concat(ref VirtualMachineExecutionContext context, out bool doRestar return true; } - return ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Concat, "concat", out doRestart); + return ExecuteBinaryOperationMetaMethod(vb, vc, ref context, OpCode.Concat, out doRestart); } static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) @@ -1452,8 +1250,9 @@ static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaVa [MethodImpl(MethodImplOptions.NoInlining)] static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc, - ref VirtualMachineExecutionContext context, string name, string description, out bool doRestart) + ref VirtualMachineExecutionContext context, OpCode opCode, out bool doRestart) { + var (name, description) = opCode.GetNameAndDescription(); doRestart = false; if (vb.TryGetMetamethod(context.State, name, out var metamethod) || vc.TryGetMetamethod(context.State, name, out metamethod)) @@ -1512,8 +1311,9 @@ static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc, [MethodImpl(MethodImplOptions.NoInlining)] static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecutionContext context, - string name, string description, bool isLen, out bool doRestart) + OpCode opCode, out bool doRestart) { + var (name, description) = opCode.GetNameAndDescription(); doRestart = false; var stack = context.Stack; if (vb.TryGetMetamethod(context.State, name, out var metamethod)) @@ -1560,7 +1360,7 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu return true; } - if (isLen && vb.TryReadTable(out var table)) + if (opCode == OpCode.Len && vb.TryReadTable(out var table)) { var RA = context.Instruction.A + context.FrameBase; stack.Get(RA) = table.ArrayLength; @@ -1573,8 +1373,9 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu [MethodImpl(MethodImplOptions.NoInlining)] static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, - ref VirtualMachineExecutionContext context, string name, string? description, out bool doRestart) + ref VirtualMachineExecutionContext context, OpCode opCode, out bool doRestart) { + var (name, description) = opCode.GetNameAndDescription(); doRestart = false; bool reverseLe = false; ReCheck: @@ -1630,7 +1431,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, return true; } - if (name == Metamethods.Le) + if (opCode == OpCode.Le) { reverseLe = true; name = Metamethods.Lt; @@ -1638,7 +1439,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, goto ReCheck; } - if (description != null) + if (opCode != OpCode.Eq) { if (reverseLe) { diff --git a/src/Lua/Runtime/Metamethods.cs b/src/Lua/Runtime/Metamethods.cs index 16957d04..b766efb6 100644 --- a/src/Lua/Runtime/Metamethods.cs +++ b/src/Lua/Runtime/Metamethods.cs @@ -21,4 +21,45 @@ public static class Metamethods public const string Pairs = "__pairs"; public const string IPairs = "__ipairs"; public new const string ToString = "__tostring"; + + internal static (string Name, string Description) GetNameAndDescription(this OpCode opCode) + { + switch (opCode) + { + case OpCode.GetTabUp: + case OpCode.GetTable: + case OpCode.Self: + return (Index, "index"); + case OpCode.SetTabUp: + case OpCode.SetTable: + return (NewIndex, "new index"); + case OpCode.Add: + return (Add, "add"); + case OpCode.Sub: + return (Sub, "sub"); + case OpCode.Mul: + return (Mul, "mul"); + case OpCode.Div: + return (Div, "div"); + case OpCode.Mod: + return (Mod, "mod"); + case OpCode.Pow: + return (Pow, "pow"); + case OpCode.Unm: + return (Unm, "unm"); + case OpCode.Len: + return (Len, "get length of"); + case OpCode.Eq: + return (Eq, "eq"); + case OpCode.Lt: + return (Lt, "lt"); + case OpCode.Le: + return (Le, "le"); + case OpCode.Call: + return (Call, "call"); + case OpCode.Concat: + return (Concat, "concat"); + default: return (opCode.ToString(), opCode.ToString()); + } + } } \ No newline at end of file From dfaa49b13afd56d741917aec385c58581b592ca5 Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:12:41 +0900 Subject: [PATCH 21/22] Refactor remove debug codes --- src/Lua/Runtime/LuaVirtualMachine.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index 36728570..3a37f4b7 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -157,8 +157,6 @@ public void Push(in CallStackFrame frame) FrameBase = frame.Base; CurrentReturnFrameBase = frame.ReturnBase; VariableArgumentCount = frame.VariableArgumentCount; - - //Console.WriteLine($"Push {frame}"); } public void PopOnTopCallStackFrames() @@ -690,7 +688,6 @@ static double ArithmeticOperation(OpCode code, double a, double b) static void VarArg(ref VirtualMachineExecutionContext context) { - var instruction = context.Instruction; var iA = instruction.A; var frameBase = context.FrameBase; @@ -833,7 +830,6 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) return false; } - //Console.WriteLine("Call ReturnBase:" + " " + context.Thread.GetHashCode() + " " + newFrame.ReturnBase + " " + context.BaseCallStackCount + " " + context.Thread.CallStack.Count); if (func is Closure) { context.Push(newFrame); @@ -870,8 +866,6 @@ static bool FuncCall(ref VirtualMachineExecutionContext context, in CallStackFra stack.NotifyTop(top); } - //Console.WriteLine(($" FuncCall Pop {context.Thread.CallStack.Count}")); - context.Thread.PopCallStackFrame(); return true; } From 79471e2273672d2085ba20634b1890d278e8589a Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Tue, 15 Apr 2025 23:27:18 +0900 Subject: [PATCH 22/22] Rename: Closure to LuaClosure, CsClosure to CSharpClosure --- src/Lua/Internal/LuaDebug.cs | 6 ++-- src/Lua/LuaCoroutine.cs | 2 +- src/Lua/LuaFunctionExecutionContext.cs | 4 +-- src/Lua/LuaState.cs | 8 ++--- src/Lua/Runtime/CSharpClosure.cs | 6 ++++ src/Lua/Runtime/CsClosure.cs | 6 ---- src/Lua/Runtime/{Closure.cs => LuaClosure.cs} | 6 ++-- src/Lua/Runtime/LuaValueRuntimeExtensions.cs | 2 +- src/Lua/Runtime/LuaVirtualMachine.cs | 34 +++++++++---------- src/Lua/Runtime/Tracebacks.cs | 14 ++++---- src/Lua/Standard/BasicLibrary.cs | 6 ++-- src/Lua/Standard/CoroutineLibrary.cs | 2 +- src/Lua/Standard/DebugLibrary.cs | 20 +++++------ src/Lua/Standard/FileHandle.cs | 2 +- src/Lua/Standard/IOLibrary.cs | 4 +-- src/Lua/Standard/ModuleLibrary.cs | 2 +- src/Lua/Standard/StringLibrary.cs | 2 +- src/Lua/Standard/TableLibrary.cs | 2 +- 18 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 src/Lua/Runtime/CSharpClosure.cs delete mode 100644 src/Lua/Runtime/CsClosure.cs rename src/Lua/Runtime/{Closure.cs => LuaClosure.cs} (90%) diff --git a/src/Lua/Internal/LuaDebug.cs b/src/Lua/Internal/LuaDebug.cs index fa1ecac9..b15098f1 100644 --- a/src/Lua/Internal/LuaDebug.cs +++ b/src/Lua/Internal/LuaDebug.cs @@ -198,7 +198,7 @@ internal void Return(uint version) internal bool GetInfo(CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunction function, int pc, ReadOnlySpan what) { - Closure? closure = function as Closure; + LuaClosure? closure = function as LuaClosure; int status = 1; foreach (var c in what) { @@ -238,7 +238,7 @@ internal bool GetInfo(CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunct case 'n': { /* calling function is a known Lua function? */ - if (prevFrame is { Function: Closure prevFrameClosure }) + if (prevFrame is { Function: LuaClosure prevFrameClosure }) NameWhat = GetFuncName(prevFrameClosure.Proto, frame?.CallerInstructionIndex ?? 0, out Name); else NameWhat = null; @@ -268,7 +268,7 @@ internal bool GetInfo(CallStackFrame? prevFrame, CallStackFrame? frame, LuaFunct void GetFuncInfo(LuaFunction f) { - if (f is not Closure cl) + if (f is not LuaClosure cl) { Source = "=[C#]"; LineDefined = -1; diff --git a/src/Lua/LuaCoroutine.cs b/src/Lua/LuaCoroutine.cs index 90541c8b..12680381 100644 --- a/src/Lua/LuaCoroutine.cs +++ b/src/Lua/LuaCoroutine.cs @@ -194,7 +194,7 @@ public override async ValueTask YieldAsync(LuaFunctionExecutionContext cont throw new LuaRuntimeException(context.State.GetTraceback(), "cannot call yield on a coroutine that is not currently running"); } - if (context.Thread.GetCallStackFrames()[^2].Function is not Closure) + if (context.Thread.GetCallStackFrames()[^2].Function is not LuaClosure) { throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield across a C#-call boundary"); } diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index 408d3dd2..4299c339 100644 --- a/src/Lua/LuaFunctionExecutionContext.cs +++ b/src/Lua/LuaFunctionExecutionContext.cs @@ -178,9 +178,9 @@ public Span GetReturnBuffer(int count) return buffer; } - public CsClosure? GetCsClosure() + public CSharpClosure? GetCsClosure() { - return Thread.GetCurrentFrame().Function as CsClosure; + return Thread.GetCurrentFrame().Function as CSharpClosure; } internal void ThrowBadArgument(int index, string message) diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index 91adde37..d2f341f5 100644 --- a/src/Lua/LuaState.cs +++ b/src/Lua/LuaState.cs @@ -70,7 +70,7 @@ public async ValueTask RunAsync(Chunk chunk, CancellationToken cancel Volatile.Write(ref isRunning, true); try { - var closure = new Closure(this, chunk); + var closure = new LuaClosure(this, chunk); await closure.InvokeAsync(new() { State = this, @@ -109,14 +109,14 @@ internal Traceback GetTraceback(LuaThread thread) list.Add(frame); } - Closure rootFunc; - if (thread.GetCallStackFrames()[0].Function is Closure closure) + LuaClosure rootFunc; + if (thread.GetCallStackFrames()[0].Function is LuaClosure closure) { rootFunc = closure; } else { - rootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function; + rootFunc = (LuaClosure)MainThread.GetCallStackFrames()[0].Function; } return new(this) diff --git a/src/Lua/Runtime/CSharpClosure.cs b/src/Lua/Runtime/CSharpClosure.cs new file mode 100644 index 00000000..2ed85fdf --- /dev/null +++ b/src/Lua/Runtime/CSharpClosure.cs @@ -0,0 +1,6 @@ +namespace Lua.Runtime; + +public sealed class CSharpClosure(string name,LuaValue[] upValues,Func> func) : LuaFunction(name, func) +{ + public readonly LuaValue[] UpValues = upValues; +} \ No newline at end of file diff --git a/src/Lua/Runtime/CsClosure.cs b/src/Lua/Runtime/CsClosure.cs deleted file mode 100644 index 12e29747..00000000 --- a/src/Lua/Runtime/CsClosure.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Lua.Runtime; - -public sealed class CsClosure(string name,LuaValue[] upValues,Func> func) : LuaFunction(name, func) -{ - public readonly LuaValue[] UpValues = upValues; -} \ No newline at end of file diff --git a/src/Lua/Runtime/Closure.cs b/src/Lua/Runtime/LuaClosure.cs similarity index 90% rename from src/Lua/Runtime/Closure.cs rename to src/Lua/Runtime/LuaClosure.cs index 22329c81..20f48226 100644 --- a/src/Lua/Runtime/Closure.cs +++ b/src/Lua/Runtime/LuaClosure.cs @@ -3,12 +3,12 @@ namespace Lua.Runtime; -public sealed class Closure : LuaFunction +public sealed class LuaClosure : LuaFunction { Chunk proto; FastListCore upValues; - public Closure(LuaState state, Chunk proto, LuaTable? environment = null) + public LuaClosure(LuaState state, Chunk proto, LuaTable? environment = null) : base(proto.Name, (context, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, ct)) { this.proto = proto; @@ -56,7 +56,7 @@ static UpValue GetUpValueFromDescription(LuaState state, LuaThread thread, UpVal return envUpValue; } - if (thread.GetCurrentFrame().Function is Closure parentClosure) + if (thread.GetCurrentFrame().Function is LuaClosure parentClosure) { return parentClosure.UpValues[description.Index]; } diff --git a/src/Lua/Runtime/LuaValueRuntimeExtensions.cs b/src/Lua/Runtime/LuaValueRuntimeExtensions.cs index e02ee65b..542c85f8 100644 --- a/src/Lua/Runtime/LuaValueRuntimeExtensions.cs +++ b/src/Lua/Runtime/LuaValueRuntimeExtensions.cs @@ -15,7 +15,7 @@ public static bool TryGetMetamethod(this LuaValue value, LuaState state, string [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetVariableArgumentCount(this LuaFunction function, int argumentCount) { - return function is Closure { Proto.HasVariableArguments: true } luaClosure + return function is LuaClosure { Proto.HasVariableArguments: true } luaClosure ?argumentCount - luaClosure.Proto.ParameterCount : 0; } diff --git a/src/Lua/Runtime/LuaVirtualMachine.cs b/src/Lua/Runtime/LuaVirtualMachine.cs index 3a37f4b7..ee45d145 100644 --- a/src/Lua/Runtime/LuaVirtualMachine.cs +++ b/src/Lua/Runtime/LuaVirtualMachine.cs @@ -20,9 +20,9 @@ struct VirtualMachineExecutionContext( { public readonly LuaState State = state; public readonly LuaStack Stack = stack; - public Closure Closure = (Closure)frame.Function; + public LuaClosure LuaClosure = (LuaClosure)frame.Function; public readonly LuaThread Thread = thread; - public Chunk Chunk => Closure.Proto; + public Chunk Chunk => LuaClosure.Proto; public int FrameBase = frame.Base; public int VariableArgumentCount = frame.VariableArgumentCount; public readonly CancellationToken CancellationToken = cancellationToken; @@ -69,7 +69,7 @@ public bool PopFromBuffer(int src, int srcCount) Pc = frame.CallerInstructionIndex; Thread.LastPc = Pc; ref readonly var lastFrame = ref frames[^2]; - Closure = Unsafe.As(lastFrame.Function); + LuaClosure = Unsafe.As(lastFrame.Function); CurrentReturnFrameBase = frame.ReturnBase; var callInstruction = Chunk.Instructions[Pc]; if (callInstruction.OpCode == OpCode.TailCall) @@ -153,7 +153,7 @@ public bool PopFromBuffer(int src, int srcCount) public void Push(in CallStackFrame frame) { Pc = -1; - Closure = (Closure)(frame.Function); + LuaClosure = (LuaClosure)(frame.Function); FrameBase = frame.Base; CurrentReturnFrameBase = frame.ReturnBase; VariableArgumentCount = frame.VariableArgumentCount; @@ -317,13 +317,13 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) stack.NotifyTop(ra1 + iB); continue; case OpCode.GetUpVal: - stack.GetWithNotifyTop(iA + frameBase) = context.Closure.GetUpValue(instruction.B); + stack.GetWithNotifyTop(iA + frameBase) = context.LuaClosure.GetUpValue(instruction.B); continue; case OpCode.GetTabUp: case OpCode.GetTable: stackHead = ref stack.FastGet(frameBase); ref readonly var vc = ref RKC(ref stackHead, ref constHead, instruction); - ref readonly var vb = ref (instruction.OpCode == OpCode.GetTable ? ref Unsafe.Add(ref stackHead, instruction.UIntB) : ref context.Closure.GetUpValueRef(instruction.B)); + ref readonly var vb = ref (instruction.OpCode == OpCode.GetTable ? ref Unsafe.Add(ref stackHead, instruction.UIntB) : ref context.LuaClosure.GetUpValueRef(instruction.B)); var doRestart = false; if (vb.TryReadTable(out var luaTable) && luaTable.TryGetValue(vc, out var resultValue) || GetTableValueSlowPath(vb, vc, ref context, out resultValue, out doRestart)) { @@ -346,7 +346,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } } - var table = opCode == OpCode.SetTabUp ? context.Closure.GetUpValue(iA) : Unsafe.Add(ref stackHead, iA); + var table = opCode == OpCode.SetTabUp ? context.LuaClosure.GetUpValue(iA) : Unsafe.Add(ref stackHead, iA); if (table.TryReadTable(out luaTable)) { @@ -367,7 +367,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) return true; case OpCode.SetUpVal: - context.Closure.SetUpValue(instruction.B, stack.FastGet(iA + frameBase)); + context.LuaClosure.SetUpValue(instruction.B, stack.FastGet(iA + frameBase)); continue; case OpCode.NewTable: @@ -680,7 +680,7 @@ static double ArithmeticOperation(OpCode code, double a, double b) case OpCode.Closure: ra1 = iA + frameBase + 1; stack.EnsureCapacity(ra1); - stack.Get(ra1 - 1) = new Closure(context.State, context.Chunk.Functions[instruction.SBx]); + stack.Get(ra1 - 1) = new LuaClosure(context.State, context.Chunk.Functions[instruction.SBx]); stack.NotifyTop(ra1); continue; case OpCode.VarArg: @@ -830,7 +830,7 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) return false; } - if (func is Closure) + if (func is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -926,7 +926,7 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest } - if (func is Closure) + if (func is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -981,7 +981,7 @@ static bool TForCall(ref VirtualMachineExecutionContext context, out bool doRest return false; } - if (iterator is Closure) + if (iterator is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -1121,7 +1121,7 @@ static bool CallGetTableFunc(LuaValue table, LuaFunction indexTable, LuaValue ke return false; } - if (indexTable is Closure) + if (indexTable is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -1221,7 +1221,7 @@ static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaVa return false; } - if (newIndexFunction is Closure) + if (newIndexFunction is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -1271,7 +1271,7 @@ static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc, return false; } - if (func is Closure) + if (func is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -1329,7 +1329,7 @@ static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecu return false; } - if (func is Closure) + if (func is LuaClosure) { context.Push(newFrame); doRestart = true; @@ -1395,7 +1395,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, return false; } - if (func is Closure) + if (func is LuaClosure) { context.Push(newFrame); doRestart = true; diff --git a/src/Lua/Runtime/Tracebacks.cs b/src/Lua/Runtime/Tracebacks.cs index 19a2d6ee..93df737b 100644 --- a/src/Lua/Runtime/Tracebacks.cs +++ b/src/Lua/Runtime/Tracebacks.cs @@ -8,7 +8,7 @@ namespace Lua.Runtime; public class Traceback(LuaState state) { public LuaState State => state; - public required Closure RootFunc { get; init; } + public required LuaClosure RootFunc { get; init; } public required CallStackFrame[] StackFrames { get; init; } internal string RootChunkName => RootFunc.Proto.Name; @@ -22,7 +22,7 @@ internal SourcePosition LastPosition { LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : RootFunc; var frame = stackFrames[index]; - if (!frame.IsTailCall && lastFunc is Closure closure) + if (!frame.IsTailCall && lastFunc is LuaClosure closure) { var p = closure.Proto; if (frame.CallerInstructionIndex < 0 || p.SourcePositions.Length <= frame.CallerInstructionIndex) @@ -54,7 +54,7 @@ public string ToString(int skipFrames) return GetTracebackString(State, RootFunc, StackFrames[..^skipFrames], LuaValue.Nil); } - internal static string GetTracebackString(LuaState state, Closure rootFunc, ReadOnlySpan stackFrames, LuaValue message, bool skipFirstCsharpCall = false) + internal static string GetTracebackString(LuaState state, LuaClosure rootFunc, ReadOnlySpan stackFrames, LuaValue message, bool skipFirstCsharpCall = false) { using var list = new PooledList(64); if (message.Type is not LuaValueType.Nil) @@ -67,7 +67,7 @@ internal static string GetTracebackString(LuaState state, Closure rootFunc, Read var intFormatBuffer = (stackalloc char[15]); var shortSourceBuffer = (stackalloc char[59]); { - if (0 < stackFrames.Length && !skipFirstCsharpCall && stackFrames[^1].Function is { } f and not Closure) + if (0 < stackFrames.Length && !skipFirstCsharpCall && stackFrames[^1].Function is { } f and not LuaClosure) { list.AddRange("\t[C#]: in function '"); list.AddRange(f.Name); @@ -78,13 +78,13 @@ internal static string GetTracebackString(LuaState state, Closure rootFunc, Read for (var index = stackFrames.Length - 1; index >= 0; index--) { LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : rootFunc; - if (lastFunc is not null and not Closure) + if (lastFunc is not null and not LuaClosure) { list.AddRange("\t[C#]: in function '"); list.AddRange(lastFunc.Name); list.AddRange("'\n"); } - else if (lastFunc is Closure closure) + else if (lastFunc is LuaClosure closure) { var frame = stackFrames[index]; @@ -141,7 +141,7 @@ internal static string GetTracebackString(LuaState state, Closure rootFunc, Read } var caller = index > 1 ? stackFrames[index - 2].Function : rootFunc; - if (index > 0 && caller is Closure callerClosure) + if (index > 0 && caller is LuaClosure callerClosure) { var t = LuaDebug.GetFuncName(callerClosure.Proto, stackFrames[index - 1].CallerInstructionIndex, out var name); if (t is not null) diff --git a/src/Lua/Standard/BasicLibrary.cs b/src/Lua/Standard/BasicLibrary.cs index 2d139b67..3577de5b 100644 --- a/src/Lua/Standard/BasicLibrary.cs +++ b/src/Lua/Standard/BasicLibrary.cs @@ -93,7 +93,7 @@ public async ValueTask DoFile(LuaFunctionExecutionContext context, Cancella var fileName = "@" + Path.GetFileName(arg0); var chunk = LuaCompiler.Default.Compile(text, fileName); - return await new Closure(context.State, chunk).InvokeAsync(context, cancellationToken); + return await new LuaClosure(context.State, chunk).InvokeAsync(context, cancellationToken); } public ValueTask Error(LuaFunctionExecutionContext context, CancellationToken cancellationToken) @@ -170,7 +170,7 @@ public async ValueTask LoadFile(LuaFunctionExecutionContext context, Cancel var text = await File.ReadAllTextAsync(arg0, cancellationToken); var fileName = "@" + Path.GetFileName(arg0); var chunk = LuaCompiler.Default.Compile(text, fileName); - return context.Return(new Closure(context.State, chunk, arg2)); + return context.Return(new LuaClosure(context.State, chunk, arg2)); } catch (Exception ex) { @@ -197,7 +197,7 @@ public ValueTask Load(LuaFunctionExecutionContext context, CancellationToke if (arg0.TryRead(out var str)) { var chunk = LuaCompiler.Default.Compile(str, arg1 ?? str); - return new(context.Return(new Closure(context.State, chunk, arg3))); + return new(context.Return(new LuaClosure(context.State, chunk, arg3))); } else if (arg0.TryRead(out var function)) { diff --git a/src/Lua/Standard/CoroutineLibrary.cs b/src/Lua/Standard/CoroutineLibrary.cs index 0d027a2c..098578c7 100644 --- a/src/Lua/Standard/CoroutineLibrary.cs +++ b/src/Lua/Standard/CoroutineLibrary.cs @@ -56,7 +56,7 @@ public ValueTask Wrap(LuaFunctionExecutionContext context, CancellationToke { var arg0 = context.GetArgument(0); var thread = new LuaCoroutine(arg0, false); - return new(context.Return(new CsClosure("wrap", [thread], + return new(context.Return(new CSharpClosure("wrap", [thread], static async (context, cancellationToken) => { var thread = context.GetCsClosure()!.UpValues[0].Read(); diff --git a/src/Lua/Standard/DebugLibrary.cs b/src/Lua/Standard/DebugLibrary.cs index ba23ac84..77cda6d4 100644 --- a/src/Lua/Standard/DebugLibrary.cs +++ b/src/Lua/Standard/DebugLibrary.cs @@ -82,7 +82,7 @@ static ref LuaValue FindLocal(LuaThread thread, int level, int index, out string var frameBase = frame.Base; - if (frame.Function is Closure closure) + if (frame.Function is LuaClosure closure) { var locals = closure.Proto.Locals; var nextFrame = callStack[^level]; @@ -128,7 +128,7 @@ public ValueTask GetLocal(LuaFunctionExecutionContext context, Cancellation { static LuaValue GetParam(LuaFunction function, int index) { - if (function is Closure closure) + if (function is LuaClosure closure) { var paramCount = closure.Proto.ParameterCount; if (0 <= index && index < paramCount) @@ -193,9 +193,9 @@ public ValueTask GetUpValue(LuaFunctionExecutionContext context, Cancellati { var func = context.GetArgument(0); var index = context.GetArgument(1) - 1; - if (func is not Closure closure) + if (func is not LuaClosure closure) { - if (func is CsClosure csClosure) + if (func is CSharpClosure csClosure) { var upValues = csClosure.UpValues; if (index < 0 || index >= upValues.Length) @@ -227,9 +227,9 @@ public ValueTask SetUpValue(LuaFunctionExecutionContext context, Cancellati var func = context.GetArgument(0); var index = context.GetArgument(1) - 1; var value = context.GetArgument(2); - if (func is not Closure closure) + if (func is not LuaClosure closure) { - if (func is CsClosure csClosure) + if (func is CSharpClosure csClosure) { var upValues = csClosure.UpValues; if (index >= 0 && index < upValues.Length) @@ -355,7 +355,7 @@ public ValueTask Traceback(LuaFunctionExecutionContext context, Cancellatio var skipCount = Math.Min(Math.Max(level - 1, 0), callStack.Length - 1); var frames = callStack[1..^skipCount]; - return new(context.Return(Runtime.Traceback.GetTracebackString(context.State, (Closure)callStack[0].Function, frames, message, level == 1))); + return new(context.Return(Runtime.Traceback.GetTracebackString(context.State, (LuaClosure)callStack[0].Function, frames, message, level == 1))); } public ValueTask GetRegistry(LuaFunctionExecutionContext context, CancellationToken cancellationToken) @@ -368,7 +368,7 @@ public ValueTask UpValueId(LuaFunctionExecutionContext context, Cancellatio var n1 = context.GetArgument(1); var f1 = context.GetArgument(0); - if (f1 is not Closure closure) + if (f1 is not LuaClosure closure) { return new(context.Return(LuaValue.Nil)); } @@ -389,7 +389,7 @@ public ValueTask UpValueJoin(LuaFunctionExecutionContext context, Cancellat var n1 = context.GetArgument(1); var f1 = context.GetArgument(0); - if (f1 is not Closure closure1 || f2 is not Closure closure2) + if (f1 is not LuaClosure closure1 || f2 is not LuaClosure closure2) { return new(context.Return(LuaValue.Nil)); } @@ -597,7 +597,7 @@ public ValueTask GetInfo(LuaFunctionExecutionContext context, CancellationT if (what.Contains('L')) { - if (functionToInspect is Closure closure) + if (functionToInspect is LuaClosure closure) { var activeLines = new LuaTable(0, 8); foreach (var pos in closure.Proto.SourcePositions) diff --git a/src/Lua/Standard/FileHandle.cs b/src/Lua/Standard/FileHandle.cs index c8d0b867..72ddeade 100644 --- a/src/Lua/Standard/FileHandle.cs +++ b/src/Lua/Standard/FileHandle.cs @@ -168,7 +168,7 @@ public void Close() : "*l"; - return new (context.Return(new CsClosure("iterator", [new (file),format],static (context, cancellationToken) => + return new (context.Return(new CSharpClosure("iterator", [new (file),format],static (context, cancellationToken) => { var upValues = context.GetCsClosure()!.UpValues.AsSpan(); var file = upValues[0].Read(); diff --git a/src/Lua/Standard/IOLibrary.cs b/src/Lua/Standard/IOLibrary.cs index 7e24f022..77a6629b 100644 --- a/src/Lua/Standard/IOLibrary.cs +++ b/src/Lua/Standard/IOLibrary.cs @@ -87,7 +87,7 @@ public ValueTask Lines(LuaFunctionExecutionContext context, CancellationTok if (context.ArgumentCount == 0) { var file = context.State.Environment["io"].Read()["stdio"].Read(); - return new(context.Return(new CsClosure("iterator", [new(file)], static (context, ct) => + return new(context.Return(new CSharpClosure("iterator", [new(file)], static (context, ct) => { var file = context.GetCsClosure()!.UpValues[0].Read(); context.Return(); @@ -113,7 +113,7 @@ public ValueTask Lines(LuaFunctionExecutionContext context, CancellationTok upValues[0] = new(file); context.Arguments[1..].CopyTo(upValues[1..]); - return new(context.Return(new CsClosure("iterator", upValues, static (context, ct) => + return new(context.Return(new CSharpClosure("iterator", upValues, static (context, ct) => { var upValues = context.GetCsClosure()!.UpValues; var file = upValues[0].Read(); diff --git a/src/Lua/Standard/ModuleLibrary.cs b/src/Lua/Standard/ModuleLibrary.cs index b46e753b..2d186a68 100644 --- a/src/Lua/Standard/ModuleLibrary.cs +++ b/src/Lua/Standard/ModuleLibrary.cs @@ -24,7 +24,7 @@ public async ValueTask Require(LuaFunctionExecutionContext context, Cancell { var module = await context.State.ModuleLoader.LoadAsync(arg0, cancellationToken); var chunk = LuaCompiler.Default.Compile(module.ReadText(), module.Name); - await new Closure(context.State, chunk).InvokeAsync(context, cancellationToken); + await new LuaClosure(context.State, chunk).InvokeAsync(context, cancellationToken); loadedTable = context.Thread.Stack.Get(context.ReturnFrameBase); loaded[arg0] = loadedTable; diff --git a/src/Lua/Standard/StringLibrary.cs b/src/Lua/Standard/StringLibrary.cs index 10d3996f..124f674d 100644 --- a/src/Lua/Standard/StringLibrary.cs +++ b/src/Lua/Standard/StringLibrary.cs @@ -429,7 +429,7 @@ public ValueTask GMatch(LuaFunctionExecutionContext context, CancellationTo var regex = StringHelper.ToRegex(pattern); var matches = regex.Matches(s); - return new (context.Return(new CsClosure("iterator", [new LuaValue(matches), 0], static (context, cancellationToken) => + return new (context.Return(new CSharpClosure("iterator", [new LuaValue(matches), 0], static (context, cancellationToken) => { var upValues = context.GetCsClosure()!.UpValues; var matches = upValues[0].Read(); diff --git a/src/Lua/Standard/TableLibrary.cs b/src/Lua/Standard/TableLibrary.cs index 77f975a0..f4385a19 100644 --- a/src/Lua/Standard/TableLibrary.cs +++ b/src/Lua/Standard/TableLibrary.cs @@ -158,7 +158,7 @@ public async ValueTask Sort(LuaFunctionExecutionContext context, Cancellat var arg0 = context.GetArgument(0); var arg1 = context.HasArgument(1) ? context.GetArgument(1) - : new Closure(context.State, defaultComparer); + : new LuaClosure(context.State, defaultComparer); context.Thread.PushCallStackFrame(new () {