| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- using System.Runtime.CompilerServices;
- using Lua.Runtime;
- using Lua.Internal;
- namespace Lua.Standard;
- public class DebugLibrary
- {
- public static readonly DebugLibrary Instance = new();
- public DebugLibrary()
- {
- var libraryName = "debug";
- Functions =
- [
- new(libraryName, "getlocal", GetLocal),
- new(libraryName, "setlocal", SetLocal),
- new(libraryName, "getupvalue", GetUpValue),
- new(libraryName, "setupvalue", SetUpValue),
- new(libraryName, "getmetatable", GetMetatable),
- new(libraryName, "setmetatable", SetMetatable),
- new(libraryName, "getuservalue", GetUserValue),
- new(libraryName, "setuservalue", SetUserValue),
- new(libraryName, "traceback", Traceback),
- new(libraryName, "getregistry", GetRegistry),
- new(libraryName, "upvalueid", UpValueId),
- new(libraryName, "upvaluejoin", UpValueJoin),
- new(libraryName, "gethook", GetHook),
- new(libraryName, "sethook", SetHook),
- new(libraryName, "getinfo", GetInfo),
- ];
- }
- public readonly LibraryFunction[] Functions;
- static LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset)
- {
- if (context.ArgumentCount < 1)
- {
- argOffset = 0;
- return context.Thread;
- }
- if (context.GetArgument(0).TryRead<LuaThread>(out var thread))
- {
- argOffset = 1;
- return thread;
- }
- argOffset = 0;
- return context.Thread;
- }
- static ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name)
- {
- if (index == 0)
- {
- name = null;
- return ref Unsafe.NullRef<LuaValue>();
- }
- var callStack = thread.GetCallStackFrames();
- var frame = callStack[^(level + 1)];
- if (index < 0)
- {
- index = -index - 1;
- var frameVariableArgumentCount = frame.VariableArgumentCount;
- if (frameVariableArgumentCount > 0 && index < frameVariableArgumentCount)
- {
- name = "(*vararg)";
- return ref thread.Stack.Get(frame.Base - frameVariableArgumentCount + index);
- }
- name = null;
- return ref Unsafe.NullRef<LuaValue>();
- }
- index -= 1;
- var frameBase = frame.Base;
- if (frame.Function is LuaClosure closure)
- {
- var locals = closure.Proto.LocalVariables;
- var nextFrame = callStack[^level];
- var currentPc = nextFrame.CallerInstructionIndex;
- {
- int nextFrameBase = (closure.Proto.Code[currentPc].OpCode is OpCode.Call or OpCode.TailCall) ? nextFrame.Base - 1 : nextFrame.Base;
- if (nextFrameBase - 1 < frameBase + index)
- {
- name = null;
- return ref Unsafe.NullRef<LuaValue>();
- }
- }
- var localId = index + 1;
- foreach (var l in locals)
- {
- if (currentPc < l.StartPc) break;
- if (l.EndPc <= currentPc) continue;
- localId--;
- if (localId == 0)
- {
- name = l.Name;
- return ref thread.Stack.Get(frameBase + index);
- }
- }
- }
- else
- {
- int nextFrameBase = level != 0 ? callStack[^level].Base : thread.Stack.Count;
- if (nextFrameBase - 1 < frameBase + index)
- {
- name = null;
- return ref Unsafe.NullRef<LuaValue>();
- }
- }
- name = "(*temporary)";
- return ref thread.Stack.Get(frameBase + index);
- }
- public ValueTask<int> GetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- static LuaValue GetParam(LuaFunction function, int index)
- {
- if (function is LuaClosure closure)
- {
- var paramCount = closure.Proto.ParameterCount;
- if (0 <= index && index < paramCount)
- {
- return closure.Proto.LocalVariables[index].Name;
- }
- }
- return LuaValue.Nil;
- }
- var thread = GetLuaThread(context, out var argOffset);
- var index = context.GetArgument<int>(argOffset + 1);
- if (context.GetArgument(argOffset).TryReadFunction(out var f))
- {
- return new(context.Return(GetParam(f, index - 1)));
- }
- var level = context.GetArgument<int>(argOffset);
- if (level < 0 || level >= thread.GetCallStackFrames().Length)
- {
- context.ThrowBadArgument(1, "level out of range");
- }
- ref var local = ref FindLocal(thread, level, index, out var name);
- if (name is null)
- {
- return new(context.Return(LuaValue.Nil));
- }
- return new(context.Return(name, local));
- }
- public ValueTask<int> SetLocal(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var thread = GetLuaThread(context, out var argOffset);
- var value = context.GetArgument(argOffset + 2);
- var index = context.GetArgument<int>(argOffset + 1);
- var level = context.GetArgument<int>(argOffset);
- if (level < 0 || level >= thread.GetCallStackFrames().Length)
- {
- context.ThrowBadArgument(1, "level out of range");
- }
- ref var local = ref FindLocal(thread, level, index, out var name);
- if (name is null)
- {
- return new(context.Return(LuaValue.Nil));
- }
- local = value;
- return new(context.Return(name));
- }
- public ValueTask<int> GetUpValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var func = context.GetArgument<LuaFunction>(0);
- var index = context.GetArgument<int>(1) - 1;
- if (func is not LuaClosure closure)
- {
- if (func is CSharpClosure csClosure)
- {
- var upValues = csClosure.UpValues;
- if (index < 0 || index >= upValues.Length)
- {
- return new(context.Return());
- }
- return new(context.Return("", upValues[index]));
- }
- return new(context.Return());
- }
- {
- var upValues = closure.UpValues;
- var descriptions = closure.Proto.UpValues;
- if (index < 0 || index >= descriptions.Length)
- {
- return new(context.Return());
- }
- var description = descriptions[index];
- return new(context.Return(description.Name.ToString(), upValues[index].GetValue()));
- }
- }
- public ValueTask<int> SetUpValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var func = context.GetArgument<LuaFunction>(0);
- var index = context.GetArgument<int>(1) - 1;
- var value = context.GetArgument(2);
- if (func is not LuaClosure closure)
- {
- if (func is CSharpClosure csClosure)
- {
- var upValues = csClosure.UpValues;
- if (index >= 0 && index < upValues.Length)
- {
- upValues[index] = value;
- return new(context.Return(""));
- }
- }
- return new(context.Return());
- }
- {
- var upValues = closure.UpValues;
- var descriptions = closure.Proto.UpValues;
- if (index < 0 || index >= descriptions.Length)
- {
- return new(context.Return());
- }
- var description = descriptions[index];
- upValues[index].SetValue(value);
- return new(context.Return(description.Name.ToString()));
- }
- }
- public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var arg0 = context.GetArgument(0);
- if (context.State.TryGetMetatable(arg0, out var table))
- {
- return new(context.Return(table));
- }
- else
- {
- return new(context.Return(LuaValue.Nil));
- }
- }
- public ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var arg0 = context.GetArgument(0);
- var arg1 = context.GetArgument(1);
- if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
- {
- LuaRuntimeException.BadArgument(context.Thread, 2, [LuaValueType.Nil, LuaValueType.Table], arg1.Type);
- }
- context.State.SetMetatable(arg0, arg1.UnsafeRead<LuaTable>());
- return new(context.Return(arg0));
- }
- public ValueTask<int> GetUserValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- if (!context.GetArgumentOrDefault(0).TryRead<ILuaUserData>(out var iUserData))
- {
- return new(context.Return(LuaValue.Nil));
- }
- var index = 1; // context.GetArgument<int>(1); //for lua 5.4
- var userValues = iUserData.UserValues;
- if (index > userValues.Length
- //index < 1 || // for lua 5.4
- )
- {
- return new(context.Return(LuaValue.Nil));
- }
- return new(context.Return(userValues[index - 1]));
- }
- public ValueTask<int> SetUserValue(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var iUserData = context.GetArgument<ILuaUserData>(0);
- var value = context.GetArgument(1);
- var index = 1; // context.GetArgument<int>(2);// for lua 5.4
- var userValues = iUserData.UserValues;
- if (index > userValues.Length
- //|| index < 1 // for lua 5.4
- )
- {
- return new(context.Return(LuaValue.Nil));
- }
- userValues[index - 1] = value;
- return new(context.Return(new LuaValue(iUserData)));
- }
- public ValueTask<int> Traceback(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var thread = GetLuaThread(context, out var argOffset);
- var message = context.GetArgumentOrDefault(argOffset);
- var level = context.GetArgumentOrDefault<int>(argOffset + 1, argOffset == 0 ? 1 : 0);
- if (message.Type is not (LuaValueType.Nil or LuaValueType.String or LuaValueType.Number))
- {
- return new(context.Return(message));
- }
- if (level < 0)
- {
- return new(context.Return(LuaValue.Nil));
- }
- if (thread is LuaCoroutine coroutine)
- {
- if (coroutine.LuaTraceback is not null)
- {
- return new(context.Return(coroutine.LuaTraceback.ToString(level)));
- }
- }
- var callStack = thread.GetCallStackFrames();
- if (callStack.Length == 0)
- {
- return new(context.Return("stack traceback:"));
- }
- var skipCount = Math.Min(Math.Max(level - 1, 0), callStack.Length - 1);
- var frames = callStack[..^skipCount];
- return new(context.Return(Runtime.Traceback.GetTracebackString(context.State, frames, message, level == 1)));
- }
- public ValueTask<int> GetRegistry(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- return new(context.Return(context.State.Registry));
- }
- public ValueTask<int> UpValueId(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var n1 = context.GetArgument<int>(1);
- var f1 = context.GetArgument<LuaFunction>(0);
- if (f1 is not LuaClosure closure)
- {
- return new(context.Return(LuaValue.Nil));
- }
- var upValues = closure.GetUpValuesSpan();
- if (n1 <= 0 || n1 > upValues.Length)
- {
- return new(context.Return(LuaValue.Nil));
- }
- return new(context.Return(LuaValue.FromObject(upValues[n1 - 1])));
- }
- public ValueTask<int> UpValueJoin(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var n2 = context.GetArgument<int>(3);
- var f2 = context.GetArgument<LuaFunction>(2);
- var n1 = context.GetArgument<int>(1);
- var f1 = context.GetArgument<LuaFunction>(0);
- if (f1 is not LuaClosure closure1 || f2 is not LuaClosure closure2)
- {
- return new(context.Return(LuaValue.Nil));
- }
- var upValues1 = closure1.GetUpValuesSpan();
- var upValues2 = closure2.GetUpValuesSpan();
- if (n1 <= 0 || n1 > upValues1.Length)
- {
- context.ThrowBadArgument(1, "invalid upvalue index");
- }
- if (n2 < 0 || n2 > upValues2.Length)
- {
- context.ThrowBadArgument(3, "invalid upvalue index");
- }
- upValues1[n1 - 1] = upValues2[n2 - 1];
- return new(0);
- }
- public async ValueTask<int> SetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var thread = GetLuaThread(context, out var argOffset);
- LuaFunction? hook = context.GetArgumentOrDefault<LuaFunction?>(argOffset);
- var mask = context.GetArgumentOrDefault<string?>(argOffset + 1) ?? "";
- var count = context.GetArgumentOrDefault<int>(argOffset + 2);
- thread.SetHook(hook, mask, count);
- if (hook is null)
- {
- return 0;
- }
- if (thread.IsReturnHookEnabled && context.Thread == thread)
- {
- var stack = thread.Stack;
- var top = stack.Count;
- stack.Push("return");
- stack.Push(LuaValue.Nil);
- context.Thread.IsInHook = true;
- var frame = context.Thread.CurrentAccess.CreateCallStackFrame(hook, 2, top, 0);
- var access = context.Thread.PushCallStackFrame(frame);
- var funcContext = new LuaFunctionExecutionContext { Access = access, ArgumentCount = stack.Count - frame.Base, ReturnFrameBase = frame.ReturnBase };
- try
- {
- await hook.Func(funcContext, cancellationToken);
- }
- finally
- {
- context.Thread.IsInHook = false;
- context.Thread.PopCallStackFrameWithStackPop();
- }
- }
- return 0;
- }
- public ValueTask<int> GetHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- var thread = GetLuaThread(context, out var argOffset);
- if (thread.Hook is null)
- {
- return new(context.Return(LuaValue.Nil, LuaValue.Nil, LuaValue.Nil));
- }
- return new(context.Return(thread.Hook,
- (
- (thread.IsCallHookEnabled ? "c" : "") +
- (thread.IsReturnHookEnabled ? "r" : "") +
- (thread.IsLineHookEnabled ? "l" : "")
- )
- , thread.BaseHookCount));
- }
- public ValueTask<int> GetInfo(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
- {
- //return new(0);
- var thread = GetLuaThread(context, out var argOffset);
- string what = context.GetArgumentOrDefault<string>(argOffset + 1, "flnStu");
- CallStackFrame? previousFrame = null;
- CallStackFrame? currentFrame = null;
- int pc = 0;
- var arg1 = context.GetArgument(argOffset);
- if (arg1.TryReadFunction(out var functionToInspect))
- {
- //what = ">" + what;
- }
- else if (arg1.TryReadNumber(out _))
- {
- var level = context.GetArgument<int>(argOffset) + 1;
- var callStack = thread.GetCallStackFrames();
- if (level <= 0 || level > callStack.Length)
- {
- return new(context.Return(LuaValue.Nil));
- }
- currentFrame = thread.GetCallStackFrames()[^(level)];
- previousFrame = level + 1 <= callStack.Length ? callStack[^(level + 1)] : null;
- if (level != 1)
- {
- pc = thread.GetCallStackFrames()[^(level - 1)].CallerInstructionIndex;
- }
- functionToInspect = currentFrame.Value.Function;
- }
- else
- {
- context.ThrowBadArgument(argOffset, "function or level expected");
- }
- using var debug = LuaDebug.Create(context.State, previousFrame, currentFrame, functionToInspect, pc, what, out var isValid);
- if (!isValid)
- {
- context.ThrowBadArgument(argOffset + 1, "invalid option");
- }
- var table = new LuaTable(0, 1);
- if (what.Contains('S'))
- {
- table["source"] = debug.Source ?? LuaValue.Nil;
- table["short_src"] = debug.ShortSource.ToString();
- table["linedefined"] = debug.LineDefined;
- table["lastlinedefined"] = debug.LastLineDefined;
- table["what"] = debug.What ?? LuaValue.Nil;
- ;
- }
- if (what.Contains('l'))
- {
- table["currentline"] = debug.CurrentLine;
- }
- if (what.Contains('u'))
- {
- table["nups"] = debug.UpValueCount;
- table["nparams"] = debug.ParameterCount;
- table["isvararg"] = debug.IsVarArg;
- }
- if (what.Contains('n'))
- {
- table["name"] = debug.Name ?? LuaValue.Nil;
- table["namewhat"] = debug.NameWhat ?? LuaValue.Nil;
- }
- if (what.Contains('t'))
- {
- table["istailcall"] = debug.IsTailCall;
- }
- if (what.Contains('f'))
- {
- table["func"] = functionToInspect;
- }
- if (what.Contains('L'))
- {
- if (functionToInspect is LuaClosure closure)
- {
- var activeLines = new LuaTable(0, 8);
- foreach (var line in closure.Proto.LineInfo)
- {
- activeLines[line] = true;
- }
- table["activelines"] = activeLines;
- }
- }
- return new(context.Return(table));
- }
- }
|