LuaVirtualMachine.cs 90 KB

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