LuaVirtualMachine.cs 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Runtime.CompilerServices;
  3. using Lua.Internal;
  4. // ReSharper disable InconsistentNaming
  5. namespace Lua.Runtime;
  6. [SuppressMessage("Reliability", "CA2012:Use ValueTasks correctly")]
  7. public static partial class LuaVirtualMachine
  8. {
  9. class VirtualMachineExecutionContext
  10. : IPoolNode<VirtualMachineExecutionContext>
  11. {
  12. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  13. public static VirtualMachineExecutionContext Get(
  14. LuaThread thread,
  15. in CallStackFrame frame,
  16. CancellationToken cancellationToken)
  17. {
  18. if (!pool.TryPop(out var executionContext))
  19. {
  20. executionContext = new VirtualMachineExecutionContext();
  21. }
  22. executionContext.Init(thread, frame, cancellationToken);
  23. return executionContext;
  24. }
  25. void Init(
  26. LuaThread thread,
  27. in CallStackFrame frame,
  28. CancellationToken cancellationToken)
  29. {
  30. Stack = thread.Stack;
  31. Thread = thread;
  32. LuaClosure = (LuaClosure)frame.Function;
  33. FrameBase = frame.Base;
  34. VariableArgumentCount = frame.VariableArgumentCount;
  35. CurrentReturnFrameBase = frame.ReturnBase;
  36. CancellationToken = cancellationToken;
  37. Pc = -1;
  38. Instruction = default;
  39. PostOperation = PostOperationType.None;
  40. BaseCallStackCount = thread.CallStack.Count;
  41. LastHookPc = -1;
  42. Task = default;
  43. }
  44. public LuaState State => Thread.State;
  45. public LuaStack Stack = default!;
  46. public LuaClosure LuaClosure = default!;
  47. public LuaThread Thread = default!;
  48. public Prototype Prototype => LuaClosure.Proto;
  49. public int FrameBase;
  50. public int VariableArgumentCount;
  51. public CancellationToken CancellationToken;
  52. public int Pc;
  53. public Instruction Instruction;
  54. public int CurrentReturnFrameBase;
  55. public ValueTask<int> Task;
  56. public int LastHookPc;
  57. public bool IsTopLevel => BaseCallStackCount == Thread.CallStack.Count;
  58. public int BaseCallStackCount;
  59. public PostOperationType PostOperation;
  60. static LinkedPool<VirtualMachineExecutionContext> pool;
  61. VirtualMachineExecutionContext? nextNode;
  62. ref VirtualMachineExecutionContext? IPoolNode<VirtualMachineExecutionContext>.NextNode => ref nextNode;
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. public bool Pop(Instruction instruction, int frameBase)
  65. {
  66. var count = instruction.B - 1;
  67. var src = instruction.A + frameBase;
  68. if (count == -1) count = Stack.Count - src;
  69. return PopFromBuffer(src, count);
  70. }
  71. [MethodImpl(MethodImplOptions.NoInlining)]
  72. public bool PopFromBuffer(int src, int srcCount)
  73. {
  74. var result = Stack.GetBuffer().Slice(src, srcCount);
  75. ref var callStack = ref Thread.CallStack;
  76. Re:
  77. var frames = callStack.AsSpan();
  78. if (frames.Length == BaseCallStackCount)
  79. {
  80. var returnBase = frames[^1].ReturnBase;
  81. if (src != returnBase)
  82. {
  83. result.CopyTo(Stack.GetBuffer()[returnBase..]);
  84. }
  85. Stack.PopUntil(returnBase + srcCount);
  86. return false;
  87. }
  88. ref readonly var frame = ref frames[^1];
  89. Pc = frame.CallerInstructionIndex;
  90. Thread.LastPc = Pc;
  91. ref readonly var lastFrame = ref frames[^2];
  92. LuaClosure = Unsafe.As<LuaClosure>(lastFrame.Function);
  93. CurrentReturnFrameBase = frame.ReturnBase;
  94. var callInstruction = Prototype.Code[Pc];
  95. if (callInstruction.OpCode == OpCode.TailCall)
  96. {
  97. Thread.PopCallStackFrame();
  98. goto Re;
  99. }
  100. FrameBase = lastFrame.Base;
  101. VariableArgumentCount = lastFrame.VariableArgumentCount;
  102. var opCode = callInstruction.OpCode;
  103. if (opCode is OpCode.Eq or OpCode.Lt or OpCode.Le)
  104. {
  105. var compareResult = srcCount > 0 && result[0].ToBoolean();
  106. if ((frame.Flags & CallStackFrameFlags.ReversedLe) != 0)
  107. {
  108. compareResult = !compareResult;
  109. }
  110. if (compareResult != (callInstruction.A == 1))
  111. {
  112. Pc++;
  113. }
  114. Thread.PopCallStackFrameWithStackPop();
  115. return true;
  116. }
  117. var target = callInstruction.A + FrameBase;
  118. var targetCount = result.Length;
  119. switch (opCode)
  120. {
  121. case OpCode.Call:
  122. {
  123. var c = callInstruction.C;
  124. if (c != 0)
  125. {
  126. targetCount = c - 1;
  127. }
  128. break;
  129. }
  130. case OpCode.TForCall:
  131. target += 3;
  132. targetCount = callInstruction.C;
  133. break;
  134. case OpCode.Self:
  135. Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0];
  136. Thread.PopCallStackFrameWithStackPop(target + 2);
  137. return true;
  138. case OpCode.SetTable or OpCode.SetTabUp:
  139. target = frame.Base;
  140. targetCount = 0;
  141. break;
  142. // Other opcodes has one result
  143. default:
  144. Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0];
  145. Thread.PopCallStackFrameWithStackPop(target + 1);
  146. return true;
  147. }
  148. Stack.EnsureCapacity(target + targetCount);
  149. if (0 < targetCount && src != target)
  150. {
  151. if (targetCount < result.Length)
  152. {
  153. result = result.Slice(0, targetCount);
  154. }
  155. result.CopyTo(Stack.GetBuffer().Slice(target, targetCount));
  156. }
  157. Stack.PopUntil(target + Math.Min(targetCount, srcCount));
  158. Stack.NotifyTop(target + targetCount);
  159. Thread.PopCallStackFrame();
  160. return true;
  161. }
  162. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  163. public void Push(in CallStackFrame frame)
  164. {
  165. Pc = -1;
  166. LuaClosure = (LuaClosure)(frame.Function);
  167. FrameBase = frame.Base;
  168. CurrentReturnFrameBase = frame.ReturnBase;
  169. VariableArgumentCount = frame.VariableArgumentCount;
  170. }
  171. public void PopOnTopCallStackFrames()
  172. {
  173. ref var callStack = ref Thread.CallStack;
  174. var count = callStack.Count;
  175. if (count == BaseCallStackCount) return;
  176. while (callStack.Count > BaseCallStackCount + 1)
  177. {
  178. callStack.TryPop();
  179. }
  180. Thread.PopCallStackFrame();
  181. }
  182. bool ExecutePostOperation(PostOperationType postOperation)
  183. {
  184. var stackCount = Stack.Count;
  185. var resultsSpan = Stack.GetBuffer()[CurrentReturnFrameBase..];
  186. switch (postOperation)
  187. {
  188. case PostOperationType.Nop: break;
  189. case PostOperationType.SetResult:
  190. var RA = Instruction.A + FrameBase;
  191. Stack.Get(RA) = stackCount > CurrentReturnFrameBase ? Stack.Get(CurrentReturnFrameBase) : LuaValue.Nil;
  192. Stack.NotifyTop(RA + 1);
  193. Stack.PopUntil(RA + 1);
  194. break;
  195. case PostOperationType.TForCall:
  196. TForCallPostOperation(this);
  197. break;
  198. case PostOperationType.Call:
  199. CallPostOperation(this);
  200. break;
  201. case PostOperationType.TailCall:
  202. if (!PopFromBuffer(CurrentReturnFrameBase, Stack.Count - CurrentReturnFrameBase))
  203. {
  204. return false;
  205. }
  206. break;
  207. case PostOperationType.Self:
  208. SelfPostOperation(this, resultsSpan);
  209. break;
  210. case PostOperationType.Compare:
  211. ComparePostOperation(this, resultsSpan);
  212. break;
  213. }
  214. return true;
  215. }
  216. public async ValueTask<int> ExecuteClosureAsyncImpl()
  217. {
  218. var returnFrameBase = CurrentReturnFrameBase;
  219. try
  220. {
  221. while (MoveNext(this))
  222. {
  223. await Task;
  224. Task = default;
  225. if (PostOperation is not (PostOperationType.TailCall or PostOperationType.DontPop))
  226. {
  227. Thread.PopCallStackFrame();
  228. }
  229. if (!ExecutePostOperation(PostOperation))
  230. {
  231. break;
  232. }
  233. }
  234. return Thread.Stack.Count - returnFrameBase;
  235. }
  236. finally
  237. {
  238. pool.TryPush(this);
  239. }
  240. }
  241. }
  242. enum PostOperationType
  243. {
  244. None,
  245. Nop,
  246. SetResult,
  247. TForCall,
  248. Call,
  249. TailCall,
  250. Self,
  251. Compare,
  252. DontPop,
  253. }
  254. internal static ValueTask<int> ExecuteClosureAsync(LuaThread thread, CancellationToken cancellationToken)
  255. {
  256. ref readonly var frame = ref thread.GetCurrentFrame();
  257. var context = VirtualMachineExecutionContext.Get(thread, in frame,
  258. cancellationToken);
  259. return context.ExecuteClosureAsyncImpl();
  260. }
  261. static bool MoveNext(VirtualMachineExecutionContext context)
  262. {
  263. try
  264. {
  265. // This is a label to restart the execution when new function is called or restarted
  266. Restart:
  267. ref var instructionsHead = ref Unsafe.AsRef(in context.Prototype.Code[0]);
  268. var frameBase = context.FrameBase;
  269. var stack = context.Stack;
  270. stack.EnsureCapacity(frameBase + context.Prototype.MaxStackSize);
  271. ref var constHead = ref MemoryMarshalEx.UnsafeElementAt(context.Prototype.Constants, 0);
  272. ref var lineAndCountHookMask = ref context.Thread.LineAndCountHookMask;
  273. goto Loop;
  274. LineHook:
  275. {
  276. context.LastHookPc = context.Pc;
  277. if (!context.Thread.IsInHook && ExecutePerInstructionHook(context))
  278. {
  279. {
  280. context.PostOperation = PostOperationType.Nop;
  281. return true;
  282. }
  283. }
  284. --context.Pc;
  285. }
  286. Loop:
  287. while (true)
  288. {
  289. var instruction = Unsafe.Add(ref instructionsHead, ++context.Pc);
  290. context.Instruction = instruction;
  291. if (lineAndCountHookMask.Value != 0 && (context.Pc != context.LastHookPc))
  292. {
  293. goto LineHook;
  294. }
  295. context.LastHookPc = -1;
  296. var iA = instruction.A;
  297. var opCode = instruction.OpCode;
  298. switch (opCode)
  299. {
  300. case OpCode.Move:
  301. Markers.Move();
  302. ref var stackHead = ref stack.FastGet(frameBase);
  303. Unsafe.Add(ref stackHead, iA) = Unsafe.Add(ref stackHead, instruction.B);
  304. stack.NotifyTop(iA + frameBase + 1);
  305. continue;
  306. case OpCode.LoadK:
  307. Markers.LoadK();
  308. stack.GetWithNotifyTop(iA + frameBase) = Unsafe.Add(ref constHead, instruction.Bx);
  309. continue;
  310. case OpCode.LoadKX:
  311. Markers.LoadKX();
  312. stack.GetWithNotifyTop(iA + frameBase) = Unsafe.Add(ref constHead, Unsafe.Add(ref instructionsHead, ++context.Pc).Ax);
  313. continue;
  314. case OpCode.LoadBool:
  315. Markers.LoadBool();
  316. stack.GetWithNotifyTop(iA + frameBase) = instruction.B != 0;
  317. if (instruction.C != 0) context.Pc++;
  318. continue;
  319. case OpCode.LoadNil:
  320. Markers.LoadNil();
  321. var ra1 = iA + frameBase + 1;
  322. var iB = instruction.B;
  323. stack.GetBuffer().Slice(ra1 - 1, iB + 1).Clear();
  324. stack.NotifyTop(ra1 + iB);
  325. continue;
  326. case OpCode.GetUpVal:
  327. Markers.GetUpVal();
  328. stack.GetWithNotifyTop(iA + frameBase) = context.LuaClosure.GetUpValue(instruction.B);
  329. continue;
  330. case OpCode.GetTabUp:
  331. case OpCode.GetTable:
  332. Markers.GetTabUp();
  333. Markers.GetTable();
  334. stackHead = ref stack.FastGet(frameBase);
  335. ref readonly var vc = ref RKC(ref stackHead, ref constHead, instruction);
  336. ref readonly var vb = ref (instruction.OpCode == OpCode.GetTable ? ref Unsafe.Add(ref stackHead, instruction.B) : ref context.LuaClosure.GetUpValueRef(instruction.B));
  337. var doRestart = false;
  338. if (vb.TryReadTable(out var luaTable) && luaTable.TryGetValue(vc, out var resultValue) || GetTableValueSlowPath(vb, vc, context, out resultValue, out doRestart))
  339. {
  340. if (doRestart) goto Restart;
  341. stack.GetWithNotifyTop(instruction.A + frameBase) = resultValue;
  342. continue;
  343. }
  344. return true;
  345. case OpCode.SetTabUp:
  346. case OpCode.SetTable:
  347. Markers.SetTabUp();
  348. Markers.SetTable();
  349. stackHead = ref stack.FastGet(frameBase);
  350. vb = ref RKB(ref stackHead, ref constHead, instruction);
  351. if (vb.TryReadNumber(out var numB))
  352. {
  353. if (double.IsNaN(numB))
  354. {
  355. ThrowLuaRuntimeException(context, "table index is NaN");
  356. return true;
  357. }
  358. }
  359. var table = opCode == OpCode.SetTabUp ? context.LuaClosure.GetUpValue(iA) : Unsafe.Add(ref stackHead, iA);
  360. if (table.TryReadTable(out luaTable))
  361. {
  362. ref var valueRef = ref luaTable.FindValue(vb);
  363. if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
  364. {
  365. valueRef = RKC(ref stackHead, ref constHead, instruction);
  366. continue;
  367. }
  368. }
  369. vc = ref RKC(ref stackHead, ref constHead, instruction);
  370. if (SetTableValueSlowPath(table, vb, vc, context, out doRestart))
  371. {
  372. if (doRestart) goto Restart;
  373. continue;
  374. }
  375. return true;
  376. case OpCode.SetUpVal:
  377. Markers.SetUpVal();
  378. context.LuaClosure.SetUpValue(instruction.B, stack.FastGet(iA + frameBase));
  379. continue;
  380. case OpCode.NewTable:
  381. Markers.NewTable();
  382. stack.GetWithNotifyTop(iA + frameBase) = new LuaTable(instruction.B, instruction.C);
  383. continue;
  384. case OpCode.Self:
  385. Markers.Self();
  386. stackHead = ref stack.FastGet(frameBase);
  387. vc = ref RKC(ref stackHead, ref constHead, instruction);
  388. table = Unsafe.Add(ref stackHead, instruction.B);
  389. doRestart = false;
  390. if ((table.TryReadTable(out luaTable) && luaTable.TryGetValue(vc, out resultValue)) || GetTableValueSlowPath(table, vc, context, out resultValue, out doRestart))
  391. {
  392. if (doRestart) goto Restart;
  393. Unsafe.Add(ref stackHead, iA) = resultValue;
  394. Unsafe.Add(ref stackHead, iA + 1) = table;
  395. stack.NotifyTop(iA + frameBase + 2);
  396. continue;
  397. }
  398. return true;
  399. case OpCode.Add:
  400. case OpCode.Sub:
  401. case OpCode.Mul:
  402. case OpCode.Div:
  403. case OpCode.Mod:
  404. case OpCode.Pow:
  405. Markers.Add();
  406. Markers.Sub();
  407. Markers.Mul();
  408. Markers.Div();
  409. Markers.Mod();
  410. Markers.Pow();
  411. stackHead = ref stack.FastGet(frameBase);
  412. vb = ref RKB(ref stackHead, ref constHead, instruction);
  413. vc = ref RKC(ref stackHead, ref constHead, instruction);
  414. [MethodImpl(MethodImplOptions.NoInlining)]
  415. static double Mod(double a, double b)
  416. {
  417. var mod = a % b;
  418. if ((b > 0 && mod < 0) || (b < 0 && mod > 0))
  419. {
  420. mod += b;
  421. }
  422. return mod;
  423. }
  424. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  425. static double ArithmeticOperation(OpCode code, double a, double b)
  426. {
  427. return code switch
  428. {
  429. OpCode.Add => a + b,
  430. OpCode.Sub => a - b,
  431. OpCode.Mul => a * b,
  432. OpCode.Div => a / b,
  433. OpCode.Mod => Mod(a, b),
  434. OpCode.Pow => Math.Pow(a, b),
  435. _ => 0
  436. };
  437. }
  438. if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
  439. {
  440. Unsafe.Add(ref stackHead, iA) = ArithmeticOperation(opCode, vb.UnsafeReadDouble(), vc.UnsafeReadDouble());
  441. stack.NotifyTop(iA + frameBase + 1);
  442. continue;
  443. }
  444. if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out var numC))
  445. {
  446. Unsafe.Add(ref stackHead, iA) = ArithmeticOperation(opCode, numB, numC);
  447. stack.NotifyTop(iA + frameBase + 1);
  448. continue;
  449. }
  450. if (ExecuteBinaryOperationMetaMethod(vb, vc, context, opCode, out doRestart))
  451. {
  452. if (doRestart) goto Restart;
  453. continue;
  454. }
  455. return true;
  456. case OpCode.Unm:
  457. Markers.Unm();
  458. stackHead = ref stack.FastGet(frameBase);
  459. vb = ref Unsafe.Add(ref stackHead, instruction.B);
  460. if (vb.TryReadDouble(out numB))
  461. {
  462. ra1 = iA + frameBase + 1;
  463. Unsafe.Add(ref stackHead, iA) = -numB;
  464. stack.NotifyTop(ra1);
  465. continue;
  466. }
  467. if (ExecuteUnaryOperationMetaMethod(vb, context, OpCode.Unm, out doRestart))
  468. {
  469. if (doRestart) goto Restart;
  470. continue;
  471. }
  472. return true;
  473. case OpCode.Not:
  474. Markers.Not();
  475. stackHead = ref stack.FastGet(frameBase);
  476. Unsafe.Add(ref stackHead, iA) = !Unsafe.Add(ref stackHead, instruction.B).ToBoolean();
  477. stack.NotifyTop(iA + frameBase + 1);
  478. continue;
  479. case OpCode.Len:
  480. Markers.Len();
  481. stackHead = ref stack.FastGet(frameBase);
  482. vb = ref Unsafe.Add(ref stackHead, instruction.B);
  483. if (vb.TryReadString(out var str))
  484. {
  485. ra1 = iA + frameBase + 1;
  486. Unsafe.Add(ref stackHead, iA) = str.Length;
  487. stack.NotifyTop(ra1);
  488. continue;
  489. }
  490. if (ExecuteUnaryOperationMetaMethod(vb, context, OpCode.Len, out doRestart))
  491. {
  492. if (doRestart) goto Restart;
  493. continue;
  494. }
  495. return true;
  496. case OpCode.Concat:
  497. Markers.Concat();
  498. if (Concat(context))
  499. {
  500. //if (doRestart) goto Restart;
  501. continue;
  502. }
  503. return true;
  504. case OpCode.Jmp:
  505. Markers.Jmp();
  506. context.Pc += instruction.SBx;
  507. if (iA != 0)
  508. {
  509. context.Thread.State.CloseUpValues(context.Thread, frameBase + iA - 1);
  510. }
  511. continue;
  512. case OpCode.Eq:
  513. Markers.Eq();
  514. stackHead = ref stack.Get(frameBase);
  515. vb = ref RKB(ref stackHead, ref constHead, instruction);
  516. vc = ref RKC(ref stackHead, ref constHead, instruction);
  517. if (vb == vc)
  518. {
  519. if (iA != 1)
  520. {
  521. context.Pc++;
  522. }
  523. continue;
  524. }
  525. if (ExecuteCompareOperationMetaMethod(vb, vc, context, OpCode.Eq, out doRestart))
  526. {
  527. if (doRestart) goto Restart;
  528. continue;
  529. }
  530. return true;
  531. case OpCode.Lt:
  532. case OpCode.Le:
  533. Markers.Lt();
  534. Markers.Le();
  535. stackHead = ref stack.Get(frameBase);
  536. vb = ref RKB(ref stackHead, ref constHead, instruction);
  537. vc = ref RKC(ref stackHead, ref constHead, instruction);
  538. if (vb.TryReadNumber(out numB) && vc.TryReadNumber(out numC))
  539. {
  540. var compareResult = opCode == OpCode.Lt ? numB < numC : numB <= numC;
  541. if (compareResult != (iA == 1))
  542. {
  543. context.Pc++;
  544. }
  545. continue;
  546. }
  547. if (vb.TryReadString(out var strB) && vc.TryReadString(out var strC))
  548. {
  549. var c = StringComparer.Ordinal.Compare(strB, strC);
  550. var compareResult = opCode == OpCode.Lt ? c < 0 : c <= 0;
  551. if (compareResult != (iA == 1))
  552. {
  553. context.Pc++;
  554. }
  555. continue;
  556. }
  557. if (ExecuteCompareOperationMetaMethod(vb, vc, context, opCode, out doRestart))
  558. {
  559. if (doRestart) goto Restart;
  560. continue;
  561. }
  562. return true;
  563. case OpCode.Test:
  564. Markers.Test();
  565. if (stack.Get(iA + frameBase).ToBoolean() != (instruction.C == 1))
  566. {
  567. context.Pc++;
  568. }
  569. continue;
  570. case OpCode.TestSet:
  571. Markers.TestSet();
  572. vb = ref stack.Get(instruction.B + frameBase);
  573. if (vb.ToBoolean() != (instruction.C == 1))
  574. {
  575. context.Pc++;
  576. }
  577. else
  578. {
  579. stack.GetWithNotifyTop(iA + frameBase) = vb;
  580. }
  581. continue;
  582. case OpCode.Call:
  583. Markers.Call();
  584. if (Call(context, out doRestart))
  585. {
  586. if (doRestart)
  587. {
  588. goto Restart;
  589. }
  590. continue;
  591. }
  592. return true;
  593. case OpCode.TailCall:
  594. Markers.TailCall();
  595. if (TailCall(context, out doRestart))
  596. {
  597. if (doRestart) goto Restart;
  598. if (context.IsTopLevel) goto End;
  599. continue;
  600. }
  601. return true;
  602. case OpCode.Return:
  603. Markers.Return();
  604. context.State.CloseUpValues(context.Thread, frameBase);
  605. if (context.Pop(instruction, frameBase))
  606. {
  607. goto Restart;
  608. }
  609. goto End;
  610. case OpCode.ForLoop:
  611. Markers.ForLoop();
  612. ref var indexRef = ref stack.Get(iA + frameBase);
  613. var limit = Unsafe.Add(ref indexRef, 1).UnsafeReadDouble();
  614. var step = Unsafe.Add(ref indexRef, 2).UnsafeReadDouble();
  615. var index = indexRef.UnsafeReadDouble() + step;
  616. if (step >= 0 ? index <= limit : limit <= index)
  617. {
  618. context.Pc += instruction.SBx;
  619. indexRef = index;
  620. Unsafe.Add(ref indexRef, 3) = index;
  621. stack.NotifyTop(iA + frameBase + 4);
  622. continue;
  623. }
  624. stack.NotifyTop(iA + frameBase + 1);
  625. continue;
  626. case OpCode.ForPrep:
  627. Markers.ForPrep();
  628. indexRef = ref stack.Get(iA + frameBase);
  629. if (!indexRef.TryReadDouble(out var init))
  630. {
  631. ThrowLuaRuntimeException(context, "'for' initial value must be a number");
  632. return true;
  633. }
  634. if (!LuaValue.TryReadOrSetDouble(ref Unsafe.Add(ref indexRef, 1), out _))
  635. {
  636. ThrowLuaRuntimeException(context, "'for' limit must be a number");
  637. return true;
  638. }
  639. if (!LuaValue.TryReadOrSetDouble(ref Unsafe.Add(ref indexRef, 2), out step))
  640. {
  641. ThrowLuaRuntimeException(context, "'for' step must be a number");
  642. return true;
  643. }
  644. indexRef = init - step;
  645. stack.NotifyTop(iA + frameBase + 1);
  646. context.Pc += instruction.SBx;
  647. continue;
  648. case OpCode.TForCall:
  649. Markers.TForCall();
  650. if (TForCall(context, out doRestart))
  651. {
  652. if (doRestart) goto Restart;
  653. continue;
  654. }
  655. return true;
  656. case OpCode.TForLoop:
  657. Markers.TForLoop();
  658. ref var forState = ref stack.Get(iA + frameBase + 1);
  659. if (forState.Type is not LuaValueType.Nil)
  660. {
  661. Unsafe.Add(ref forState, -1) = forState;
  662. context.Pc += instruction.SBx;
  663. }
  664. continue;
  665. case OpCode.SetList:
  666. Markers.SetList();
  667. SetList(context);
  668. continue;
  669. case OpCode.Closure:
  670. Markers.Closure();
  671. ra1 = iA + frameBase + 1;
  672. stack.EnsureCapacity(ra1);
  673. stack.Get(ra1 - 1) = new LuaClosure(context.Thread, context.Prototype.ChildPrototypes[instruction.Bx]);
  674. stack.NotifyTop(ra1);
  675. continue;
  676. case OpCode.VarArg:
  677. Markers.VarArg();
  678. VarArg(context);
  679. static void VarArg(VirtualMachineExecutionContext context)
  680. {
  681. var instruction = context.Instruction;
  682. var iA = instruction.A;
  683. var frameBase = context.FrameBase;
  684. var frameVariableArgumentCount = context.VariableArgumentCount;
  685. var count = instruction.B == 0
  686. ? frameVariableArgumentCount
  687. : instruction.B - 1;
  688. var ra = iA + frameBase;
  689. var stack = context.Stack;
  690. stack.EnsureCapacity(ra + count);
  691. ref var stackHead = ref stack.Get(0);
  692. for (int i = 0; i < count; i++)
  693. {
  694. Unsafe.Add(ref stackHead, ra + i) = frameVariableArgumentCount > i
  695. ? Unsafe.Add(ref stackHead, frameBase - (frameVariableArgumentCount - i))
  696. : default;
  697. }
  698. stack.NotifyTop(ra + count);
  699. }
  700. continue;
  701. case OpCode.ExtraArg:
  702. default:
  703. ThrowLuaNotImplementedException(context, context.Instruction.OpCode);
  704. return true;
  705. }
  706. }
  707. End:
  708. context.PostOperation = PostOperationType.None;
  709. return false;
  710. }
  711. catch (Exception e)
  712. {
  713. context.State.CloseUpValues(context.Thread, context.FrameBase);
  714. if (e is not LuaRuntimeException)
  715. {
  716. var newException = new LuaRuntimeException(context.Thread, e);
  717. throw newException;
  718. }
  719. throw;
  720. }
  721. }
  722. static void ThrowLuaRuntimeException(VirtualMachineExecutionContext context, string message)
  723. {
  724. throw new LuaRuntimeException(context.Thread, message);
  725. }
  726. static void ThrowLuaNotImplementedException(VirtualMachineExecutionContext context, OpCode opcode)
  727. {
  728. throw new LuaRuntimeException(context.Thread, $"OpCode {opcode} is not implemented");
  729. }
  730. static void SelfPostOperation(VirtualMachineExecutionContext context, Span<LuaValue> results)
  731. {
  732. var stack = context.Stack;
  733. var instruction = context.Instruction;
  734. var RA = instruction.A + context.FrameBase;
  735. var RB = instruction.B + context.FrameBase;
  736. ref var stackHead = ref stack.Get(0);
  737. var table = Unsafe.Add(ref stackHead, RB);
  738. Unsafe.Add(ref stackHead, RA + 1) = table;
  739. Unsafe.Add(ref stackHead, RA) = results.Length == 0 ? LuaValue.Nil : results[0];
  740. stack.NotifyTop(RA + 2);
  741. }
  742. [MethodImpl(MethodImplOptions.NoInlining)]
  743. static bool Concat(VirtualMachineExecutionContext context)
  744. {
  745. var instruction = context.Instruction;
  746. var stack = context.Stack;
  747. var b = instruction.B;
  748. var c = instruction.C;
  749. stack.NotifyTop(context.FrameBase + c + 1);
  750. var a = instruction.A;
  751. var task = Concat(context, context.FrameBase + a, c - b + 1);
  752. if (task.IsCompleted)
  753. {
  754. return true;
  755. }
  756. context.Task = task;
  757. context.PostOperation = PostOperationType.None;
  758. return false;
  759. }
  760. static async ValueTask<int> Concat(VirtualMachineExecutionContext context, int target, int total)
  761. {
  762. static bool ToString(ref LuaValue v)
  763. {
  764. if (v.Type == LuaValueType.String) return true;
  765. if (v.Type == LuaValueType.Number)
  766. {
  767. v = v.ToString();
  768. return true;
  769. }
  770. return false;
  771. }
  772. var stack = context.Stack;
  773. do
  774. {
  775. var top = context.Thread.Stack.Count;
  776. var n = 2;
  777. ref var lhs = ref stack.Get(top - 2);
  778. ref var rhs = ref stack.Get(top - 1);
  779. if (!(lhs.Type is LuaValueType.String or LuaValueType.Number) || !ToString(ref rhs))
  780. {
  781. await ExecuteBinaryOperationMetaMethod(top - 2, lhs, rhs, context, OpCode.Concat);
  782. }
  783. else if (rhs.UnsafeReadString().Length == 0)
  784. {
  785. ToString(ref lhs);
  786. }
  787. else if (lhs.TryReadString(out var str) && str.Length == 0)
  788. {
  789. lhs = rhs;
  790. }
  791. else
  792. {
  793. var tl = rhs.UnsafeReadString().Length;
  794. int i = 1;
  795. for (; i < total; i++)
  796. {
  797. ref var v = ref stack.Get(top - i - 1);
  798. if (!ToString(ref v))
  799. {
  800. break;
  801. }
  802. tl += v.UnsafeReadString().Length;
  803. }
  804. n = i;
  805. stack.Get(top - n) = string.Create(tl, (stack, top - n), static (span, pair) =>
  806. {
  807. var (stack, index) = pair;
  808. foreach (var v in stack.AsSpan().Slice(index))
  809. {
  810. var s = v.UnsafeReadString();
  811. if (s.Length == 0) continue;
  812. s.AsSpan().CopyTo(span);
  813. span = span[s.Length..];
  814. }
  815. });
  816. }
  817. total -= n - 1;
  818. stack.PopUntil(top - (n - 1));
  819. } while (total > 1);
  820. stack.Get(target) = stack.AsSpan()[^1];
  821. return 1;
  822. }
  823. internal static async ValueTask<LuaValue> Concat(LuaThread thread, int total, CancellationToken ct)
  824. {
  825. static bool ToString(ref LuaValue v)
  826. {
  827. if (v.Type == LuaValueType.String) return true;
  828. if (v.Type == LuaValueType.Number)
  829. {
  830. v = v.ToString();
  831. return true;
  832. }
  833. return false;
  834. }
  835. var stack = thread.Stack;
  836. do
  837. {
  838. var top = thread.Stack.Count;
  839. var n = 2;
  840. ref var lhs = ref stack.Get(top - 2);
  841. ref var rhs = ref stack.Get(top - 1);
  842. if (!(lhs.Type is LuaValueType.String or LuaValueType.Number) || !ToString(ref rhs))
  843. {
  844. var value = await ExecuteBinaryOperationMetaMethod(thread, lhs, rhs, OpCode.Concat, ct);
  845. stack.Get(top - 2) = value;
  846. }
  847. else if (rhs.UnsafeReadString().Length == 0)
  848. {
  849. ToString(ref lhs);
  850. }
  851. else if (lhs.TryReadString(out var str) && str.Length == 0)
  852. {
  853. lhs = rhs;
  854. }
  855. else
  856. {
  857. var tl = rhs.UnsafeReadString().Length;
  858. int i = 1;
  859. for (; i < total; i++)
  860. {
  861. ref var v = ref stack.Get(top - i - 1);
  862. if (!ToString(ref v))
  863. {
  864. break;
  865. }
  866. tl += v.UnsafeReadString().Length;
  867. }
  868. n = i;
  869. stack.Get(top - n) = string.Create(tl, (stack, top - n), static (span, pair) =>
  870. {
  871. var (stack, index) = pair;
  872. foreach (var v in stack.AsSpan().Slice(index))
  873. {
  874. var s = v.UnsafeReadString();
  875. if (s.Length == 0) continue;
  876. s.AsSpan().CopyTo(span);
  877. span = span[s.Length..];
  878. }
  879. });
  880. }
  881. total -= n - 1;
  882. stack.PopUntil(top - (n - 1));
  883. } while (total > 1);
  884. return stack.AsSpan()[^1];
  885. }
  886. static async ValueTask ExecuteBinaryOperationMetaMethod(int target, LuaValue vb, LuaValue vc,
  887. VirtualMachineExecutionContext context, OpCode opCode)
  888. {
  889. var (name, description) = opCode.GetNameAndDescription();
  890. if (vb.TryGetMetamethod(context.State, name, out var metamethod) ||
  891. vc.TryGetMetamethod(context.State, name, out metamethod))
  892. {
  893. if (!metamethod.TryReadFunction(out var func))
  894. {
  895. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", metamethod);
  896. }
  897. var stack = context.Stack;
  898. stack.Push(vb);
  899. stack.Push(vc);
  900. var varArgCount = func.GetVariableArgumentCount(2);
  901. var newFrame = func.CreateNewFrame(context, stack.Count - 2 + varArgCount, target, varArgCount);
  902. context.Thread.PushCallStackFrame(newFrame);
  903. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  904. {
  905. await ExecuteCallHook(context, newFrame, 2);
  906. }
  907. await func.Invoke(context, newFrame, 2);
  908. stack.PopUntil(target + 1);
  909. context.Thread.PopCallStackFrame();
  910. context.PostOperation = PostOperationType.DontPop;
  911. return;
  912. }
  913. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), description, vb, vc);
  914. return;
  915. }
  916. static bool Call(VirtualMachineExecutionContext context, out bool doRestart)
  917. {
  918. var instruction = context.Instruction;
  919. var RA = instruction.A + context.FrameBase;
  920. var newBase = RA + 1;
  921. var va = context.Stack.Get(RA);
  922. bool isMetamethod = false;
  923. if (!va.TryReadFunction(out var func))
  924. {
  925. if (va.TryGetMetamethod(context.State, Metamethods.Call, out var metamethod) &&
  926. metamethod.TryReadFunction(out func))
  927. {
  928. newBase -= 1;
  929. isMetamethod = true;
  930. }
  931. else
  932. {
  933. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", va);
  934. }
  935. }
  936. var thread = context.Thread;
  937. var (argumentCount, variableArgumentCount) = PrepareForFunctionCall(thread, func, instruction, newBase, isMetamethod);
  938. newBase += variableArgumentCount;
  939. thread.Stack.PopUntil(newBase + argumentCount);
  940. var newFrame = func.CreateNewFrame(context, newBase, RA, variableArgumentCount);
  941. thread.PushCallStackFrame(newFrame);
  942. if (thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  943. {
  944. context.PostOperation = PostOperationType.Call;
  945. context.Task = ExecuteCallHook(context, newFrame, argumentCount);
  946. doRestart = false;
  947. return false;
  948. }
  949. if (func is LuaClosure)
  950. {
  951. context.Push(newFrame);
  952. doRestart = true;
  953. return true;
  954. }
  955. doRestart = false;
  956. return FuncCall(context, in newFrame, func, newBase, argumentCount);
  957. static bool FuncCall(VirtualMachineExecutionContext context, in CallStackFrame newFrame, LuaFunction func, int newBase, int argumentCount)
  958. {
  959. var task = func.Invoke(context, newFrame, argumentCount);
  960. if (!task.IsCompleted)
  961. {
  962. context.Task = task;
  963. return false;
  964. }
  965. var awaiter = task.GetAwaiter();
  966. awaiter.GetResult();
  967. var instruction = context.Instruction;
  968. var ic = instruction.C;
  969. if (ic != 0)
  970. {
  971. var resultCount = ic - 1;
  972. var stack = context.Stack;
  973. var top = instruction.A + context.FrameBase + resultCount;
  974. stack.EnsureCapacity(top);
  975. stack.PopUntil(top);
  976. stack.NotifyTop(top);
  977. }
  978. context.Thread.PopCallStackFrame();
  979. return true;
  980. }
  981. }
  982. static void CallPostOperation(VirtualMachineExecutionContext context)
  983. {
  984. var instruction = context.Instruction;
  985. var ic = instruction.C;
  986. if (ic != 0)
  987. {
  988. var resultCount = ic - 1;
  989. var stack = context.Stack;
  990. var top = instruction.A + context.FrameBase + resultCount;
  991. stack.EnsureCapacity(top);
  992. stack.PopUntil(top);
  993. stack.NotifyTop(top);
  994. }
  995. }
  996. static bool TailCall(VirtualMachineExecutionContext context, out bool doRestart)
  997. {
  998. var instruction = context.Instruction;
  999. var stack = context.Stack;
  1000. var RA = instruction.A + context.FrameBase;
  1001. var newBase = RA + 1;
  1002. bool isMetamethod = false;
  1003. var state = context.State;
  1004. var thread = context.Thread;
  1005. state.CloseUpValues(thread, context.FrameBase);
  1006. var va = stack.Get(RA);
  1007. if (!va.TryReadFunction(out var func))
  1008. {
  1009. if (va.TryGetMetamethod(state, Metamethods.Call, out var metamethod) &&
  1010. metamethod.TryReadFunction(out func))
  1011. {
  1012. isMetamethod = true;
  1013. newBase -= 1;
  1014. }
  1015. else
  1016. {
  1017. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", metamethod);
  1018. }
  1019. }
  1020. var (argumentCount, variableArgumentCount) = PrepareForFunctionTailCall(thread, func, instruction, newBase, isMetamethod);
  1021. newBase = context.FrameBase + variableArgumentCount;
  1022. stack.PopUntil(newBase + argumentCount);
  1023. var lastPc = thread.GetCurrentFrame().CallerInstructionIndex;
  1024. context.Thread.PopCallStackFrame();
  1025. var newFrame = func.CreateNewTailCallFrame(context, newBase, context.CurrentReturnFrameBase, variableArgumentCount);
  1026. newFrame.CallerInstructionIndex = lastPc;
  1027. thread.PushCallStackFrame(newFrame);
  1028. if (thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1029. {
  1030. context.PostOperation = PostOperationType.TailCall;
  1031. context.Task = ExecuteCallHook(context, newFrame, argumentCount, true);
  1032. doRestart = false;
  1033. return false;
  1034. }
  1035. if (func is LuaClosure)
  1036. {
  1037. context.Push(newFrame);
  1038. doRestart = true;
  1039. return true;
  1040. }
  1041. doRestart = false;
  1042. var task = func.Invoke(context, newFrame, argumentCount);
  1043. if (!task.IsCompleted)
  1044. {
  1045. context.PostOperation = PostOperationType.TailCall;
  1046. context.Task = task;
  1047. return false;
  1048. }
  1049. task.GetAwaiter().GetResult();
  1050. if (!context.PopFromBuffer(context.CurrentReturnFrameBase, context.Stack.Count - context.CurrentReturnFrameBase))
  1051. {
  1052. return true;
  1053. }
  1054. doRestart = true;
  1055. return true;
  1056. }
  1057. static bool TForCall(VirtualMachineExecutionContext context, out bool doRestart)
  1058. {
  1059. doRestart = false;
  1060. var instruction = context.Instruction;
  1061. var stack = context.Stack;
  1062. var RA = instruction.A + context.FrameBase;
  1063. bool isMetamethod = false;
  1064. var iteratorRaw = stack.Get(RA);
  1065. if (!iteratorRaw.TryReadFunction(out var iterator))
  1066. {
  1067. if (iteratorRaw.TryGetMetamethod(context.State, Metamethods.Call, out var metamethod) &&
  1068. metamethod.TryReadFunction(out iterator))
  1069. {
  1070. isMetamethod = true;
  1071. }
  1072. else
  1073. {
  1074. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", metamethod);
  1075. }
  1076. }
  1077. var newBase = RA + 3 + instruction.C;
  1078. if (isMetamethod)
  1079. {
  1080. stack.Get(newBase) = iteratorRaw;
  1081. stack.Get(newBase + 1) = stack.Get(RA + 1);
  1082. stack.Get(newBase + 2) = stack.Get(RA + 2);
  1083. stack.SetTop(newBase + 3);
  1084. }
  1085. else
  1086. {
  1087. stack.Get(newBase) = stack.Get(RA + 1);
  1088. stack.Get(newBase + 1) = stack.Get(RA + 2);
  1089. stack.SetTop(newBase + 2);
  1090. }
  1091. var argumentCount = isMetamethod ? 3 : 2;
  1092. var variableArgumentCount = iterator.GetVariableArgumentCount(argumentCount);
  1093. if (variableArgumentCount != 0)
  1094. {
  1095. PrepareVariableArgument(stack, newBase, argumentCount, variableArgumentCount);
  1096. newBase += variableArgumentCount;
  1097. }
  1098. stack.PopUntil(newBase + argumentCount);
  1099. var newFrame = iterator.CreateNewFrame(context, newBase, RA + 3, variableArgumentCount);
  1100. context.Thread.PushCallStackFrame(newFrame);
  1101. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1102. {
  1103. context.PostOperation = PostOperationType.TForCall;
  1104. context.Task = ExecuteCallHook(context, newFrame, 2);
  1105. doRestart = false;
  1106. return false;
  1107. }
  1108. if (iterator is LuaClosure)
  1109. {
  1110. context.Push(newFrame);
  1111. doRestart = true;
  1112. return true;
  1113. }
  1114. var task = iterator.Invoke(context, newFrame, 2);
  1115. if (!task.IsCompleted)
  1116. {
  1117. context.PostOperation = PostOperationType.TForCall;
  1118. context.Task = task;
  1119. return false;
  1120. }
  1121. var awaiter = task.GetAwaiter();
  1122. awaiter.GetResult();
  1123. context.Thread.PopCallStackFrame();
  1124. TForCallPostOperation(context);
  1125. return true;
  1126. }
  1127. // ReSharper disable once InconsistentNaming
  1128. static void TForCallPostOperation(VirtualMachineExecutionContext context)
  1129. {
  1130. var stack = context.Stack;
  1131. var instruction = context.Instruction;
  1132. var RA = instruction.A + context.FrameBase;
  1133. stack.SetTop(RA + instruction.C + 3);
  1134. }
  1135. static void SetList(VirtualMachineExecutionContext context)
  1136. {
  1137. var instruction = context.Instruction;
  1138. var stack = context.Stack;
  1139. var RA = instruction.A + context.FrameBase;
  1140. if (!stack.Get(RA).TryReadTable(out var table))
  1141. {
  1142. throw new LuaException("internal error");
  1143. }
  1144. var count = instruction.B == 0
  1145. ? stack.Count - (RA + 1)
  1146. : instruction.B;
  1147. table.EnsureArrayCapacity((instruction.C - 1) * 50 + count);
  1148. stack.GetBuffer().Slice(RA + 1, count)
  1149. .CopyTo(table.GetArraySpan()[((instruction.C - 1) * 50)..]);
  1150. stack.PopUntil(RA + 1);
  1151. }
  1152. static void ComparePostOperation(VirtualMachineExecutionContext context, Span<LuaValue> results)
  1153. {
  1154. var compareResult = results.Length != 0 && results[0].ToBoolean();
  1155. if (compareResult != (context.Instruction.A == 1))
  1156. {
  1157. context.Pc++;
  1158. }
  1159. results.Clear();
  1160. }
  1161. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1162. static ref readonly LuaValue RKB(ref LuaValue stack, ref LuaValue constants, Instruction instruction)
  1163. {
  1164. var index = instruction.B;
  1165. return ref (index >= 256 ? ref Unsafe.Add(ref constants, index - 256) : ref Unsafe.Add(ref stack, index));
  1166. }
  1167. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1168. static ref readonly LuaValue RKC(ref LuaValue stack, ref LuaValue constants, Instruction instruction)
  1169. {
  1170. var index = instruction.C;
  1171. return ref (index >= 256 ? ref Unsafe.Add(ref constants, index - 256) : ref Unsafe.Add(ref stack, index));
  1172. }
  1173. [MethodImpl(MethodImplOptions.NoInlining)]
  1174. static bool GetTableValueSlowPath(LuaValue table, LuaValue key, VirtualMachineExecutionContext context, out LuaValue value, out bool doRestart)
  1175. {
  1176. var targetTable = table;
  1177. const int MAX_LOOP = 100;
  1178. doRestart = false;
  1179. var skip = targetTable.Type == LuaValueType.Table;
  1180. for (int i = 0; i < MAX_LOOP; i++)
  1181. {
  1182. if (table.TryReadTable(out var luaTable))
  1183. {
  1184. if (!skip && luaTable.TryGetValue(key, out value))
  1185. {
  1186. return true;
  1187. }
  1188. skip = false;
  1189. var metatable = luaTable.Metatable;
  1190. if (metatable != null && metatable.TryGetValue(Metamethods.Index, out table))
  1191. {
  1192. goto Function;
  1193. }
  1194. value = default;
  1195. return true;
  1196. }
  1197. if (!table.TryGetMetamethod(context.State, Metamethods.Index, out var metatableValue))
  1198. {
  1199. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "index", table);
  1200. }
  1201. table = metatableValue;
  1202. Function:
  1203. if (table.TryReadFunction(out var function))
  1204. {
  1205. return CallGetTableFunc(targetTable, function, key, context, out value, out doRestart);
  1206. }
  1207. }
  1208. throw new LuaRuntimeException(GetThreadWithCurrentPc(context), "loop in gettable");
  1209. }
  1210. [MethodImpl(MethodImplOptions.NoInlining)]
  1211. static bool CallGetTableFunc(LuaValue table, LuaFunction indexTable, LuaValue key, VirtualMachineExecutionContext context, out LuaValue result, out bool doRestart)
  1212. {
  1213. doRestart = false;
  1214. var stack = context.Stack;
  1215. stack.Push(table);
  1216. stack.Push(key);
  1217. var newFrame = indexTable.CreateNewFrame(context, stack.Count - 2);
  1218. context.Thread.PushCallStackFrame(newFrame);
  1219. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1220. {
  1221. context.PostOperation = context.Instruction.OpCode == OpCode.GetTable ? PostOperationType.SetResult : PostOperationType.Self;
  1222. context.Task = ExecuteCallHook(context, newFrame, 2);
  1223. doRestart = false;
  1224. result = default;
  1225. return false;
  1226. }
  1227. if (indexTable is LuaClosure)
  1228. {
  1229. context.Push(newFrame);
  1230. doRestart = true;
  1231. result = default;
  1232. return true;
  1233. }
  1234. var task = indexTable.Invoke(context, newFrame, 2);
  1235. if (!task.IsCompleted)
  1236. {
  1237. context.PostOperation = context.Instruction.OpCode == OpCode.GetTable ? PostOperationType.SetResult : PostOperationType.Self;
  1238. context.Task = task;
  1239. result = default;
  1240. return false;
  1241. }
  1242. var awaiter = task.GetAwaiter();
  1243. awaiter.GetResult();
  1244. var results = stack.GetBuffer()[newFrame.Base..];
  1245. result = results.Length == 0 ? default : results[0];
  1246. context.Thread.PopCallStackFrameWithStackPop();
  1247. return true;
  1248. }
  1249. [MethodImpl(MethodImplOptions.NoInlining)]
  1250. internal static ValueTask<LuaValue> ExecuteGetTableSlowPath(LuaThread thread, LuaValue table, LuaValue key, CancellationToken ct)
  1251. {
  1252. var targetTable = table;
  1253. const int MAX_LOOP = 100;
  1254. var skip = targetTable.Type == LuaValueType.Table;
  1255. for (int i = 0; i < MAX_LOOP; i++)
  1256. {
  1257. if (table.TryReadTable(out var luaTable))
  1258. {
  1259. if (!skip && luaTable.TryGetValue(key, out var value))
  1260. {
  1261. return new(value);
  1262. }
  1263. skip = false;
  1264. var metatable = luaTable.Metatable;
  1265. if (metatable != null && metatable.TryGetValue(Metamethods.Index, out table))
  1266. {
  1267. goto Function;
  1268. }
  1269. return default(ValueTask<LuaValue>);
  1270. }
  1271. if (!table.TryGetMetamethod(thread.State, Metamethods.Index, out var metatableValue))
  1272. {
  1273. LuaRuntimeException.AttemptInvalidOperation(thread, "index", table);
  1274. }
  1275. table = metatableValue;
  1276. Function:
  1277. if (table.TryReadFunction(out var function))
  1278. {
  1279. return CallGetTableFunc(thread, function, targetTable, key, ct);
  1280. }
  1281. }
  1282. throw new LuaRuntimeException(thread, "loop in gettable");
  1283. }
  1284. [MethodImpl(MethodImplOptions.NoInlining)]
  1285. static async ValueTask<LuaValue> CallGetTableFunc(LuaThread thread, LuaFunction indexTable, LuaValue table, LuaValue key, CancellationToken ct)
  1286. {
  1287. var stack = thread.Stack;
  1288. var top = stack.Count;
  1289. stack.Push(table);
  1290. stack.Push(key);
  1291. var varArgCount = indexTable.GetVariableArgumentCount(3);
  1292. var newFrame = new CallStackFrame() { Base = thread.Stack.Count - 2 + varArgCount, VariableArgumentCount = varArgCount, Function = indexTable, ReturnBase = top };
  1293. thread.PushCallStackFrame(newFrame);
  1294. var functionContext = new LuaFunctionExecutionContext() { Thread = thread, ArgumentCount = 3, ReturnFrameBase = top };
  1295. if (thread.CallOrReturnHookMask.Value != 0 && !thread.IsInHook)
  1296. {
  1297. await ExecuteCallHook(functionContext, ct);
  1298. }
  1299. await indexTable.Func(functionContext, ct);
  1300. var results = stack.GetBuffer()[newFrame.ReturnBase..];
  1301. var result = results.Length == 0 ? default : results[0];
  1302. results.Clear();
  1303. thread.PopCallStackFrameWithStackPop();
  1304. return result;
  1305. }
  1306. [MethodImpl(MethodImplOptions.NoInlining)]
  1307. static bool SetTableValueSlowPath(LuaValue table, LuaValue key, LuaValue value,
  1308. VirtualMachineExecutionContext context, out bool doRestart)
  1309. {
  1310. var targetTable = table;
  1311. const int MAX_LOOP = 100;
  1312. doRestart = false;
  1313. var skip = targetTable.Type == LuaValueType.Table;
  1314. for (int i = 0; i < MAX_LOOP; i++)
  1315. {
  1316. if (table.TryReadTable(out var luaTable))
  1317. {
  1318. targetTable = luaTable;
  1319. ref var valueRef = ref (skip ? ref Unsafe.NullRef<LuaValue>() : ref luaTable.FindValue(key));
  1320. skip = false;
  1321. if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
  1322. {
  1323. valueRef = value;
  1324. return true;
  1325. }
  1326. var metatable = luaTable.Metatable;
  1327. if (metatable == null || !metatable.TryGetValue(Metamethods.NewIndex, out table))
  1328. {
  1329. if (Unsafe.IsNullRef(ref valueRef))
  1330. {
  1331. luaTable[key] = value;
  1332. return true;
  1333. }
  1334. valueRef = value;
  1335. return true;
  1336. }
  1337. goto Function;
  1338. }
  1339. if (!table.TryGetMetamethod(context.State, Metamethods.NewIndex, out var metatableValue))
  1340. {
  1341. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "index", table);
  1342. }
  1343. table = metatableValue;
  1344. Function:
  1345. if (table.TryReadFunction(out var function))
  1346. {
  1347. context.PostOperation = PostOperationType.Nop;
  1348. return CallSetTableFunc(targetTable, function, key, value, context, out doRestart);
  1349. }
  1350. }
  1351. throw new LuaRuntimeException(GetThreadWithCurrentPc(context), "loop in settable");
  1352. }
  1353. [MethodImpl(MethodImplOptions.NoInlining)]
  1354. static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaValue key, LuaValue value, VirtualMachineExecutionContext context, out bool doRestart)
  1355. {
  1356. doRestart = false;
  1357. var thread = context.Thread;
  1358. var stack = thread.Stack;
  1359. stack.Push(table);
  1360. stack.Push(key);
  1361. stack.Push(value);
  1362. var newFrame = newIndexFunction.CreateNewFrame(context, stack.Count - 3);
  1363. context.Thread.PushCallStackFrame(newFrame);
  1364. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1365. {
  1366. context.PostOperation = PostOperationType.Nop;
  1367. context.Task = ExecuteCallHook(context, newFrame, 3);
  1368. doRestart = false;
  1369. return false;
  1370. }
  1371. if (newIndexFunction is LuaClosure)
  1372. {
  1373. context.Push(newFrame);
  1374. doRestart = true;
  1375. return true;
  1376. }
  1377. var task = newIndexFunction.Invoke(context, newFrame, 3);
  1378. if (!task.IsCompleted)
  1379. {
  1380. context.PostOperation = PostOperationType.Nop;
  1381. context.Task = task;
  1382. return false;
  1383. }
  1384. task.GetAwaiter().GetResult();
  1385. thread.PopCallStackFrameWithStackPop();
  1386. return true;
  1387. }
  1388. internal static ValueTask ExecuteSetTableSlowPath(LuaThread thread, LuaValue table, LuaValue key, LuaValue value, CancellationToken ct)
  1389. {
  1390. var targetTable = table;
  1391. const int MAX_LOOP = 100;
  1392. var skip = targetTable.Type == LuaValueType.Table;
  1393. for (int i = 0; i < MAX_LOOP; i++)
  1394. {
  1395. if (table.TryReadTable(out var luaTable))
  1396. {
  1397. targetTable = luaTable;
  1398. ref var valueRef = ref (skip ? ref Unsafe.NullRef<LuaValue>() : ref luaTable.FindValue(key));
  1399. skip = false;
  1400. if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
  1401. {
  1402. valueRef = value;
  1403. return default(ValueTask);
  1404. }
  1405. var metatable = luaTable.Metatable;
  1406. if (metatable == null || !metatable.TryGetValue(Metamethods.NewIndex, out table))
  1407. {
  1408. if (Unsafe.IsNullRef(ref valueRef))
  1409. {
  1410. luaTable[key] = value;
  1411. return default(ValueTask);
  1412. }
  1413. valueRef = value;
  1414. return default;
  1415. }
  1416. goto Function;
  1417. }
  1418. if (!table.TryGetMetamethod(thread.State, Metamethods.NewIndex, out var metatableValue))
  1419. {
  1420. LuaRuntimeException.AttemptInvalidOperation(thread, "index", table);
  1421. }
  1422. table = metatableValue;
  1423. Function:
  1424. if (table.TryReadFunction(out var function))
  1425. {
  1426. return CallSetTableFunc(thread, function, targetTable, key, value, ct);
  1427. }
  1428. }
  1429. throw new LuaRuntimeException(thread, "loop in settable");
  1430. }
  1431. [MethodImpl(MethodImplOptions.NoInlining)]
  1432. static async ValueTask CallSetTableFunc(LuaThread thread, LuaFunction newIndexFunction, LuaValue table, LuaValue key, LuaValue value, CancellationToken ct)
  1433. {
  1434. var stack = thread.Stack;
  1435. var top = stack.Count;
  1436. stack.Push(table);
  1437. stack.Push(key);
  1438. stack.Push(value);
  1439. var varArgCount = newIndexFunction.GetVariableArgumentCount(3);
  1440. var newFrame = new CallStackFrame() { Base = thread.Stack.Count - 3 + varArgCount, VariableArgumentCount = varArgCount, Function = newIndexFunction, ReturnBase = top };
  1441. thread.PushCallStackFrame(newFrame);
  1442. var functionContext = new LuaFunctionExecutionContext() { Thread = thread, ArgumentCount = 3, ReturnFrameBase = top };
  1443. if (thread.CallOrReturnHookMask.Value != 0 && !thread.IsInHook)
  1444. {
  1445. await ExecuteCallHook(functionContext, ct);
  1446. }
  1447. await newIndexFunction.Func(functionContext, ct);
  1448. var results = stack.GetBuffer()[newFrame.ReturnBase..];
  1449. results.Clear();
  1450. thread.PopCallStackFrameWithStackPop();
  1451. }
  1452. [MethodImpl(MethodImplOptions.NoInlining)]
  1453. static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc,
  1454. VirtualMachineExecutionContext context, OpCode opCode, out bool doRestart)
  1455. {
  1456. var (name, description) = opCode.GetNameAndDescription();
  1457. doRestart = false;
  1458. if (vb.TryGetMetamethod(context.State, name, out var metamethod) ||
  1459. vc.TryGetMetamethod(context.State, name, out metamethod))
  1460. {
  1461. if (!metamethod.TryReadFunction(out var func))
  1462. {
  1463. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", metamethod);
  1464. }
  1465. var stack = context.Stack;
  1466. stack.Push(vb);
  1467. stack.Push(vc);
  1468. var varArgCount = func.GetVariableArgumentCount(2);
  1469. var newFrame = func.CreateNewFrame(context, stack.Count - 2 + varArgCount, context.FrameBase + context.Instruction.A, varArgCount);
  1470. context.Thread.PushCallStackFrame(newFrame);
  1471. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1472. {
  1473. context.PostOperation = PostOperationType.SetResult;
  1474. context.Task = ExecuteCallHook(context, newFrame, 2);
  1475. doRestart = false;
  1476. return false;
  1477. }
  1478. if (func is LuaClosure)
  1479. {
  1480. context.Push(newFrame);
  1481. doRestart = true;
  1482. return true;
  1483. }
  1484. var task = func.Invoke(context, newFrame, 2);
  1485. if (!task.IsCompleted)
  1486. {
  1487. context.PostOperation = PostOperationType.SetResult;
  1488. context.Task = task;
  1489. return false;
  1490. }
  1491. task.GetAwaiter().GetResult();
  1492. var RA = context.Instruction.A + context.FrameBase;
  1493. var results = stack.GetBuffer()[newFrame.ReturnBase..];
  1494. stack.Get(RA) = results.Length == 0 ? default : results[0];
  1495. results.Clear();
  1496. context.Thread.PopCallStackFrameWithStackPop();
  1497. return true;
  1498. }
  1499. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), description, vb, vc);
  1500. return false;
  1501. }
  1502. [MethodImpl(MethodImplOptions.NoInlining)]
  1503. internal static async ValueTask<LuaValue> ExecuteBinaryOperationMetaMethod(LuaThread thread, LuaValue vb, LuaValue vc, OpCode opCode, CancellationToken ct)
  1504. {
  1505. var (name, description) = opCode.GetNameAndDescription();
  1506. if (vb.TryGetMetamethod(thread.State, name, out var metamethod) ||
  1507. vc.TryGetMetamethod(thread.State, name, out metamethod))
  1508. {
  1509. if (!metamethod.TryReadFunction(out var func))
  1510. {
  1511. LuaRuntimeException.AttemptInvalidOperation(thread, "call", metamethod);
  1512. }
  1513. var stack = thread.Stack;
  1514. var top = stack.Count;
  1515. stack.Push(vb);
  1516. stack.Push(vc);
  1517. var varArgCount = func.GetVariableArgumentCount(2);
  1518. var newFrame = new CallStackFrame() { Base = thread.Stack.Count - 2 + varArgCount, VariableArgumentCount = varArgCount, Function = func, ReturnBase = top };
  1519. thread.PushCallStackFrame(newFrame);
  1520. var functionContext = new LuaFunctionExecutionContext() { Thread = thread, ArgumentCount = 2, ReturnFrameBase = top };
  1521. if (thread.CallOrReturnHookMask.Value != 0 && !thread.IsInHook)
  1522. {
  1523. await ExecuteCallHook(functionContext, ct);
  1524. }
  1525. await func.Func(functionContext, ct);
  1526. var results = stack.GetBuffer()[newFrame.ReturnBase..];
  1527. var result = results.Length == 0 ? default : results[0];
  1528. results.Clear();
  1529. thread.PopCallStackFrameWithStackPop();
  1530. return result;
  1531. }
  1532. LuaRuntimeException.AttemptInvalidOperation(thread, description, vb, vc);
  1533. return default;
  1534. }
  1535. [MethodImpl(MethodImplOptions.NoInlining)]
  1536. static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, VirtualMachineExecutionContext context,
  1537. OpCode opCode, out bool doRestart)
  1538. {
  1539. var (name, description) = opCode.GetNameAndDescription();
  1540. doRestart = false;
  1541. var stack = context.Stack;
  1542. if (vb.TryGetMetamethod(context.State, name, out var metamethod))
  1543. {
  1544. if (!metamethod.TryReadFunction(out var func))
  1545. {
  1546. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", metamethod);
  1547. }
  1548. stack.Push(vb);
  1549. stack.Push(vb);
  1550. var varArgCount = func.GetVariableArgumentCount(2);
  1551. var newFrame = func.CreateNewFrame(context, stack.Count - 2 + varArgCount, context.FrameBase + context.Instruction.A, varArgCount);
  1552. context.Thread.PushCallStackFrame(newFrame);
  1553. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1554. {
  1555. context.PostOperation = PostOperationType.SetResult;
  1556. context.Task = ExecuteCallHook(context, newFrame, 1);
  1557. doRestart = false;
  1558. return false;
  1559. }
  1560. if (func is LuaClosure)
  1561. {
  1562. context.Push(newFrame);
  1563. doRestart = true;
  1564. return true;
  1565. }
  1566. var task = func.Invoke(context, newFrame, 1);
  1567. if (!task.IsCompleted)
  1568. {
  1569. context.PostOperation = PostOperationType.SetResult;
  1570. context.Task = task;
  1571. return false;
  1572. }
  1573. var RA = context.Instruction.A + context.FrameBase;
  1574. var results = stack.GetBuffer()[newFrame.Base..];
  1575. stack.Get(RA) = results.Length == 0 ? default : results[0];
  1576. results.Clear();
  1577. context.Thread.PopCallStackFrameWithStackPop();
  1578. return true;
  1579. }
  1580. if (opCode == OpCode.Len && vb.TryReadTable(out var table))
  1581. {
  1582. var RA = context.Instruction.A + context.FrameBase;
  1583. stack.Get(RA) = table.ArrayLength;
  1584. return true;
  1585. }
  1586. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), description, vb);
  1587. return true;
  1588. }
  1589. [MethodImpl(MethodImplOptions.NoInlining)]
  1590. internal static async ValueTask<LuaValue> ExecuteUnaryOperationMetaMethod(LuaThread thread, LuaValue vb, OpCode opCode, CancellationToken ct)
  1591. {
  1592. var (name, description) = opCode.GetNameAndDescription();
  1593. if (vb.TryGetMetamethod(thread.State, name, out var metamethod))
  1594. {
  1595. if (!metamethod.TryReadFunction(out var func))
  1596. {
  1597. LuaRuntimeException.AttemptInvalidOperation(thread, "call", metamethod);
  1598. }
  1599. var stack = thread.Stack;
  1600. var top = stack.Count;
  1601. stack.Push(vb);
  1602. var varArgCount = func.GetVariableArgumentCount(1);
  1603. var newFrame = new CallStackFrame() { Base = thread.Stack.Count - 1 + varArgCount, VariableArgumentCount = varArgCount, Function = func, ReturnBase = top };
  1604. thread.PushCallStackFrame(newFrame);
  1605. var functionContext = new LuaFunctionExecutionContext() { Thread = thread, ArgumentCount = 2, ReturnFrameBase = top };
  1606. if (thread.CallOrReturnHookMask.Value != 0 && !thread.IsInHook)
  1607. {
  1608. await ExecuteCallHook(functionContext, ct);
  1609. }
  1610. await func.Func(functionContext, ct);
  1611. var results = stack.GetBuffer()[newFrame.ReturnBase..];
  1612. var result = results.Length == 0 ? default : results[0];
  1613. results.Clear();
  1614. thread.PopCallStackFrameWithStackPop();
  1615. return result;
  1616. }
  1617. LuaRuntimeException.AttemptInvalidOperation(thread, description, vb);
  1618. return default;
  1619. }
  1620. [MethodImpl(MethodImplOptions.NoInlining)]
  1621. static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc,
  1622. VirtualMachineExecutionContext context, OpCode opCode, out bool doRestart)
  1623. {
  1624. var (name, description) = opCode.GetNameAndDescription();
  1625. doRestart = false;
  1626. bool reverseLe = false;
  1627. ReCheck:
  1628. if (vb.TryGetMetamethod(context.State, name, out var metamethod) ||
  1629. vc.TryGetMetamethod(context.State, name, out metamethod))
  1630. {
  1631. if (!metamethod.TryReadFunction(out var func))
  1632. {
  1633. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), "call", metamethod);
  1634. }
  1635. var stack = context.Stack;
  1636. stack.Push(vb);
  1637. stack.Push(vc);
  1638. var newFrame = func.CreateNewFrame(context, stack.Count - 2);
  1639. if (reverseLe) newFrame.Flags |= CallStackFrameFlags.ReversedLe;
  1640. context.Thread.PushCallStackFrame(newFrame);
  1641. if (context.Thread.CallOrReturnHookMask.Value != 0 && !context.Thread.IsInHook)
  1642. {
  1643. context.PostOperation = PostOperationType.Compare;
  1644. context.Task = ExecuteCallHook(context, newFrame, 2);
  1645. doRestart = false;
  1646. return false;
  1647. }
  1648. if (func is LuaClosure)
  1649. {
  1650. context.Push(newFrame);
  1651. doRestart = true;
  1652. return true;
  1653. }
  1654. var task = func.Invoke(context, newFrame, 2);
  1655. if (!task.IsCompleted)
  1656. {
  1657. context.PostOperation = PostOperationType.Compare;
  1658. context.Task = task;
  1659. return false;
  1660. }
  1661. var results = stack.GetBuffer()[newFrame.Base..];
  1662. var compareResult = results.Length == 0 && results[0].ToBoolean();
  1663. compareResult = reverseLe ? !compareResult : compareResult;
  1664. if (compareResult != (context.Instruction.A == 1))
  1665. {
  1666. context.Pc++;
  1667. }
  1668. results.Clear();
  1669. context.Thread.PopCallStackFrameWithStackPop();
  1670. return true;
  1671. }
  1672. if (opCode == OpCode.Le)
  1673. {
  1674. reverseLe = true;
  1675. name = Metamethods.Lt;
  1676. (vb, vc) = (vc, vb);
  1677. goto ReCheck;
  1678. }
  1679. if (opCode != OpCode.Eq)
  1680. {
  1681. if (reverseLe)
  1682. {
  1683. (vb, vc) = (vc, vb);
  1684. }
  1685. LuaRuntimeException.AttemptInvalidOperation(GetThreadWithCurrentPc(context), description, vb, vc);
  1686. }
  1687. else
  1688. {
  1689. if (context.Instruction.A == 1)
  1690. {
  1691. context.Pc++;
  1692. }
  1693. }
  1694. return true;
  1695. }
  1696. [MethodImpl(MethodImplOptions.NoInlining)]
  1697. internal static async ValueTask<bool> ExecuteCompareOperationMetaMethod(LuaThread thread, LuaValue vb, LuaValue vc, OpCode opCode, CancellationToken ct)
  1698. {
  1699. var (name, description) = opCode.GetNameAndDescription();
  1700. bool reverseLe = false;
  1701. ReCheck:
  1702. if (vb.TryGetMetamethod(thread.State, name, out var metamethod) ||
  1703. vc.TryGetMetamethod(thread.State, name, out metamethod))
  1704. {
  1705. if (!metamethod.TryReadFunction(out var func))
  1706. {
  1707. LuaRuntimeException.AttemptInvalidOperation(thread, "call", metamethod);
  1708. }
  1709. var stack = thread.Stack;
  1710. var top = stack.Count;
  1711. stack.Push(vb);
  1712. stack.Push(vc);
  1713. var varArgCount = func.GetVariableArgumentCount(2);
  1714. var newFrame = new CallStackFrame() { Base = thread.Stack.Count - 2 + varArgCount, VariableArgumentCount = varArgCount, Function = func, ReturnBase = top };
  1715. thread.PushCallStackFrame(newFrame);
  1716. var functionContext = new LuaFunctionExecutionContext() { Thread = thread, ArgumentCount = 2, ReturnFrameBase = top };
  1717. if (thread.CallOrReturnHookMask.Value != 0 && !thread.IsInHook)
  1718. {
  1719. await ExecuteCallHook(functionContext, ct);
  1720. }
  1721. await func.Func(functionContext, ct);
  1722. var results = stack.GetBuffer()[newFrame.ReturnBase..];
  1723. var result = results.Length == 0 ? default : results[0];
  1724. results.Clear();
  1725. thread.PopCallStackFrameWithStackPop();
  1726. return result.ToBoolean();
  1727. }
  1728. if (opCode == OpCode.Le)
  1729. {
  1730. reverseLe = true;
  1731. name = Metamethods.Lt;
  1732. (vb, vc) = (vc, vb);
  1733. goto ReCheck;
  1734. }
  1735. if (opCode != OpCode.Eq)
  1736. {
  1737. if (reverseLe)
  1738. {
  1739. (vb, vc) = (vc, vb);
  1740. }
  1741. LuaRuntimeException.AttemptInvalidOperation(thread, description, vb, vc);
  1742. }
  1743. return default;
  1744. }
  1745. // 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.
  1746. // see: https://wubingzheng.github.io/build-lua-in-rust/en/ch08-02.arguments.html
  1747. [MethodImpl(MethodImplOptions.NoInlining)]
  1748. internal static void PrepareVariableArgument(LuaStack stack, int argumentCount,
  1749. int variableArgumentCount)
  1750. {
  1751. var top = stack.Count;
  1752. var newBase = stack.Count - argumentCount;
  1753. var temp = newBase;
  1754. newBase += variableArgumentCount;
  1755. stack.EnsureCapacity(newBase + argumentCount);
  1756. stack.NotifyTop(newBase + argumentCount);
  1757. var stackBuffer = stack.GetBuffer()[temp..];
  1758. stackBuffer[..argumentCount].CopyTo(stackBuffer[variableArgumentCount..]);
  1759. stackBuffer.Slice(argumentCount, variableArgumentCount).CopyTo(stackBuffer);
  1760. ;
  1761. stack.PopUntil(top);
  1762. }
  1763. // 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.
  1764. // see: https://wubingzheng.github.io/build-lua-in-rust/en/ch08-02.arguments.html
  1765. [MethodImpl(MethodImplOptions.NoInlining)]
  1766. static (int ArgumentCount, int VariableArgumentCount) PrepareVariableArgument(LuaStack stack, int newBase, int argumentCount,
  1767. int variableArgumentCount)
  1768. {
  1769. var temp = newBase;
  1770. newBase += variableArgumentCount;
  1771. stack.EnsureCapacity(newBase + argumentCount);
  1772. stack.NotifyTop(newBase + argumentCount);
  1773. var stackBuffer = stack.GetBuffer()[temp..];
  1774. stackBuffer[..argumentCount].CopyTo(stackBuffer[variableArgumentCount..]);
  1775. stackBuffer.Slice(argumentCount, variableArgumentCount).CopyTo(stackBuffer);
  1776. return (argumentCount, variableArgumentCount);
  1777. }
  1778. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1779. static (int ArgumentCount, int VariableArgumentCount) PrepareForFunctionCall(LuaThread thread, LuaFunction function,
  1780. Instruction instruction, int newBase, bool isMetamethod)
  1781. {
  1782. var argumentCount = instruction.B - 1;
  1783. if (argumentCount == -1)
  1784. {
  1785. argumentCount = (ushort)(thread.Stack.Count - newBase);
  1786. }
  1787. else
  1788. {
  1789. if (isMetamethod)
  1790. {
  1791. argumentCount += 1;
  1792. }
  1793. thread.Stack.SetTop(newBase + argumentCount);
  1794. }
  1795. var variableArgumentCount = function.GetVariableArgumentCount(argumentCount);
  1796. if (variableArgumentCount < 0)
  1797. {
  1798. thread.Stack.SetTop(thread.Stack.Count - variableArgumentCount);
  1799. argumentCount -= variableArgumentCount;
  1800. variableArgumentCount = 0;
  1801. }
  1802. if (variableArgumentCount == 0)
  1803. {
  1804. return (argumentCount, 0);
  1805. }
  1806. return PrepareVariableArgument(thread.Stack, newBase, argumentCount, variableArgumentCount);
  1807. }
  1808. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1809. static (int ArgumentCount, int VariableArgumentCount) PrepareForFunctionTailCall(LuaThread thread, LuaFunction function,
  1810. Instruction instruction, int newBase, bool isMetamethod)
  1811. {
  1812. var stack = thread.Stack;
  1813. var argumentCount = instruction.B - 1;
  1814. if (instruction.B == 0)
  1815. {
  1816. argumentCount = (ushort)(stack.Count - newBase);
  1817. }
  1818. else
  1819. {
  1820. if (isMetamethod)
  1821. {
  1822. argumentCount += 1;
  1823. }
  1824. thread.Stack.SetTop(newBase + argumentCount);
  1825. }
  1826. // In the case of tailcall, the local variables of the caller are immediately discarded, so there is no need to retain them.
  1827. // Therefore, a call can be made without allocating new registers.
  1828. var currentBase = thread.GetCurrentFrame().Base;
  1829. {
  1830. var stackBuffer = stack.GetBuffer();
  1831. if (argumentCount > 0)
  1832. stackBuffer.Slice(newBase, argumentCount).CopyTo(stackBuffer.Slice(currentBase, argumentCount));
  1833. newBase = currentBase;
  1834. }
  1835. var variableArgumentCount = function.GetVariableArgumentCount(argumentCount);
  1836. if (variableArgumentCount <= 0)
  1837. {
  1838. return (argumentCount, 0);
  1839. }
  1840. return PrepareVariableArgument(thread.Stack, newBase, argumentCount, variableArgumentCount);
  1841. }
  1842. static LuaThread GetThreadWithCurrentPc(VirtualMachineExecutionContext context)
  1843. {
  1844. GetThreadWithCurrentPc(context.Thread, context.Pc);
  1845. return context.Thread;
  1846. }
  1847. static void GetThreadWithCurrentPc(LuaThread thread, int pc)
  1848. {
  1849. var frame = thread.GetCurrentFrame();
  1850. thread.PushCallStackFrame(frame with { CallerInstructionIndex = pc });
  1851. }
  1852. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1853. static CallStackFrame CreateNewFrame(this LuaFunction function, VirtualMachineExecutionContext context, int newBase)
  1854. {
  1855. return new()
  1856. {
  1857. Base = newBase,
  1858. ReturnBase = newBase,
  1859. Function = function,
  1860. VariableArgumentCount = 0,
  1861. CallerInstructionIndex = context.Pc,
  1862. };
  1863. }
  1864. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1865. static CallStackFrame CreateNewFrame(this LuaFunction function, VirtualMachineExecutionContext context, int newBase, int returnBase, int variableArgumentCount)
  1866. {
  1867. return new()
  1868. {
  1869. Base = newBase,
  1870. ReturnBase = returnBase,
  1871. Function = function,
  1872. VariableArgumentCount = variableArgumentCount,
  1873. CallerInstructionIndex = context.Pc,
  1874. };
  1875. }
  1876. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1877. static CallStackFrame CreateNewTailCallFrame(this LuaFunction function, VirtualMachineExecutionContext context, int newBase, int returnBase, int variableArgumentCount)
  1878. {
  1879. return new()
  1880. {
  1881. Base = newBase,
  1882. ReturnBase = returnBase,
  1883. Function = function,
  1884. VariableArgumentCount = variableArgumentCount,
  1885. CallerInstructionIndex = context.Pc,
  1886. Flags = CallStackFrameFlags.TailCall
  1887. };
  1888. }
  1889. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1890. static ValueTask<int> Invoke(this LuaFunction function, VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments)
  1891. {
  1892. return function.Func(new() { Thread = context.Thread, ArgumentCount = arguments, ReturnFrameBase = frame.ReturnBase, }, context.CancellationToken);
  1893. }
  1894. }