LuaVirtualMachine.Debug.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. using System.Runtime.CompilerServices;
  2. namespace Lua.Runtime;
  3. public static partial class LuaVirtualMachine
  4. {
  5. [MethodImpl(MethodImplOptions.NoInlining)]
  6. static bool ExecutePerInstructionHook(VirtualMachineExecutionContext context)
  7. {
  8. var r = Impl(context);
  9. if (r.IsCompleted)
  10. {
  11. if (r.Result == 0)
  12. {
  13. context.Thread.PopCallStackFrameWithStackPop();
  14. }
  15. return false;
  16. }
  17. context.Task = r;
  18. context.Pc--;
  19. return true;
  20. static async ValueTask<int> Impl(VirtualMachineExecutionContext context)
  21. {
  22. bool countHookIsDone = false;
  23. var pc = context.Pc;
  24. var prototype = context.Prototype;
  25. if (context.Thread.IsCountHookEnabled && --context.Thread.HookCount == 0)
  26. {
  27. context.Thread.HookCount = context.Thread.BaseHookCount;
  28. var hook = context.Thread.Hook!;
  29. var stack = context.Thread.Stack;
  30. stack.Push("count");
  31. stack.Push(LuaValue.Nil);
  32. var funcContext = new LuaFunctionExecutionContext { Thread = context.Thread, ArgumentCount = 2, ReturnFrameBase = context.Thread.Stack.Count - 2, };
  33. var frame = new CallStackFrame
  34. {
  35. Base = funcContext.FrameBase,
  36. ReturnBase = funcContext.ReturnFrameBase,
  37. VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount),
  38. Function = hook,
  39. CallerInstructionIndex = context.Pc,
  40. };
  41. frame.Flags |= CallStackFrameFlags.InHook;
  42. context.Thread.IsInHook = true;
  43. context.Thread.PushCallStackFrame(frame);
  44. await hook.Func(funcContext, context.CancellationToken);
  45. context.Thread.IsInHook = false;
  46. countHookIsDone = true;
  47. }
  48. if (context.Thread.IsLineHookEnabled)
  49. {
  50. var sourcePositions = prototype.LineInfo;
  51. var line = sourcePositions[pc];
  52. if (countHookIsDone || pc == 0 || context.Thread.LastPc < 0 || pc <= context.Thread.LastPc || sourcePositions[context.Thread.LastPc] != line)
  53. {
  54. if (countHookIsDone)
  55. {
  56. context.Thread.PopCallStackFrameWithStackPop();
  57. }
  58. var hook = context.Thread.Hook!;
  59. var stack = context.Thread.Stack;
  60. stack.Push("line");
  61. stack.Push(line);
  62. var funcContext = new LuaFunctionExecutionContext { Thread = context.Thread, ArgumentCount = 2, ReturnFrameBase = context.Thread.Stack.Count - 2, };
  63. var frame = new CallStackFrame
  64. {
  65. Base = funcContext.FrameBase,
  66. ReturnBase = funcContext.ReturnFrameBase,
  67. VariableArgumentCount = hook.GetVariableArgumentCount(funcContext.ArgumentCount),
  68. Function = hook,
  69. CallerInstructionIndex = pc,
  70. };
  71. frame.Flags |= CallStackFrameFlags.InHook;
  72. context.Thread.IsInHook = true;
  73. context.Thread.PushCallStackFrame(frame);
  74. await hook.Func(funcContext, context.CancellationToken);
  75. context.Thread.IsInHook = false;
  76. context.Thread.LastPc = pc;
  77. return 0;
  78. }
  79. context.Thread.LastPc = pc;
  80. }
  81. if (countHookIsDone)
  82. {
  83. return 0;
  84. }
  85. return -1;
  86. }
  87. }
  88. [MethodImpl(MethodImplOptions.NoInlining)]
  89. static ValueTask<int> ExecuteCallHook(VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments, bool isTailCall = false)
  90. {
  91. return ExecuteCallHook(new()
  92. {
  93. Thread = context.Thread, ArgumentCount = arguments, ReturnFrameBase = frame.ReturnBase, CallerInstructionIndex = frame.CallerInstructionIndex,
  94. }, context.CancellationToken, isTailCall);
  95. }
  96. internal static async ValueTask<int> ExecuteCallHook(LuaFunctionExecutionContext context, CancellationToken cancellationToken, bool isTailCall = false)
  97. {
  98. var argCount = context.ArgumentCount;
  99. var hook = context.Thread.Hook!;
  100. var stack = context.Thread.Stack;
  101. if (context.Thread.IsCallHookEnabled)
  102. {
  103. stack.Push((isTailCall ? "tail call" : "call"));
  104. stack.Push(LuaValue.Nil);
  105. var funcContext = new LuaFunctionExecutionContext { Thread = context.Thread, ArgumentCount = 2, ReturnFrameBase = context.Thread.Stack.Count - 2, };
  106. CallStackFrame frame = new()
  107. {
  108. Base = funcContext.FrameBase,
  109. ReturnBase = funcContext.ReturnFrameBase,
  110. VariableArgumentCount = hook.GetVariableArgumentCount(2),
  111. Function = hook,
  112. CallerInstructionIndex = 0,
  113. Flags = CallStackFrameFlags.InHook
  114. };
  115. context.Thread.PushCallStackFrame(frame);
  116. try
  117. {
  118. context.Thread.IsInHook = true;
  119. await hook.Func(funcContext, cancellationToken);
  120. }
  121. finally
  122. {
  123. context.Thread.IsInHook = false;
  124. context.Thread.PopCallStackFrameWithStackPop();
  125. }
  126. }
  127. {
  128. ref readonly var frame = ref context.Thread.GetCurrentFrame();
  129. var task = frame.Function.Func(new() { Thread = context.Thread, ArgumentCount = argCount, ReturnFrameBase = frame.ReturnBase, }, cancellationToken);
  130. var r = await task;
  131. if (isTailCall || !context.Thread.IsReturnHookEnabled)
  132. {
  133. return r;
  134. }
  135. stack.Push("return");
  136. stack.Push(LuaValue.Nil);
  137. var funcContext = new LuaFunctionExecutionContext { Thread = context.Thread, ArgumentCount = 2, ReturnFrameBase = context.Thread.Stack.Count - 2, };
  138. context.Thread.PushCallStackFrame(new()
  139. {
  140. Base = funcContext.FrameBase,
  141. ReturnBase = funcContext.ReturnFrameBase,
  142. VariableArgumentCount = hook.GetVariableArgumentCount(2),
  143. Function = hook,
  144. CallerInstructionIndex = 0,
  145. Flags = CallStackFrameFlags.InHook
  146. });
  147. try
  148. {
  149. context.Thread.IsInHook = true;
  150. await hook.Func(funcContext, cancellationToken);
  151. }
  152. finally
  153. {
  154. context.Thread.IsInHook = false;
  155. }
  156. context.Thread.PopCallStackFrameWithStackPop();
  157. return r;
  158. }
  159. }
  160. }