DebugLibrary.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. using System.Runtime.CompilerServices;
  2. using Lua.Runtime;
  3. namespace Lua.Standard;
  4. public class DebugLibrary
  5. {
  6. public static readonly DebugLibrary Instance = new();
  7. public DebugLibrary()
  8. {
  9. Functions =
  10. [
  11. new("getlocal", GetLocal),
  12. new("setlocal", SetLocal),
  13. new("getupvalue", GetUpValue),
  14. new("setupvalue", SetUpValue),
  15. new("getmetatable", GetMetatable),
  16. new("setmetatable", SetMetatable),
  17. new("traceback", Traceback),
  18. new("getregistry", GetRegistry),
  19. new("upvaluejoin", UpValueJoin)
  20. ];
  21. }
  22. public readonly LuaFunction[] Functions;
  23. LuaThread GetLuaThread(in LuaFunctionExecutionContext context, out int argOffset)
  24. {
  25. if (context.ArgumentCount < 1)
  26. {
  27. argOffset = 0;
  28. return context.Thread;
  29. }
  30. if (context.GetArgument(0).TryRead<LuaThread>(out var thread))
  31. {
  32. argOffset = 1;
  33. return thread;
  34. }
  35. argOffset = 0;
  36. return context.Thread;
  37. }
  38. ref LuaValue FindLocal(LuaThread thread, int level, int index, out string? name)
  39. {
  40. if (index == 0)
  41. {
  42. name = null;
  43. return ref Unsafe.NullRef<LuaValue>();
  44. }
  45. var callStack = thread.GetCallStackFrames();
  46. var frame = callStack[^(level + 1)];
  47. if (index < 0)
  48. {
  49. index = -index - 1;
  50. var frameVariableArgumentCount = frame.VariableArgumentCount;
  51. if (frameVariableArgumentCount > 0 && index < frameVariableArgumentCount)
  52. {
  53. name = "(vararg)";
  54. return ref thread.Stack.Get(frame.Base - frameVariableArgumentCount + index);
  55. }
  56. name = null;
  57. return ref Unsafe.NullRef<LuaValue>();
  58. }
  59. index -= 1;
  60. var frameBase = frame.Base;
  61. var nextFrameBase = level != 0 ? callStack[^level].Base : thread.Stack.Count;
  62. if (nextFrameBase - frameBase <= index)
  63. {
  64. name = null;
  65. return ref Unsafe.NullRef<LuaValue>();
  66. }
  67. if (frame.Function is Closure closure)
  68. {
  69. var locals = closure.Proto.Locals;
  70. var currentPc = callStack[^level].CallerInstructionIndex;
  71. foreach (var local in locals)
  72. {
  73. if (local.Index == index && currentPc >= local.StartPc && currentPc < local.EndPc)
  74. {
  75. name = local.Name.ToString();
  76. return ref thread.Stack.Get(frameBase + local.Index);
  77. }
  78. if (local.Index >= index)
  79. {
  80. break;
  81. }
  82. }
  83. }
  84. name = "(*temporary)";
  85. return ref thread.Stack.Get(frameBase + index);
  86. }
  87. public ValueTask<int> GetLocal(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  88. {
  89. static LuaValue GetParam(LuaFunction function, int index)
  90. {
  91. if (function is Closure closure)
  92. {
  93. var paramCount = closure.Proto.ParameterCount;
  94. if (0 <= index && index < paramCount)
  95. {
  96. return closure.Proto.Locals[index].Name.ToString();
  97. }
  98. }
  99. return LuaValue.Nil;
  100. }
  101. var thread = GetLuaThread(context, out var argOffset);
  102. var index = context.GetArgument<int>(argOffset + 1);
  103. if (context.GetArgument(argOffset).TryReadFunction(out var f))
  104. {
  105. buffer.Span[0] = GetParam(f, index - 1);
  106. return new(1);
  107. }
  108. var level = context.GetArgument<int>(argOffset);
  109. if (level < 0 || level >= thread.GetCallStackFrames().Length)
  110. {
  111. context.ThrowBadArgument(1, "level out of range");
  112. }
  113. ref var local = ref FindLocal(thread, level, index, out var name);
  114. if (name is null)
  115. {
  116. buffer.Span[0] = LuaValue.Nil;
  117. return new(1);
  118. }
  119. buffer.Span[0] = name;
  120. buffer.Span[1] = local;
  121. return new(2);
  122. }
  123. public ValueTask<int> SetLocal(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  124. {
  125. var thread = GetLuaThread(context, out var argOffset);
  126. var value = context.GetArgument(argOffset + 2);
  127. var index = context.GetArgument<int>(argOffset + 1);
  128. var level = context.GetArgument<int>(argOffset);
  129. if (level < 0 || level >= thread.GetCallStackFrames().Length)
  130. {
  131. context.ThrowBadArgument(1, "level out of range");
  132. }
  133. ref var local = ref FindLocal(thread, level, index, out var name);
  134. if (name is null)
  135. {
  136. buffer.Span[0] = LuaValue.Nil;
  137. return new(1);
  138. }
  139. buffer.Span[0] = name;
  140. local = value;
  141. return new(1);
  142. }
  143. public ValueTask<int> GetUpValue(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  144. {
  145. var func = context.GetArgument<LuaFunction>(0);
  146. var index = context.GetArgument<int>(1) - 1;
  147. if (func is not Closure closure)
  148. {
  149. return new(0);
  150. }
  151. var upValues = closure.UpValues;
  152. var descriptions = closure.Proto.UpValues;
  153. if (index < 0 || index >= descriptions.Length)
  154. {
  155. return new(0);
  156. }
  157. var description = descriptions[index];
  158. buffer.Span[0] = description.Name.ToString();
  159. buffer.Span[1] = upValues[index].GetValue();
  160. return new(2);
  161. }
  162. public ValueTask<int> SetUpValue(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  163. {
  164. var func = context.GetArgument<LuaFunction>(0);
  165. var index = context.GetArgument<int>(1) - 1;
  166. var value = context.GetArgument(2);
  167. if (func is not Closure closure)
  168. {
  169. return new(0);
  170. }
  171. var upValues = closure.UpValues;
  172. var descriptions = closure.Proto.UpValues;
  173. if (index < 0 || index >= descriptions.Length)
  174. {
  175. return new(0);
  176. }
  177. var description = descriptions[index];
  178. buffer.Span[0] = description.Name.ToString();
  179. upValues[index].SetValue(value);
  180. return new(1);
  181. }
  182. public ValueTask<int> GetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  183. {
  184. var arg0 = context.GetArgument(0);
  185. if (context.State.TryGetMetatable(arg0, out var table))
  186. {
  187. buffer.Span[0] = table;
  188. }
  189. else
  190. {
  191. buffer.Span[0] = LuaValue.Nil;
  192. }
  193. return new(1);
  194. }
  195. public ValueTask<int> SetMetatable(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  196. {
  197. var arg0 = context.GetArgument(0);
  198. var arg1 = context.GetArgument(1);
  199. if (arg1.Type is not (LuaValueType.Nil or LuaValueType.Table))
  200. {
  201. LuaRuntimeException.BadArgument(context.State.GetTraceback(), 2, "setmetatable", [LuaValueType.Nil, LuaValueType.Table]);
  202. }
  203. context.State.SetMetatable(arg0, arg1.UnsafeRead<LuaTable>());
  204. buffer.Span[0] = arg0;
  205. return new(1);
  206. }
  207. public ValueTask<int> Traceback(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  208. {
  209. var thread = (GetLuaThread(context, out var argOffset));
  210. var message = context.GetArgumentOrDefault(argOffset);
  211. var level = context.GetArgumentOrDefault<int>(argOffset + 1, 1);
  212. if (message.Type is not (LuaValueType.Nil or LuaValueType.String or LuaValueType.Number))
  213. {
  214. buffer.Span[0] = message;
  215. return new(1);
  216. }
  217. if (level < 0)
  218. {
  219. buffer.Span[0] = LuaValue.Nil;
  220. return new(1);
  221. }
  222. thread.PushCallStackFrame(thread.GetCurrentFrame());
  223. var callStack = thread.GetCallStackFrames();
  224. var skipCount = Math.Min(level, callStack.Length - 1);
  225. var frames = callStack[1..^skipCount];
  226. buffer.Span[0] = Runtime.Traceback.GetTracebackString((Closure)callStack[0].Function, frames, message);
  227. thread.PopCallStackFrame();
  228. return new(1);
  229. }
  230. public ValueTask<int> GetRegistry(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  231. {
  232. buffer.Span[0] = context.State.Registry;
  233. return new(1);
  234. }
  235. public ValueTask<int> UpValueJoin(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  236. {
  237. var n2 = context.GetArgument<int>(3);
  238. var f2 = context.GetArgument<LuaFunction>(2);
  239. var n1 = context.GetArgument<int>(1);
  240. var f1 = context.GetArgument<LuaFunction>(0);
  241. if (f1 is not Closure closure1 || f2 is not Closure closure2)
  242. {
  243. buffer.Span[0] = LuaValue.Nil;
  244. return new(1);
  245. }
  246. var upValues1 = closure1.GetUpValuesSpan();
  247. var upValues2 = closure2.GetUpValuesSpan();
  248. if (n1 <= 0 || n1 > upValues1.Length)
  249. {
  250. context.ThrowBadArgument(1, "invalid upvalue index");
  251. }
  252. if (n2 < 0 || n2 > upValues2.Length)
  253. {
  254. context.ThrowBadArgument(3, "invalid upvalue index");
  255. }
  256. upValues1[n1 - 1] = upValues2[n2 - 1];
  257. return new(0);
  258. }
  259. }