LuaVirtualMachine.cs 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
  1. using System.Buffers;
  2. using System.Runtime.CompilerServices;
  3. using Lua.Internal;
  4. namespace Lua.Runtime;
  5. public static partial class LuaVirtualMachine
  6. {
  7. internal async static ValueTask<int> ExecuteClosureAsync(LuaState state, Closure closure, CallStackFrame frame, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  8. {
  9. var thread = state.CurrentThread;
  10. var stack = thread.Stack;
  11. var chunk = closure.Proto;
  12. var rootChunk = chunk.GetRoot();
  13. var resultBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
  14. try
  15. {
  16. for (var pc = 0; pc < chunk.Instructions.Length; pc++)
  17. {
  18. var instruction = chunk.Instructions[pc];
  19. var RA = instruction.A + frame.Base;
  20. var RB = instruction.B + frame.Base;
  21. switch (instruction.OpCode)
  22. {
  23. case OpCode.Move:
  24. stack.EnsureCapacity(RA + 1);
  25. stack.UnsafeGet(RA) = stack.UnsafeGet(RB);
  26. stack.NotifyTop(RA + 1);
  27. break;
  28. case OpCode.LoadK:
  29. stack.EnsureCapacity(RA + 1);
  30. stack.UnsafeGet(RA) = chunk.Constants[instruction.Bx];
  31. stack.NotifyTop(RA + 1);
  32. break;
  33. case OpCode.LoadKX:
  34. throw new NotImplementedException();
  35. case OpCode.LoadBool:
  36. stack.EnsureCapacity(RA + 1);
  37. stack.UnsafeGet(RA) = instruction.B != 0;
  38. stack.NotifyTop(RA + 1);
  39. if (instruction.C != 0) pc++;
  40. break;
  41. case OpCode.LoadNil:
  42. stack.EnsureCapacity(RA + instruction.B + 1);
  43. stack.GetBuffer().Slice(RA, instruction.B + 1).Clear();
  44. stack.NotifyTop(RA + instruction.B + 1);
  45. break;
  46. case OpCode.GetUpVal:
  47. {
  48. stack.EnsureCapacity(RA + 1);
  49. var upValue = closure.UpValues[instruction.B];
  50. stack.UnsafeGet(RA) = upValue.GetValue();
  51. stack.NotifyTop(RA + 1);
  52. break;
  53. }
  54. case OpCode.GetTabUp:
  55. {
  56. stack.EnsureCapacity(RA + 1);
  57. var vc = RK(stack, chunk, instruction.C, frame.Base);
  58. var upValue = closure.UpValues[instruction.B];
  59. var table = upValue.GetValue();
  60. (var task, var funcContext) = GetTableValue(state, thread, chunk, rootChunk, pc, table, vc, resultBuffer.AsMemory(), cancellationToken);
  61. try
  62. {
  63. await task;
  64. stack.UnsafeGet(RA) = resultBuffer[0];
  65. stack.NotifyTop(RA + 1);
  66. }
  67. finally
  68. {
  69. if (funcContext != null)
  70. {
  71. LuaFunctionExecutionContextPool.Return(funcContext);
  72. }
  73. }
  74. break;
  75. }
  76. case OpCode.GetTable:
  77. {
  78. stack.EnsureCapacity(RA + 1);
  79. var table = stack.UnsafeGet(RB);
  80. var vc = RK(stack, chunk, instruction.C, frame.Base);
  81. (var task, var funcContext) = GetTableValue(state, thread, chunk, rootChunk, pc, table, vc, resultBuffer.AsMemory(), cancellationToken);
  82. try
  83. {
  84. await task;
  85. stack.UnsafeGet(RA) = resultBuffer[0];
  86. stack.NotifyTop(RA + 1);
  87. }
  88. finally
  89. {
  90. if (funcContext != null)
  91. {
  92. LuaFunctionExecutionContextPool.Return(funcContext);
  93. }
  94. }
  95. }
  96. break;
  97. case OpCode.SetTabUp:
  98. {
  99. var vb = RK(stack, chunk, instruction.B, frame.Base);
  100. var vc = RK(stack, chunk, instruction.C, frame.Base);
  101. var upValue = closure.UpValues[instruction.A];
  102. var table = upValue.GetValue();
  103. (var task, var funcContext) = SetTableValue(state, thread, chunk, rootChunk, pc, table, vb, vc, resultBuffer.AsMemory(), cancellationToken);
  104. try
  105. {
  106. await task;
  107. }
  108. finally
  109. {
  110. if (funcContext != null)
  111. {
  112. LuaFunctionExecutionContextPool.Return(funcContext);
  113. }
  114. }
  115. }
  116. break;
  117. case OpCode.SetUpVal:
  118. {
  119. var upValue = closure.UpValues[instruction.B];
  120. upValue.SetValue(stack.UnsafeGet(RA));
  121. break;
  122. }
  123. case OpCode.SetTable:
  124. {
  125. var table = stack.UnsafeGet(RA);
  126. var vb = RK(stack, chunk, instruction.B, frame.Base);
  127. var vc = RK(stack, chunk, instruction.C, frame.Base);
  128. (var task, var funcContext) = SetTableValue(state, thread, chunk, rootChunk, pc, table, vb, vc, resultBuffer.AsMemory(), cancellationToken);
  129. try
  130. {
  131. await task;
  132. }
  133. finally
  134. {
  135. if (funcContext != null)
  136. {
  137. LuaFunctionExecutionContextPool.Return(funcContext);
  138. }
  139. }
  140. }
  141. break;
  142. case OpCode.NewTable:
  143. stack.EnsureCapacity(RA + 1);
  144. stack.UnsafeGet(RA) = new LuaTable(instruction.B, instruction.C);
  145. stack.NotifyTop(RA + 1);
  146. break;
  147. case OpCode.Self:
  148. {
  149. stack.EnsureCapacity(RA + 2);
  150. var table = stack.UnsafeGet(RB);
  151. var vc = RK(stack, chunk, instruction.C, frame.Base);
  152. (var task, var funcContext) = GetTableValue(state, thread, chunk, rootChunk, pc, table, vc, resultBuffer.AsMemory(), cancellationToken);
  153. try
  154. {
  155. await task;
  156. var value = resultBuffer[0];
  157. stack.UnsafeGet(RA + 1) = table;
  158. stack.UnsafeGet(RA) = value;
  159. stack.NotifyTop(RA + 2);
  160. }
  161. finally
  162. {
  163. if (funcContext != null)
  164. {
  165. LuaFunctionExecutionContextPool.Return(funcContext);
  166. }
  167. }
  168. }
  169. break;
  170. case OpCode.Add:
  171. {
  172. stack.EnsureCapacity(RA + 1);
  173. var vb = RK(stack, chunk, instruction.B, frame.Base);
  174. var vc = RK(stack, chunk, instruction.C, frame.Base);
  175. if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  176. {
  177. stack.UnsafeGet(RA) = valueB + valueC;
  178. }
  179. else if (vb.TryGetMetamethod(state, Metamethods.Add, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Add, out metamethod))
  180. {
  181. if (!metamethod.TryRead<LuaFunction>(out var func))
  182. {
  183. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  184. }
  185. stack.Push(vb);
  186. stack.Push(vc);
  187. var funcContext = LuaFunctionExecutionContextPool.Rent();
  188. funcContext.State = state;
  189. funcContext.Thread = thread;
  190. funcContext.ArgumentCount = 2;
  191. funcContext.FrameBase = stack.Count - 2;
  192. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  193. funcContext.ChunkName = chunk.Name;
  194. funcContext.RootChunkName = rootChunk.Name;
  195. try
  196. {
  197. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  198. }
  199. finally
  200. {
  201. LuaFunctionExecutionContextPool.Return(funcContext);
  202. }
  203. stack.UnsafeGet(RA) = resultBuffer[0];
  204. }
  205. else
  206. {
  207. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "add", vb, vc);
  208. }
  209. stack.NotifyTop(RA + 1);
  210. }
  211. break;
  212. case OpCode.Sub:
  213. {
  214. stack.EnsureCapacity(RA + 1);
  215. var vb = RK(stack, chunk, instruction.B, frame.Base);
  216. var vc = RK(stack, chunk, instruction.C, frame.Base);
  217. if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  218. {
  219. stack.UnsafeGet(RA) = valueB - valueC;
  220. }
  221. else if (vb.TryGetMetamethod(state, Metamethods.Sub, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Sub, out metamethod))
  222. {
  223. if (!metamethod.TryRead<LuaFunction>(out var func))
  224. {
  225. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  226. }
  227. stack.Push(vb);
  228. stack.Push(vc);
  229. var funcContext = LuaFunctionExecutionContextPool.Rent();
  230. funcContext.State = state;
  231. funcContext.Thread = thread;
  232. funcContext.ArgumentCount = 2;
  233. funcContext.FrameBase = stack.Count - 2;
  234. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  235. funcContext.ChunkName = chunk.Name;
  236. funcContext.RootChunkName = rootChunk.Name;
  237. try
  238. {
  239. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  240. }
  241. finally
  242. {
  243. LuaFunctionExecutionContextPool.Return(funcContext);
  244. }
  245. stack.UnsafeGet(RA) = resultBuffer[0];
  246. }
  247. else
  248. {
  249. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "sub", vb, vc);
  250. }
  251. stack.NotifyTop(RA + 1);
  252. }
  253. break;
  254. case OpCode.Mul:
  255. {
  256. stack.EnsureCapacity(RA + 1);
  257. var vb = RK(stack, chunk, instruction.B, frame.Base);
  258. var vc = RK(stack, chunk, instruction.C, frame.Base);
  259. if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  260. {
  261. stack.UnsafeGet(RA) = valueB * valueC;
  262. }
  263. else if (vb.TryGetMetamethod(state, Metamethods.Mul, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Mul, out metamethod))
  264. {
  265. if (!metamethod.TryRead<LuaFunction>(out var func))
  266. {
  267. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  268. }
  269. stack.Push(vb);
  270. stack.Push(vc);
  271. var funcContext = LuaFunctionExecutionContextPool.Rent();
  272. funcContext.State = state;
  273. funcContext.Thread = thread;
  274. funcContext.ArgumentCount = 2;
  275. funcContext.FrameBase = stack.Count - 2;
  276. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  277. funcContext.ChunkName = chunk.Name;
  278. funcContext.RootChunkName = rootChunk.Name;
  279. try
  280. {
  281. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  282. }
  283. finally
  284. {
  285. LuaFunctionExecutionContextPool.Return(funcContext);
  286. }
  287. stack.UnsafeGet(RA) = resultBuffer[0];
  288. }
  289. else
  290. {
  291. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "mul", vb, vc);
  292. }
  293. stack.NotifyTop(RA + 1);
  294. }
  295. break;
  296. case OpCode.Div:
  297. {
  298. stack.EnsureCapacity(RA + 1);
  299. var vb = RK(stack, chunk, instruction.B, frame.Base);
  300. var vc = RK(stack, chunk, instruction.C, frame.Base);
  301. if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  302. {
  303. stack.UnsafeGet(RA) = valueB / valueC;
  304. }
  305. else if (vb.TryGetMetamethod(state, Metamethods.Div, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Div, out metamethod))
  306. {
  307. if (!metamethod.TryRead<LuaFunction>(out var func))
  308. {
  309. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  310. }
  311. stack.Push(vb);
  312. stack.Push(vc);
  313. var funcContext = LuaFunctionExecutionContextPool.Rent();
  314. funcContext.State = state;
  315. funcContext.Thread = thread;
  316. funcContext.ArgumentCount = 2;
  317. funcContext.FrameBase = stack.Count - 2;
  318. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  319. funcContext.ChunkName = chunk.Name;
  320. funcContext.RootChunkName = rootChunk.Name;
  321. try
  322. {
  323. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  324. }
  325. finally
  326. {
  327. LuaFunctionExecutionContextPool.Return(funcContext);
  328. }
  329. stack.UnsafeGet(RA) = resultBuffer[0];
  330. }
  331. else
  332. {
  333. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "div", vb, vc);
  334. }
  335. stack.NotifyTop(RA + 1);
  336. }
  337. break;
  338. case OpCode.Mod:
  339. {
  340. stack.EnsureCapacity(RA + 1);
  341. var vb = RK(stack, chunk, instruction.B, frame.Base);
  342. var vc = RK(stack, chunk, instruction.C, frame.Base);
  343. if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  344. {
  345. var mod = valueB % valueC;
  346. if ((valueC > 0 && mod < 0) || (valueC < 0 && mod > 0))
  347. {
  348. mod += valueC;
  349. }
  350. stack.UnsafeGet(RA) = mod;
  351. }
  352. else if (vb.TryGetMetamethod(state, Metamethods.Mod, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Mod, out metamethod))
  353. {
  354. if (!metamethod.TryRead<LuaFunction>(out var func))
  355. {
  356. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  357. }
  358. stack.Push(vb);
  359. stack.Push(vc);
  360. var funcContext = LuaFunctionExecutionContextPool.Rent();
  361. funcContext.State = state;
  362. funcContext.Thread = thread;
  363. funcContext.ArgumentCount = 2;
  364. funcContext.FrameBase = stack.Count - 2;
  365. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  366. funcContext.ChunkName = chunk.Name;
  367. funcContext.RootChunkName = rootChunk.Name;
  368. try
  369. {
  370. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  371. }
  372. finally
  373. {
  374. LuaFunctionExecutionContextPool.Return(funcContext);
  375. }
  376. stack.UnsafeGet(RA) = resultBuffer[0];
  377. }
  378. else
  379. {
  380. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "mod", vb, vc);
  381. }
  382. stack.NotifyTop(RA + 1);
  383. }
  384. break;
  385. case OpCode.Pow:
  386. {
  387. stack.EnsureCapacity(RA + 1);
  388. var vb = RK(stack, chunk, instruction.B, frame.Base);
  389. var vc = RK(stack, chunk, instruction.C, frame.Base);
  390. if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  391. {
  392. stack.UnsafeGet(RA) = Math.Pow(valueB, valueC);
  393. }
  394. else if (vb.TryGetMetamethod(state, Metamethods.Pow, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Pow, out metamethod))
  395. {
  396. if (!metamethod.TryRead<LuaFunction>(out var func))
  397. {
  398. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  399. }
  400. stack.Push(vb);
  401. stack.Push(vc);
  402. var funcContext = LuaFunctionExecutionContextPool.Rent();
  403. funcContext.State = state;
  404. funcContext.Thread = thread;
  405. funcContext.ArgumentCount = 2;
  406. funcContext.FrameBase = stack.Count - 2;
  407. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  408. funcContext.ChunkName = chunk.Name;
  409. funcContext.RootChunkName = rootChunk.Name;
  410. try
  411. {
  412. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  413. }
  414. finally
  415. {
  416. LuaFunctionExecutionContextPool.Return(funcContext);
  417. }
  418. stack.UnsafeGet(RA) = resultBuffer[0];
  419. }
  420. else
  421. {
  422. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "pow", vb, vc);
  423. }
  424. stack.NotifyTop(RA + 1);
  425. }
  426. break;
  427. case OpCode.Unm:
  428. {
  429. stack.EnsureCapacity(RA + 1);
  430. var vb = stack.UnsafeGet(RB);
  431. if (vb.TryRead<double>(out var valueB))
  432. {
  433. stack.UnsafeGet(RA) = -valueB;
  434. }
  435. else if (vb.TryGetMetamethod(state, Metamethods.Unm, out var metamethod))
  436. {
  437. if (!metamethod.TryRead<LuaFunction>(out var func))
  438. {
  439. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  440. }
  441. stack.Push(vb);
  442. var funcContext = LuaFunctionExecutionContextPool.Rent();
  443. funcContext.State = state;
  444. funcContext.Thread = thread;
  445. funcContext.ArgumentCount = 1;
  446. funcContext.FrameBase = stack.Count - 1;
  447. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  448. funcContext.ChunkName = chunk.Name;
  449. funcContext.RootChunkName = rootChunk.Name;
  450. try
  451. {
  452. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  453. }
  454. finally
  455. {
  456. LuaFunctionExecutionContextPool.Return(funcContext);
  457. }
  458. stack.UnsafeGet(RA) = resultBuffer[0];
  459. }
  460. else
  461. {
  462. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "unm", vb);
  463. }
  464. stack.NotifyTop(RA + 1);
  465. }
  466. break;
  467. case OpCode.Not:
  468. {
  469. stack.EnsureCapacity(RA + 1);
  470. stack.UnsafeGet(RA) = !stack.UnsafeGet(RB).ToBoolean();
  471. stack.NotifyTop(RA + 1);
  472. }
  473. break;
  474. case OpCode.Len:
  475. {
  476. stack.EnsureCapacity(RA + 1);
  477. var vb = stack.UnsafeGet(RB);
  478. if (vb.TryRead<string>(out var str))
  479. {
  480. stack.UnsafeGet(RA) = str.Length;
  481. }
  482. else if (vb.TryGetMetamethod(state, Metamethods.Len, out var metamethod))
  483. {
  484. if (!metamethod.TryRead<LuaFunction>(out var func))
  485. {
  486. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  487. }
  488. stack.Push(vb);
  489. var funcContext = LuaFunctionExecutionContextPool.Rent();
  490. funcContext.State = state;
  491. funcContext.Thread = thread;
  492. funcContext.ArgumentCount = 1;
  493. funcContext.FrameBase = stack.Count - 1;
  494. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  495. funcContext.ChunkName = chunk.Name;
  496. funcContext.RootChunkName = rootChunk.Name;
  497. try
  498. {
  499. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  500. }
  501. finally
  502. {
  503. LuaFunctionExecutionContextPool.Return(funcContext);
  504. }
  505. stack.UnsafeGet(RA) = resultBuffer[0];
  506. }
  507. else if (vb.TryRead<LuaTable>(out var table))
  508. {
  509. stack.UnsafeGet(RA) = table.ArrayLength;
  510. }
  511. else
  512. {
  513. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "get length of", vb);
  514. }
  515. stack.NotifyTop(RA + 1);
  516. }
  517. break;
  518. case OpCode.Concat:
  519. {
  520. stack.EnsureCapacity(RA + 1);
  521. var vb = RK(stack, chunk, instruction.B, frame.Base);
  522. var vc = RK(stack, chunk, instruction.C, frame.Base);
  523. var bIsValid = vb.TryRead<string>(out var strB);
  524. var cIsValid = vc.TryRead<string>(out var strC);
  525. if (!bIsValid && vb.TryRead<double>(out var numB))
  526. {
  527. strB = numB.ToString();
  528. bIsValid = true;
  529. }
  530. if (!cIsValid && vc.TryRead<double>(out var numC))
  531. {
  532. strC = numC.ToString();
  533. cIsValid = true;
  534. }
  535. if (bIsValid && cIsValid)
  536. {
  537. stack.UnsafeGet(RA) = strB + strC;
  538. }
  539. else if (vb.TryGetMetamethod(state, Metamethods.Concat, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Concat, out metamethod))
  540. {
  541. if (!metamethod.TryRead<LuaFunction>(out var func))
  542. {
  543. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  544. }
  545. stack.Push(vb);
  546. stack.Push(vc);
  547. var funcContext = LuaFunctionExecutionContextPool.Rent();
  548. funcContext.State = state;
  549. funcContext.Thread = thread;
  550. funcContext.ArgumentCount = 2;
  551. funcContext.FrameBase = stack.Count - 2;
  552. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  553. funcContext.ChunkName = chunk.Name;
  554. funcContext.RootChunkName = rootChunk.Name;
  555. try
  556. {
  557. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  558. }
  559. finally
  560. {
  561. LuaFunctionExecutionContextPool.Return(funcContext);
  562. }
  563. stack.UnsafeGet(RA) = resultBuffer[0];
  564. }
  565. else
  566. {
  567. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "concat", vb, vc);
  568. }
  569. stack.NotifyTop(RA + 1);
  570. }
  571. break;
  572. case OpCode.Jmp:
  573. pc += instruction.SBx;
  574. if (instruction.A != 0)
  575. {
  576. state.CloseUpValues(thread, instruction.A - 1);
  577. }
  578. break;
  579. case OpCode.Eq:
  580. {
  581. var vb = RK(stack, chunk, instruction.B, frame.Base);
  582. var vc = RK(stack, chunk, instruction.C, frame.Base);
  583. var compareResult = vb == vc;
  584. if (!compareResult && (vb.TryGetMetamethod(state, Metamethods.Eq, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Eq, out metamethod)))
  585. {
  586. if (!metamethod.TryRead<LuaFunction>(out var func))
  587. {
  588. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  589. }
  590. stack.Push(vb);
  591. stack.Push(vc);
  592. var funcContext = LuaFunctionExecutionContextPool.Rent();
  593. funcContext.State = state;
  594. funcContext.Thread = thread;
  595. funcContext.ArgumentCount = 2;
  596. funcContext.FrameBase = stack.Count - 2;
  597. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  598. funcContext.ChunkName = chunk.Name;
  599. funcContext.RootChunkName = rootChunk.Name;
  600. try
  601. {
  602. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  603. }
  604. finally
  605. {
  606. LuaFunctionExecutionContextPool.Return(funcContext);
  607. }
  608. compareResult = resultBuffer[0].ToBoolean();
  609. }
  610. if (compareResult != (instruction.A == 1))
  611. {
  612. pc++;
  613. }
  614. }
  615. break;
  616. case OpCode.Lt:
  617. {
  618. var vb = RK(stack, chunk, instruction.B, frame.Base);
  619. var vc = RK(stack, chunk, instruction.C, frame.Base);
  620. var compareResult = false;
  621. if (vb.TryRead<string>(out var strB) && vc.TryRead<string>(out var strC))
  622. {
  623. compareResult = StringComparer.Ordinal.Compare(strB, strC) < 0;
  624. }
  625. else if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  626. {
  627. compareResult = valueB < valueC;
  628. }
  629. else if (vb.TryGetMetamethod(state, Metamethods.Lt, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Lt, out metamethod))
  630. {
  631. if (!metamethod.TryRead<LuaFunction>(out var func))
  632. {
  633. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  634. }
  635. stack.Push(vb);
  636. stack.Push(vc);
  637. var funcContext = LuaFunctionExecutionContextPool.Rent();
  638. funcContext.State = state;
  639. funcContext.Thread = thread;
  640. funcContext.ArgumentCount = 2;
  641. funcContext.FrameBase = stack.Count - 2;
  642. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  643. funcContext.ChunkName = chunk.Name;
  644. funcContext.RootChunkName = rootChunk.Name;
  645. try
  646. {
  647. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  648. }
  649. finally
  650. {
  651. LuaFunctionExecutionContextPool.Return(funcContext);
  652. }
  653. compareResult = resultBuffer[0].ToBoolean();
  654. }
  655. else
  656. {
  657. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "less than", vb, vc);
  658. }
  659. if (compareResult != (instruction.A == 1))
  660. {
  661. pc++;
  662. }
  663. }
  664. break;
  665. case OpCode.Le:
  666. {
  667. var vb = RK(stack, chunk, instruction.B, frame.Base);
  668. var vc = RK(stack, chunk, instruction.C, frame.Base);
  669. var compareResult = false;
  670. if (vb.TryRead<string>(out var strB) && vc.TryRead<string>(out var strC))
  671. {
  672. compareResult = StringComparer.Ordinal.Compare(strB, strC) <= 0;
  673. }
  674. else if (vb.TryRead<double>(out var valueB) && vc.TryRead<double>(out var valueC))
  675. {
  676. compareResult = valueB <= valueC;
  677. }
  678. else if (vb.TryGetMetamethod(state, Metamethods.Le, out var metamethod) || vc.TryGetMetamethod(state, Metamethods.Le, out metamethod))
  679. {
  680. if (!metamethod.TryRead<LuaFunction>(out var func))
  681. {
  682. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  683. }
  684. stack.Push(vb);
  685. stack.Push(vc);
  686. var funcContext = LuaFunctionExecutionContextPool.Rent();
  687. funcContext.State = state;
  688. funcContext.Thread = thread;
  689. funcContext.ArgumentCount = 2;
  690. funcContext.FrameBase = stack.Count - 2;
  691. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  692. funcContext.ChunkName = chunk.Name;
  693. funcContext.RootChunkName = rootChunk.Name;
  694. try
  695. {
  696. await func.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  697. }
  698. finally
  699. {
  700. LuaFunctionExecutionContextPool.Return(funcContext);
  701. }
  702. compareResult = resultBuffer[0].ToBoolean();
  703. }
  704. else
  705. {
  706. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "less than or equals", vb, vc);
  707. }
  708. if (compareResult != (instruction.A == 1))
  709. {
  710. pc++;
  711. }
  712. }
  713. break;
  714. case OpCode.Test:
  715. {
  716. if (stack.UnsafeGet(RA).ToBoolean() != (instruction.C == 1))
  717. {
  718. pc++;
  719. }
  720. }
  721. break;
  722. case OpCode.TestSet:
  723. {
  724. if (stack.UnsafeGet(RB).ToBoolean() != (instruction.C == 1))
  725. {
  726. pc++;
  727. }
  728. else
  729. {
  730. stack.UnsafeGet(RA) = stack.UnsafeGet(RB);
  731. stack.NotifyTop(RA + 1);
  732. }
  733. }
  734. break;
  735. case OpCode.Call:
  736. {
  737. var va = stack.UnsafeGet(RA);
  738. if (!va.TryRead<LuaFunction>(out var func))
  739. {
  740. if (va.TryGetMetamethod(state, Metamethods.Call, out var metamethod) && metamethod.TryRead<LuaFunction>(out func))
  741. {
  742. }
  743. else
  744. {
  745. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  746. }
  747. }
  748. (var newBase, var argumentCount) = PrepareForFunctionCall(thread, func, instruction, RA, resultBuffer.AsSpan(), false);
  749. var callPosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  750. var chunkName = chunk.Name ?? LuaState.DefaultChunkName;
  751. var rootChunkName = rootChunk.Name ?? LuaState.DefaultChunkName;
  752. thread.PushCallStackFrame(new CallStackFrame
  753. {
  754. Base = newBase,
  755. CallPosition = callPosition,
  756. ChunkName = chunkName,
  757. RootChunkName = rootChunkName,
  758. VariableArgumentCount = func is Closure cl ? Math.Max(argumentCount - cl.Proto.ParameterCount, 0) : 0,
  759. Function = func,
  760. });
  761. int rawResultCount;
  762. try
  763. {
  764. var funcContext = LuaFunctionExecutionContextPool.Rent();
  765. funcContext.State = state;
  766. funcContext.Thread = thread;
  767. funcContext.ArgumentCount = argumentCount;
  768. funcContext.FrameBase = newBase;
  769. funcContext.SourcePosition = callPosition;
  770. funcContext.ChunkName = chunkName;
  771. funcContext.RootChunkName = rootChunkName;
  772. try
  773. {
  774. rawResultCount = await func.InternalInvokeAsyncCore(funcContext, resultBuffer.AsMemory(), cancellationToken);
  775. }
  776. finally
  777. {
  778. LuaFunctionExecutionContextPool.Return(funcContext);
  779. }
  780. }
  781. finally
  782. {
  783. thread.PopCallStackFrame();
  784. }
  785. var resultCount = rawResultCount;
  786. if (instruction.C != 0)
  787. {
  788. resultCount = instruction.C - 1;
  789. }
  790. if (resultCount == 0)
  791. {
  792. stack.Pop();
  793. }
  794. else
  795. {
  796. stack.EnsureCapacity(RA + resultCount);
  797. for (int i = 0; i < resultCount; i++)
  798. {
  799. stack.UnsafeGet(RA + i) = i >= rawResultCount
  800. ? LuaValue.Nil
  801. : resultBuffer[i];
  802. }
  803. stack.NotifyTop(RA + resultCount);
  804. }
  805. }
  806. break;
  807. case OpCode.TailCall:
  808. {
  809. state.CloseUpValues(thread, frame.Base);
  810. var va = stack.UnsafeGet(RA);
  811. if (!va.TryRead<LuaFunction>(out var func))
  812. {
  813. if (!va.TryGetMetamethod(state, Metamethods.Call, out var metamethod) && !metamethod.TryRead<LuaFunction>(out func))
  814. {
  815. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  816. }
  817. }
  818. (var newBase, var argumentCount) = PrepareForFunctionCall(thread, func, instruction, RA, resultBuffer.AsSpan(), true);
  819. var funcContext = LuaFunctionExecutionContextPool.Rent();
  820. funcContext.State = state;
  821. funcContext.Thread = thread;
  822. funcContext.ArgumentCount = argumentCount;
  823. funcContext.FrameBase = newBase;
  824. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  825. funcContext.ChunkName = chunk.Name;
  826. funcContext.RootChunkName = rootChunk.Name;
  827. try
  828. {
  829. return await func.InvokeAsync(funcContext, buffer, cancellationToken);
  830. }
  831. finally
  832. {
  833. LuaFunctionExecutionContextPool.Return(funcContext);
  834. }
  835. }
  836. case OpCode.Return:
  837. {
  838. state.CloseUpValues(thread, frame.Base);
  839. var retCount = instruction.B - 1;
  840. if (retCount == -1)
  841. {
  842. retCount = stack.Count - RA;
  843. }
  844. for (int i = 0; i < retCount; i++)
  845. {
  846. buffer.Span[i] = stack.UnsafeGet(RA + i);
  847. }
  848. return retCount;
  849. }
  850. case OpCode.ForLoop:
  851. {
  852. stack.EnsureCapacity(RA + 4);
  853. if (!stack.UnsafeGet(RA).TryRead<double>(out var init))
  854. {
  855. throw new LuaRuntimeException(state.GetTraceback(), "'for' initial value must be a number");
  856. }
  857. if (!stack.UnsafeGet(RA + 1).TryRead<double>(out var limit))
  858. {
  859. throw new LuaRuntimeException(state.GetTraceback(), "'for' limit must be a number");
  860. }
  861. if (!stack.UnsafeGet(RA + 2).TryRead<double>(out var step))
  862. {
  863. throw new LuaRuntimeException(state.GetTraceback(), "'for' step must be a number");
  864. }
  865. var va = init + step;
  866. stack.UnsafeGet(RA) = va;
  867. if (step >= 0 ? va <= limit : va >= limit)
  868. {
  869. pc += instruction.SBx;
  870. stack.UnsafeGet(RA + 3) = va;
  871. stack.NotifyTop(RA + 4);
  872. }
  873. else
  874. {
  875. stack.NotifyTop(RA + 1);
  876. }
  877. }
  878. break;
  879. case OpCode.ForPrep:
  880. {
  881. if (!stack.UnsafeGet(RA).TryRead<double>(out var init))
  882. {
  883. throw new LuaRuntimeException(state.GetTraceback(), "'for' initial value must be a number");
  884. }
  885. if (!stack.UnsafeGet(RA + 2).TryRead<double>(out var step))
  886. {
  887. throw new LuaRuntimeException(state.GetTraceback(), "'for' step must be a number");
  888. }
  889. stack.UnsafeGet(RA) = init - step;
  890. stack.NotifyTop(RA + 1);
  891. pc += instruction.SBx;
  892. }
  893. break;
  894. case OpCode.TForCall:
  895. {
  896. var iteratorRaw = stack.UnsafeGet(RA);
  897. if (!iteratorRaw.TryRead<LuaFunction>(out var iterator))
  898. {
  899. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", iteratorRaw);
  900. }
  901. var nextBase = RA + 3 + instruction.C;
  902. stack.UnsafeGet(nextBase) = stack.UnsafeGet(RA + 1);
  903. stack.UnsafeGet(nextBase + 1) = stack.UnsafeGet(RA + 2);
  904. stack.NotifyTop(nextBase + 2);
  905. var funcContext = LuaFunctionExecutionContextPool.Rent();
  906. funcContext.State = state;
  907. funcContext.Thread = thread;
  908. funcContext.ArgumentCount = 2;
  909. funcContext.FrameBase = nextBase;
  910. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  911. funcContext.ChunkName = chunk.Name;
  912. funcContext.RootChunkName = rootChunk.Name;
  913. int resultCount;
  914. try
  915. {
  916. resultCount = await iterator.InvokeAsync(funcContext, resultBuffer.AsMemory(), cancellationToken);
  917. }
  918. finally
  919. {
  920. LuaFunctionExecutionContextPool.Return(funcContext);
  921. }
  922. stack.EnsureCapacity(RA + instruction.C + 3);
  923. for (int i = 1; i <= instruction.C; i++)
  924. {
  925. var index = i - 1;
  926. stack.UnsafeGet(RA + 2 + i) = index >= resultCount
  927. ? LuaValue.Nil
  928. : resultBuffer[i - 1];
  929. }
  930. stack.NotifyTop(RA + instruction.C + 3);
  931. }
  932. break;
  933. case OpCode.TForLoop:
  934. {
  935. var forState = stack.UnsafeGet(RA + 1);
  936. if (forState.Type is not LuaValueType.Nil)
  937. {
  938. stack.UnsafeGet(RA) = forState;
  939. pc += instruction.SBx;
  940. }
  941. }
  942. break;
  943. case OpCode.SetList:
  944. {
  945. if (!stack.UnsafeGet(RA).TryRead<LuaTable>(out var table))
  946. {
  947. throw new LuaException("internal error");
  948. }
  949. var count = instruction.B == 0
  950. ? stack.Count - (RA + 1)
  951. : instruction.B;
  952. table.EnsureArrayCapacity((instruction.C - 1) * 50 + count);
  953. stack.AsSpan().Slice(RA + 1, count)
  954. .CopyTo(table.GetArraySpan()[((instruction.C - 1) * 50)..]);
  955. }
  956. break;
  957. case OpCode.Closure:
  958. stack.EnsureCapacity(RA + 1);
  959. stack.UnsafeGet(RA) = new Closure(state, chunk.Functions[instruction.SBx]);
  960. stack.NotifyTop(RA + 1);
  961. break;
  962. case OpCode.VarArg:
  963. {
  964. var count = instruction.B == 0
  965. ? frame.VariableArgumentCount
  966. : instruction.B - 1;
  967. stack.EnsureCapacity(RA + count);
  968. for (int i = 0; i < count; i++)
  969. {
  970. stack.UnsafeGet(RA + i) = frame.VariableArgumentCount > i
  971. ? stack.UnsafeGet(frame.Base - (frame.VariableArgumentCount - i))
  972. : LuaValue.Nil;
  973. }
  974. stack.NotifyTop(RA + count);
  975. }
  976. break;
  977. case OpCode.ExtraArg:
  978. throw new NotImplementedException();
  979. default:
  980. break;
  981. }
  982. }
  983. }
  984. catch (Exception)
  985. {
  986. state.CloseUpValues(thread, frame.Base);
  987. throw;
  988. }
  989. finally
  990. {
  991. ArrayPool<LuaValue>.Shared.Return(resultBuffer);
  992. }
  993. return 0;
  994. }
  995. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  996. static ref LuaValue RK(LuaStack stack, Chunk chunk, ushort index, int frameBase)
  997. {
  998. if (index >= 256)
  999. {
  1000. return ref MemoryMarshalEx.UnsafeElementAt(chunk.Constants, index - 256);
  1001. }
  1002. else
  1003. {
  1004. return ref stack.UnsafeGet(index + frameBase);
  1005. }
  1006. }
  1007. static (ValueTask<int> Task, LuaFunctionExecutionContext? Context) GetTableValue(LuaState state, LuaThread thread, Chunk chunk, Chunk rootChunk, int pc, LuaValue table, LuaValue key, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  1008. {
  1009. var stack = thread.Stack;
  1010. var isTable = table.TryRead<LuaTable>(out var t);
  1011. if (isTable && t.TryGetValue(key, out var result))
  1012. {
  1013. buffer.Span[0] = result;
  1014. return (new(1), null);
  1015. }
  1016. else if (table.TryGetMetamethod(state, Metamethods.Index, out var metamethod))
  1017. {
  1018. if (!metamethod.TryRead<LuaFunction>(out var indexTable))
  1019. {
  1020. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  1021. }
  1022. stack.Push(table);
  1023. stack.Push(key);
  1024. var funcContext = LuaFunctionExecutionContextPool.Rent();
  1025. funcContext.State = state;
  1026. funcContext.Thread = thread;
  1027. funcContext.ArgumentCount = 2;
  1028. funcContext.FrameBase = stack.Count - 2;
  1029. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  1030. funcContext.ChunkName = chunk.Name;
  1031. funcContext.RootChunkName = rootChunk.Name;
  1032. return (indexTable.InvokeAsync(funcContext, buffer, cancellationToken), funcContext);
  1033. }
  1034. else if (isTable)
  1035. {
  1036. buffer.Span[0] = LuaValue.Nil;
  1037. return (new(1), null);
  1038. }
  1039. else
  1040. {
  1041. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "index", table);
  1042. return default; // dummy
  1043. }
  1044. }
  1045. static (ValueTask<int> Task, LuaFunctionExecutionContext? Context) SetTableValue(LuaState state, LuaThread thread, Chunk chunk, Chunk rootChunk, int pc, LuaValue table, LuaValue key, LuaValue value, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  1046. {
  1047. var stack = thread.Stack;
  1048. var isTable = table.TryRead<LuaTable>(out var t);
  1049. if (key.Type is LuaValueType.Number)
  1050. {
  1051. var d = key.UnsafeRead<double>();
  1052. if (double.IsNaN(d))
  1053. {
  1054. throw new LuaRuntimeException(GetTracebacks(state, chunk, pc), "table index is NaN");
  1055. }
  1056. }
  1057. if (isTable)
  1058. {
  1059. t[key] = value;
  1060. return (new(1), null);
  1061. }
  1062. else if (table.TryGetMetamethod(state, Metamethods.NewIndex, out var metamethod))
  1063. {
  1064. if (!metamethod.TryRead<LuaFunction>(out var indexTable))
  1065. {
  1066. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", metamethod);
  1067. }
  1068. stack.Push(table);
  1069. stack.Push(key);
  1070. stack.Push(value);
  1071. var funcContext = LuaFunctionExecutionContextPool.Rent();
  1072. funcContext.State = state;
  1073. funcContext.Thread = thread;
  1074. funcContext.ArgumentCount = 3;
  1075. funcContext.FrameBase = stack.Count - 3;
  1076. funcContext.SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
  1077. funcContext.ChunkName = chunk.Name;
  1078. funcContext.RootChunkName = rootChunk.Name;
  1079. return (indexTable.InvokeAsync(funcContext, buffer, cancellationToken), funcContext);
  1080. }
  1081. else
  1082. {
  1083. LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "index", table);
  1084. return default; // dummy
  1085. }
  1086. }
  1087. static (int FrameBase, int ArgumentCount) PrepareForFunctionCall(LuaThread thread, LuaFunction function, Instruction instruction, int RA, Span<LuaValue> buffer, bool isTailCall)
  1088. {
  1089. var stack = thread.Stack;
  1090. var argumentCount = instruction.B - 1;
  1091. if (instruction.B == 0)
  1092. {
  1093. argumentCount = (ushort)(stack.Count - (RA + 1));
  1094. }
  1095. var newBase = RA + 1;
  1096. // In the case of tailcall, the local variables of the caller are immediately discarded, so there is no need to retain them.
  1097. // Therefore, a call can be made without allocating new registers.
  1098. if (isTailCall)
  1099. {
  1100. var currentBase = thread.GetCurrentFrame().Base;
  1101. var stackBuffer = stack.GetBuffer();
  1102. stackBuffer.Slice(newBase, argumentCount).CopyTo(stackBuffer.Slice(currentBase, argumentCount));
  1103. newBase = currentBase;
  1104. }
  1105. var variableArgumentCount = function.GetVariableArgumentCount(argumentCount);
  1106. // 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.
  1107. // see: https://wubingzheng.github.io/build-lua-in-rust/en/ch08-02.arguments.html
  1108. if (variableArgumentCount > 0)
  1109. {
  1110. var temp = newBase;
  1111. newBase += variableArgumentCount;
  1112. stack.EnsureCapacity(newBase + argumentCount);
  1113. stack.NotifyTop(newBase + argumentCount);
  1114. var stackBuffer = stack.GetBuffer();
  1115. stackBuffer.Slice(temp, argumentCount).CopyTo(buffer);
  1116. buffer.Slice(0, argumentCount).CopyTo(stackBuffer[newBase..]);
  1117. buffer.Slice(argumentCount - variableArgumentCount, variableArgumentCount).CopyTo(stackBuffer[temp..]);
  1118. }
  1119. return (newBase, argumentCount);
  1120. }
  1121. static Traceback GetTracebacks(LuaState state, Chunk chunk, int pc)
  1122. {
  1123. var frame = state.CurrentThread.GetCurrentFrame();
  1124. state.CurrentThread.PushCallStackFrame(frame with
  1125. {
  1126. CallPosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
  1127. ChunkName = chunk.Name,
  1128. RootChunkName = chunk.GetRoot().Name,
  1129. });
  1130. var tracebacks = state.GetTraceback();
  1131. state.CurrentThread.PopCallStackFrame();
  1132. return tracebacks;
  1133. }
  1134. }