From 5210c37ad194276d0ec2975669d8d8f77d12e93a Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:24:49 +0900 Subject: [PATCH 01/89] Change: Return values on LuaStack --- sandbox/Benchmark/AddBenchmark.cs | 7 +- sandbox/Benchmark/InterpreterSteps.cs | 4 +- sandbox/ConsoleApp1/Program.cs | 7 +- .../LuaObjectGenerator.Emit.cs | 30 +- .../CodeAnalysis/Compilation/LuaCompiler.cs | 95 +-- .../Syntax/Nodes/BooleanLiteralNode.cs | 2 +- src/Lua/Internal/BitFlags.cs | 2 +- src/Lua/Internal/BitFlags256.cs | 3 +- src/Lua/Internal/LuaResult.cs | 26 + src/Lua/LuaCoroutine.cs | 62 +- src/Lua/LuaFunction.cs | 23 +- src/Lua/LuaFunctionExecutionContext.cs | 62 ++ src/Lua/LuaFunctionExtensions.cs | 12 +- src/Lua/LuaMainThread.cs | 10 +- src/Lua/LuaState.cs | 51 +- src/Lua/LuaStateExtensions.cs | 43 +- src/Lua/LuaThread.cs | 10 +- src/Lua/LuaThreadExtensions.cs | 26 +- src/Lua/LuaValue.cs | 15 +- src/Lua/Runtime/CSharpClosure.cs | 4 +- src/Lua/Runtime/CallStackFrame.cs | 5 +- src/Lua/Runtime/LuaClosure.cs | 2 +- src/Lua/Runtime/LuaStack.cs | 12 + src/Lua/Runtime/LuaVirtualMachine.Debug.cs | 49 +- src/Lua/Runtime/LuaVirtualMachine.cs | 806 ++++++------------ src/Lua/Runtime/Metamethods.cs | 41 + src/Lua/Runtime/OpCode.cs | 83 +- src/Lua/Standard/BasicLibrary.cs | 242 ++---- src/Lua/Standard/BitwiseLibrary.cs | 82 +- src/Lua/Standard/CoroutineLibrary.cs | 91 +- src/Lua/Standard/DebugLibrary.cs | 164 ++-- src/Lua/Standard/FileHandle.cs | 66 +- src/Lua/Standard/IOLibrary.cs | 104 ++- src/Lua/Standard/Internal/IOHelper.cs | 44 +- src/Lua/Standard/MathematicsLibrary.cs | 176 ++-- src/Lua/Standard/ModuleLibrary.cs | 11 +- src/Lua/Standard/OpenLibsExtensions.cs | 12 +- src/Lua/Standard/OperatingSystemLibrary.cs | 73 +- src/Lua/Standard/StringLibrary.cs | 144 ++-- src/Lua/Standard/TableLibrary.cs | 78 +- tests/Lua.Tests/MetatableTests.cs | 6 +- 41 files changed, 1173 insertions(+), 1612 deletions(-) create mode 100644 src/Lua/Internal/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..b96173ae 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,7 +41,7 @@ catch (Exception ex) { Console.WriteLine(ex); - if(ex is LuaRuntimeException { InnerException: not null } luaEx) + if (ex is LuaRuntimeException { InnerException: not null } luaEx) { Console.WriteLine(luaEx.InnerException); } diff --git a/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs b/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs index 4772b3ef..9024c0ea 100644 --- a/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs +++ b/src/Lua.SourceGenerator/LuaObjectGenerator.Emit.cs @@ -178,7 +178,7 @@ static bool ValidateMembers(TypeMetadata typeMetadata, Compilation compilation, PARAMETERS: foreach (var typeSymbol in method.Symbol.Parameters - .Select(x => x.Type)) + .Select(x => x.Type)) { if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.LuaValue)) continue; if (SymbolEqualityComparer.Default.Equals(typeSymbol, typeMetadata.Symbol)) continue; @@ -201,7 +201,7 @@ 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()) { @@ -224,17 +224,17 @@ static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builde } foreach (var methodMetadata in typeMetadata.Methods - .Where(x => x.HasMemberAttribute)) + .Where(x => x.HasMemberAttribute)) { builder.AppendLine(@$"""{methodMetadata.LuaMemberName}"" => new global::Lua.LuaValue(__function_{methodMetadata.LuaMemberName}),"); } builder.AppendLine(@$"_ => global::Lua.LuaValue.Nil,"); } + builder.AppendLine(";"); - builder.AppendLine("buffer.Span[0] = result;"); - builder.AppendLine("return new(1);"); + builder.AppendLine("return new global::System.Threading.Tasks.ValueTask(context.Return(result));"); } builder.AppendLine(");"); @@ -244,7 +244,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()) { @@ -278,7 +278,7 @@ static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder bui } foreach (var methodMetadata in typeMetadata.Methods - .Where(x => x.HasMemberAttribute)) + .Where(x => x.HasMemberAttribute)) { builder.AppendLine(@$"case ""{methodMetadata.LuaMemberName}"":"); @@ -296,7 +296,7 @@ static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder bui } } - builder.AppendLine("return new(0);"); + builder.AppendLine("return new global::System.Threading.Tasks.ValueTask(context.Return());"); } builder.AppendLine(");"); @@ -348,7 +348,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()) { @@ -388,6 +388,7 @@ static void EmitMethodFunction(string functionName, string chunkName, TypeMetada builder.AppendLine($"var arg{index} = context.GetArgument<{parameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>({index});"); } } + index++; } @@ -414,24 +415,24 @@ static void EmitMethodFunction(string functionName, string chunkName, TypeMetada 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(");"); builder.AppendLine(); } @@ -453,6 +454,7 @@ static bool TryEmitMetatable(CodeBuilder builder, IEnumerable functionName, ScopeCompilationCont if (hasSelfParameter) { - funcContext.Scope.AddLocalVariable("self".AsMemory(), new() - { - RegisterIndex = 0, - StartPc = 0, - }); + funcContext.Scope.AddLocalVariable("self".AsMemory(), new() { RegisterIndex = 0, StartPc = 0, }); funcContext.Scope.StackPosition++; } @@ -701,11 +686,7 @@ int CompileFunctionProto(ReadOnlyMemory functionName, ScopeCompilationCont for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; - funcContext.Scope.AddLocalVariable(parameter.Name, new() - { - RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)), - StartPc = 0, - }); + funcContext.Scope.AddLocalVariable(parameter.Name, new() { RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)), StartPc = 0, }); funcContext.Scope.StackPosition++; } @@ -749,10 +730,7 @@ public bool VisitDoStatementNode(DoStatementNode node, ScopeCompilationContext c public bool VisitBreakStatementNode(BreakStatementNode node, ScopeCompilationContext context) { - context.Function.AddUnresolvedBreak(new() - { - Index = context.Function.Instructions.Length - }, node.Position); + context.Function.AddUnresolvedBreak(new() { Index = context.Function.Instructions.Length }, node.Position); context.PushInstruction(Instruction.Jmp(0, 0), node.Position); return true; @@ -921,30 +899,14 @@ public 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 index)".AsMemory(), new() { RegisterIndex = startPosition, StartPc = context.Function.Instructions.Length, }); - scopeContext.AddLocalVariable("(for limit)".AsMemory(), new() - { - RegisterIndex = (byte)(startPosition + 1), - StartPc = context.Function.Instructions.Length, - }); + scopeContext.AddLocalVariable("(for limit)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 1), StartPc = context.Function.Instructions.Length, }); - scopeContext.AddLocalVariable("(for step)".AsMemory(), new() - { - RegisterIndex = (byte)(startPosition + 2), - StartPc = context.Function.Instructions.Length, - }); + scopeContext.AddLocalVariable("(for step)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 2), StartPc = context.Function.Instructions.Length, }); // add local variable - scopeContext.AddLocalVariable(node.VariableName, new() - { - RegisterIndex = (byte)(startPosition + 3), - StartPc = context.Function.Instructions.Length, - }); + scopeContext.AddLocalVariable(node.VariableName, new() { RegisterIndex = (byte)(startPosition + 3), StartPc = context.Function.Instructions.Length, }); foreach (var childNode in node.StatementNodes) { @@ -984,33 +946,17 @@ public 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 generator)".AsMemory(), new() { RegisterIndex = (byte)(startPosition), StartPc = context.Function.Instructions.Length, }); - scopeContext.AddLocalVariable("(for state)".AsMemory(), new() - { - RegisterIndex = (byte)(startPosition + 1), - StartPc = context.Function.Instructions.Length, - }); + scopeContext.AddLocalVariable("(for state)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 1), StartPc = context.Function.Instructions.Length, }); - scopeContext.AddLocalVariable("(for control)".AsMemory(), new() - { - RegisterIndex = (byte)(startPosition + 2), - StartPc = context.Function.Instructions.Length, - }); + scopeContext.AddLocalVariable("(for control)".AsMemory(), new() { RegisterIndex = (byte)(startPosition + 2), StartPc = context.Function.Instructions.Length, }); // add local variables for (int i = 0; i < node.Names.Length; i++) { var name = node.Names[i]; - scopeContext.AddLocalVariable(name.Name, new() - { - RegisterIndex = (byte)(startPosition + 3 + i), - StartPc = context.Function.Instructions.Length, - }); + scopeContext.AddLocalVariable(name.Name, new() { RegisterIndex = (byte)(startPosition + 3 + i), StartPc = context.Function.Instructions.Length, }); } foreach (var childNode in node.StatementNodes) @@ -1037,12 +983,7 @@ public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeComp public bool VisitLabelStatementNode(LabelStatementNode node, ScopeCompilationContext context) { - var desc = new LabelDescription() - { - Name = node.Name, - Index = context.Function.Instructions.Length, - RegisterIndex = context.StackPosition - }; + var desc = new LabelDescription() { Name = node.Name, Index = context.Function.Instructions.Length, RegisterIndex = context.StackPosition }; context.AddLabel(desc); context.Function.ResolveGoto(desc); @@ -1058,11 +999,7 @@ public bool VisitGotoStatementNode(GotoStatementNode node, ScopeCompilationConte } else { - context.Function.AddUnresolvedGoto(new() - { - Name = node.Name, - JumpInstructionIndex = context.Function.Instructions.Length - }); + context.Function.AddUnresolvedGoto(new() { Name = node.Name, JumpInstructionIndex = context.Function.Instructions.Length }); // add uninitialized jmp instruction context.PushInstruction(Instruction.Jmp(0, 0), node.Position); diff --git a/src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs b/src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs index b1aca763..547bd6f3 100644 --- a/src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs +++ b/src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs @@ -6,4 +6,4 @@ public override TResult Accept(ISyntaxNodeVisitor (Value & 2) == 2; diff --git a/src/Lua/Internal/BitFlags256.cs b/src/Lua/Internal/BitFlags256.cs index 0e3a3176..3d05d740 100644 --- a/src/Lua/Internal/BitFlags256.cs +++ b/src/Lua/Internal/BitFlags256.cs @@ -3,7 +3,7 @@ namespace Lua.Internal; internal unsafe struct BitFlags256 { internal fixed long Data[4]; - + public bool this[int index] { get => (Data[index >> 6] & (1L << (index & 63))) != 0; @@ -19,5 +19,6 @@ public bool this[int index] } } } + public void Set(int index) => Data[index >> 6] |= 1L << (index & 63); } \ No newline at end of file diff --git a/src/Lua/Internal/LuaResult.cs b/src/Lua/Internal/LuaResult.cs new file mode 100644 index 00000000..15ad55a1 --- /dev/null +++ b/src/Lua/Internal/LuaResult.cs @@ -0,0 +1,26 @@ +using Lua.Runtime; + +namespace Lua.Internal; + +public readonly struct LuaResult : IDisposable +{ + readonly LuaStack stack; + readonly int returnBase; + + internal LuaResult(LuaStack stack, int returnBase) + { + this.stack = stack; + this.returnBase = returnBase; + } + + public int Count => stack.Count - returnBase; + public int Length => stack.Count - returnBase; + public ReadOnlySpan 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/LuaCoroutine.cs b/src/Lua/LuaCoroutine.cs index 55d8e262..c43feaa6 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) { @@ -219,10 +199,7 @@ public override async ValueTask YieldAsync(LuaFunctionExecutionContext cont throw new LuaRuntimeException(context.State.GetTraceback(), "attempt to yield across a C#-call boundary"); } - resume.SetResult(new() - { - Results = context.Arguments.ToArray(), - }); + resume.SetResult(new() { Results = context.Arguments.ToArray(), }); Volatile.Write(ref status, (byte)LuaThreadStatus.Suspended); @@ -240,12 +217,7 @@ public 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 3476277c..3a75cbe2 100644 --- a/src/Lua/LuaFunction.cs +++ b/src/Lua/LuaFunction.cs @@ -1,37 +1,28 @@ -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 LuaClosure closure ? Math.Max(context.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, - Function = this, - }; - + var frame = new CallStackFrame { Base = context.FrameBase, VariableArgumentCount = this.GetVariableArgumentCount(context.ArgumentCount), Function = this, ReturnBase = context.ReturnFrameBase }; context.Thread.PushCallStackFrame(frame); - - try { if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook) { - return await LuaVirtualMachine.ExecuteCallHook(context, buffer, cancellationToken); + return await LuaVirtualMachine.ExecuteCallHook(context, cancellationToken); } - return await Func(context, buffer, cancellationToken); + return await Func(context, cancellationToken); } finally { diff --git a/src/Lua/LuaFunctionExecutionContext.cs b/src/Lua/LuaFunctionExecutionContext.cs index 9b763831..4299c339 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 CSharpClosure? GetCsClosure() { return Thread.GetCurrentFrame().Function as CSharpClosure; diff --git a/src/Lua/LuaFunctionExtensions.cs b/src/Lua/LuaFunctionExtensions.cs index 7027164f..a2c82ac5 100644 --- a/src/Lua/LuaFunctionExtensions.cs +++ b/src/Lua/LuaFunctionExtensions.cs @@ -6,11 +6,9 @@ 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; - + for (int i = 0; i < arguments.Length; i++) { thread.Stack.Push(arguments[i]); @@ -22,8 +20,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..b4d4e184 100644 --- a/src/Lua/LuaMainThread.cs +++ b/src/Lua/LuaMainThread.cs @@ -12,15 +12,13 @@ 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"); } -} +} \ No newline at end of file diff --git a/src/Lua/LuaState.cs b/src/Lua/LuaState.cs index 69b9c073..d2f341f5 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 LuaClosure(this, chunk); - return await closure.InvokeAsync(new() + await closure.InvokeAsync(new() { State = this, Thread = CurrentThread, ArgumentCount = 0, FrameBase = 0, + ReturnFrameBase = 0, SourcePosition = null, RootChunkName = chunk.Name, ChunkName = chunk.Name, - }, buffer, cancellationToken); + }, cancellationToken); + + return new LuaResult(CurrentThread.Stack, 0); } finally { @@ -93,36 +98,7 @@ public void Push(LuaValue value) public Traceback GetTraceback() { - if (threadStack.Count == 0) - { - return new(this) - { - RootFunc = (LuaClosure)MainThread.GetCallStackFrames()[0].Function, - StackFrames = MainThread.GetCallStackFrames()[1..] - .ToArray() - }; - } - - using var list = new PooledList(8); - foreach (var frame in MainThread.GetCallStackFrames()[1..]) - { - list.Add(frame); - } - - foreach (var thread in threadStack.AsSpan()) - { - if (thread.CallStack.Count == 0) continue; - foreach (var frame in thread.GetCallStackFrames()[1..]) - { - list.Add(frame); - } - } - - return new(this) - { - RootFunc = (LuaClosure)MainThread.GetCallStackFrames()[0].Function, - StackFrames = list.AsSpan().ToArray() - }; + return GetTraceback(CurrentThread); } internal Traceback GetTraceback(LuaThread thread) @@ -132,6 +108,7 @@ internal Traceback GetTraceback(LuaThread thread) { list.Add(frame); } + LuaClosure rootFunc; if (thread.GetCallStackFrames()[0].Function is LuaClosure 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..04dee82d 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,15 @@ 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); + return func.InvokeAsync(context with { ArgumentCount = 1, FrameBase = stack.Count - 1, ReturnFrameBase = stack.Count - 1, }, cancellationToken); } else { - buffer.Span[0] = ToString(); - return new(1); + context.Thread.Stack.Push(ToString()); + return default; } } } \ No newline at end of file diff --git a/src/Lua/Runtime/CSharpClosure.cs b/src/Lua/Runtime/CSharpClosure.cs index ff32c9f3..c08535ba 100644 --- a/src/Lua/Runtime/CSharpClosure.cs +++ b/src/Lua/Runtime/CSharpClosure.cs @@ -1,6 +1,6 @@ namespace Lua.Runtime; -public sealed class CSharpClosure(string name,LuaValue[] upValues,Func, CancellationToken, ValueTask> func) : LuaFunction(name, func) +public sealed class CSharpClosure(string name, LuaValue[] upValues, Func> func) : LuaFunction(name, func) { - public readonly LuaValue[] UpValues = upValues; + public readonly LuaValue[] UpValues = upValues; } \ 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/LuaClosure.cs b/src/Lua/Runtime/LuaClosure.cs index 80d51cc0..20f48226 100644 --- a/src/Lua/Runtime/LuaClosure.cs +++ b/src/Lua/Runtime/LuaClosure.cs @@ -9,7 +9,7 @@ public sealed class LuaClosure : LuaFunction FastListCore upValues; public LuaClosure(LuaState state, Chunk proto, LuaTable? environment = null) - : base(proto.Name, (context, buffer, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, buffer, ct)) + : base(proto.Name, (context, ct) => LuaVirtualMachine.ExecuteClosureAsync(context.State, ct)) { this.proto = proto; diff --git a/src/Lua/Runtime/LuaStack.cs b/src/Lua/Runtime/LuaStack.cs index 8ac48ae7..ea584db0 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 a782d5c2..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 LuaClosure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + ReturnBase = funcContext.ReturnFrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount), Function = hook, CallerInstructionIndex = context.Pc, }; frame.Flags |= CallStackFrameFlags.InHook; context.Thread.IsInHook = true; context.Thread.PushCallStackFrame(frame); - await hook.Func(funcContext, Memory.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 LuaClosure closure ? Math.Max(funcContext.ArgumentCount - closure.Proto.ParameterCount, 0) : 0, + ReturnBase = funcContext.ReturnFrameBase, + VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount), Function = hook, CallerInstructionIndex = pc, }; frame.Flags |= CallStackFrameFlags.InHook; context.Thread.IsInHook = true; context.Thread.PushCallStackFrame(frame); - await hook.Func(funcContext, Memory.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 95ce2c36..78e09169 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 LuaClosure LuaClosure = (LuaClosure)frame.Function; - public readonly LuaValue[] ResultsBuffer = resultsBuffer; - public readonly Memory Buffer = buffer; public readonly LuaThread Thread = thread; public Chunk Chunk => LuaClosure.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]; LuaClosure = 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; } @@ -96,49 +105,47 @@ public bool PopFromBuffer(Span result) switch (opCode) { case OpCode.Call: - { - var c = callInstruction.C; - if (c != 0) { - targetCount = c - 1; - } + var c = callInstruction.C; + if (c != 0) + { + targetCount = c - 1; + } - break; - } + break; + } case OpCode.TForCall: target += 3; targetCount = callInstruction.C; 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,8 +153,9 @@ public bool PopFromBuffer(Span result) public void Push(in CallStackFrame frame) { Pc = -1; - LuaClosure = (frame.Function as LuaClosure)!; + LuaClosure = (LuaClosure)(frame.Function); FrameBase = frame.Base; + CurrentReturnFrameBase = frame.ReturnBase; VariableArgumentCount = frame.VariableArgumentCount; } @@ -164,42 +172,18 @@ 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(); - } - - 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 +192,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 +244,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(); @@ -302,50 +283,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.LuaClosure.GetUpValue(instruction.B); + stack.GetWithNotifyTop(iA + frameBase) = context.LuaClosure.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.LuaClosure.GetUpValueRef(instruction.B)); @@ -359,7 +334,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)) @@ -371,7 +346,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } } - var table = context.LuaClosure.GetUpValue(instruction.A); + var table = opCode == OpCode.SetTabUp ? context.LuaClosure.GetUpValue(iA) : Unsafe.Add(ref stackHead, iA); if (table.TryReadTable(out luaTable)) { @@ -391,52 +366,14 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } return true; - case OpCode.SetUpVal: - instruction = instructionRef; - context.LuaClosure.SetUpValue(instruction.B, stack.FastGet(instruction.A + frameBase)); + context.LuaClosure.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); @@ -453,164 +390,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; - 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; @@ -618,8 +448,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); @@ -631,7 +459,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; @@ -639,30 +467,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; @@ -678,9 +500,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); @@ -688,8 +509,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); @@ -703,7 +522,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; @@ -711,52 +530,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++; @@ -765,9 +546,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++; @@ -776,7 +558,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; @@ -784,15 +566,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)) { @@ -800,7 +580,7 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) } else { - stack.GetWithNotifyTop(instruction.A + frameBase) = vb; + stack.GetWithNotifyTop(iA + frameBase) = vb; } continue; @@ -808,7 +588,11 @@ static bool MoveNext(ref VirtualMachineExecutionContext context) case OpCode.Call: if (Call(ref context, out doRestart)) { - if (doRestart) goto Restart; + if (doRestart) + { + goto Restart; + } + continue; } @@ -823,44 +607,32 @@ 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) - { - retCount = stack.Count - (ra1 - 1); - } - - if (0 < retCount) + if (context.Pop(instruction, frameBase)) { - stack.GetBuffer().Slice(ra1 - 1, retCount).CopyTo(context.Buffer.Span); + goto Restart; } - context.ResultCount = retCount; 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)) { @@ -881,8 +653,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)) @@ -891,13 +663,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) { @@ -910,32 +678,37 @@ 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 LuaClosure(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: @@ -946,13 +719,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 +749,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 +758,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) @@ -1026,15 +796,15 @@ 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) { var instruction = context.Instruction; var RA = instruction.A + context.FrameBase; - var va = context.Stack.Get(RA); var newBase = RA + 1; + var va = context.Stack.Get(RA); bool isMetamethod = false; if (!va.TryReadFunction(out var func)) { @@ -1053,13 +823,13 @@ static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart) var thread = context.Thread; var (argumentCount, variableArgumentCount) = PrepareForFunctionCall(thread, func, instruction, newBase, isMetamethod); newBase += variableArgumentCount; - 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; } @@ -1080,46 +850,27 @@ 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); } + context.Thread.PopCallStackFrame(); return true; } } @@ -1127,35 +878,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); } } @@ -1189,25 +921,25 @@ static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRest var (argumentCount, variableArgumentCount) = PrepareForFunctionTailCall(thread, func, instruction, newBase, isMetamethod); newBase = context.FrameBase + variableArgumentCount; - 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 LuaClosure) { + context.Push(newFrame); doRestart = true; return true; } @@ -1222,18 +954,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; } @@ -1282,8 +1010,16 @@ static bool TForCall(ref VirtualMachineExecutionContext context, out bool doRest newBase += variableArgumentCount; } - var newFrame = iterator.CreateNewFrame(ref context, newBase, variableArgumentCount); + var newFrame = iterator.CreateNewFrame(ref context, newBase, RA + 3, variableArgumentCount); 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 LuaClosure) { context.Push(newFrame); @@ -1292,7 +1028,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; @@ -1302,7 +1037,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; @@ -1313,19 +1048,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) @@ -1349,15 +1072,15 @@ 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++; } - context.ClearResultsBuffer(); + results.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1408,7 +1131,7 @@ static bool GetTableValueSlowPath(LuaValue table, LuaValue key, ref VirtualMachi } table = metatableValue; - Function: + Function: if (table.TryReadFunction(out var function)) { return CallGetTableFunc(targetTable, function, key, ref context, out value, out doRestart); @@ -1431,7 +1154,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; @@ -1456,10 +1179,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; } @@ -1506,7 +1229,7 @@ static bool SetTableValueSlowPath(LuaValue table, LuaValue key, LuaValue value, table = metatableValue; - Function: + Function: if (table.TryReadFunction(out var function)) { context.PostOperation = PostOperationType.Nop; @@ -1532,7 +1255,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; } @@ -1552,21 +1275,17 @@ 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; } [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)) @@ -1580,13 +1299,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; } @@ -1608,11 +1327,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; } @@ -1622,8 +1344,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)) @@ -1640,7 +1363,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; } @@ -1662,15 +1385,15 @@ 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; } - 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; @@ -1683,8 +1406,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: @@ -1705,10 +1429,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 LuaClosure) { context.Push(newFrame); @@ -1725,21 +1450,21 @@ 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; } - if (name == Metamethods.Le) + if (opCode == OpCode.Le) { reverseLe = true; name = Metamethods.Lt; @@ -1747,7 +1472,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, goto ReCheck; } - if (description != null) + if (opCode != OpCode.Eq) { if (reverseLe) { @@ -1770,7 +1495,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, // If there are variable arguments, the base of the stack is moved by that number and the values of the variable arguments are placed in front of it. // see: https://wubingzheng.github.io/build-lua-in-rust/en/ch08-02.arguments.html [MethodImpl(MethodImplOptions.NoInlining)] - static ( int ArgumentCount, int VariableArgumentCount) PrepareVariableArgument(LuaStack stack, int newBase, int argumentCount, + static (int ArgumentCount, int VariableArgumentCount) PrepareVariableArgument(LuaStack stack, int newBase, int argumentCount, int variableArgumentCount) { var temp = newBase; @@ -1787,7 +1512,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, [MethodImpl(MethodImplOptions.AggressiveInlining)] static (int ArgumentCount, int VariableArgumentCount) PrepareForFunctionCall(LuaThread thread, LuaFunction function, - Instruction instruction, int newBase, bool isMetaMethod) + Instruction instruction, int newBase, bool isMetamethod) { var argumentCount = instruction.B - 1; if (argumentCount == -1) @@ -1796,7 +1521,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, } else { - if (isMetaMethod) + if (isMetamethod) { argumentCount += 1; } @@ -1816,7 +1541,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, [MethodImpl(MethodImplOptions.AggressiveInlining)] static (int ArgumentCount, int VariableArgumentCount) PrepareForFunctionTailCall(LuaThread thread, LuaFunction function, - Instruction instruction, int newBase, bool isMetaMethod) + Instruction instruction, int newBase, bool isMetamethod) { var stack = thread.Stack; @@ -1827,7 +1552,7 @@ static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc, } else { - if (isMetaMethod) + if (isMetamethod) { argumentCount += 1; } @@ -1864,27 +1589,53 @@ static Traceback GetTracebacks(ref VirtualMachineExecutionContext context) static Traceback GetTracebacks(LuaState state, int pc) { var frame = state.CurrentThread.GetCurrentFrame(); - state.CurrentThread.PushCallStackFrame(frame with - { - CallerInstructionIndex = pc - }); + state.CurrentThread.PushCallStackFrame(frame with { 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) { @@ -1894,7 +1645,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/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 diff --git a/src/Lua/Runtime/OpCode.cs b/src/Lua/Runtime/OpCode.cs index b5766583..78c4afea 100644 --- a/src/Lua/Runtime/OpCode.cs +++ b/src/Lua/Runtime/OpCode.cs @@ -2,60 +2,61 @@ namespace Lua.Runtime; public enum OpCode : byte { - Move, // A B R(A) := R(B) - LoadK, // A Bx R(A) := Kst(Bx) - LoadKX, // A R(A) := Kst(extra arg) - LoadBool, // A B C R(A) := (Bool)B; if (C) pc++ - LoadNil, // A B R(A), R(A+1), ..., R(A+B) := nil + Move, // A B R(A) := R(B) + LoadK, // A Bx R(A) := Kst(Bx) + LoadKX, // A R(A) := Kst(extra arg) + LoadBool, // A B C R(A) := (Bool)B; if (C) pc++ + LoadNil, // A B R(A), R(A+1), ..., R(A+B) := nil - GetUpVal, // A B R(A) := UpValue[B] - GetTabUp, // A B C R(A) := UpValue[B][RK(C)] - GetTable, // A B C R(A) := R(B)[RK(C)] + GetUpVal, // A B R(A) := UpValue[B] + GetTabUp, // A B C R(A) := UpValue[B][RK(C)] + GetTable, // A B C R(A) := R(B)[RK(C)] - SetTabUp, // A B C UpValue[A][RK(B)] := RK(C) - SetUpVal, // A B UpValue[B] := R(A) - SetTable, // A B C R(A)[RK(B)] := RK(C) + SetTabUp, // A B C UpValue[A][RK(B)] := RK(C) + SetUpVal, // A B UpValue[B] := R(A) + SetTable, // A B C R(A)[RK(B)] := RK(C) - NewTable, // A B C R(A) := {} (size = B,C) + NewTable, // A B C R(A) := {} (size = B,C) - Self, // A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] + Self, // A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] - Add, // A B C R(A) := RK(B) + RK(C) - Sub, // A B C R(A) := RK(B) - RK(C) - Mul, // A B C R(A) := RK(B) * RK(C) - Div, // A B C R(A) := RK(B) / RK(C) - Mod, // A B C R(A) := RK(B) % RK(C) - Pow, // A B C R(A) := RK(B) ^ RK(C) - Unm, // A B R(A) := -R(B) - Not, // A B R(A) := not R(B) - Len, // A B R(A) := length of R(B) + Add, // A B C R(A) := RK(B) + RK(C) + Sub, // A B C R(A) := RK(B) - RK(C) + Mul, // A B C R(A) := RK(B) * RK(C) + Div, // A B C R(A) := RK(B) / RK(C) + Mod, // A B C R(A) := RK(B) % RK(C) + Pow, // A B C R(A) := RK(B) ^ RK(C) + Unm, // A B R(A) := -R(B) + Not, // A B R(A) := not R(B) + Len, // A B R(A) := length of R(B) - Concat, // A B C R(A) := R(B).. ... ..R(C) + Concat, // A B C R(A) := R(B).. ... ..R(C) - Jmp, // A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) - Eq, // A B C if ((RK(B) == RK(C)) ~= A) then pc++ - Lt, // A B C if ((RK(B) < RK(C)) ~= A) then pc++ - Le, // A B C if ((RK(B) <= RK(C)) ~= A) then pc++ + Jmp, // A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) + Eq, // A B C if ((RK(B) == RK(C)) ~= A) then pc++ + Lt, // A B C if ((RK(B) < RK(C)) ~= A) then pc++ + Le, // A B C if ((RK(B) <= RK(C)) ~= A) then pc++ - Test, // A C if not (R(A) <=> C) then pc++ - TestSet, // A B C if (R(B) <=> C) then R(A) := R(B) else pc++ + Test, // A C if not (R(A) <=> C) then pc++ + TestSet, // A B C if (R(B) <=> C) then R(A) := R(B) else pc++ - Call, // A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) - TailCall, // A B C return R(A)(R(A+1), ... ,R(A+B-1)) - Return, // A B return R(A), ... ,R(A+B-2) (see note) + Call, // A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) + TailCall, // A B C return R(A)(R(A+1), ... ,R(A+B-1)) + Return, // A B return R(A), ... ,R(A+B-2) (see note) - ForLoop, // A sBx R(A)+=R(A+2); - // if R(A) + 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 LuaClosure(context.State, chunk).InvokeAsync(context, buffer, cancellationToken); + return await new LuaClosure(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 LuaClosure(context.State, chunk, arg2); - return 1; + return context.Return(new LuaClosure(context.State, chunk, arg2)); } catch (Exception ex) { - buffer.Span[0] = LuaValue.Nil; - buffer.Span[1] = ex.Message; - return 2; + return context.Return(LuaValue.Nil, ex.Message); } } - public ValueTask 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 LuaClosure(context.State, chunk, arg3); - return new(1); + return new(context.Return(new LuaClosure(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,94 @@ 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 count = await arg0.InvokeAsync(context with { State = context.State, ArgumentCount = context.ArgumentCount - 1, FrameBase = context.FrameBase + 1, ReturnFrameBase = context.ReturnFrameBase + 1 }, cancellationToken); - var resultCount = await arg0.InvokeAsync(context with - { - State = context.State, - ArgumentCount = context.ArgumentCount - 1, - FrameBase = context.FrameBase + 1, - }, methodBuffer.AsMemory(), cancellationToken); - - buffer.Span[0] = true; - methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]); - - return resultCount + 1; + context.Thread.Stack.Get(context.ReturnFrameBase) = true; + return count + 1; } catch (Exception ex) { - buffer.Span[0] = false; if (ex is LuaRuntimeException { ErrorObject: not null } luaEx) { - buffer.Span[1] = luaEx.ErrorObject.Value; + return context.Return(false, luaEx.ErrorObject.Value); } else { - buffer.Span[1] = ex.Message; + return context.Return(false, ex.Message); } - - return 2; } } - public async ValueTask 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 +345,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 +358,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 +381,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 +432,7 @@ public ValueTask ToNumber(LuaFunctionExecutionContext context, Memory 2 && span[0] is '0' && span[1] is 'x' or 'X') @@ -500,13 +456,13 @@ public ValueTask ToNumber(LuaFunctionExecutionContext context, Memory text, int toBase) @@ -566,17 +522,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 +541,35 @@ 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 - { - State = context.State, - ArgumentCount = context.ArgumentCount - 2, - FrameBase = context.FrameBase + 2, - }, methodBuffer.AsMemory(), cancellationToken); - - buffer.Span[0] = true; - methodBuffer.AsSpan()[..resultCount].CopyTo(buffer.Span[1..]); + var count = await arg0.InvokeAsync(context with { State = context.State, ArgumentCount = context.ArgumentCount - 2, FrameBase = context.FrameBase + 2, ReturnFrameBase = context.ReturnFrameBase + 1 }, cancellationToken); - return resultCount + 1; + context.Thread.Stack.Get(context.ReturnFrameBase) = true; + return count + 1; } catch (Exception ex) { - methodBuffer.AsSpan().Clear(); var error = ex is LuaRuntimeException { ErrorObject: not null } luaEx ? luaEx.ErrorObject.Value : ex.Message; context.State.Push(error); // invoke error handler - await arg1.InvokeAsync(context with - { - State = context.State, - ArgumentCount = 1, - FrameBase = context.Thread.Stack.Count - 1, - }, methodBuffer.AsMemory(), cancellationToken); - - buffer.Span[0] = false; - buffer.Span[1] = methodBuffer[0]; - - - return 2; + var count = await arg1.InvokeAsync(context with { State = context.State, ArgumentCount = 1, FrameBase = context.Thread.Stack.Count - 1, ReturnFrameBase = context.ReturnFrameBase + 1 }, cancellationToken); + context.Thread.Stack.Get(context.ReturnFrameBase) = false; + return count + 1; } } } \ 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 ff470f48..cdb8d36f 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,73 @@ 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 CSharpClosure("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() + return new(context.Return(new CSharpClosure("wrap", [thread], + static async (context, cancellationToken) => { - Base = frameBase, - VariableArgumentCount = 0, - Function = coroutine.Function, - }); - try - { - var resultCount = await thread.ResumeAsync(context with + var thread = context.GetCsClosure()!.UpValues[0].Read(); + if (thread is not LuaCoroutine coroutine) { - ArgumentCount = context.ArgumentCount + 1, - FrameBase = frameBase, - }, buffer, cancellationToken); - - buffer.Span[1..].CopyTo(buffer.Span[0..]); - return resultCount - 1; - } - finally - { - context.Thread.PopCallStackFrame(); - } + return await thread.ResumeAsync(context, cancellationToken); + } - - }); + var stack = context.Thread.Stack; + var frameBase = stack.Count; - return new(1); + stack.Push(thread); + stack.PushRange(context.Arguments); + context.Thread.PushCallStackFrame(new() { Base = frameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = coroutine.Function }); + try + { + await thread.ResumeAsync(context with { ArgumentCount = context.ArgumentCount + 1, FrameBase = frameBase, ReturnFrameBase = context.ReturnFrameBase, }, cancellationToken); + var result = context.GetReturnBuffer(context.Thread.Stack.Count - context.ReturnFrameBase); + result[1..].CopyTo(result); + context.Thread.Stack.Pop(); + return result.Length - 1; + } + finally + { + context.Thread.PopCallStackFrame(); + } + }))); } - public ValueTask 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 b3c00774..524cb407 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,12 @@ 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 +247,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 +282,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 +298,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 +314,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 +330,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 LuaClosure closure) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } var upValues = closure.GetUpValuesSpan(); if (n1 <= 0 || n1 > upValues.Length) { - buffer.Span[0] = LuaValue.Nil; - return new(1); + return new(context.Return(LuaValue.Nil)); } - buffer.Span[0] = new LuaValue(upValues[n1 - 1]); - return new(1); + return new(context.Return(new LuaValue(upValues[n1 - 1]))); } - public ValueTask 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 +390,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,54 +464,49 @@ 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, - VariableArgumentCount = hook.GetVariableArgumentCount(2), - Function = hook, + Base = funcContext.FrameBase, ReturnBase = funcContext.ReturnFrameBase, VariableArgumentCount = hook.GetVariableArgumentCount(2), Function = hook, }; frame.Flags |= CallStackFrameFlags.InHook; thread.PushCallStackFrame(frame); try { thread.IsInHook = true; - await hook.Func(funcContext, Memory.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 +528,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 +578,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,12 @@ public class FileHandle : ILuaUserData "setvbuf" => SetVBufFunction!, "write" => WriteFunction!, _ => LuaValue.Nil, - }; + })); } else { - buffer.Span[0] = LuaValue.Nil; + return new(context.Return(LuaValue.Nil)); } - - return new(1); }); Stream stream; @@ -131,45 +129,37 @@ public 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 +167,25 @@ public void Close() : "*l"; - buffer.Span[0] = new CSharpClosure("iterator", [new (file),format],static (context, buffer, cancellationToken) => + return new(context.Return(new CSharpClosure("iterator", [new(file), format], static (context, cancellationToken) => { var upValues = context.GetCsClosure()!.UpValues.AsSpan(); var file = upValues[0].Read(); - var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], buffer, true); + context.Return(); + var resultCount = IOHelper.Read(context.State, file, "lines", 0, upValues[1..], context.Thread.Stack, true); return new(resultCount); - }); - - return new(1); + }))); }); - static readonly LuaFunction ReadFunction = new("read", (context, buffer, cancellationToken) => + static readonly LuaFunction ReadFunction = new("read", (context, cancellationToken) => { var file = context.GetArgument(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 +202,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); @@ -234,14 +220,14 @@ public void Close() 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 d0dd0864..77a6629b 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 CSharpClosure("iterator",[new (file)] ,static (context, buffer, ct) => + return new(context.Return(new CSharpClosure("iterator", [new(file)], static (context, ct) => { var file = context.GetCsClosure()!.UpValues[0].Read(); - 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 CSharpClosure("iterator", upValues, static (context, buffer, ct) => + return new(context.Return(new CSharpClosure("iterator", upValues, static (context, ct) => { var upValues = context.GetCsClosure()!.UpValues; var file = upValues[0].Read(); 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 7eb16567..d1ad5787 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,109 +179,85 @@ 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; - // When we call it without arguments, it returns a pseudo-random real number with uniform distribution in the interval [0,1 + if (context.ArgumentCount == 0) { - buffer.Span[0] = rand.NextDouble(); + return new(context.Return(rand.NextDouble())); } - // When we call it with only one argument, an integer n, it returns an integer pseudo-random number such that 1 <= x <= n. - // This is different from the C# random functions. - // See: https://www.lua.org/pil/18.html else if (context.ArgumentCount == 1) { - var arg0 = context.GetArgument(0); - if (arg0 < 0) - { - LuaRuntimeException.BadArgument(context.State.GetTraceback(), 0, "random"); - } - buffer.Span[0] = rand.Next(1, arg0 + 1); + var arg0 = context.GetArgument(0); + return new(context.Return(rand.NextDouble() * (arg0 - 1) + 1)); } - // Finally, we can call random with two integer arguments, l and u, to get a pseudo-random integer x such that l <= x <= u. else { - var arg0 = context.GetArgument(0); - var arg1 = context.GetArgument(1); - if (arg0 < 1 || arg1 <= arg0) - { - LuaRuntimeException.BadArgument(context.State.GetTraceback(), 1, "random"); - } - buffer.Span[0] = rand.Next(arg0, arg1 + 1); + var arg0 = context.GetArgument(0); + var arg1 = context.GetArgument(1); + 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 e93e1eb3..2d186a68 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 LuaClosure(context.State, chunk).InvokeAsync(context, cancellationToken); - using var methodBuffer = new PooledArray(1); - await new LuaClosure(context.State, chunk).InvokeAsync(context, methodBuffer.AsMemory(), cancellationToken); - - loadedTable = methodBuffer[0]; + loadedTable = context.Thread.Stack.Get(context.ReturnFrameBase); loaded[arg0] = loadedTable; } - buffer.Span[0] = loadedTable; - return 1; + return context.Return(loadedTable); } } \ No newline at end of file diff --git a/src/Lua/Standard/OpenLibsExtensions.cs b/src/Lua/Standard/OpenLibsExtensions.cs index e2f1f4c3..bb6829f4 100644 --- a/src/Lua/Standard/OpenLibsExtensions.cs +++ b/src/Lua/Standard/OpenLibsExtensions.cs @@ -40,16 +40,16 @@ public static void OpenCoroutineLibrary(this LuaState state) public static void OpenIOLibrary(this LuaState state) { - var io = new LuaTable(0, IOLibrary.Instance.Functions.Length); foreach (var func in IOLibrary.Instance.Functions) { io[func.Name] = func; } + io["stdio"] = new LuaValue(new FileHandle(ConsoleHelper.OpenStandardInput())); io["stdout"] = new LuaValue(new FileHandle(ConsoleHelper.OpenStandardOutput())); io["stderr"] = new LuaValue(new FileHandle(ConsoleHelper.OpenStandardError())); - + state.Environment["io"] = io; state.LoadedModules["io"] = io; } @@ -110,13 +110,11 @@ public static 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])); }); } @@ -131,7 +129,7 @@ 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); diff --git a/src/Lua/Standard/OperatingSystemLibrary.cs b/src/Lua/Standard/OperatingSystemLibrary.cs index 11dcb8dc..08e1287e 100644 --- a/src/Lua/Standard/OperatingSystemLibrary.cs +++ b/src/Lua/Standard/OperatingSystemLibrary.cs @@ -9,7 +9,8 @@ public sealed class OperatingSystemLibrary public OperatingSystemLibrary() { - Functions = [ + Functions = + [ new("clock", Clock), new("date", Date), new("difftime", DiffTime), @@ -26,13 +27,12 @@ public 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 +74,22 @@ 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 e92702c7..22f7fa22 100644 --- a/src/Lua/Standard/StringLibrary.cs +++ b/src/Lua/Standard/StringLibrary.cs @@ -12,7 +12,8 @@ public sealed class StringLibrary public StringLibrary() { - Functions = [ + Functions = + [ new("byte", Byte), new("char", Char), new("dump", Dump), @@ -31,7 +32,7 @@ public 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) @@ -45,20 +46,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); @@ -69,17 +70,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); @@ -101,16 +101,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)..]; @@ -120,14 +117,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 { @@ -137,22 +131,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; @@ -241,6 +232,7 @@ public async ValueTask Format(LuaFunctionExecutionContext context, Memory 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) @@ -314,13 +309,16 @@ public async ValueTask Format(LuaFunctionExecutionContext context, Memory().ToString(CultureInfo.InvariantCulture); 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': @@ -385,6 +383,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); @@ -433,7 +430,7 @@ public ValueTask GMatch(LuaFunctionExecutionContext context, Memory + return new(context.Return(new CSharpClosure("iterator", [new LuaValue(matches), 0], static (context, cancellationToken) => { var upValues = context.GetCsClosure()!.UpValues; var matches = upValues[0].Read(); @@ -444,32 +441,30 @@ 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); @@ -521,14 +516,10 @@ 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); - result = methodBuffer[0]; + await func.InvokeAsync(context with { ArgumentCount = match.Groups.Count, FrameBase = context.Thread.Stack.Count - context.ArgumentCount, }, cancellationToken); + + result = context.Thread.Stack.Get(context.ReturnFrameBase); } else { @@ -558,25 +549,22 @@ 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); @@ -598,22 +586,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); @@ -624,14 +610,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 49fb9e3a..1fa01099 100644 --- a/src/Lua/Standard/TableLibrary.cs +++ b/src/Lua/Standard/TableLibrary.cs @@ -8,11 +8,12 @@ namespace Lua.Standard; public sealed class TableLibrary { public static readonly TableLibrary Instance = new(); - + public TableLibrary() { - Functions = [ + Functions = + [ new("concat", Concat), new("insert", Insert), new("pack", Pack), @@ -30,25 +31,27 @@ public TableLibrary() Name = "comp", Functions = [], Constants = [], - Instructions = [ + Instructions = + [ Instruction.Le(1, 0, 1), Instruction.LoadBool(2, 1, 1), Instruction.LoadBool(2, 0, 0), Instruction.Return(2, 2), ], - SourcePositions = [ + SourcePositions = + [ default, default, default, default, ], ParameterCount = 2, UpValues = [], - Locals = [new LocalValueInfo(){Name = "a".AsMemory(),StartPc = 0,Index = 0,EndPc = 4}, new LocalValueInfo(){Name = "b".AsMemory(),StartPc = 0,Index = 1,EndPc = 4}], + Locals = [new LocalValueInfo() { Name = "a".AsMemory(), StartPc = 0, Index = 0, EndPc = 4 }, new LocalValueInfo() { Name = "b".AsMemory(), StartPc = 0, Index = 1, EndPc = 4 }], MaxStackPosition = 2, HasVariableArguments = false, LineDefined = 0, LastLineDefined = 0, }; - public ValueTask 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 +86,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 +111,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); @@ -121,13 +123,13 @@ public ValueTask Pack(LuaFunctionExecutionContext context, Memory { table[i + 1] = span[i]; } + 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,43 +144,35 @@ 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) ? context.GetArgument(1) : new LuaClosure(context.State, defaultComparer); - - context.Thread.PushCallStackFrame(new () - { - Base = context.FrameBase, - VariableArgumentCount = 0, - Function = arg1 - }); + + context.Thread.PushCallStackFrame(new() { Base = context.FrameBase, ReturnBase = context.ReturnFrameBase, VariableArgumentCount = 0, Function = arg1 }); try { await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken); - return 0; + return context.Return(); } finally { - context.Thread.PopCallStackFrameUnsafe(context.FrameBase); + context.Thread.PopCallStackFrameWithStackPop(); } } @@ -194,26 +188,24 @@ async ValueTask QuickSortAsync(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); - await comparer.InvokeAsync(context with - { - ArgumentCount = 2, - FrameBase = context.Thread.Stack.Count - context.ArgumentCount, - }, methodBuffer.AsMemory(), cancellationToken); + var stack = context.Thread.Stack; + var top = stack.Count; + stack.Push(memory.Span[j]); + stack.Push(pivot); + await comparer.InvokeAsync(context with { ArgumentCount = 2, FrameBase = stack.Count - context.ArgumentCount, ReturnFrameBase = top }, cancellationToken); - if (methodBuffer[0].ToBoolean()) + if (context.Thread.Stack.Get(top).ToBoolean()) { i++; Swap(memory.Span, i, j); } + + context.Thread.Stack.PopUntil(top); } Swap(memory.Span, i + 1, high); @@ -226,7 +218,7 @@ 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 +229,11 @@ public ValueTask Unpack(LuaFunctionExecutionContext context, Memory Date: Sat, 26 Apr 2025 21:31:29 +0900 Subject: [PATCH 02/89] Remove: LuaValueArrayPool --- src/Lua/Internal/LuaValueArrayPool.cs | 73 --------------------------- 1 file changed, 73 deletions(-) delete mode 100644 src/Lua/Internal/LuaValueArrayPool.cs 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 From ae7ac649d365b2e1c20d3da997e1b2036b175b8a Mon Sep 17 00:00:00 2001 From: Akeit0 <90429982+Akeit0@users.noreply.github.com> Date: Sun, 27 Apr 2025 00:44:51 +0900 Subject: [PATCH 03/89] Change: New lua compiler and API --- sandbox/Benchmark/InterpreterSteps.cs | 60 +- sandbox/ConsoleApp1/ConsoleApp1.csproj | 30 +- sandbox/ConsoleApp1/LVec3.cs | 5 +- sandbox/ConsoleApp1/Program.cs | 29 +- sandbox/ConsoleApp1/test.lua | 117 +- .../Utilities/CodeBuilder.cs | 2 + .../CodeAnalysis/Compilation/Declarements.cs | 120 ++ .../CodeAnalysis/Compilation/Descriptions.cs | 36 - src/Lua/CodeAnalysis/Compilation/Dump.cs | 486 +++++ src/Lua/CodeAnalysis/Compilation/Function.cs | 1616 +++++++++++++++++ .../Compilation/FunctionCompilationContext.cs | 499 ----- .../CodeAnalysis/Compilation/LuaCompiler.cs | 1287 ------------- src/Lua/CodeAnalysis/Compilation/Parser.cs | 1026 +++++++++++ .../Compilation/PrototypeBuilder.cs | 77 + src/Lua/CodeAnalysis/Compilation/Scanner.cs | 835 +++++++++ .../Compilation/ScopeCompilationContext.cs | 188 -- src/Lua/CodeAnalysis/Compilation/Token.cs | 17 + src/Lua/CodeAnalysis/LocalVariable.cs | 7 + src/Lua/CodeAnalysis/SourcePosition.cs | 14 - .../Syntax/DisplayStringSyntaxVisitor.cs | 555 ------ .../CodeAnalysis/Syntax/ISyntaxNodeVisitor.cs | 40 - src/Lua/CodeAnalysis/Syntax/Keywords.cs | 61 - src/Lua/CodeAnalysis/Syntax/Lexer.cs | 543 ------ src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs | 30 - .../Syntax/Nodes/AssignmentStatementNode.cs | 9 - .../Syntax/Nodes/BinaryExpressionNode.cs | 54 - .../Syntax/Nodes/BooleanLiteralNode.cs | 9 - .../Syntax/Nodes/BreakStatementNode.cs | 9 - .../Nodes/CallFunctionExpressionNode.cs | 9 - .../Syntax/Nodes/CallFunctionStatementNode.cs | 9 - .../Nodes/CallTableMethodExpressionNode.cs | 9 - .../Nodes/CallTableMethodStatementNode.cs | 9 - .../Syntax/Nodes/DoStatementNode.cs | 9 - .../Syntax/Nodes/ExpressionNode.cs | 3 - .../FunctionDeclarationExpressionNode.cs | 9 - .../Nodes/FunctionDeclarationStatementNode.cs | 9 - .../Syntax/Nodes/GenericForStatementNode.cs | 9 - .../Syntax/Nodes/GotoStatementNode.cs | 9 - .../Syntax/Nodes/GroupedExpressionNode.cs | 9 - .../Syntax/Nodes/IdentifierNode.cs | 9 - .../Syntax/Nodes/IfStatementNode.cs | 16 - .../Syntax/Nodes/LabelStatementNode.cs | 9 - .../Nodes/LocalAssignmentStatementNode.cs | 9 - .../Nodes/LocalFunctionDeclarationNode.cs | 9 - .../Syntax/Nodes/NilLiteralNode.cs | 9 - .../Syntax/Nodes/NumericForStatementNode.cs | 9 - .../Syntax/Nodes/NumericLiteralNode.cs | 9 - .../Syntax/Nodes/RepeatStatementNode.cs | 9 - .../Syntax/Nodes/ReturnStatementNode.cs | 9 - .../Syntax/Nodes/StatementNode.cs | 3 - .../Syntax/Nodes/StringLiteralNode.cs | 9 - .../Nodes/TableConstructorExpressionNode.cs | 14 - .../Nodes/TableIndexerAccessExpressionNode.cs | 9 - .../Nodes/TableMemberAccessExpressionNode.cs | 9 - .../TableMethodDeclarationStatementNode.cs | 9 - .../Syntax/Nodes/UnaryExpressionNode.cs | 30 - .../Nodes/VariableArgumentsExpressionNode.cs | 9 - .../Syntax/Nodes/WhileStatementNode.cs | 9 - .../CodeAnalysis/Syntax/OperatorPrecedence.cs | 49 - src/Lua/CodeAnalysis/Syntax/Parser.cs | 1037 ----------- src/Lua/CodeAnalysis/Syntax/SyntaxNode.cs | 6 - src/Lua/CodeAnalysis/Syntax/SyntaxToken.cs | 364 ---- .../Syntax/SyntaxTokenEnumerator.cs | 60 - src/Lua/CodeAnalysis/UpValueDesc.cs | 8 + src/Lua/Exceptions.cs | 95 +- src/Lua/Internal/BitFlags256.cs | 24 - src/Lua/Internal/CancellationExtensions.cs | 1 - src/Lua/Internal/Constants.cs | 12 + src/Lua/Internal/FarmHash.cs | 27 +- src/Lua/Internal/FastListCore.cs | 14 +- src/Lua/Internal/FastStackCore.cs | 1 + src/Lua/Internal/LuaDebug.cs | 65 +- src/Lua/Internal/Pool.cs | 67 + src/Lua/Internal/PooledList.cs | 7 +- src/Lua/Internal/StringHelper.cs | 3 + src/Lua/Loaders/CompositeModuleLoader.cs | 31 +- src/Lua/Loaders/FileModuleLoader.cs | 1 - src/Lua/LuaCoroutine.cs | 2 +- src/Lua/LuaFunctionExecutionContext.cs | 3 +- src/Lua/LuaState.cs | 51 +- src/Lua/LuaStateExtensions.cs | 30 +- src/Lua/LuaTable.cs | 6 +- src/Lua/Runtime/Chunk.cs | 30 - src/Lua/Runtime/Instruction.cs | 515 ++++-- src/Lua/Runtime/LuaClosure.cs | 36 +- src/Lua/Runtime/LuaValueRuntimeExtensions.cs | 4 +- src/Lua/Runtime/LuaVirtualMachine.Debug.cs | 15 +- src/Lua/Runtime/LuaVirtualMachine.cs | 371 ++-- src/Lua/Runtime/Prototype.cs | 31 + src/Lua/Runtime/Tracebacks.cs | 47 +- src/Lua/Standard/BasicLibrary.cs | 36 +- src/Lua/Standard/DebugLibrary.cs | 28 +- src/Lua/Standard/Internal/ConsoleHelper.cs | 6 +- .../Standard/Internal/LuaPlatformUtility.cs | 7 +- src/Lua/Standard/ModuleLibrary.cs | 3 +- src/Lua/Standard/TableLibrary.cs | 26 +- tests/Lua.Tests/LexerTests.cs | 241 --- tests/Lua.Tests/LuaTests.cs | 81 +- tests/Lua.Tests/OperatorTests.cs | 2 +- tests/Lua.Tests/ParserTests.cs | 29 - tests/Lua.Tests/StringTests.cs | 3 +- 101 files changed, 5277 insertions(+), 6250 deletions(-) create mode 100644 src/Lua/CodeAnalysis/Compilation/Declarements.cs delete mode 100644 src/Lua/CodeAnalysis/Compilation/Descriptions.cs create mode 100644 src/Lua/CodeAnalysis/Compilation/Dump.cs create mode 100644 src/Lua/CodeAnalysis/Compilation/Function.cs delete mode 100644 src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs delete mode 100644 src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs create mode 100644 src/Lua/CodeAnalysis/Compilation/Parser.cs create mode 100644 src/Lua/CodeAnalysis/Compilation/PrototypeBuilder.cs create mode 100644 src/Lua/CodeAnalysis/Compilation/Scanner.cs delete mode 100644 src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs create mode 100644 src/Lua/CodeAnalysis/Compilation/Token.cs create mode 100644 src/Lua/CodeAnalysis/LocalVariable.cs delete mode 100644 src/Lua/CodeAnalysis/SourcePosition.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/DisplayStringSyntaxVisitor.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/ISyntaxNodeVisitor.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Keywords.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Lexer.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/LuaSyntaxTree.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/AssignmentStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/BinaryExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/BooleanLiteralNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/BreakStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/CallFunctionExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/CallFunctionStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/CallTableMethodExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/CallTableMethodStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/DoStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/ExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/FunctionDeclarationStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/GenericForStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/GotoStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/GroupedExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/IdentifierNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/IfStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/LabelStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/LocalAssignmentStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/LocalFunctionDeclarationNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/NilLiteralNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/NumericForStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/NumericLiteralNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/RepeatStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/ReturnStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/StatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/StringLiteralNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/TableConstructorExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/TableIndexerAccessExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/TableMemberAccessExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/TableMethodDeclarationStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/UnaryExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/VariableArgumentsExpressionNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Nodes/WhileStatementNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/OperatorPrecedence.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/Parser.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/SyntaxNode.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/SyntaxToken.cs delete mode 100644 src/Lua/CodeAnalysis/Syntax/SyntaxTokenEnumerator.cs create mode 100644 src/Lua/CodeAnalysis/UpValueDesc.cs delete mode 100644 src/Lua/Internal/BitFlags256.cs create mode 100644 src/Lua/Internal/Constants.cs create mode 100644 src/Lua/Internal/Pool.cs delete mode 100644 src/Lua/Runtime/Chunk.cs create mode 100644 src/Lua/Runtime/Prototype.cs delete mode 100644 tests/Lua.Tests/LexerTests.cs delete mode 100644 tests/Lua.Tests/ParserTests.cs diff --git a/sandbox/Benchmark/InterpreterSteps.cs b/sandbox/Benchmark/InterpreterSteps.cs index 70f2de8e..e422d077 100644 --- a/sandbox/Benchmark/InterpreterSteps.cs +++ b/sandbox/Benchmark/InterpreterSteps.cs @@ -1,7 +1,5 @@ using BenchmarkDotNet.Attributes; using Lua; -using Lua.CodeAnalysis.Compilation; -using Lua.CodeAnalysis.Syntax; using Lua.Runtime; using Lua.Standard; @@ -10,38 +8,16 @@ public class InterpreterSteps { string sourceText = default!; LuaState state = default!; - SyntaxToken[] tokens = []; - LuaSyntaxTree ast = default!; - Chunk chunk = default!; - LuaValue[] results = new LuaValue[1]; + LuaClosure closure = default!; [GlobalSetup] public void GlobalSetup() { var filePath = FileHelper.GetAbsolutePath("n-body.lua"); sourceText = File.ReadAllText(filePath); - - var lexer = new Lexer - { - Source = sourceText.AsMemory() - }; - - var buffer = new List(); - while (lexer.MoveNext()) - { - buffer.Add(lexer.Current); - } - - tokens = buffer.ToArray(); - - var parser = new Parser(); - foreach (var token in tokens) - { - parser.Add(token); - } - - ast = parser.Parse(); - chunk = LuaCompiler.Default.Compile(ast); + state = LuaState.Create(); + state.OpenStandardLibraries(); + closure = state.Compile(sourceText,sourceText); } [IterationSetup] @@ -60,39 +36,17 @@ public void CreateState() LuaState.Create(); } - [Benchmark] - public void Lexer() - { - var lexer = new Lexer - { - Source = sourceText.AsMemory() - }; - - while (lexer.MoveNext()) { } - } - - [Benchmark] - public LuaSyntaxTree Parser() - { - var parser = new Parser(); - foreach (var token in tokens) - { - parser.Add(token); - } - - return parser.Parse(); - } [Benchmark] - public Chunk Compile() + public LuaClosure Compile() { - return LuaCompiler.Default.Compile(ast); + return state.Compile(sourceText, sourceText); } [Benchmark] public async ValueTask RunAsync() { - using (await state.RunAsync(chunk)) + using (await state.RunAsync(closure)) { } } diff --git a/sandbox/ConsoleApp1/ConsoleApp1.csproj b/sandbox/ConsoleApp1/ConsoleApp1.csproj index 8153f6a6..5f2e3c99 100644 --- a/sandbox/ConsoleApp1/ConsoleApp1.csproj +++ b/sandbox/ConsoleApp1/ConsoleApp1.csproj @@ -1,21 +1,21 @@  - - - - Analyzer - false - - + + + + Analyzer + false + + - - Exe - net8.0 - enable - enable + + Exe + net8.0 + enable + enable - false - Generated - + false + Generated + diff --git a/sandbox/ConsoleApp1/LVec3.cs b/sandbox/ConsoleApp1/LVec3.cs index 040ae42d..63aa7b49 100644 --- a/sandbox/ConsoleApp1/LVec3.cs +++ b/sandbox/ConsoleApp1/LVec3.cs @@ -30,10 +30,7 @@ public float Z [LuaMember("create")] public static LVec3 Create(float x, float y, float z) { - return new LVec3() - { - value = new Vector3(x, y, z) - }; + return new LVec3() { value = new Vector3(x, y, z) }; } public override string ToString() diff --git a/sandbox/ConsoleApp1/Program.cs b/sandbox/ConsoleApp1/Program.cs index b96173ae..91eb862e 100644 --- a/sandbox/ConsoleApp1/Program.cs +++ b/sandbox/ConsoleApp1/Program.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using Lua.CodeAnalysis.Syntax; using Lua.CodeAnalysis.Compilation; using Lua.Runtime; using Lua; @@ -14,20 +13,18 @@ { var source = File.ReadAllText(GetAbsolutePath("test.lua")); - var syntaxTree = LuaSyntaxTree.Parse(source, "test.lua"); Console.WriteLine("Source Code " + new string('-', 50)); - var debugger = new DisplayStringSyntaxVisitor(); - Console.WriteLine(debugger.GetDisplayString(syntaxTree)); + Console.WriteLine(source); - var chunk = LuaCompiler.Default.Compile(syntaxTree, "test.lua"); + var closure = state.Compile(source, "test.lua"); - DebugChunk(chunk, 0); + DebugChunk(closure.Proto, 0); Console.WriteLine("Output " + new string('-', 50)); - using var results = await state.RunAsync(chunk); + using var results = await state.RunAsync(closure); Console.WriteLine("Result " + new string('-', 50)); @@ -52,24 +49,24 @@ static string GetAbsolutePath(string relativePath, [CallerFilePath] string calle return Path.Combine(Path.GetDirectoryName(callerFilePath)!, relativePath); } -static void DebugChunk(Chunk chunk, int id) +static void DebugChunk(Prototype chunk, int id) { Console.WriteLine($"Chunk[{id}]" + new string('=', 50)); Console.WriteLine($"Parameters:{chunk.ParameterCount}"); - Console.WriteLine("Instructions " + new string('-', 50)); + Console.WriteLine("Code " + new string('-', 50)); var index = 0; - foreach (var inst in chunk.Instructions.ToArray()) + foreach (var inst in chunk.Code) { - Console.WriteLine($"[{index}]\t{chunk.SourcePositions[index]}\t\t{inst}"); + Console.WriteLine($"[{index}]\t{chunk.LineInfo[index]}\t\t{inst}"); index++; } - Console.WriteLine("Locals " + new string('-', 50)); + Console.WriteLine("LocalVariables " + new string('-', 50)); index = 0; - foreach (var local in chunk.Locals.ToArray()) + foreach (var local in chunk.LocalVariables) { - Console.WriteLine($"[{index}]\t{local.Index}\t{local.Name}\t{local.StartPc}\t{local.EndPc}"); + Console.WriteLine($"[{index}]\t{local.Name}\t{local.StartPc}\t{local.EndPc}"); index++; } @@ -85,14 +82,14 @@ static void DebugChunk(Chunk chunk, int id) index = 0; foreach (var upValue in chunk.UpValues.ToArray()) { - Console.WriteLine($"[{index}]\t{upValue.Name}\t{(upValue.IsInRegister ? 1 : 0)}\t{upValue.Index}"); + Console.WriteLine($"[{index}]\t{upValue.Name}\t{(upValue.IsLocal ? 1 : 0)}\t{upValue.Index}"); index++; } Console.WriteLine(); var nestedChunkId = 0; - foreach (var localChunk in chunk.Functions) + foreach (var localChunk in chunk.ChildPrototypes) { DebugChunk(localChunk, nestedChunkId); nestedChunkId++; diff --git a/sandbox/ConsoleApp1/test.lua b/sandbox/ConsoleApp1/test.lua index b708245b..03e6c592 100644 --- a/sandbox/ConsoleApp1/test.lua +++ b/sandbox/ConsoleApp1/test.lua @@ -1,115 +1,6 @@ -sun = {} -jupiter = {} -saturn = {} -uranus = {} -neptune = {} -local sqrt = math.sqrt - -local PI = 3.141592653589793 -local SOLAR_MASS = 4 * PI * PI -local DAYS_PER_YEAR = 365.24 -sun.x = 0.0 -sun.y = 0.0 -sun.z = 0.0 -sun.vx = 0.0 -sun.vy = 0.0 -sun.vz = 0.0 -sun.mass = SOLAR_MASS -jupiter.x = 4.84143144246472090e+00 -jupiter.y = -1.16032004402742839e+00 -jupiter.z = -1.03622044471123109e-01 -jupiter.vx = 1.66007664274403694e-03 * DAYS_PER_YEAR -jupiter.vy = 7.69901118419740425e-03 * DAYS_PER_YEAR -jupiter.vz = -6.90460016972063023e-05 * DAYS_PER_YEAR -jupiter.mass = 9.54791938424326609e-04 * SOLAR_MASS -saturn.x = 8.34336671824457987e+00 -saturn.y = 4.12479856412430479e+00 -saturn.z = -4.03523417114321381e-01 -saturn.vx = -2.76742510726862411e-03 * DAYS_PER_YEAR -saturn.vy = 4.99852801234917238e-03 * DAYS_PER_YEAR -saturn.vz = 2.30417297573763929e-05 * DAYS_PER_YEAR -saturn.mass = 2.85885980666130812e-04 * SOLAR_MASS -uranus.x = 1.28943695621391310e+01 -uranus.y = -1.51111514016986312e+01 -uranus.z = -2.23307578892655734e-01 -uranus.vx = 2.96460137564761618e-03 * DAYS_PER_YEAR -uranus.vy = 2.37847173959480950e-03 * DAYS_PER_YEAR -uranus.vz = -2.96589568540237556e-05 * DAYS_PER_YEAR -uranus.mass = 4.36624404335156298e-05 * SOLAR_MASS -neptune.x = 1.53796971148509165e+01 -neptune.y = -2.59193146099879641e+01 -neptune.z = 1.79258772950371181e-01 -neptune.vx = 2.68067772490389322e-03 * DAYS_PER_YEAR -neptune.vy = 1.62824170038242295e-03 * DAYS_PER_YEAR -neptune.vz = -9.51592254519715870e-05 * DAYS_PER_YEAR -neptune.mass = 5.15138902046611451e-05 * SOLAR_MASS - -local bodies = { sun, jupiter, saturn, uranus, neptune } - -local function advance(bodies, nbody, dt) - for i = 1, nbody do - local bi = bodies[i] - local bix, biy, biz, bimass = bi.x, bi.y, bi.z, bi.mass - local bivx, bivy, bivz = bi.vx, bi.vy, bi.vz - for j = i + 1, nbody do - local bj = bodies[j] - local dx, dy, dz = bix - bj.x, biy - bj.y, biz - bj.z - local dist2 = dx * dx + dy * dy + dz * dz - local mag = sqrt(dist2) - mag = dt / (mag * dist2) - local bm = bj.mass * mag - bivx = bivx - (dx * bm) - bivy = bivy - (dy * bm) - bivz = bivz - (dz * bm) - bm = bimass * mag - bj.vx = bj.vx + (dx * bm) - bj.vy = bj.vy + (dy * bm) - bj.vz = bj.vz + (dz * bm) - end - bi.vx = bivx - bi.vy = bivy - bi.vz = bivz - bi.x = bix + dt * bivx - bi.y = biy + dt * bivy - bi.z = biz + dt * bivz - end -end - -local function energy(bodies, nbody) - local e = 0 - for i = 1, nbody do - local bi = bodies[i] - local vx, vy, vz, bim = bi.vx, bi.vy, bi.vz, bi.mass - e = e + (0.5 * bim * (vx * vx + vy * vy + vz * vz)) - for j = i + 1, nbody do - local bj = bodies[j] - local dx, dy, dz = bi.x - bj.x, bi.y - bj.y, bi.z - bj.z - local distance = sqrt(dx * dx + dy * dy + dz * dz) - e = e - ((bim * bj.mass) / distance) - end - end - return e +function f(n, a, ...) + local b + n, b, a = n-1, ..., a + assert(b == ...) end - -local function offsetMomentum(b, nbody) - local px, py, pz = 0, 0, 0 - for i = 1, nbody do - local bi = b[i] - local bim = bi.mass - px = px + (bi.vx * bim) - py = py + (bi.vy * bim) - pz = pz + (bi.vz * bim) - end - b[1].vx = -px / SOLAR_MASS - b[1].vy = -py / SOLAR_MASS - b[1].vz = -pz / SOLAR_MASS -end - -local N = tonumber(arg and arg[1]) or 1000 -local nbody = #bodies - -offsetMomentum(bodies, nbody) -energy(bodies, nbody) -for i = 1, N do advance(bodies, nbody, 0.01) end -energy(bodies, nbody) diff --git a/src/Lua.SourceGenerator/Utilities/CodeBuilder.cs b/src/Lua.SourceGenerator/Utilities/CodeBuilder.cs index cae5f3bf..b3f663a7 100644 --- a/src/Lua.SourceGenerator/Utilities/CodeBuilder.cs +++ b/src/Lua.SourceGenerator/Utilities/CodeBuilder.cs @@ -82,9 +82,11 @@ public void AppendByteArrayString(byte[] bytes) { buffer.Append(", "); } + buffer.Append(x); first = false; } + buffer.Append(" }"); } diff --git a/src/Lua/CodeAnalysis/Compilation/Declarements.cs b/src/Lua/CodeAnalysis/Compilation/Declarements.cs new file mode 100644 index 00000000..0a023088 --- /dev/null +++ b/src/Lua/CodeAnalysis/Compilation/Declarements.cs @@ -0,0 +1,120 @@ +using Lua.Internal; +using System.Runtime.CompilerServices; + +namespace Lua.CodeAnalysis.Compilation; + + +unsafe struct TextReader (char* Ptr, int Length) +{ + public int Position; + + public (char, bool) Read() + { + if (Position >= Length) return ('\0', false); + return (Ptr[Position++], true); + } + + public bool TryRead(out char c) + { + if (Position >= Length) + { + c = '\0'; + return false; + } + + c = Ptr[Position++]; + return true; + } + + public char Current => Ptr[Position]; +} + +internal unsafe struct AssignmentTarget (ref AssignmentTarget previous, ExprDesc exprDesc) +{ + public readonly AssignmentTarget* Previous = (AssignmentTarget*)Unsafe.AsPointer(ref previous); + public ExprDesc Description = exprDesc; +} + +internal struct Label +{ + public string Name; + public int Pc, Line; + public int ActiveVariableCount; +} + + +internal class Block : IPoolNode +{ + public Block? Previous; + public int FirstLabel, FirstGoto; + public int ActiveVariableCount; + public bool HasUpValue, IsLoop; + Block(){} + ref Block? IPoolNode.NextNode => ref Previous; + + static LinkedPool pool; + + public static Block Get(Block? previous, int firstLabel, int firstGoto, int activeVariableCount, bool hasUpValue, bool isLoop) + { + if (!pool.TryPop(out var block)) + { + block = new Block(); + } + + block.Previous = previous; + block.FirstLabel = firstLabel; + block.FirstGoto = firstGoto; + block.ActiveVariableCount = activeVariableCount; + block.HasUpValue = hasUpValue; + block.IsLoop = isLoop; + + + return block; + } + + public void Release() + { + Previous = null; + pool.TryPush(this); + } +} + + +internal struct ExprDesc +{ + public Kind Kind; + public int Index; + public int Table; + public Kind TableType; + public int Info; + public int T, F; + public double Value; + public readonly bool HasJumps() => T != F; + + public readonly bool IsNumeral() => Kind == Kind.Number && T == Function.NoJump && F == Function.NoJump; + + public readonly bool IsVariable() => Kind.Local <= Kind && Kind <= Kind.Indexed; + + public readonly bool HasMultipleReturns() => Kind == Kind.Call || Kind == Kind.VarArg; +} + + + +internal enum Kind +{ + Void = 0, + Nil = 1, + True = 2, + False = 3, + Constant = 4, + Number = 5, + NonRelocatable = 6, + Local = 7, + UpValue = 8, + Indexed = 9, + Jump = 10, + Relocatable = 11, + Call = 12, + VarArg = 13 +} + diff --git a/src/Lua/CodeAnalysis/Compilation/Descriptions.cs b/src/Lua/CodeAnalysis/Compilation/Descriptions.cs deleted file mode 100644 index e3313e45..00000000 --- a/src/Lua/CodeAnalysis/Compilation/Descriptions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Lua.Internal; -using Lua.Runtime; - -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 - { - public required int Index { get; init; } - public required int? ReturnValueCount { get; init; } - public required Chunk Chunk { get; init; } - } - - public readonly record struct LabelDescription - { - public required ReadOnlyMemory Name { get; init; } - public required int Index { get; init; } - public required byte RegisterIndex { get; init; } - } - - public readonly record struct GotoDescription - { - public required ReadOnlyMemory Name { get; init; } - public required int JumpInstructionIndex { get; init; } - } - - public record struct BreakDescription - { - public required int Index { get; set; } - } -} \ No newline at end of file diff --git a/src/Lua/CodeAnalysis/Compilation/Dump.cs b/src/Lua/CodeAnalysis/Compilation/Dump.cs new file mode 100644 index 00000000..55689581 --- /dev/null +++ b/src/Lua/CodeAnalysis/Compilation/Dump.cs @@ -0,0 +1,486 @@ +using Lua.Internal; +using Lua.Runtime; +using System.Buffers; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Lua.CodeAnalysis.Compilation; + +[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal unsafe struct Header +{ + public static ReadOnlySpan LuaSignature => "\eLua"u8; + public static ReadOnlySpan LuaTail => [0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a]; + public fixed byte Signature[4]; + public byte Version, Format, Endianness, IntSize; + public byte PointerSize, InstructionSize; + public byte NumberSize, IntegralNumber; + public fixed byte Tail[6]; + + public const int Size = 18; + + public Header(bool isLittleEndian) + { + fixed (byte* signature = this.Signature) + { + LuaSignature.CopyTo(new(signature, 4)); + } + + Version = Constants.VersionMajor << 4 | Constants.VersionMinor; + Format = 0; + Endianness = (byte)(isLittleEndian ? 1 : 0); + IntSize = 4; + PointerSize = (byte)sizeof(IntPtr); + InstructionSize = 4; + NumberSize = 8; + IntegralNumber = 0; + fixed (byte* tail = this.Tail) + { + LuaTail.CopyTo(new(tail, 6)); + } + } + + public void Validate(ReadOnlySpan name) + { + fixed (byte* signature = this.Signature) + { + if (!LuaSignature.SequenceEqual(new(signature, 4))) + { + throw new Exception($"{name.ToString()}: is not a precompiled chunk"); + } + } + + var major = Version >> 4; + var minor = Version & 0xF; + if (major != Constants.VersionMajor || minor != Constants.VersionMinor) + { + throw new Exception($"{name.ToString()}: version mismatch in precompiled chunk {major}.{minor} != {Constants.VersionMajor}.{Constants.VersionMinor}"); + } + + if (IntSize != 4 || Format != 0 || IntegralNumber != 0 || PointerSize is not (4 or 8) || InstructionSize != 4 || NumberSize != 8) + { + goto ErrIncompatible; + } + + fixed (byte* tail = this.Tail) + { + if (!LuaTail.SequenceEqual(new(tail, 6))) + { + goto ErrIncompatible; + } + } + + return; + ErrIncompatible: + throw new Exception($"{name.ToString()}: incompatible precompiled chunk"); + } +} + +internal unsafe ref struct DumpState(IBufferWriter writer, bool reversedEndian) +{ + public readonly IBufferWriter Writer = writer; + Span UnWritten; + + void Write(ReadOnlySpan span) + { + var toWrite = span; + var remaining = UnWritten.Length; + if (span.Length > remaining) + { + span[..remaining].CopyTo(UnWritten); + Writer.Advance(remaining); + toWrite = span[remaining..]; + UnWritten = Writer.GetSpan(toWrite.Length); + } + + toWrite.CopyTo(UnWritten); + Writer.Advance(toWrite.Length); + UnWritten = UnWritten[toWrite.Length..]; + } + + public bool IsReversedEndian => reversedEndian; + + void DumpHeader() + { + var header = new Header(BitConverter.IsLittleEndian ^ IsReversedEndian); + Write(new(&header, Header.Size)); + } + + public void Dump(Prototype prototype) + { + if (UnWritten.Length == 0) + { + UnWritten = Writer.GetSpan(Header.Size + 32); + } + + DumpHeader(); + DumpFunction(prototype); + } + + + void DumpFunction(Prototype prototype) + { + WriteInt(prototype.LineDefined); //4 + WriteInt(prototype.LastLineDefined); //4 + WriteByte((byte)prototype.ParameterCount); //1 + WriteByte((byte)prototype.MaxStackSize); //1 + WriteByte((byte)(prototype.HasVariableArguments ? 1 : 0)); //1 + WriteIntSpanWithLength(MemoryMarshal.Cast(prototype.Code)); //4 + WriteConstants(prototype.Constants); //4 + WritePrototypes(prototype.ChildPrototypes); //4 + WriteUpValues(prototype.UpValues); //4 + + //Debug + WriteString(prototype.Source); + WriteIntSpanWithLength(prototype.LineInfo); + WriteLocalVariables(prototype.LocalVariables); + WriteInt(prototype.UpValues.Length); + foreach (var desc in prototype.UpValues) + { + WriteString(desc.Name); + } + } + + void WriteInt(int v) + { + if (reversedEndian) + { + v = BinaryPrimitives.ReverseEndianness(v); + } + + Write(new(&v, sizeof(int))); + } + + void WriteLong(long v) + { + if (reversedEndian) + { + v = BinaryPrimitives.ReverseEndianness(v); + } + + Write(new(&v, sizeof(long))); + } + + void WriteByte(byte v) + { + Write(new(&v, sizeof(byte))); + } + + void WriteDouble(double v) + { + long l = BitConverter.DoubleToInt64Bits(v); + WriteLong(l); + } + + + void WriteIntSpanWithLength(ReadOnlySpan v) + { + WriteInt(v.Length); + if (IsReversedEndian) + { + foreach (var i in v) + { + var reversed = BinaryPrimitives.ReverseEndianness(i); + Write(new(&reversed, 4)); + } + } + else + { + Write(MemoryMarshal.Cast(v)); + } + } + + void WriteBool(bool v) + { + WriteByte(v ? (byte)1 : (byte)0); + } + + void WriteString(string v) + { + var bytes = Encoding.UTF8.GetBytes(v); + var len = bytes.Length; + if (bytes.Length != 0) len++; + if (sizeof(IntPtr) == 8) WriteLong(len); + else WriteInt(len); + if (len != 0) + { + Write(bytes); + WriteByte(0); + } + } + + void WriteConstants(ReadOnlySpan constants) + { + WriteInt(constants.Length); + foreach (var c in constants) + { + WriteByte((byte)c.Type); + switch (c.Type) + { + case LuaValueType.Nil: break; + case LuaValueType.Boolean: + WriteBool(c.UnsafeReadDouble() != 0); + break; + case LuaValueType.Number: + WriteDouble(c.UnsafeReadDouble()); + break; + case LuaValueType.String: + WriteString(c.UnsafeRead()!); + break; + } + } + } + + void WritePrototypes(ReadOnlySpan prototypes) + { + WriteInt(prototypes.Length); + foreach (var p in prototypes) + { + DumpFunction(p); + } + } + + void WriteLocalVariables(ReadOnlySpan localVariables) + { + WriteInt(localVariables.Length); + foreach (var v in localVariables) + { + WriteString(v.Name); + WriteInt(v.StartPc); + WriteInt(v.EndPc); + } + } + + void WriteUpValues(ReadOnlySpan upValues) + { + WriteInt(upValues.Length); + foreach (var u in upValues) + { + WriteBool(u.IsLocal); + WriteByte((byte)u.Index); + } + } +} + +internal unsafe ref struct UnDumpState(ReadOnlySpan span,ReadOnlySpan name) +{ + public ReadOnlySpan Unread = span; + bool otherEndian; + int pointerSize; + readonly ReadOnlySpan name = name; + + void Throw(string why) => throw new Exception($"{name.ToString()}: {why} precompiled chunk"); + void ThrowTooShort() => Throw("truncate"); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Read(Span dst) + { + if (Unread.Length < dst.Length) ThrowTooShort(); + Unread[..dst.Length].CopyTo(dst); + + Unread = Unread[dst.Length..]; + } + + byte ReadByte() + { + if (0 < Unread.Length) + { + var b = Unread[0]; + Unread = Unread[1..]; + return b; + } + + ThrowTooShort(); + return 0; + } + + bool ReadBool() + { + if (0 < Unread.Length) + { + var b = Unread[0]; + Unread = Unread[1..]; + return b != 0; + } + + ThrowTooShort(); + + return false; + } + + int ReadInt() + { + var i = 0; + var span = new Span(&i, sizeof(int)); + Read(span); + + if (otherEndian) i = BinaryPrimitives.ReverseEndianness(i); + + return i; + } + + long ReadLong() + { + long i = 0; + var span = new Span(&i, sizeof(long)); + Read(span); + + if (otherEndian) i = BinaryPrimitives.ReverseEndianness(i); + return i; + } + + double ReadDouble() + { + long i = ReadLong(); + + return *(double*)(&i); + } + + + public Prototype UnDump() + { + Header h = default; + var span = new Span(&h, sizeof(Header)); + Read(span); + + h.Validate(name); + otherEndian = BitConverter.IsLittleEndian ^ (h.Endianness == 1); + pointerSize = h.PointerSize; + return UnDumpFunction(); + } + + + Prototype UnDumpFunction() + { + var lineDefined = ReadInt(); //4 + var lastLineDefined = ReadInt(); //4 + var parameterCount = ReadByte(); //1 + var maxStackSize = ReadByte(); //1 + var isVarArg = ReadByte() == 1; //1 + var codeLength = ReadInt(); + var code = new Instruction[codeLength]; + ReadInToIntSpan(MemoryMarshal.Cast(code)); + var constants = ReadConstants(); + var prototypes = ReadPrototypes(); + var upValues = ReadUpValues(); + + //Debug + var source = ReadString(); + var lineInfoLength = ReadInt(); + var lineInfo = new int[lineInfoLength]; + ReadInToIntSpan(lineInfo.AsSpan()); + var localVariables = ReadLocalVariables(); + + foreach (ref var desc in upValues.AsSpan()) + { + var name = ReadString(); + desc.Name = name; + } + + return new Prototype(source, lineDefined, lastLineDefined, parameterCount, maxStackSize, isVarArg, constants, code, prototypes, lineInfo, localVariables, upValues); + } + + + void ReadInToIntSpan(Span toWrite) + { + for (int i = 0; i < toWrite.Length; i++) + { + toWrite[i] = ReadInt(); + } + } + + + string ReadString() + { + var len = pointerSize == 4 ? ReadInt() : (int)ReadLong(); + if (len == 0) return ""; + len--; + var arrayPooled = ArrayPool.Shared.Rent(len); + try + { + var span = arrayPooled.AsSpan(0, len); + Read(span); + + var l = ReadByte(); + Debug.Assert(l == 0); + var str = Encoding.UTF8.GetString(span); + return str; + } + finally + { + ArrayPool.Shared.Return(arrayPooled); + } + } + + LuaValue[] ReadConstants() + { + var count = ReadInt(); + var constants = new LuaValue[count]; + for (int i = 0; i < count; i++) + { + var type = (LuaValueType)ReadByte(); + switch (type) + { + case LuaValueType.Nil: break; + case LuaValueType.Boolean: + constants[i] = (ReadByte() == 1); + break; + case LuaValueType.Number: + constants[i] = (ReadDouble()); + break; + case LuaValueType.String: + constants[i] = (ReadString()); + break; + } + } + + return constants; + } + + Prototype[] ReadPrototypes() + { + var count = ReadInt(); + var prototypes = count != 0 ? new Prototype[count] : []; + for (int i = 0; i < count; i++) + { + prototypes[i] = UnDumpFunction(); + } + + return prototypes; + } + + LocalVariable[] ReadLocalVariables() + { + var count = ReadInt(); + var localVariables = new LocalVariable[count]; + for (int i = 0; i < count; i++) + { + var name = ReadString(); + var startPc = ReadInt(); + var endPc = ReadInt(); + localVariables[i] = new LocalVariable() { Name = name, StartPc = startPc, EndPc = endPc }; + } + + return localVariables; + } + + UpValueDesc[] ReadUpValues() + { + var count = ReadInt(); + Debug.Assert(count < 100, $" too many upvalues :{count}"); + var upValues = new UpValueDesc[count]; + for (int i = 0; i < count; i++) + { + var isLocal = ReadBool(); + var index = ReadByte(); + upValues[i] = new UpValueDesc() { IsLocal = isLocal, Index = index }; + } + + return upValues; + } +} \ No newline at end of file diff --git a/src/Lua/CodeAnalysis/Compilation/Function.cs b/src/Lua/CodeAnalysis/Compilation/Function.cs new file mode 100644 index 00000000..2df31f6e --- /dev/null +++ b/src/Lua/CodeAnalysis/Compilation/Function.cs @@ -0,0 +1,1616 @@ +using Lua.Runtime; +using System.Diagnostics; +using Constants = Lua.Internal.Constants; + +namespace Lua.CodeAnalysis.Compilation; + +using static Debug; +using static Lua.Runtime.Instruction; +using Internal; +using static Constants; + +internal class Function : IPoolNode +{ + public readonly Dictionary ConstantLookup = new(); + public PrototypeBuilder Proto = null!; + public Function? Previous; + public Parser P = null!; + public Block Block = null!; + public int JumpPc = NoJump, LastTarget; + public int FreeRegisterCount; + public int ActiveVariableCount; + public int FirstLocal; + internal Function() { } + + static LinkedPool pool; + + ref Function? IPoolNode.NextNode => ref Previous; + + internal static Function Get(Parser p, PrototypeBuilder proto) + { + if (!pool.TryPop(out var f)) + { + f = new Function(); + } + + f.P = p; + f.Proto = proto; + return f; + } + + internal void Release() + { + Previous = null; + ConstantLookup.Clear(); + JumpPc = NoJump; + Proto = null!; + LastTarget = 0; + FreeRegisterCount = 0; + ActiveVariableCount = 0; + FirstLocal = 0; + pool.TryPush(this); + } + + + public const int OprMinus = 0; + + public const int OprNot = 1; + + public const int OprLength = 2; + + public const int OprNoUnary = 3; + public const int NoJump = -1; + + public const int NoRegister = MaxArgA; + + public const int MaxLocalVariables = 200; + + + public const int OprAdd = 0; + + public const int OprSub = 1; + + public const int OprMul = 2; + + public const int OprDiv = 3; + + public const int OprMod = 4; + + public const int OprPow = 5; + + public const int OprConcat = 6; + + public const int OprEq = 7; + + public const int OprLT = 8; + + public const int OprLE = 9; + + public const int OprNE = 10; + + public const int OprGT = 11; + + public const int OprGE = 12; + + public const int OprAnd = 13; + + public const int OprOr = 14; + + public const int OprNoBinary = 15; + + + public void OpenFunction(int line) + { + var newProto = PrototypeBuilder.Get(P.Scanner.Source); + newProto.Source = P.Scanner.Source; + newProto.MaxStackSize = 2; + newProto.LineDefined = line; + + Proto.PrototypeList.Add(newProto); + var f = Get(P, Proto.PrototypeList[^1]); + f.Previous = this; + f.FirstLocal = P.ActiveVariables.Length; + P.Function = f; + + P.Function.EnterBlock(false); + } + + + public ExprDesc CloseFunction() + { + var e = P.Function!.Previous!.ExpressionToNextRegister(MakeExpression(Kind.Relocatable, Previous!.EncodeABx(OpCode.Closure, 0, Previous!.Proto.PrototypeList.Length - 1))); + P.Function.ReturnNone(); + P.Function.LeaveBlock(); + Assert(P.Function.Block == null); + var f = P.Function; + P.Function = f.Previous; + f.Release(); + return e; + } + + + public void EnterBlock(bool isLoop) + { + var b = Block.Get(Block, P.ActiveLabels.Length, P.PendingGotos.Length, ActiveVariableCount, false, isLoop); + Block = b; + Assert(FreeRegisterCount == ActiveVariableCount); + } + + + public void UndefinedGotoError(Label g) + { + if (Scanner.IsReserved(g.Name)) + { + SemanticError($"<{g.Name}> at line {g.Line} not inside a loop"); + } + else + { + SemanticError($"no visible label '{g.Name}' for at line {g.Line}"); + } + } + + + public ref LocalVariable LocalVariable(int i) + { + var index = P.ActiveVariables[FirstLocal + i]; + return ref Proto.LocalVariablesList[index]; + } + + + public void AdjustLocalVariables(int n) + { + for (ActiveVariableCount += n; n != 0; n--) + { + LocalVariable(ActiveVariableCount - n).StartPc = ((Proto.CodeList.Length)); + } + } + + + public void RemoveLocalVariables(int level) + { + for (var i = level; i < ActiveVariableCount; i++) + { + LocalVariable(i).EndPc = ((Proto.CodeList.Length)); + } + + P.ActiveVariables.Shrink((P.ActiveVariables.Length - (ActiveVariableCount - level))); + ActiveVariableCount = level; + } + + + public void MakeLocalVariable(string name) + { + var r = Proto.LocalVariablesList.Length; + Proto.LocalVariablesList.Add(new() { Name = name }); + P.CheckLimit((P.ActiveVariables.Length + 1 - FirstLocal), MaxLocalVariables, "local variables"); + P.ActiveVariables.Add(r); + } + + + public void MakeGoto(string name, int line, int pc) + { + P.PendingGotos.Add(new() { Name = name, Line = line, Pc = pc, ActiveVariableCount = ActiveVariableCount }); + FindLabel((P.PendingGotos.Length - 1)); + } + + + public int MakeLabel(string name, int line) + { + P.ActiveLabels.Add(new() { Name = name, Line = line, Pc = Proto.CodeList.Length, ActiveVariableCount = ActiveVariableCount }); + return (P.ActiveLabels.Length - 1); + } + + + public void CloseGoto(int i, Label l) + { + var g = P.PendingGotos[i]; + Assert(g.Name == l.Name); + if (g.ActiveVariableCount < l.ActiveVariableCount) + { + SemanticError($" at line {g.Line} jumps into the scope of local '{LocalVariable(g.ActiveVariableCount).Name}'"); + } + + PatchList(g.Pc, l.Pc); + P.PendingGotos.RemoveAtSwapBack(i); + } + + + public int FindLabel(int i) + { + var g = P.PendingGotos[i]; + var b = Block; + foreach (var l in P.ActiveLabels.AsSpan().Slice(b.FirstLabel)) + { + if (l.Name == g.Name) + { + if (g.ActiveVariableCount > l.ActiveVariableCount && (b.HasUpValue || P.ActiveLabels.Length > b.FirstLabel)) + { + PatchClose(g.Pc, l.ActiveVariableCount); + } + + CloseGoto(i, l); + return 0; + } + } + + return 1; + } + + + public void CheckRepeatedLabel(string name) + { + foreach (var l in P.ActiveLabels.AsSpan().Slice(Block!.FirstLabel)) + { + if (l.Name == name) + { + SemanticError($"label '{name}' already defined on line {l.Line}"); + } + } + } + + + public void FindGotos(int label) + { + for (var i = Block!.FirstGoto; i < P.PendingGotos.Length;) + { + var l = P.ActiveLabels[label]; + if (P.PendingGotos[i].Name == l.Name) + { + CloseGoto(i, l); + } + else + { + i++; + } + } + } + + public void MoveGotosOut(Block b) + { + for (var i = b.FirstGoto; i < P.PendingGotos.Length; i += FindLabel(i)) + { + if (P.PendingGotos[i].ActiveVariableCount > b.ActiveVariableCount) + { + if (b.HasUpValue) + { + PatchClose(P.PendingGotos[i].Pc, b.ActiveVariableCount); + } + + P.PendingGotos[i].ActiveVariableCount = b.ActiveVariableCount; + } + } + } + + + public void LeaveBlock() + { + var b = Block; + if (b.Previous != null && b.HasUpValue) // create a 'jump to here' to close upvalues + { + var j = Jump(); + PatchClose(j, b.ActiveVariableCount); + PatchToHere(j); + } + + if (b.IsLoop) + { + BreakLabel(); + } + + Block = b.Previous!; + RemoveLocalVariables(b.ActiveVariableCount); + Assert(b.ActiveVariableCount == ActiveVariableCount); + FreeRegisterCount = ActiveVariableCount; + P.ActiveLabels.Shrink(b.FirstLabel); + if (b.Previous != null) // inner block + { + MoveGotosOut(b); // update pending gotos to outer block + } + else if (b.FirstGoto < P.PendingGotos.Length) // pending gotos in outer block + { + UndefinedGotoError(P.PendingGotos[b.FirstGoto]); + } + + b.Release(); + } + + + public static int Not(int b) => b == 0 ? 1 : 0; + + + public static ExprDesc MakeExpression(Kind kind, int info) => new() { F = NoJump, T = NoJump, Kind = kind, Info = info }; + + + public void SemanticError(string message) + { + P.Scanner.Token.T = default; + P.Scanner.SyntaxError(message); + } + + + public void BreakLabel() => FindGotos(MakeLabel("break", 0)); + + [Conditional("DEBUG")] + public void Unreachable() => Assert(false); + + + public ref Instruction Instruction(ExprDesc e) => ref Proto.CodeList[e.Info]; + + + [Conditional("DEBUG")] + public void AssertEqual(int a, int b) => Assert(a == b, $"{a} != {b}"); + + + public int Encode(Instruction i) + { + Assert(Proto.CodeList.Length == Proto.LineInfoList.Length); + DischargeJumpPc(); + Proto.CodeList.Add(i); + Proto.LineInfoList.Add(P.Scanner.LastLine); + return Proto.CodeList.Length - 1; + } + + public void DropLastInstruction() + { + Assert(Proto.CodeList.Length == Proto.LineInfoList.Length); + Proto.CodeList.Pop(); + Proto.LineInfoList.Pop(); + } + + + public int EncodeABC(OpCode op, int a, int b, int c) + { + return Encode(CreateABC(op, a, b, c)); + } + + + public int EncodeABx(OpCode op, int a, int bx) => Encode(CreateABx(op, a, bx)); + + + public int EncodeAsBx(OpCode op, int a, int sbx) => EncodeABx(op, a, sbx + MaxArgSBx); + + public int EncodeExtraArg(int a) => Encode(CreateAx(OpCode.ExtraArg, a)); + + + public int EncodeConstant(int r, int constant) + { + if (constant <= MaxArgBx) + return EncodeABx(OpCode.LoadK, r, constant); + else + { + var pc = EncodeABx(OpCode.LoadK, r, 0); + EncodeExtraArg(constant); + return pc; + } + } + + + public ExprDesc EncodeString(string s) => MakeExpression(Kind.Constant, StringConstant(s)); + + + public void LoadNil(int from, int n) + { + if (Proto.CodeList.Length > LastTarget) // no jumps to current position + { + ref var previous = ref Proto.CodeList[^1]; + if (previous.OpCode == OpCode.LoadNil) + { + var pf = previous.A; + var pl = previous.A + previous.B; + var l = from + n - 1; + if (pf <= from && from <= pl + 1 || from <= pf && pf <= l + 1) // can connect both + { + from = Math.Min(from, pf); + l = Math.Max(l, pl); + previous.A = from; + previous.B = l - from; + return; + } + } + } + + EncodeABC(OpCode.LoadNil, from, n - 1, 0); + } + + + public int Jump() + { + Assert(IsJumpListWalkable(JumpPc)); + var jumpPc = JumpPc; + JumpPc = NoJump; + return Concatenate(EncodeAsBx(OpCode.Jmp, 0, NoJump), jumpPc); + } + + public void JumpTo(int target) + { + PatchList(Jump(), target); + } + + public void ReturnNone() + { + EncodeABC(OpCode.Return, 0, 1, 0); + } + + public void SetMultipleReturns(ExprDesc e) + { + SetReturns(e, MultipleReturns); + } + + + public void Return(ExprDesc e, int resultCount) + { + if (e.HasMultipleReturns()) + { + SetMultipleReturns(e); + if (e.Kind == Kind.Call && resultCount == 1) + { + Instruction(e).OpCode = OpCode.TailCall; + Assert(Instruction(e).A == ActiveVariableCount); + } + + EncodeABC(OpCode.Return, ActiveVariableCount, MultipleReturns + 1, 0); + } + else if (resultCount == 1) + EncodeABC(OpCode.Return, ExpressionToAnyRegister(e).Info, 1 + 1, 0); + else + { + ExpressionToNextRegister(e); + Assert(resultCount == FreeRegisterCount - ActiveVariableCount); + EncodeABC(OpCode.Return, ActiveVariableCount, resultCount + 1, 0); + } + } + + + public int ConditionalJump(OpCode op, int a, int b, int c) + { + EncodeABC(op, a, b, c); + return Jump(); + } + + + public void FixJump(int pc, int dest) + { + Assert(IsJumpListWalkable(pc)); + Assert(dest != NoJump); + var offset = dest - (pc + 1); + if (Math.Abs(offset) > MaxArgSBx) + P.Scanner.SyntaxError("control structure too long"); + Proto.CodeList[pc].SBx = (offset); + } + + public int Label() + { + LastTarget = Proto.CodeList.Length; + return LastTarget; + } + + + public int Jump(int pc) + { + Assert(IsJumpListWalkable(pc)); + var offset = Proto.CodeList[pc].SBx; + if (offset != NoJump) + return pc + 1 + offset; + return NoJump; + } + + + public bool IsJumpListWalkable(int list) + { + if (list == NoJump) + return true; + if (list < 0 || list >= Proto.CodeList.Length) + return false; + var offset = Proto.CodeList[list].SBx; + return offset == NoJump || IsJumpListWalkable(list + 1 + offset); + } + + + public ref Instruction JumpControl(int pc) + { + if (pc >= 1 && TestTMode(Proto.CodeList[pc - 1].OpCode)) + return ref Proto.CodeList[pc - 1]; + return ref Proto.CodeList[pc]; + } + + + public bool NeedValue(int list) + { + Assert(IsJumpListWalkable(list)); + for (; list != NoJump; list = Jump(list)) + { + if (JumpControl(list).OpCode != OpCode.TestSet) + return true; + } + + return false; + } + + + public bool PatchTestRegister(int node, int register) + { + ref var i = ref JumpControl(node); + if (i.OpCode != OpCode.TestSet) + return false; + if (register != NoRegister && register != i.B) + i.A = register; + else + i = CreateABC(OpCode.Test, i.B, 0, i.C); + return true; + } + + public void RemoveValues(int list) + { + Assert(IsJumpListWalkable(list)); + for (; list != NoJump; list = Jump(list)) + { + PatchTestRegister(list, NoRegister); + } + } + + + public void PatchListHelper(int list, int target, int register, int defaultTarget) + { + Assert(IsJumpListWalkable(list)); + + while (list != NoJump) + { + var next = Jump(list); + if (PatchTestRegister(list, register)) + { + FixJump(list, target); + } + else + { + FixJump(list, defaultTarget); + } + + list = next; + } + } + + public void DischargeJumpPc() + { + Assert(IsJumpListWalkable(JumpPc)); + PatchListHelper(JumpPc, Proto.CodeList.Length, NoRegister, Proto.CodeList.Length); + JumpPc = NoJump; + } + + public void PatchList(int list, int target) + { + if (target == Proto.CodeList.Length) + { + PatchToHere(list); + } + else + { + Assert(target < Proto.CodeList.Length); + PatchListHelper(list, target, NoRegister, target); + } + } + + public void PatchClose(int list, int level) + { + Assert(IsJumpListWalkable(list)); + level++; + for (int next; list != NoJump; list = next) + { + next = Jump(list); + Assert(Proto.CodeList[list].OpCode == OpCode.Jmp && Proto.CodeList[list].A == 0 || Proto.CodeList[list].A >= level); + Proto.CodeList[list].A = level; + } + } + + + public void PatchToHere(int list) + { + Assert(IsJumpListWalkable(list)); + Assert(IsJumpListWalkable(JumpPc)); + Label(); + JumpPc = Concatenate(JumpPc, list); + Assert(IsJumpListWalkable(JumpPc)); + } + + public int Concatenate(int l1, int l2) + { + Assert(IsJumpListWalkable(l1)); + + if (l2 == NoJump) return l1; + if (l1 == NoJump) + { + return l2; + } + + var list = l1; + for (var next = Jump(list); next != NoJump;) + { + (list, next) = (next, Jump(next)); + } + + FixJump(list, l2); + return l1; + } + + public int AddConstant(LuaValue k, LuaValue v) + { + if (ConstantLookup.TryGetValue(k, out var index) && Proto.ConstantsList[index] == v) + { + return index; + } + + index = Proto.ConstantsList.Length; + ConstantLookup[k] = index; + Proto.ConstantsList.Add(v); + return index; + } + + + public unsafe int NumberConstant(double n) + { + if (n == 0.0 || double.IsNaN(n)) + { + return AddConstant(*(long*)&n, n); + } + + return AddConstant(n, n); + } + + public void CheckStack(int n) + { + n += FreeRegisterCount; + if (n >= MaxStack) + { + P.Scanner.SyntaxError("function or expression too complex"); + } + else if (n > Proto.MaxStackSize) + { + Proto.MaxStackSize = n; + } + } + + + public void ReserveRegisters(int n) + { + CheckStack(n); + FreeRegisterCount += n; + } + + + public void FreeRegister(int r) + { + if (!IsConstant(r) && r >= ActiveVariableCount) + { + FreeRegisterCount--; + AssertEqual(r, FreeRegisterCount); + } + } + + public void FreeExpression(ExprDesc e) + { + if (e.Kind == Kind.NonRelocatable) + { + FreeRegister(e.Info); + } + } + + + public int StringConstant(string s) + { + return AddConstant(s, s); + } + + public int BooleanConstant(bool b) + { + return AddConstant(b, b); + } + + public int NilConstant() + { + return AddConstant(default, default); + } + + public void SetReturns(ExprDesc e, int resultCount) + { + if (e.Kind == Kind.Call) + { + Instruction(e).C = resultCount + 1; + } + else if (e.Kind == Kind.VarArg) + { + Instruction(e).B = resultCount + 1; + Instruction(e).A = FreeRegisterCount; + ReserveRegisters(1); + } + } + + public ExprDesc SetReturn(ExprDesc e) + { + if (e.Kind == Kind.Call) + { + e.Kind = Kind.NonRelocatable; + e.Info = Instruction(e).A; + } + else if (e.Kind == Kind.VarArg) + { + Instruction(e).B = 2; + e.Kind = Kind.Relocatable; + } + + return e; + } + + public ExprDesc DischargeVariables(ExprDesc e) + { + switch (e.Kind) + { + case Kind.Local: + e.Kind = Kind.NonRelocatable; + break; + case Kind.UpValue: + e.Kind = Kind.Relocatable; + e.Info = EncodeABC(OpCode.GetUpVal, 0, e.Info, 0); + break; + case Kind.Indexed: + FreeRegister(e.Index); + { + if (e.TableType == Kind.Local) + { + FreeRegister(e.Table); + e.Kind = Kind.Relocatable; + e.Info = EncodeABC(OpCode.GetTable, 0, e.Table, e.Index); + } + else + { + e.Kind = Kind.Relocatable; + e.Info = EncodeABC(OpCode.GetTabUp, 0, e.Table, e.Index); + } + } + break; + case Kind.VarArg: + case Kind.Call: + e = SetReturn(e); + break; + } + + return e; + } + + public ExprDesc DischargeToRegister(ExprDesc e, int r) + { + e = DischargeVariables(e); + switch (e.Kind) + { + case Kind.Nil: + LoadNil(r, 1); + break; + case Kind.False: + EncodeABC(OpCode.LoadBool, r, 0, 0); + break; + case Kind.True: + EncodeABC(OpCode.LoadBool, r, 1, 0); + break; + case Kind.Constant: + EncodeConstant(r, e.Info); + break; + case Kind.Number: + EncodeConstant(r, NumberConstant(e.Value)); + break; + case Kind.Relocatable: + Instruction(e).A = r; + break; + case Kind.NonRelocatable: + if (r != e.Info) + { + EncodeABC(OpCode.Move, r, e.Info, 0); + } + + break; + default: + Assert(e.Kind == Kind.Void || e.Kind == Kind.Jump); + return e; + } + + e.Kind = Kind.NonRelocatable; + e.Info = r; + return e; + } + + + public ExprDesc DischargeToAnyRegister(ExprDesc e) + { + if (e.Kind != Kind.NonRelocatable) + { + ReserveRegisters(1); + e = DischargeToRegister(e, FreeRegisterCount - 1); + } + + return e; + } + + + public int EncodeLabel(int a, int b, int jump) + { + Label(); + return EncodeABC(OpCode.LoadBool, a, b, jump); + } + + + public ExprDesc ExpressionToRegister(ExprDesc e, int r) + { + e = DischargeToRegister(e, r); + if (e.Kind == Kind.Jump) + { + e.T = Concatenate(e.T, e.Info); + } + + if (e.HasJumps()) + { + int loadFalse = NoJump; + int loadTrue = NoJump; + if (NeedValue(e.T) || NeedValue(e.F)) + { + int jump = NoJump; + if (e.Kind != Kind.Jump) jump = Jump(); + loadFalse = EncodeLabel(r, 0, 1); + loadTrue = EncodeLabel(r, 1, 0); + PatchToHere(jump); + } + + int end = Label(); + PatchListHelper(e.F, end, r, loadFalse); + PatchListHelper(e.T, end, r, loadTrue); + } + + e.F = e.T = NoJump; + e.Info = r; + e.Kind = Kind.NonRelocatable; + return e; + } + + + public ExprDesc ExpressionToNextRegister(ExprDesc e) + { + e = DischargeVariables(e); + FreeExpression(e); + ReserveRegisters(1); + return ExpressionToRegister(e, FreeRegisterCount - 1); + } + + + public ExprDesc ExpressionToAnyRegister(ExprDesc e) + { + e = DischargeVariables(e); + if (e.Kind == Kind.NonRelocatable) + { + if (!e.HasJumps()) + return e; + if (e.Info >= ActiveVariableCount) + { + return ExpressionToRegister(e, e.Info); + } + } + + return ExpressionToNextRegister(e); + } + + + public ExprDesc ExpressionToAnyRegisterOrUpValue(ExprDesc e) + { + if (e.Kind != Kind.UpValue || e.HasJumps()) + { + e = ExpressionToAnyRegister(e); + } + + return e; + } + + + public ExprDesc ExpressionToValue(ExprDesc e) + { + if (e.HasJumps()) return ExpressionToAnyRegister(e); + return DischargeVariables(e); + } + + + public (ExprDesc, int) ExpressionToRegisterOrConstant(ExprDesc e) + { + e = ExpressionToValue(e); + switch (e.Kind) + { + case Kind.True: + case Kind.False: + if (Proto.ConstantsList.Length <= MaxIndexRK) + { + e.Info = BooleanConstant(e.Kind == Kind.True); + e.Kind = Kind.Constant; + return (e, AsConstant(e.Info)); + } + + break; + case Kind.Nil: + if (Proto.ConstantsList.Length <= MaxIndexRK) + { + e.Info = NilConstant(); + e.Kind = Kind.Constant; + return (e, AsConstant(e.Info)); + } + + break; + case Kind.Number: + e.Info = NumberConstant(e.Value); + e.Kind = Kind.Constant; + goto case Kind.Constant; + case Kind.Constant: + if (e.Info <= MaxIndexRK) + { + return (e, AsConstant(e.Info)); + } + + break; + } + + e = ExpressionToAnyRegister(e); + return (e, e.Info); + } + + + public void StoreVariable(ExprDesc v, ExprDesc e) + { + switch (v.Kind) + { + case Kind.Local: + FreeExpression(e); + ExpressionToRegister(e, v.Info); + return; + case Kind.UpValue: + e = ExpressionToAnyRegister(e); + EncodeABC(OpCode.SetUpVal, e.Info, v.Info, 0); + break; + case Kind.Indexed: + var r = 0; + (e, r) = ExpressionToRegisterOrConstant(e); + EncodeABC(v.TableType == Kind.Local ? OpCode.SetTable : OpCode.SetTabUp, v.Table, v.Index, r); + + break; + default: + Unreachable(); + break; + } + + FreeExpression(e); + } + + + public ExprDesc Self(ExprDesc e, ExprDesc key) + { + e = ExpressionToAnyRegister(e); + var r = e.Info; + FreeExpression(e); + var result = new ExprDesc { Info = FreeRegisterCount, Kind = Kind.NonRelocatable }; // base register for opSelf + ReserveRegisters(2); // function and 'self' produced by opSelf + (key, var k) = ExpressionToRegisterOrConstant(key); + EncodeABC(OpCode.Self, result.Info, r, k); + FreeExpression(key); + return result; + } + + + public void InvertJump(int pc) + { + ref var i = ref JumpControl(pc); + Assert(TestTMode(i.OpCode) && i.OpCode is not (OpCode.TestSet or OpCode.Test)); + i.A = Not(i.A); + } + + + public int JumpOnCondition(ExprDesc e, int cond) + { + if (e.Kind == Kind.Relocatable) + { + var i = Instruction(e); + if (i.OpCode == OpCode.Not) + { + DropLastInstruction(); // remove previous opNot + return ConditionalJump(OpCode.Test, i.B, 0, Not(cond)); + } + } + + e = DischargeToAnyRegister(e); + FreeExpression(e); + return ConditionalJump(OpCode.TestSet, NoRegister, e.Info, cond); + } + + + public ExprDesc GoIfTrue(ExprDesc e) + { + var pc = NoJump; + e = DischargeVariables(e); + switch (e.Kind) + { + case Kind.Jump: + InvertJump(e.Info); + pc = e.Info; + break; + case Kind.Constant: + case Kind.Number: + case Kind.True: + break; + default: + pc = JumpOnCondition(e, 0); + break; + } + + e.F = Concatenate(e.F, pc); + PatchToHere(e.T); + e.T = NoJump; + return e; + } + + + public ExprDesc GoIfFalse(ExprDesc e) + { + var pc = NoJump; + e = DischargeVariables(e); + switch (e.Kind) + { + case Kind.Jump: + pc = e.Info; + break; + case Kind.Nil: + case Kind.False: + break; + default: + pc = JumpOnCondition(e, 1); + break; + } + + e.T = Concatenate(e.T, pc); + PatchToHere(e.F); + e.F = NoJump; + return e; + } + + + public ExprDesc EncodeNot(ExprDesc e) + { + e = DischargeVariables(e); + switch (e.Kind) + { + case Kind.Nil: + case Kind.False: + e.Kind = Kind.True; + break; + case Kind.Constant: + case Kind.Number: + case Kind.True: + e.Kind = Kind.False; + break; + case Kind.Jump: + InvertJump(e.Info); + break; + case Kind.Relocatable: + case Kind.NonRelocatable: + e = DischargeToAnyRegister(e); + FreeExpression(e); + e.Info = EncodeABC(OpCode.Not, 0, e.Info, 0); + e.Kind = Kind.Relocatable; + break; + default: + Unreachable(); + break; + } + + (e.T, e.F) = (e.F, e.T); + RemoveValues(e.F); + RemoveValues(e.T); + return e; + } + + + public ExprDesc Indexed(ExprDesc t, ExprDesc k) + { + Assert(!t.HasJumps()); + var r = MakeExpression(Kind.Indexed, 0); + r.Table = t.Info; + var (_, i) = ExpressionToRegisterOrConstant(k); + r.Index = i; + if (t.Kind == Kind.UpValue) + r.TableType = Kind.UpValue; + else + { + Assert(t.Kind == Kind.NonRelocatable || t.Kind == Kind.Local); + r.TableType = Kind.Local; + } + + return r; + } + + + private static double Arith(OpCode op, double v1, double v2) + { + switch (op) + { + case OpCode.Add: + return v1 + v2; + case OpCode.Sub: + return v1 - v2; + case OpCode.Mul: + return v1 * v2; + case OpCode.Div: + return v1 / v2; + case OpCode.Mod: + return v1 - Math.Floor(v1 / v2) * v2; + case OpCode.Pow: + return Math.Pow(v1, v2); + case OpCode.Unm: + return -v1; + } + + throw new("not an arithmetic op code (" + op + ")"); + } + + public static (ExprDesc, bool) FoldConstants(OpCode op, ExprDesc e1, ExprDesc e2) + { + if (!e1.IsNumeral() || !e2.IsNumeral()) + return (e1, false); + if ((op == OpCode.Div || op == OpCode.Mod) && e2.Value == 0.0) + return (e1, false); + e1.Value = Arith(op, e1.Value, e2.Value); + return (e1, true); + } + + + public ExprDesc EncodeArithmetic(OpCode op, ExprDesc e1, ExprDesc e2, int line) + { + var (e, folded) = FoldConstants(op, e1, e2); + if (folded) + return e; + var o2 = 0; + if (op != OpCode.Unm && op != OpCode.Len) + { + (e2, o2) = ExpressionToRegisterOrConstant(e2); + } + + (e1, var o1) = ExpressionToRegisterOrConstant(e1); + if (o1 > o2) + { + FreeExpression(e1); + FreeExpression(e2); + } + else + { + FreeExpression(e2); + FreeExpression(e1); + } + + e1.Info = EncodeABC(op, 0, o1, o2); + e1.Kind = Kind.Relocatable; + FixLine(line); + return e1; + } + + + public ExprDesc Prefix(int op, ExprDesc e, int line) + { + switch (op) + { + case OprMinus: + if (e.IsNumeral()) + { + e.Value = -e.Value; + return e; + } + + return EncodeArithmetic(OpCode.Unm, ExpressionToAnyRegister(e), MakeExpression(Kind.Number, 0), line); + case OprNot: + return EncodeNot(e); + case OprLength: + return EncodeArithmetic(OpCode.Len, ExpressionToAnyRegister(e), MakeExpression(Kind.Number, 0), line); + } + + throw new("unreachable"); + } + + + public ExprDesc Infix(int op, ExprDesc e) + { + switch (op) + { + case OprAnd: + e = GoIfTrue(e); + break; + case OprOr: + e = GoIfFalse(e); + break; + case OprConcat: + e = ExpressionToNextRegister(e); + break; + case OprAdd: + case OprSub: + case OprMul: + case OprDiv: + case OprMod: + case OprPow: + if (!e.IsNumeral()) + (e, _) = ExpressionToRegisterOrConstant(e); + break; + default: + (e, _) = ExpressionToRegisterOrConstant(e); + break; + } + + return e; + } + + + public ExprDesc EncodeComparison(OpCode op, int cond, ExprDesc e1, ExprDesc e2) + { + (e1, var o1) = ExpressionToRegisterOrConstant(e1); + (e2, var o2) = ExpressionToRegisterOrConstant(e2); + FreeExpression(e2); + FreeExpression(e1); + if (cond == 0 && op != OpCode.Eq) + { + (o1, o2, cond) = (o2, o1, 1); + } + + return MakeExpression(Kind.Jump, ConditionalJump(op, cond, o1, o2)); + } + + + public ExprDesc Postfix(int op, ExprDesc e1, ExprDesc e2, int line) + { + switch (op) + { + case OprAnd: + Assert(e1.T == NoJump); + e2 = DischargeVariables(e2); + e2.F = Concatenate(e2.F, e1.F); + return e2; + case OprOr: + Assert(e1.F == NoJump); + e2 = DischargeVariables(e2); + e2.T = Concatenate(e2.T, e1.T); + return e2; + case OprConcat: + e2 = ExpressionToValue(e2); + if (e2.Kind == Kind.Relocatable && Instruction(e2).OpCode == OpCode.Concat) + { + Assert(e1.Info == Instruction(e2).B - 1); + FreeExpression(e1); + Instruction(e2).B = (e1.Info); + return MakeExpression(Kind.Relocatable, e2.Info); + } + + return EncodeArithmetic(OpCode.Concat, e1, ExpressionToNextRegister(e2), line); + case OprAdd: + case OprSub: + case OprMul: + case OprDiv: + case OprMod: + case OprPow: + return EncodeArithmetic((OpCode)(op - OprAdd + (byte)OpCode.Add), e1, e2, line); + case OprEq: + case OprLT: + case OprLE: + return EncodeComparison((OpCode)(op - OprEq + (byte)OpCode.Eq), 1, e1, e2); + case OprNE: + case OprGT: + case OprGE: + return EncodeComparison((OpCode)(op - OprNE + (byte)OpCode.Eq), 0, e1, e2); + default: + throw new("unreachable"); + } + } + + + public void FixLine(int line) => Proto.LineInfoList[Proto.CodeList.Length - 1] = line; + + + public void SetList(int @base, int elementCount, int storeCount) + { + Assert(storeCount != 0); + if (storeCount == MultipleReturns) + { + storeCount = 0; + } + + var c = (elementCount - 1) / ListItemsPerFlush + 1; + if (c <= MaxArgC) + { + EncodeABC(OpCode.SetList, @base, storeCount, c); + } + else if (c <= MaxArgAx) + { + EncodeABC(OpCode.SetList, @base, storeCount, 0); + EncodeExtraArg(c); + } + else + { + P.Scanner.SyntaxError("constructor too long"); + } + + FreeRegisterCount = @base + 1; + } + + + public unsafe void CheckConflict(AssignmentTarget tv, ExprDesc e) + { + var extra = FreeRegisterCount; + var conflict = false; + var t = &tv; + while (t != null) + { + ref var d = ref t->Description; + if (d.Kind == Kind.Indexed) + { + if (d.TableType == e.Kind && d.Table == e.Info) + { + conflict = true; + d.Table = extra; + d.TableType = Kind.Local; + } + + if (e.Kind == Kind.Local && d.Index == e.Info) + { + conflict = true; + d.Index = extra; + } + } + + t = t->Previous; + } + + if (conflict) + { + if (e.Kind == Kind.Local) + { + EncodeABC(OpCode.Move, extra, e.Info, 0); + } + else + { + EncodeABC(OpCode.GetUpVal, extra, e.Info, 0); + } + + ReserveRegisters(1); + } + } + + + public void AdjustAssignment(int variableCount, int expressionCount, ExprDesc e) + { + var extra = variableCount - expressionCount; + if (e.HasMultipleReturns()) + { + extra++; + if (extra < 0) + { + extra = 0; + } + + SetReturns(e, extra); + if (extra > 1) + { + ReserveRegisters(extra - 1); + } + } + else + { + if (expressionCount > 0) + { + ExpressionToNextRegister(e); + } + + if (extra > 0) + { + var r = FreeRegisterCount; + ReserveRegisters(extra); + LoadNil(r, extra); + } + } + } + + + public int MakeUpValue(string name, ExprDesc e) + { + P.CheckLimit(Proto.UpValuesList.Length + 1, MaxUpValue, "upvalues"); + Proto.UpValuesList.Add(new() { Name = name, IsLocal = e.Kind == Kind.Local, Index = e.Info }); + return Proto.UpValuesList.Length - 1; + } + + + public static (ExprDesc, bool) SingleVariableHelper(Function? f, string name, bool b) + { + static Block owningBlock(Block b1, int level) + { + while (b1.ActiveVariableCount > level) + { + b1 = b1.Previous!; + } + + return b1; + } + + ; + + static (int, bool) find(Function f, string name) + { + for (var i = f.ActiveVariableCount - 1; i >= 0; i--) + { + if (name == f.LocalVariable(i).Name) + { + return (i, true); + } + } + + return (0, false); + } + + ; + + static (int, bool) findUpValue(Function f, string name) + { + for (var i = 0; i < f.Proto.UpValuesList.Length; i++) + { + if (f.Proto.UpValuesList[i].Name == name) + { + return (i, true); + } + } + + return (0, false); + } + + ; + + if (f == null) + { + return default; + } + + var (v, found) = find(f, name); + if (found) + { + var e = MakeExpression(Kind.Local, v); + if (!b) + { + owningBlock(f.Block, v).HasUpValue = true; + } + + return (e, true); + } + + (v, found) = findUpValue(f, name); + if (found) + { + return (MakeExpression(Kind.UpValue, v), true); + } + + { + (var e, found) = SingleVariableHelper(f.Previous, name, false); + if (!found) + { + return (e, found); + } + + return (MakeExpression(Kind.UpValue, f.MakeUpValue(name, e)), true); + } + } + + + public ExprDesc SingleVariable(string name) + { + var (e, found) = SingleVariableHelper(this, name, true); + if (!found) + { + (e, found) = SingleVariableHelper(this, "_ENV", true); + Assert(found && (e.Kind == Kind.Local || e.Kind == Kind.UpValue)); + e = Indexed(e, EncodeString(name)); + } + + return e; + } + + + public (int pc, ExprDesc t) OpenConstructor() + { + var pc = EncodeABC(OpCode.NewTable, 0, 0, 0); + var t = ExpressionToNextRegister(MakeExpression(Kind.Relocatable, pc)); + return (pc, t); + } + + + public void FlushFieldToConstructor(int tableRegister, int freeRegisterCount, ExprDesc k, Func v) + { + (_, var rk) = ExpressionToRegisterOrConstant(k); + (_, var rv) = ExpressionToRegisterOrConstant(v()); + EncodeABC(OpCode.SetTable, tableRegister, rk, rv); + FreeRegisterCount = freeRegisterCount; + } + + + public int FlushToConstructor(int tableRegister, int pending, int arrayCount, ExprDesc e) + { + ExpressionToNextRegister(e); + if (pending == ListItemsPerFlush) + { + SetList(tableRegister, arrayCount, ListItemsPerFlush); + pending = 0; + } + + return pending; + } + + + public void CloseConstructor(int pc, int tableRegister, int pending, int arrayCount, int hashCount, ExprDesc e) + { + if (pending != 0) + { + if (e.HasMultipleReturns()) + { + SetMultipleReturns(e); + SetList(tableRegister, arrayCount, MultipleReturns); + arrayCount--; + } + else + { + if (e.Kind != Kind.Void) + { + ExpressionToNextRegister(e); + } + + SetList(tableRegister, arrayCount, pending); + } + } + + Proto.CodeList[pc].B = (((arrayCount))); + Proto.CodeList[pc].C = (((hashCount))); + } + + + public int OpenForBody(int @base, int n, bool isNumeric) + { + var prep = isNumeric ? EncodeAsBx(OpCode.ForPrep, @base, NoJump) : Jump(); + EnterBlock(false); + AdjustLocalVariables(n); + ReserveRegisters(n); + return prep; + } + + + public void CloseForBody(int prep, int @base, int line, int n, bool isNumeric) + { + LeaveBlock(); + PatchToHere(prep); + int end; + if (isNumeric) + { + end = EncodeAsBx(OpCode.ForLoop, @base, NoJump); + } + else + { + EncodeABC(OpCode.TForCall, @base, 0, n); + FixLine(line); + end = EncodeAsBx(OpCode.TForLoop, @base + 2, NoJump); + } + + PatchList(end, prep + 1); + FixLine(line); + } + + + public void OpenMainFunction() + { + EnterBlock(false); + MakeUpValue("_ENV", MakeExpression(Kind.Local, 0)); + } + + + public Function CloseMainFunction() + { + ReturnNone(); + LeaveBlock(); + Assert(Block == null); + return Previous!; + } +} \ No newline at end of file diff --git a/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs b/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs deleted file mode 100644 index 5996deff..00000000 --- a/src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs +++ /dev/null @@ -1,499 +0,0 @@ -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using Lua.Runtime; -using Lua.Internal; - -namespace Lua.CodeAnalysis.Compilation; - -public class FunctionCompilationContext : IDisposable -{ - static class Pool - { - static readonly ConcurrentStack stack = new(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FunctionCompilationContext Rent() - { - if (!stack.TryPop(out var context)) - { - context = new(); - } - - return context; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Return(FunctionCompilationContext context) - { - context.Reset(); - stack.Push(context); - } - } - - internal static FunctionCompilationContext Create(ScopeCompilationContext? parentScope) - { - var context = Pool.Rent(); - context.ParentScope = parentScope; - return context; - } - - FunctionCompilationContext() - { - Scope = new() - { - Function = this - }; - } - - // instructions - FastListCore instructions; - FastListCore instructionPositions; - - // constants - Dictionary constantIndexMap = new(16); - FastListCore constants; - - // functions - Dictionary, int> functionMap = new(32, Utf16StringMemoryComparer.Default); - FastListCore functions; - - // upvalues - FastListCore upvalues; - FastListCore localVariables; - - // loop - FastListCore breakQueue; - FastListCore gotoQueue; - - /// - /// Maximum local stack size - /// - public byte MaxStackPosition { get; set; } - - /// - /// Chunk name (for debug) - /// - public string? ChunkName { get; set; } - - /// - /// Level of nesting of while, repeat, and for loops - /// - public int LoopLevel { get; set; } - - /// - /// Number of parameters - /// - public int ParameterCount { get; set; } - - /// - /// Weather the function has variable arguments - /// - 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 - /// - public ScopeCompilationContext? ParentScope { get; private set; } - - /// - /// Top-level scope context - /// - public ScopeCompilationContext Scope { get; } - - /// - /// Instructions - /// - public Span Instructions => instructions.AsSpan(); - - /// - /// Push the new instruction. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PushInstruction(in Instruction instruction, in SourcePosition position) - { - instructions.Add(instruction); - instructionPositions.Add(position); - } - - /// - /// Push or merge the new instruction. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PushOrMergeInstruction(in Instruction instruction, in SourcePosition position, ref bool incrementStackPosition) - { - if (instructions.Length == 0) - { - instructions.Add(instruction); - instructionPositions.Add(position); - return; - } - - var activeLocals = Scope.ActiveLocalVariables; - - ref var lastInstruction = ref instructions.AsSpan()[^1]; - var opcode = instruction.OpCode; - switch (opcode) - { - case OpCode.Move: - - if ( - // available to merge and last A is not local variable - lastInstruction.A == instruction.B && !activeLocals[lastInstruction.A]) - { - switch (lastInstruction.OpCode) - { - case OpCode.LoadK: - case OpCode.LoadBool when lastInstruction.C == 0: - case OpCode.LoadNil when lastInstruction.B == 0: - case OpCode.GetUpVal: - case OpCode.GetTabUp: - case OpCode.GetTable when !activeLocals[lastInstruction.B]: - case OpCode.NewTable: - case OpCode.Self: - case OpCode.Add: - case OpCode.Sub: - case OpCode.Mul: - case OpCode.Div: - case OpCode.Mod: - case OpCode.Pow: - case OpCode.Unm: - case OpCode.Not: - case OpCode.Len: - case OpCode.Concat: - { - lastInstruction.A = instruction.A; - incrementStackPosition = false; - return; - } - } - } - - break; - case OpCode.GetTable: - { - // Merge MOVE GetTable - if (lastInstruction.OpCode == OpCode.Move && !activeLocals[lastInstruction.A]) - { - if (lastInstruction.A == instruction.B) - { - lastInstruction = Instruction.GetTable(instruction.A, lastInstruction.B, instruction.C); - instructionPositions[^1] = position; - incrementStackPosition = false; - return; - } - } - - break; - } - case OpCode.SetTable: - { - // Merge MOVE SETTABLE - if (lastInstruction.OpCode == OpCode.Move && !activeLocals[lastInstruction.A]) - { - var lastB = lastInstruction.B; - var lastA = lastInstruction.A; - if (lastB < 255 && lastA == instruction.A) - { - // Merge MOVE MOVE SETTABLE - if (instructions.Length > 2) - { - ref var last2Instruction = ref instructions.AsSpan()[^2]; - var last2A = last2Instruction.A; - if (last2Instruction.OpCode == OpCode.Move && !activeLocals[last2A] && instruction.C == last2A) - { - last2Instruction = Instruction.SetTable((byte)(lastB), instruction.B, last2Instruction.B); - instructions.RemoveAtSwapback(instructions.Length - 1); - instructionPositions.RemoveAtSwapback(instructionPositions.Length - 1); - instructionPositions[^1] = position; - incrementStackPosition = false; - return; - } - } - - lastInstruction = Instruction.SetTable((byte)(lastB), instruction.B, instruction.C); - instructionPositions[^1] = position; - incrementStackPosition = false; - return; - } - - if (lastA == instruction.C) - { - lastInstruction = Instruction.SetTable(instruction.A, instruction.B, lastB); - instructionPositions[^1] = position; - incrementStackPosition = false; - return; - } - } - else if (lastInstruction.OpCode == OpCode.GetTabUp && instructions.Length >= 2) - { - ref var last2Instruction = ref instructions[^2]; - var last2OpCode = last2Instruction.OpCode; - if (last2OpCode is OpCode.LoadK or OpCode.Move) - { - var last2A = last2Instruction.A; - if (!activeLocals[last2A] && instruction.C == last2A) - { - var c = last2OpCode == OpCode.LoadK ? last2Instruction.Bx + 256 : last2Instruction.B; - last2Instruction = lastInstruction; - lastInstruction = instruction with { C = (ushort)c }; - instructionPositions[^2] = instructionPositions[^1]; - instructionPositions[^1] = position; - incrementStackPosition = false; - return; - } - } - } - - break; - } - case OpCode.Unm: - case OpCode.Not: - case OpCode.Len: - if (lastInstruction.OpCode == OpCode.Move && !activeLocals[lastInstruction.A] && lastInstruction.A == instruction.B) - { - lastInstruction = instruction with { B = lastInstruction.B }; - instructionPositions[^1] = position; - incrementStackPosition = false; - return; - } - - break; - } - - instructions.Add(instruction); - instructionPositions.Add(position); - } - - /// - /// Gets the index of the constant from the value, or if the constant is not registered it is added and its index is returned. - /// - public uint GetConstantIndex(in LuaValue value) - { - if (!constantIndexMap.TryGetValue(value, out var index)) - { - index = constants.Length; - - constants.Add(value); - constantIndexMap.Add(value, index); - } - - return (uint)index; - } - - public void AddOrSetFunctionProto(ReadOnlyMemory name, Chunk chunk, out int index) - { - index = functions.Length; - functionMap[name] = functions.Length; - functions.Add(chunk); - } - - public void AddFunctionProto(Chunk chunk, out int index) - { - index = functions.Length; - functions.Add(chunk); - } - - public bool TryGetFunctionProto(ReadOnlyMemory name, [NotNullWhen(true)] out Chunk? proto) - { - if (functionMap.TryGetValue(name, out var index)) - { - proto = functions[index]; - return true; - } - else - { - proto = null; - 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) - { - upvalues.Add(upValue); - } - - public bool TryGetUpValue(ReadOnlyMemory name, out UpValueInfo description) - { - var span = upvalues.AsSpan(); - for (int i = 0; i < span.Length; i++) - { - var info = span[i]; - if (info.Name.Span.SequenceEqual(name.Span)) - { - description = info; - return true; - } - } - - if (ParentScope == null) - { - description = default; - return false; - } - - if (ParentScope.TryGetLocalVariable(name, out var localVariable)) - { - ParentScope.HasCapturedLocalVariables = true; - - description = new() - { - Name = name, - Index = localVariable.RegisterIndex, - Id = upvalues.Length, - IsInRegister = true, - }; - upvalues.Add(description); - - return true; - } - else if (ParentScope.Function.TryGetUpValue(name, out var parentUpValue)) - { - description = new() - { - Name = name, - Index = parentUpValue.Id, - Id = upvalues.Length, - IsInRegister = false, - }; - upvalues.Add(description); - - return true; - } - - description = default; - return false; - } - - public void AddUnresolvedBreak(BreakDescription description, SourcePosition sourcePosition) - { - if (LoopLevel == 0) - { - LuaParseException.BreakNotInsideALoop(ChunkName, sourcePosition); - } - - breakQueue.Add(description); - } - - public void ResolveAllBreaks(byte startPosition, int endPosition, ScopeCompilationContext loopScope) - { - foreach (var description in breakQueue.AsSpan()) - { - ref var instruction = ref Instructions[description.Index]; - if (loopScope.HasCapturedLocalVariables) - { - instruction.A = startPosition; - } - - instruction.SBx = endPosition - description.Index; - } - - breakQueue.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddUnresolvedGoto(GotoDescription description) - { - gotoQueue.Add(description); - } - - public void ResolveGoto(LabelDescription labelDescription) - { - for (int i = 0; i < gotoQueue.Length; i++) - { - var gotoDesc = gotoQueue[i]; - if (gotoDesc.Name.Span.SequenceEqual(labelDescription.Name.Span)) - { - instructions[gotoDesc.JumpInstructionIndex] = Instruction.Jmp(labelDescription.RegisterIndex, labelDescription.Index - gotoDesc.JumpInstructionIndex - 1); - gotoQueue.RemoveAtSwapback(i); - i--; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Chunk ToChunk() - { - // add return - instructions.Add(Instruction.Return(0, 1)); - instructionPositions.Add( new (LastLineDefined, 0)); - Scope.RegisterLocalsToFunction(); - var locals = localVariables.AsSpan().ToArray(); - Array.Sort(locals, (x, y) => x.Index.CompareTo(y.Index)); - var chunk = new Chunk() - { - Name = ChunkName ?? "chunk", - Instructions = instructions.AsSpan().ToArray(), - SourcePositions = instructionPositions.AsSpan().ToArray(), - Constants = constants.AsSpan().ToArray(), - UpValues = upvalues.AsSpan().ToArray(), - Locals = locals, - Functions = functions.AsSpan().ToArray(), - ParameterCount = ParameterCount, - HasVariableArguments = HasVariableArguments, - MaxStackPosition = MaxStackPosition, - LineDefined = LineDefined, - LastLineDefined = LastLineDefined, - }; - - foreach (var function in functions.AsSpan()) - { - function.Parent = chunk; - } - - return chunk; - } - - /// - /// Resets the values ​​held in the context. - /// - public void Reset() - { - Scope.Reset(); - instructions.Clear(); - instructionPositions.Clear(); - constantIndexMap.Clear(); - constants.Clear(); - upvalues.Clear(); - localVariables.Clear(); - functionMap.Clear(); - functions.Clear(); - breakQueue.Clear(); - gotoQueue.Clear(); - ChunkName = null; - LoopLevel = 0; - ParameterCount = 0; - HasVariableArguments = false; - MaxStackPosition = 0; - } - - /// - /// Returns the context object to the pool. - /// - public void Dispose() - { - ParentScope = null; - Pool.Return(this); - } -} \ No newline at end of file diff --git a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs b/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs deleted file mode 100644 index c72dc896..00000000 --- a/src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs +++ /dev/null @@ -1,1287 +0,0 @@ -using Lua.Internal; -using Lua.CodeAnalysis.Syntax; -using Lua.CodeAnalysis.Syntax.Nodes; -using Lua.Runtime; - -namespace Lua.CodeAnalysis.Compilation; - -public sealed class LuaCompiler : ISyntaxNodeVisitor -{ - public static readonly LuaCompiler Default = new(); - - public Chunk Compile(string source, string? chunkName = null) - { - return Compile(LuaSyntaxTree.Parse(source, chunkName), chunkName); - } - - /// - /// Returns a compiled chunk of the syntax tree. - /// - 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() - { - Name = "_ENV".AsMemory(), Id = 0, Index = -1, IsInRegister = false, - }); - - context.ChunkName = chunkName; - - syntaxTree.Accept(this, context.Scope); - return context.ToChunk(); - } - - // Syntax Tree - public bool VisitSyntaxTree(LuaSyntaxTree node, ScopeCompilationContext context) - { - foreach (var childNode in node.Nodes) - { - childNode.Accept(this, context); - } - - return true; - } - - // Literals - public bool VisitNilLiteralNode(NilLiteralNode node, ScopeCompilationContext context) - { - context.PushInstruction(Instruction.LoadNil(context.StackPosition, 1), node.Position, true); - return true; - } - - public bool VisitBooleanLiteralNode(BooleanLiteralNode node, ScopeCompilationContext context) - { - context.PushInstruction(Instruction.LoadBool(context.StackPosition, (ushort)(node.Value ? 1 : 0), 0), node.Position, true); - return true; - } - - public bool VisitNumericLiteralNode(NumericLiteralNode node, ScopeCompilationContext context) - { - var index = context.Function.GetConstantIndex(node.Value); - context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.Position, true); - return true; - } - - public bool VisitStringLiteralNode(StringLiteralNode node, ScopeCompilationContext context) - { - string? str; - if (node.IsShortLiteral) - { - if (!StringHelper.TryFromStringLiteral(node.Text.Span, out str)) - { - throw new LuaParseException(context.Function.ChunkName, node.Position, $"invalid escape sequence near '{node.Text}'"); - } - } - else - { - str = node.Text.ToString(); - } - - var index = context.Function.GetConstantIndex(str); - context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.Position, true); - return true; - } - - // identifier - public bool VisitIdentifierNode(IdentifierNode node, ScopeCompilationContext context) - { - GetOrLoadIdentifier(node.Name, context, node.Position, false); - return true; - } - - // vararg - public bool VisitVariableArgumentsExpressionNode(VariableArgumentsExpressionNode node, ScopeCompilationContext context) - { - CompileVariableArgumentsExpression(node, context, 1); - return true; - } - - void CompileVariableArgumentsExpression(VariableArgumentsExpressionNode node, ScopeCompilationContext context, int resultCount) - { - context.PushInstruction(Instruction.VarArg(context.StackPosition, (ushort)(resultCount == -1 ? 0 : resultCount + 1)), node.Position, true); - } - - // Unary/Binary expression - public bool VisitUnaryExpressionNode(UnaryExpressionNode node, ScopeCompilationContext context) - { - var b = context.StackPosition; - node.Node.Accept(this, context); - - switch (node.Operator) - { - case UnaryOperator.Negate: - context.PushInstruction(Instruction.Unm(b, b), node.Position); - break; - case UnaryOperator.Not: - context.PushInstruction(Instruction.Not(b, b), node.Position); - break; - case UnaryOperator.Length: - context.PushInstruction(Instruction.Len(b, b), node.Position); - break; - } - - return true; - } - - public bool VisitBinaryExpressionNode(BinaryExpressionNode node, ScopeCompilationContext context) - { - var r = context.StackPosition; - if (node.OperatorType is BinaryOperator.And or BinaryOperator.Or) - { - byte a; - if (node.LeftNode is IdentifierNode leftIdentifier) - { - a = GetOrLoadIdentifier(leftIdentifier.Name, context, leftIdentifier.Position, true); - } - else - { - node.LeftNode.Accept(this, context); - a = context.StackTopPosition; - } - - context.PushInstruction(Instruction.Test(a, 0), node.Position); - if (node.OperatorType is BinaryOperator.Or) - { - context.PushInstruction(Instruction.Jmp(0, 2), node.Position); - context.PushInstruction(Instruction.Move(r, a), node.Position); - } - - var testJmpIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.Jmp(0, 0), node.Position); - - context.StackPosition = r; - node.RightNode.Accept(this, context); - - context.Function.Instructions[testJmpIndex].SBx = context.Function.Instructions.Length - testJmpIndex - 1; - } - else - { - var b = (ushort)GetRKIndex(node.LeftNode, context); - var c = (ushort)GetRKIndex(node.RightNode, context); - - switch (node.OperatorType) - { - case BinaryOperator.Addition: - context.PushInstruction(Instruction.Add(r, b, c), node.Position); - break; - case BinaryOperator.Subtraction: - context.PushInstruction(Instruction.Sub(r, b, c), node.Position); - break; - case BinaryOperator.Multiplication: - context.PushInstruction(Instruction.Mul(r, b, c), node.Position); - break; - case BinaryOperator.Division: - context.PushInstruction(Instruction.Div(r, b, c), node.Position); - break; - case BinaryOperator.Modulo: - context.PushInstruction(Instruction.Mod(r, b, c), node.Position); - break; - case BinaryOperator.Exponentiation: - context.PushInstruction(Instruction.Pow(r, b, c), node.Position); - break; - case BinaryOperator.Equality: - context.PushInstruction(Instruction.Eq(1, b, c), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position); - break; - case BinaryOperator.Inequality: - context.PushInstruction(Instruction.Eq(0, b, c), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position); - break; - case BinaryOperator.GreaterThan: - context.PushInstruction(Instruction.Lt(1, c, b), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position); - break; - case BinaryOperator.GreaterThanOrEqual: - context.PushInstruction(Instruction.Le(1, c, b), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position); - break; - case BinaryOperator.LessThan: - context.PushInstruction(Instruction.Lt(1, b, c), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position); - break; - case BinaryOperator.LessThanOrEqual: - context.PushInstruction(Instruction.Le(1, b, c), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 1, 1), node.Position); - context.PushInstruction(Instruction.LoadBool(r, 0, 0), node.Position); - break; - case BinaryOperator.Concat: - context.PushInstruction(Instruction.Concat(r, b, c), node.Position); - break; - } - - context.StackPosition = (byte)(r + 1); - } - - return true; - } - - public bool VisitGroupedExpressionNode(GroupedExpressionNode node, ScopeCompilationContext context) - { - return node.Expression.Accept(this, context); - } - - // table - public bool VisitTableConstructorExpressionNode(TableConstructorExpressionNode node, ScopeCompilationContext context) - { - var tableRegisterIndex = context.StackPosition; - var newTableInstructionIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.NewTable(tableRegisterIndex, 0, 0), node.Position, true); - - var currentArrayChunkSize = 0; - ushort hashMapSize = 0; - ushort arrayBlock = 1; - - ListTableConstructorField? lastField = null; - if (node.Fields.LastOrDefault() is ListTableConstructorField t) - { - lastField = t; - } - - foreach (var group in node.Fields.GroupConsecutiveBy(x => x.GetType())) - { - foreach (var field in group) - { - var p = context.StackPosition; - - switch (field) - { - case ListTableConstructorField listItem: - context.StackPosition = (byte)(p + currentArrayChunkSize - 50 * (arrayBlock - 1)); - - // For the last element, we need to take into account variable arguments and multiple return values. - if (listItem == lastField) - { - bool isFixedItems = true; - switch (listItem.Expression) - { - case CallFunctionExpressionNode call: - CompileCallFunctionExpression(call, context, false, -1); - isFixedItems = false; - break; - case CallTableMethodExpressionNode method: - CompileTableMethod(method, context, false, -1); - break; - case VariableArgumentsExpressionNode varArg: - CompileVariableArgumentsExpression(varArg, context, -1); - isFixedItems = false; - break; - default: - listItem.Expression.Accept(this, context); - break; - } - - context.PushInstruction(Instruction.SetList(tableRegisterIndex, (ushort)(isFixedItems ? context.StackTopPosition - tableRegisterIndex : 0), arrayBlock), listItem.Position); - currentArrayChunkSize = 0; - } - else - { - listItem.Expression.Accept(this, context); - - currentArrayChunkSize++; - - if (currentArrayChunkSize == 50) - { - context.PushInstruction(Instruction.SetList(tableRegisterIndex, 50, arrayBlock), listItem.Position); - currentArrayChunkSize = 0; - arrayBlock++; - } - } - - break; - case RecordTableConstructorField recordItem: - recordItem.ValueExpression.Accept(this, context); - var keyConstIndex = context.Function.GetConstantIndex(recordItem.Key) + 256; - - context.PushInstruction(Instruction.SetTable(tableRegisterIndex, (ushort)keyConstIndex, p), recordItem.Position); - hashMapSize++; - break; - case GeneralTableConstructorField generalItem: - var keyIndex = context.StackPosition; - generalItem.KeyExpression.Accept(this, context); - var valueIndex = context.StackPosition; - generalItem.ValueExpression.Accept(this, context); - - context.PushInstruction(Instruction.SetTable(tableRegisterIndex, keyIndex, valueIndex), generalItem.Position); - hashMapSize++; - break; - default: - throw new NotSupportedException(); - } - - context.StackPosition = p; - } - - if (currentArrayChunkSize > 0) - { - context.PushInstruction(Instruction.SetList(tableRegisterIndex, (ushort)currentArrayChunkSize, arrayBlock), node.Position); - currentArrayChunkSize = 0; - arrayBlock = 1; - } - } - - context.Function.Instructions[newTableInstructionIndex].B = (ushort)(currentArrayChunkSize + (arrayBlock - 1) * 50); - context.Function.Instructions[newTableInstructionIndex].C = hashMapSize; - - return true; - } - - public bool VisitTableIndexerAccessExpressionNode(TableIndexerAccessExpressionNode node, ScopeCompilationContext context) - { - // load table - var tablePosition = context.StackPosition; - node.TableNode.Accept(this, context); - - // load key - var keyPosition = (ushort)GetRKIndex(node.KeyNode, context); - - // push interuction - context.PushInstruction(Instruction.GetTable(tablePosition, tablePosition, keyPosition), node.Position); - context.StackPosition = (byte)(tablePosition + 1); - - return true; - } - - public bool VisitTableMemberAccessExpressionNode(TableMemberAccessExpressionNode node, ScopeCompilationContext context) - { - // load table - var tablePosition = context.StackPosition; - node.TableNode.Accept(this, context); - - // load key - var keyIndex = context.Function.GetConstantIndex(node.MemberName) + 256; - - // push interuction - context.PushInstruction(Instruction.GetTable(tablePosition, tablePosition, (ushort)keyIndex), node.Position); - context.StackPosition = (byte)(tablePosition + 1); - - return true; - } - - public bool VisitCallTableMethodExpressionNode(CallTableMethodExpressionNode node, ScopeCompilationContext context) - { - CompileTableMethod(node, context, false, 1); - return true; - } - - public bool VisitCallTableMethodStatementNode(CallTableMethodStatementNode node, ScopeCompilationContext context) - { - CompileTableMethod(node.Expression, context, false, 0); - return true; - } - - void CompileTableMethod(CallTableMethodExpressionNode node, ScopeCompilationContext context, bool isTailCall, int resultCount) - { - // load table - var tablePosition = context.StackPosition; - node.TableNode.Accept(this, context); - - // load key - var keyIndex = context.Function.GetConstantIndex(node.MethodName) + 256; - - // get closure - context.PushInstruction(Instruction.Self(tablePosition, tablePosition, (ushort)keyIndex), node.Position); - context.StackPosition = (byte)(tablePosition + 2); - - // load arguments - var b = node.ArgumentNodes.Length + 2; - if (node.ArgumentNodes.Length > 0 && !IsFixedNumberOfReturnValues(node.ArgumentNodes[^1])) - { - b = 0; - } - - CompileExpressionList(node, node.ArgumentNodes, b - 2, context); - - // push call interuction - if (isTailCall) - { - context.PushInstruction(Instruction.TailCall(tablePosition, (ushort)b, 0), node.Position); - context.StackPosition = tablePosition; - } - else - { - context.PushInstruction(Instruction.Call(tablePosition, (ushort)b, (ushort)(resultCount < 0 ? 0 : resultCount + 1)), node.Position); - context.StackPosition = (byte)(tablePosition + resultCount); - } - } - - // return - public bool VisitReturnStatementNode(ReturnStatementNode node, ScopeCompilationContext context) - { - ushort b; - - // tail call - if (node.Nodes.Length == 1) - { - var lastNode = node.Nodes[^1]; - - if (lastNode is CallFunctionExpressionNode call) - { - CompileCallFunctionExpression(call, context, true, -1); - return true; - } - else if (lastNode is CallTableMethodExpressionNode callMethod) - { - CompileTableMethod(callMethod, context, true, -1); - return true; - } - } - - b = node.Nodes.Length > 0 && !IsFixedNumberOfReturnValues(node.Nodes[^1]) - ? (ushort)0 - : (ushort)(node.Nodes.Length + 1); - - var a = context.StackPosition; - - CompileExpressionList(node, node.Nodes, b - 1, context); - - context.PushInstruction(Instruction.Return(a, b), node.Position); - - return true; - } - - // assignment - public bool VisitLocalAssignmentStatementNode(LocalAssignmentStatementNode node, ScopeCompilationContext context) - { - var startPosition = context.StackPosition; - CompileExpressionList(node, node.RightNodes, node.LeftNodes.Length, context); - - for (int i = 0; i < node.Identifiers.Length; i++) - { - context.StackPosition = (byte)(startPosition + i + 1); - - var identifier = node.Identifiers[i]; - - if (context.TryGetLocalVariableInThisScope(identifier.Name, out var variable)) - { - // assign local variable - context.PushInstruction(Instruction.Move(variable.RegisterIndex, (ushort)(context.StackPosition - 1)), node.Position, true); - } - else - { - // register local variable - context.AddLocalVariable(identifier.Name, new() { RegisterIndex = (byte)(context.StackPosition - 1), StartPc = context.Function.Instructions.Length, }); - } - } - - return true; - } - - public bool VisitAssignmentStatementNode(AssignmentStatementNode node, ScopeCompilationContext context) - { - var startPosition = context.StackPosition; - - CompileExpressionList(node, node.RightNodes, node.LeftNodes.Length, context); - - for (int i = 0; i < node.LeftNodes.Length; i++) - { - context.StackPosition = (byte)(startPosition + i + 1); - var leftNode = node.LeftNodes[i]; - - switch (leftNode) - { - case IdentifierNode identifier: - { - if (context.TryGetLocalVariable(identifier.Name, out var variable)) - { - // assign local variable - context.PushInstruction(Instruction.Move(variable.RegisterIndex, (ushort)(context.StackPosition - 1)), node.Position, true); - } - else if (context.Function.TryGetUpValue(identifier.Name, out var upValue)) - { - // assign upvalue - context.PushInstruction(Instruction.SetUpVal((byte)(context.StackPosition - 1), (ushort)upValue.Id), node.Position); - } - else if (context.TryGetLocalVariable("_ENV".AsMemory(), out variable)) - { - // assign env element - var index = context.Function.GetConstantIndex(identifier.Name.ToString()) + 256; - context.PushInstruction(Instruction.SetTable(variable.RegisterIndex, (ushort)index, (ushort)(context.StackPosition - 1)), node.Position); - } - else - { - // assign global variable - var index = context.Function.GetConstantIndex(identifier.Name.ToString()) + 256; - context.PushInstruction(Instruction.SetTabUp(0, (ushort)index, (ushort)(context.StackPosition - 1)), node.Position); - } - } - break; - case TableIndexerAccessExpressionNode tableIndexer: - { - var valueIndex = context.StackPosition - 1; - tableIndexer.TableNode.Accept(this, context); - var tableIndex = context.StackPosition - 1; - tableIndexer.KeyNode.Accept(this, context); - var keyIndex = context.StackPosition - 1; - context.PushInstruction(Instruction.SetTable((byte)tableIndex, (ushort)keyIndex, (ushort)valueIndex), node.Position); - } - break; - case TableMemberAccessExpressionNode tableMember: - { - var valueIndex = context.StackPosition - 1; - tableMember.TableNode.Accept(this, context); - var tableIndex = context.StackPosition - 1; - var keyIndex = context.Function.GetConstantIndex(tableMember.MemberName) + 256; - context.PushInstruction(Instruction.SetTable((byte)tableIndex, (ushort)keyIndex, (ushort)valueIndex), node.Position); - } - break; - default: - throw new LuaParseException(default, default, "An error occurred while parsing the code"); // TODO: add message - } - } - - context.StackPosition = startPosition; - - return true; - } - - // function call - public bool VisitCallFunctionStatementNode(CallFunctionStatementNode node, ScopeCompilationContext context) - { - CompileCallFunctionExpression(node.Expression, context, false, 0); - return true; - } - - public bool VisitCallFunctionExpressionNode(CallFunctionExpressionNode node, ScopeCompilationContext context) - { - CompileCallFunctionExpression(node, context, false, 1); - return true; - } - - void CompileCallFunctionExpression(CallFunctionExpressionNode node, ScopeCompilationContext context, bool isTailCall, int resultCount) - { - // get closure - var r = context.StackPosition; - node.FunctionNode.Accept(this, context); - - // load arguments - var b = node.ArgumentNodes.Length + 1; - if (node.ArgumentNodes.Length > 0 && !IsFixedNumberOfReturnValues(node.ArgumentNodes[^1])) - { - b = 0; - } - - CompileExpressionList(node, node.ArgumentNodes, b - 1, context); - - // push call interuction - if (isTailCall) - { - context.PushInstruction(Instruction.TailCall(r, (ushort)b, 0), node.Position); - context.StackPosition = r; - } - else - { - context.PushInstruction(Instruction.Call(r, (ushort)b, (ushort)(resultCount == -1 ? 0 : resultCount + 1)), node.Position); - context.StackPosition = (byte)(r + resultCount); - } - } - - // 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.EndPosition.Line); - - // push closure instruction - context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.EndPosition, true); - - return true; - } - - public bool VisitLocalFunctionDeclarationStatementNode(LocalFunctionDeclarationStatementNode node, ScopeCompilationContext context) - { - // assign local variable - context.AddLocalVariable(node.Name, new() { RegisterIndex = context.StackPosition, StartPc = context.Function.Instructions.Length, }); - - // compile function - var funcIndex = CompileFunctionProto(node.Name, context, node.ParameterNodes, node.Nodes, node.ParameterNodes.Length, node.HasVariableArguments, false, node.LineDefined, node.EndPosition.Line); - - // push closure instruction - 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.EndPosition.Line); - - // add closure - var index = context.Function.GetConstantIndex(node.Name.ToString()); - - // push closure instruction - context.PushInstruction(Instruction.Closure(context.StackPosition, funcIndex), node.EndPosition, true); - - if (context.TryGetLocalVariableInThisScope(node.Name, out var variable)) - { - // assign local variable - context.PushInstruction(Instruction.Move(variable.RegisterIndex, (ushort)(context.StackPosition - 1)), node.Position, true); - } - else - { - // assign global variable - context.PushInstruction(Instruction.SetTabUp(0, (ushort)(index + 256), (ushort)(context.StackPosition - 1)), node.Position); - } - - return true; - } - - 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.EndPosition.Line); - - // add closure - var index = context.Function.GetConstantIndex(funcIdentifier.Name.ToString()); - - var r = context.StackPosition; - - // assign global variable - var first = node.MemberPath[0]; - var tableIndex = GetOrLoadIdentifier(first.Name, context, first.Position, true); - - for (int i = 1; i < node.MemberPath.Length - 1; i++) - { - var member = node.MemberPath[i]; - var constant = context.Function.GetConstantIndex(member.Name.ToString()); - context.PushInstruction(Instruction.GetTable(context.StackPosition, tableIndex, (ushort)(constant + 256)), member.Position, true); - tableIndex = context.StackTopPosition; - } - - // push closure instruction - var closureIndex = context.StackPosition; - context.PushInstruction(Instruction.Closure(closureIndex, funcIndex), node.EndPosition, true); - - // set table - context.PushInstruction(Instruction.SetTable(tableIndex, (ushort)(index + 256), closureIndex), funcIdentifier.Position); - - context.StackPosition = r; - return true; - } - - 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) - { - funcContext.Scope.AddLocalVariable("self".AsMemory(), new() { RegisterIndex = 0, StartPc = 0, }); - - funcContext.Scope.StackPosition++; - } - - // add arguments - for (int i = 0; i < parameters.Length; i++) - { - var parameter = parameters[i]; - funcContext.Scope.AddLocalVariable(parameter.Name, new() { RegisterIndex = (byte)(i + (hasSelfParameter ? 1 : 0)), StartPc = 0, }); - - funcContext.Scope.StackPosition++; - } - - foreach (var statement in statements) - { - statement.Accept(this, funcContext.Scope); - } - - // compile function - var chunk = funcContext.ToChunk(); - - int index; - if (functionName.Length == 0) - { - // anonymous function - context.Function.AddFunctionProto(chunk, out index); - } - else - { - context.Function.AddOrSetFunctionProto(functionName, chunk, out index); - } - - return index; - } - - // control statements - public bool VisitDoStatementNode(DoStatementNode node, ScopeCompilationContext context) - { - using var scopeContext = context.CreateChildScope(); - - foreach (var childNode in node.StatementNodes) - { - childNode.Accept(this, scopeContext); - } - - scopeContext.TryPushCloseUpValue(scopeContext.StackTopPosition, node.Position); - - return true; - } - - public bool VisitBreakStatementNode(BreakStatementNode node, ScopeCompilationContext context) - { - context.Function.AddUnresolvedBreak(new() { Index = context.Function.Instructions.Length }, node.Position); - context.PushInstruction(Instruction.Jmp(0, 0), node.Position); - - return true; - } - - public bool VisitIfStatementNode(IfStatementNode node, ScopeCompilationContext context) - { - using var endJumpIndexList = new PooledList(8); - var hasElse = node.ElseNodes.Length > 0; - var stackPositionToClose = (byte)(context.StackPosition + 1); - // if - using (var scopeContext = context.CreateChildScope()) - { - CompileConditionNode(node.IfNode.ConditionNode, scopeContext, true, node.IfNode.Position); - - var ifPosition = scopeContext.Function.Instructions.Length; - scopeContext.PushInstruction(Instruction.Jmp(0, 0), node.IfNode.Position); - - foreach (var childNode in node.IfNode.ThenNodes) - { - childNode.Accept(this, scopeContext); - } - - stackPositionToClose = scopeContext.HasCapturedLocalVariables ? stackPositionToClose : (byte)0; - if (hasElse) - { - endJumpIndexList.Add(scopeContext.Function.Instructions.Length); - scopeContext.PushInstruction(Instruction.Jmp(stackPositionToClose, 0), node.IfNode.ThenNodes[^1].Position, true); - } - else - { - scopeContext.TryPushCloseUpValue(stackPositionToClose, node.Position); - } - - scopeContext.Function.Instructions[ifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - ifPosition; - } - - // elseif - foreach (var elseIf in node.ElseIfNodes) - { - using var scopeContext = context.CreateChildScope(); - - CompileConditionNode(elseIf.ConditionNode, scopeContext, true); - - var elseifPosition = scopeContext.Function.Instructions.Length; - scopeContext.PushInstruction(Instruction.Jmp(0, 0), elseIf.Position); - - foreach (var childNode in elseIf.ThenNodes) - { - childNode.Accept(this, scopeContext); - } - - stackPositionToClose = scopeContext.HasCapturedLocalVariables ? stackPositionToClose : (byte)0; - // skip if node doesn't have else statements - if (hasElse) - { - endJumpIndexList.Add(scopeContext.Function.Instructions.Length); - scopeContext.PushInstruction(Instruction.Jmp(stackPositionToClose, 0), elseIf.Position); - } - else - { - scopeContext.TryPushCloseUpValue(stackPositionToClose, elseIf.Position); - } - - scopeContext.Function.Instructions[elseifPosition].SBx = scopeContext.Function.Instructions.Length - 1 - elseifPosition; - } - - // else nodes - using (var scopeContext = context.CreateChildScope()) - { - foreach (var childNode in node.ElseNodes) - { - childNode.Accept(this, scopeContext); - } - - scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position); - } - - // set JMP sBx - foreach (var index in endJumpIndexList.AsSpan()) - { - context.Function.Instructions[index].SBx = context.Function.Instructions.Length - 1 - index; - } - - return true; - } - - public bool VisitRepeatStatementNode(RepeatStatementNode node, ScopeCompilationContext context) - { - var startIndex = context.Function.Instructions.Length; - - context.Function.LoopLevel++; - - using var scopeContext = context.CreateChildScope(); - var stackPosition = scopeContext.StackPosition; - foreach (var childNode in node.Nodes) - { - childNode.Accept(this, scopeContext); - } - - CompileConditionNode(node.ConditionNode, scopeContext, true); - var a = scopeContext.HasCapturedLocalVariables ? (byte)(stackPosition + 1) : (byte)0; - 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--; - - // resolve break statements inside repeat block - context.Function.ResolveAllBreaks(a, context.Function.Instructions.Length - 1, scopeContext); - - return true; - } - - public bool VisitWhileStatementNode(WhileStatementNode node, ScopeCompilationContext context) - { - var conditionIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.Jmp(0, 0), node.Position); - - context.Function.LoopLevel++; - - using var scopeContext = context.CreateChildScope(); - var stackPosition = scopeContext.StackPosition; - - foreach (var childNode in node.Nodes) - { - childNode.Accept(this, scopeContext); - } - - context.Function.LoopLevel--; - - // set JMP sBx - scopeContext.Function.Instructions[conditionIndex].SBx = scopeContext.Function.Instructions.Length - 1 - conditionIndex; - - CompileConditionNode(node.ConditionNode, scopeContext, false); - var a = scopeContext.HasCapturedLocalVariables ? (byte)(1 + stackPosition) : (byte)0; - scopeContext.PushInstruction(Instruction.Jmp(a, conditionIndex - context.Function.Instructions.Length), node.Position); - scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position); - - // resolve break statements inside while block - context.Function.ResolveAllBreaks(scopeContext.StackPosition, context.Function.Instructions.Length - 1, scopeContext); - - return true; - } - - public bool VisitNumericForStatementNode(NumericForStatementNode node, ScopeCompilationContext context) - { - var startPosition = context.StackPosition; - - node.InitNode.Accept(this, context); - node.LimitNode.Accept(this, context); - if (node.StepNode != null) - { - node.StepNode.Accept(this, context); - } - else - { - var index = context.Function.GetConstantIndex(1); - context.PushInstruction(Instruction.LoadK(context.StackPosition, index), node.DoPosition, true); - } - - var prepIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.ForPrep(startPosition, 0), node.DoPosition, true); - - // compile statements - 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 = (byte)(startPosition + 3), StartPc = context.Function.Instructions.Length, }); - - foreach (var childNode in node.StatementNodes) - { - childNode.Accept(this, scopeContext); - } - - scopeContext.TryPushCloseUpValue((byte)(startPosition + 1), node.Position); - } - context.Function.LoopLevel--; - - // set ForPrep - context.Function.Instructions[prepIndex].SBx = context.Function.Instructions.Length - prepIndex - 1; - - // push ForLoop - context.PushInstruction(Instruction.ForLoop(startPosition, prepIndex - context.Function.Instructions.Length), node.Position); - - context.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext); - - context.StackPosition = startPosition; - - return true; - } - - public bool VisitGenericForStatementNode(GenericForStatementNode node, ScopeCompilationContext context) - { - // get iterator - var startPosition = context.StackPosition; - CompileExpressionList(node, node.ExpressionNodes, 3, context); - - // jump to TFORCALL - var startJumpIndex = context.Function.Instructions.Length; - context.PushInstruction(Instruction.Jmp(0, 0), node.DoPosition); - - // compile statements - context.Function.LoopLevel++; - using var scopeContext = context.CreateChildScope(); - { - 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), StartPc = context.Function.Instructions.Length, }); - } - - foreach (var childNode in node.StatementNodes) - { - childNode.Accept(this, scopeContext); - } - - scopeContext.TryPushCloseUpValue(scopeContext.StackPosition, node.Position); - } - context.Function.LoopLevel--; - - // set jump - context.Function.Instructions[startJumpIndex].SBx = context.Function.Instructions.Length - startJumpIndex - 1; - - // 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.Function.ResolveAllBreaks((byte)(startPosition + 1), context.Function.Instructions.Length - 1, scopeContext); - context.StackPosition = startPosition; - - return true; - } - - public bool VisitLabelStatementNode(LabelStatementNode node, ScopeCompilationContext context) - { - var desc = new LabelDescription() { Name = node.Name, Index = context.Function.Instructions.Length, RegisterIndex = context.StackPosition }; - - context.AddLabel(desc); - context.Function.ResolveGoto(desc); - - return true; - } - - public bool VisitGotoStatementNode(GotoStatementNode node, ScopeCompilationContext context) - { - if (context.TryGetLabel(node.Name, out var description)) - { - context.PushInstruction(Instruction.Jmp(description.RegisterIndex, description.Index - context.Function.Instructions.Length - 1), node.Position); - } - else - { - context.Function.AddUnresolvedGoto(new() { Name = node.Name, JumpInstructionIndex = context.Function.Instructions.Length }); - - // add uninitialized jmp instruction - context.PushInstruction(Instruction.Jmp(0, 0), node.Position); - } - - return true; - } - - static byte GetOrLoadIdentifier(ReadOnlyMemory name, ScopeCompilationContext context, SourcePosition sourcePosition, bool dontLoadLocalVariable) - { - var p = context.StackPosition; - - if (context.TryGetLocalVariable(name, out var variable)) - { - if (dontLoadLocalVariable) - { - return variable.RegisterIndex; - } - else if (p == variable.RegisterIndex) - { - context.StackPosition++; - return p; - } - else - { - context.PushInstruction(Instruction.Move(p, variable.RegisterIndex), sourcePosition, true); - return p; - } - } - else if (context.Function.TryGetUpValue(name, out var upValue)) - { - context.PushInstruction(Instruction.GetUpVal(p, (ushort)upValue.Id), sourcePosition, true); - return p; - } - else if (context.TryGetLocalVariable("_ENV".AsMemory(), out variable)) - { - var keyStringIndex = context.Function.GetConstantIndex(name.ToString()) + 256; - context.PushInstruction(Instruction.GetTable(p, variable.RegisterIndex, (ushort)keyStringIndex), sourcePosition, true); - return p; - } - else - { - context.Function.TryGetUpValue("_ENV".AsMemory(), out upValue); - var index = context.Function.GetConstantIndex(name.ToString()) + 256; - context.PushInstruction(Instruction.GetTabUp(p, (ushort)upValue.Id, (ushort)index), sourcePosition, true); - return p; - } - } - - uint GetRKIndex(ExpressionNode node, ScopeCompilationContext context) - { - if (node is IdentifierNode identifier) - { - return GetOrLoadIdentifier(identifier.Name, context, identifier.Position, true); - } - else if (TryGetConstant(node, context, out var constant)) - { - return context.Function.GetConstantIndex(constant) + 256; - } - else - { - node.Accept(this, context); - return context.StackTopPosition; - } - } - - static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, out LuaValue value) - { - switch (node) - { - case NilLiteralNode: - value = LuaValue.Nil; - return true; - case BooleanLiteralNode booleanLiteral: - value = booleanLiteral.Value; - return true; - case NumericLiteralNode numericLiteral: - value = numericLiteral.Value; - return true; - case StringLiteralNode stringLiteral: - if (stringLiteral.IsShortLiteral) - { - if (!StringHelper.TryFromStringLiteral(stringLiteral.Text.Span, out var str)) - { - throw new LuaParseException(context.Function.ChunkName, stringLiteral.Position, $"invalid escape sequence near '{stringLiteral.Text}'"); - } - - value = str; - } - else - { - value = stringLiteral.Text.ToString(); - } - - return true; - case UnaryExpressionNode unaryExpression: - if (TryGetConstant(unaryExpression.Node, context, out var unaryNodeValue)) - { - switch (unaryExpression.Operator) - { - case UnaryOperator.Negate: - if (unaryNodeValue.TryRead(out var d1)) - { - value = -d1; - return true; - } - - break; - case UnaryOperator.Not: - if (unaryNodeValue.TryRead(out var b)) - { - value = !b; - return true; - } - - break; - } - } - - break; - case BinaryExpressionNode binaryExpression: - if (TryGetConstant(binaryExpression.LeftNode, context, out var leftValue) && - TryGetConstant(binaryExpression.RightNode, context, out var rightValue)) - { - switch (binaryExpression.OperatorType) - { - case BinaryOperator.Addition: - { - if (leftValue.TryRead(out var d1) && rightValue.TryRead(out var d2)) - { - value = d1 + d2; - return true; - } - } - break; - case BinaryOperator.Subtraction: - { - if (leftValue.TryRead(out var d1) && rightValue.TryRead(out var d2)) - { - value = d1 - d2; - return true; - } - } - break; - case BinaryOperator.Multiplication: - { - if (leftValue.TryRead(out var d1) && rightValue.TryRead(out var d2)) - { - value = d1 * d2; - return true; - } - } - break; - case BinaryOperator.Division: - { - if (leftValue.TryRead(out var d1) && rightValue.TryRead(out var d2) && d2 != 0) - { - value = d1 / d2; - return true; - } - } - break; - } - } - - break; - } - - value = default; - return false; - } - - static bool IsFixedNumberOfReturnValues(ExpressionNode node) - { - return node is not (CallFunctionExpressionNode or CallTableMethodExpressionNode or VariableArgumentsExpressionNode); - } - - /// - /// Compiles a conditional boolean branch: if true (or false), the next instruction added is skipped. - /// - /// Condition node - /// Context - /// If true, generates an instruction sequence that skips the next instruction if the condition is false. - /// Position of the test instruction - void CompileConditionNode(ExpressionNode node, ScopeCompilationContext context, bool falseIsSkip, SourcePosition? testPosition = null) - { - if (node is BinaryExpressionNode binaryExpression) - { - switch (binaryExpression.OperatorType) - { - case BinaryOperator.Equality: - { - var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context); - var c = (ushort)GetRKIndex(binaryExpression.RightNode, context); - context.PushInstruction(Instruction.Eq(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position); - return; - } - case BinaryOperator.Inequality: - { - var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context); - var c = (ushort)GetRKIndex(binaryExpression.RightNode, context); - context.PushInstruction(Instruction.Eq(falseIsSkip ? (byte)1 : (byte)0, b, c), node.Position); - return; - } - case BinaryOperator.LessThan: - { - var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context); - var c = (ushort)GetRKIndex(binaryExpression.RightNode, context); - context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position); - return; - } - case BinaryOperator.LessThanOrEqual: - { - var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context); - var c = (ushort)GetRKIndex(binaryExpression.RightNode, context); - context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position); - return; - } - case BinaryOperator.GreaterThan: - { - var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context); - var c = (ushort)GetRKIndex(binaryExpression.RightNode, context); - context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position); - return; - } - case BinaryOperator.GreaterThanOrEqual: - { - var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context); - var c = (ushort)GetRKIndex(binaryExpression.RightNode, context); - context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position); - return; - } - } - } - - node.Accept(this, context); - 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) - { - var isLastFunction = false; - for (int i = 0; i < expressions.Length; i++) - { - var expression = expressions[i]; - var isLast = i == expressions.Length - 1; - var resultCount = isLast ? (minimumCount == -1 ? -1 : minimumCount - i) : 1; - - if (expression is CallFunctionExpressionNode call) - { - CompileCallFunctionExpression(call, context, false, resultCount); - isLastFunction = isLast; - } - else if (expression is CallTableMethodExpressionNode method) - { - CompileTableMethod(method, context, false, resultCount); - isLastFunction = isLast; - } - else if (expression is VariableArgumentsExpressionNode varArg) - { - CompileVariableArgumentsExpression(varArg, context, resultCount); - isLastFunction = isLast; - } - else if (TryGetConstant(expression, context, out var constant)) - { - var index = context.Function.GetConstantIndex(constant); - context.PushInstruction(Instruction.LoadK(context.StackPosition, index), expression.Position, true); - isLastFunction = false; - } - else - { - expression.Accept(this, context); - isLastFunction = false; - } - } - - // fill space with nil - var varCount = minimumCount - expressions.Length; - if (varCount > 0 && !isLastFunction) - { - context.PushInstruction(Instruction.LoadNil(context.StackPosition, (ushort)varCount), rootNode.Position); - context.StackPosition = (byte)(context.StackPosition + varCount); - } - } -} \ No newline at end of file diff --git a/src/Lua/CodeAnalysis/Compilation/Parser.cs b/src/Lua/CodeAnalysis/Compilation/Parser.cs new file mode 100644 index 00000000..c68e6aaa --- /dev/null +++ b/src/Lua/CodeAnalysis/Compilation/Parser.cs @@ -0,0 +1,1026 @@ +using Lua.Internal; +using Lua.Runtime; +using System.Buffers; + +namespace Lua.CodeAnalysis.Compilation; + +using System.Runtime.CompilerServices; +using static Lua.Runtime.Instruction; +using static System.Diagnostics.Debug; +using static Function; +using static Scanner; +using static Constants; + +internal class Parser : IPoolNode, IDisposable +{ + /// inline + internal Scanner Scanner; + + internal int T => Scanner.Token.T; + internal bool TestNext(int token) => Scanner.TestNext(token); + internal void Next() => Scanner.Next(); + + internal Function Function = null!; + internal FastListCore ActiveVariables; + internal FastListCore