| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785 |
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using Lua.Internal;
- namespace Lua.Runtime;
- [SuppressMessage("Reliability", "CA2012:Use ValueTasks correctly")]
- public static partial class LuaVirtualMachine
- {
- [StructLayout(LayoutKind.Auto)]
- [method: MethodImpl(MethodImplOptions.AggressiveInlining)]
- struct VirtualMachineExecutionContext(
- LuaState state,
- LuaStack stack,
- LuaValue[] resultsBuffer,
- Memory<LuaValue> buffer,
- LuaThread thread,
- in CallStackFrame frame,
- CancellationToken cancellationToken)
- {
- public readonly LuaState State = state;
- public readonly LuaStack Stack = stack;
- public Closure Closure = (Closure)frame.Function;
- public readonly LuaValue[] ResultsBuffer = resultsBuffer;
- public readonly Memory<LuaValue> Buffer = buffer;
- public readonly LuaThread Thread = thread;
- public Chunk Chunk => Closure.Proto;
- public int FrameBase = frame.Base;
- public int VariableArgumentCount = frame.VariableArgumentCount;
- public readonly CancellationToken CancellationToken = cancellationToken;
- public int Pc = -1;
- public Instruction Instruction;
- public int ResultCount;
- public int TaskResult;
- public ValueTask<int> Task;
- public bool IsTopLevel => BaseCallStackCount == Thread.CallStack.Count;
- readonly int BaseCallStackCount = thread.CallStack.Count;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool Pop(Instruction instruction, int frameBase)
- {
- if (BaseCallStackCount == Thread.CallStack.Count) return false;
- var count = instruction.B - 1;
- var src = instruction.A + frameBase;
- if (count == -1) count = Stack.Count - src;
- return PopFromBuffer(Stack.GetBuffer().Slice(src, count));
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- public bool PopFromBuffer(Span<LuaValue> result)
- {
- ref var callStack = ref Thread.CallStack;
- Re:
- var frames = callStack.AsSpan();
- if (frames.Length == BaseCallStackCount) return false;
- ref readonly var frame = ref frames[^1];
- Pc = frame.CallerInstructionIndex;
- ref readonly var lastFrame = ref frames[^2];
- Closure = Unsafe.As<Closure>(lastFrame.Function);
- var callInstruction = Chunk.Instructions[Pc];
- FrameBase = lastFrame.Base;
- VariableArgumentCount = lastFrame.VariableArgumentCount;
- if (callInstruction.OpCode == OpCode.TailCall)
- {
- Thread.PopCallStackFrameUnsafe();
- goto Re;
- }
- var opCode = callInstruction.OpCode;
- if (opCode is OpCode.Eq or OpCode.Lt or OpCode.Le)
- {
- var compareResult = result.Length > 0 && result[0].ToBoolean();
- if ((frame.Flags & CallStackFrameFlags.ReversedLe) != 0)
- {
- compareResult = !compareResult;
- }
- if (compareResult != (callInstruction.A == 1))
- {
- Pc++;
- }
- Thread.PopCallStackFrameUnsafe(frame.Base);
- return true;
- }
- var target = callInstruction.A + FrameBase;
- var targetCount = result.Length;
- switch (opCode)
- {
- case OpCode.Call:
- {
- var c = callInstruction.C;
- if (c != 0)
- {
- targetCount = c - 1;
- }
- break;
- }
- case OpCode.TForCall:
- target += 3;
- targetCount = callInstruction.C;
- break;
- case OpCode.Self:
- Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0];
- Thread.PopCallStackFrameUnsafe(target + 2);
- return true;
- case OpCode.SetTable or OpCode.SetTabUp:
- targetCount = 0;
- break;
- // Other opcodes has one result
- default:
- targetCount = 1;
- break;
- }
- var count = Math.Min(result.Length, targetCount);
- Stack.EnsureCapacity(target + targetCount);
- var stackBuffer = Stack.GetBuffer();
- if (count > 0)
- {
- result[..count].CopyTo(stackBuffer.Slice(target, count));
- }
- if (targetCount > count)
- {
- stackBuffer.Slice(target + count, targetCount - count).Clear();
- }
- Stack.NotifyTop(target + targetCount);
- Thread.PopCallStackFrameUnsafe(target + targetCount);
- return true;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Push(in CallStackFrame frame)
- {
- Pc = -1;
- Closure = (frame.Function as Closure)!;
- FrameBase = frame.Base;
- VariableArgumentCount = frame.VariableArgumentCount;
- }
- public void PopOnTopCallStackFrames()
- {
- ref var callStack = ref Thread.CallStack;
- var count = callStack.Count;
- if (count == BaseCallStackCount) return;
- while (callStack.Count > BaseCallStackCount + 1)
- {
- callStack.TryPop();
- }
- Thread.PopCallStackFrame();
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ClearResultsBuffer()
- {
- if (TaskResult == 0) return;
- if (TaskResult == 1)
- {
- ResultsBuffer[0] = default;
- return;
- }
- ResultsBuffer.AsSpan(0, TaskResult).Clear();
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ClearResultsBuffer(int count)
- {
- if (count == 0) return;
- if (count == 1)
- {
- ResultsBuffer[0] = default;
- return;
- }
- ResultsBuffer.AsSpan(0, count).Clear();
- }
- public async ValueTask<int> ExecuteClosureAsyncImpl()
- {
- while (MoveNext(ref this, out var postOperation))
- {
- TaskResult = await Task;
- Task = default;
- Thread.PopCallStackFrame();
- switch (postOperation)
- {
- case PostOperationType.Nop: break;
- case PostOperationType.SetResult:
- var RA = Instruction.A + FrameBase;
- Stack.Get(RA) = TaskResult == 0 ? LuaValue.Nil : ResultsBuffer[0];
- Stack.NotifyTop(RA + 1);
- ClearResultsBuffer();
- break;
- case PostOperationType.TForCall:
- TForCallPostOperation(ref this);
- break;
- case PostOperationType.Call:
- CallPostOperation(ref this);
- break;
- case PostOperationType.TailCall:
- var resultsSpan = ResultsBuffer.AsSpan(0, TaskResult);
- if (!PopFromBuffer(resultsSpan))
- {
- ResultCount = TaskResult;
- resultsSpan.CopyTo(Buffer.Span);
- resultsSpan.Clear();
- LuaValueArrayPool.Return1024(ResultsBuffer);
- return TaskResult;
- }
- resultsSpan.Clear();
- break;
- case PostOperationType.Self:
- SelfPostOperation(ref this);
- break;
- case PostOperationType.Compare:
- ComparePostOperation(ref this);
- break;
- }
- }
- return ResultCount;
- }
- }
- enum PostOperationType
- {
- None,
- Nop,
- SetResult,
- TForCall,
- Call,
- TailCall,
- Self,
- Compare,
- }
- internal static ValueTask<int> ExecuteClosureAsync(LuaState luaState, Memory<LuaValue> buffer, CancellationToken cancellationToken)
- {
- var thread = luaState.CurrentThread;
- ref readonly var frame = ref thread.GetCallStackFrames()[^1];
- var resultBuffer = LuaValueArrayPool.Rent1024();
- var context = new VirtualMachineExecutionContext(luaState, thread.Stack, resultBuffer, buffer, thread, in frame,
- cancellationToken);
- return context.ExecuteClosureAsyncImpl();
- }
- static bool MoveNext(ref VirtualMachineExecutionContext context, out PostOperationType postOperation)
- {
- postOperation = PostOperationType.None;
- try
- {
- // This is a label to restart the execution when new function is called or restarted
- Restart:
- ref var instructionsHead = ref context.Chunk.Instructions[0];
- var frameBase = context.FrameBase;
- var stack = context.Stack;
- stack.EnsureCapacity(frameBase + context.Chunk.MaxStackPosition);
- ref var constHead = ref MemoryMarshalEx.UnsafeElementAt(context.Chunk.Constants, 0);
- while (true)
- {
- var instructionRef = Unsafe.Add(ref instructionsHead, ++context.Pc);
- context.Instruction = instructionRef;
- switch (instructionRef.OpCode)
- {
- case OpCode.Move:
- var instruction = instructionRef;
- ref var stackHead = ref stack.FastGet(frameBase);
- var iA = instruction.A;
- Unsafe.Add(ref stackHead, iA) = Unsafe.Add(ref stackHead, instruction.UIntB);
- stack.NotifyTop(iA + frameBase + 1);
- continue;
- case OpCode.LoadK:
- instruction = instructionRef;
- stack.GetWithNotifyTop(instruction.A + frameBase) = Unsafe.Add(ref constHead, instruction.Bx);
- continue;
- case OpCode.LoadBool:
- instruction = instructionRef;
- stack.GetWithNotifyTop(instruction.A + frameBase) = instruction.B != 0;
- if (instruction.C != 0) context.Pc++;
- continue;
- case OpCode.LoadNil:
- instruction = instructionRef;
- var ra1 = instruction.A + frameBase + 1;
- var iB = instruction.B;
- stack.GetBuffer().Slice(ra1 - 1, iB + 1).Clear();
- stack.NotifyTop(ra1 + iB);
- continue;
- case OpCode.GetUpVal:
- instruction = instructionRef;
- stack.GetWithNotifyTop(instruction.A + frameBase) = context.Closure.GetUpValue(instruction.B);
- continue;
- case OpCode.GetTabUp:
- instruction = instructionRef;
- stackHead = ref stack.FastGet(frameBase);
- ref readonly var vc = ref RKC(ref stackHead, ref constHead, instruction);
- var table = context.Closure.GetUpValue(instruction.B);
- var doRestart = false;
- if (table.TryReadTable(out var luaTable) && luaTable.TryGetValue(vc, out var resultValue) || TryGetValueWithSync(table, vc, ref context, out resultValue, out doRestart))
- {
- if (doRestart) goto Restart;
- stack.GetWithNotifyTop(instruction.A + frameBase) = resultValue;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.GetTable:
- instruction = instructionRef;
- stackHead = ref stack.FastGet(frameBase);
- ref readonly var vb = ref Unsafe.Add(ref stackHead, instruction.UIntB);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- doRestart = false;
- if (vb.TryReadTable(out luaTable) && luaTable.TryGetValue(vc, out resultValue) || TryGetValueWithSync(vb, vc, ref context, out resultValue, out doRestart))
- {
- if (doRestart) goto Restart;
- stack.GetWithNotifyTop(instruction.A + frameBase) = resultValue;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.SetTabUp:
- instruction = instructionRef;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- if (vb.TryReadNumber(out var numB))
- {
- if (double.IsNaN(numB))
- {
- ThrowLuaRuntimeException(ref context, "table index is NaN");
- return true;
- }
- }
- table = context.Closure.GetUpValue(instruction.A);
- if (table.TryReadTable(out luaTable))
- {
- ref var valueRef = ref luaTable.FindValue(vb);
- if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
- {
- valueRef = RKC(ref stackHead, ref constHead, instruction);
- continue;
- }
- }
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (TrySetMetaTableValueWithSync(table, vb, vc, ref context, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.Nop;
- return true;
- case OpCode.SetUpVal:
- instruction = instructionRef;
- context.Closure.SetUpValue(instruction.B, stack.FastGet(instruction.A + frameBase));
- continue;
- case OpCode.SetTable:
- instruction = instructionRef;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- if (vb.TryReadNumber(out numB))
- {
- if (double.IsNaN(numB))
- {
- ThrowLuaRuntimeException(ref context, " table index is NaN");
- return true;
- }
- }
- table = Unsafe.Add(ref stackHead, instruction.A);
- if (table.TryReadTable(out luaTable))
- {
- ref var valueRef = ref luaTable.FindValue(vb);
- if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
- {
- valueRef = RKC(ref stackHead, ref constHead, instruction);
- continue;
- }
- }
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (TrySetMetaTableValueWithSync(table, vb, vc, ref context, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.Nop;
- return true;
- case OpCode.NewTable:
- instruction = instructionRef;
- stack.GetWithNotifyTop(instruction.A + frameBase) = new LuaTable(instruction.B, instruction.C);
- continue;
- case OpCode.Self:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- table = Unsafe.Add(ref stackHead, instruction.UIntB);
- if (TryGetValueWithSync(table, vc, ref context, out resultValue, out doRestart))
- {
- if (doRestart) goto Restart;
- Unsafe.Add(ref stackHead, iA) = resultValue;
- Unsafe.Add(ref stackHead, iA + 1) = table;
- stack.NotifyTop(iA + frameBase + 2);
- continue;
- }
- postOperation = PostOperationType.Self;
- return true;
- case OpCode.Add:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
- {
- Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() + vc.UnsafeReadDouble();
- stack.NotifyTop(iA + frameBase + 1);
- continue;
- }
- if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out var numC))
- {
- Unsafe.Add(ref stackHead, iA) = numB + numC;
- stack.NotifyTop(iA + frameBase + 1);
- continue;
- }
- if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Add, "add", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Sub:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() - vc.UnsafeReadDouble();
- stack.NotifyTop(ra1);
- continue;
- }
- if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = numB - numC;
- stack.NotifyTop(ra1);
- continue;
- }
- if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Sub, "sub", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Mul:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() * vc.UnsafeReadDouble();
- stack.NotifyTop(ra1);
- continue;
- }
- if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = numB * numC;
- stack.NotifyTop(ra1);
- continue;
- }
- if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Mul, "mul", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Div:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() / vc.UnsafeReadDouble();
- stack.NotifyTop(ra1);
- continue;
- }
- if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = numB / numC;
- stack.NotifyTop(ra1);
- continue;
- }
- if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Div, "div", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Mod:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
- {
- var mod = numB % numC;
- if ((numC > 0 && mod < 0) || (numC < 0 && mod > 0))
- {
- mod += numC;
- }
- Unsafe.Add(ref stackHead, iA) = mod;
- continue;
- }
- if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Mod, "mod", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Pow:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = Math.Pow(numB, numC);
- stack.NotifyTop(ra1);
- continue;
- }
- if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Pow, "pow", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Unm:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref Unsafe.Add(ref stackHead, instruction.UIntB);
- if (vb.TryReadDouble(out numB))
- {
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = -numB;
- stack.NotifyTop(ra1);
- continue;
- }
- if (ExecuteUnaryOperationMetaMethod(vb, ref context, Metamethods.Unm, "unm", false, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Not:
- instruction = instructionRef;
- iA = instruction.A;
- ra1 = iA + frameBase + 1;
- stackHead = ref stack.FastGet(frameBase);
- Unsafe.Add(ref stackHead, iA) = !Unsafe.Add(ref stackHead, instruction.UIntB).ToBoolean();
- stack.NotifyTop(ra1);
- continue;
- case OpCode.Len:
- instruction = instructionRef;
- stackHead = ref stack.FastGet(frameBase);
- vb = ref Unsafe.Add(ref stackHead, instruction.UIntB);
- if (vb.TryReadString(out var str))
- {
- iA = instruction.A;
- ra1 = iA + frameBase + 1;
- Unsafe.Add(ref stackHead, iA) = str.Length;
- stack.NotifyTop(ra1);
- continue;
- }
- if (ExecuteUnaryOperationMetaMethod(vb, ref context, Metamethods.Len, "get length of", true, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Concat:
- if (Concat(ref context, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.SetResult;
- return true;
- case OpCode.Jmp:
- instruction = instructionRef;
- context.Pc += instruction.SBx;
- iA = instruction.A;
- if (iA != 0)
- {
- context.State.CloseUpValues(context.Thread, frameBase + iA - 1);
- }
- continue;
- case OpCode.Eq:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.Get(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb == vc)
- {
- if (iA != 1)
- {
- context.Pc++;
- }
- continue;
- }
- if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, Metamethods.Eq, null, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.Compare;
- return true;
- case OpCode.Lt:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.Get(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.TryReadNumber(out numB) && vc.TryReadNumber(out numC))
- {
- var compareResult = numB < numC;
- if (compareResult != (iA == 1))
- {
- context.Pc++;
- }
- continue;
- }
- if (vb.TryReadString(out var strB) && vc.TryReadString(out var strC))
- {
- var compareResult = StringComparer.Ordinal.Compare(strB, strC) < 0;
- if (compareResult != (iA == 1))
- {
- context.Pc++;
- }
- continue;
- }
- if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, Metamethods.Lt, "less than", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.Compare;
- return true;
- case OpCode.Le:
- instruction = instructionRef;
- iA = instruction.A;
- stackHead = ref stack.Get(frameBase);
- vb = ref RKB(ref stackHead, ref constHead, instruction);
- vc = ref RKC(ref stackHead, ref constHead, instruction);
- if (vb.TryReadNumber(out numB) && vc.TryReadNumber(out numC))
- {
- var compareResult = numB <= numC;
- if (compareResult != (iA == 1))
- {
- context.Pc++;
- }
- continue;
- }
- if (vb.TryReadString(out strB) && vc.TryReadString(out strC))
- {
- var compareResult = StringComparer.Ordinal.Compare(strB, strC) <= 0;
- if (compareResult != (iA == 1))
- {
- context.Pc++;
- }
- continue;
- }
- if (ExecuteCompareOperationMetaMethod(vb, vc, ref context, Metamethods.Le, "less than or equals", out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.Compare;
- return true;
- case OpCode.Test:
- instruction = instructionRef;
- if (stack.Get(instruction.A + frameBase).ToBoolean() != (instruction.C == 1))
- {
- context.Pc++;
- }
- continue;
- case OpCode.TestSet:
- instruction = instructionRef;
- vb = ref stack.Get(instruction.B + frameBase);
- if (vb.ToBoolean() != (instruction.C == 1))
- {
- context.Pc++;
- }
- else
- {
- stack.GetWithNotifyTop(instruction.A + frameBase) = vb;
- }
- continue;
- case OpCode.Call:
- if (Call(ref context, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.Call;
- return true;
- case OpCode.TailCall:
- if (TailCall(ref context, out doRestart))
- {
- if (doRestart) goto Restart;
- if (context.IsTopLevel) goto End;
- continue;
- }
- postOperation = PostOperationType.TailCall;
- return true;
- case OpCode.Return:
- instruction = instructionRef;
- iA = instruction.A;
- ra1 = iA + frameBase + 1;
- context.State.CloseUpValues(context.Thread, frameBase);
- if (context.Pop(instruction, frameBase)) goto Restart;
- var retCount = instruction.B - 1;
- if (retCount == -1)
- {
- retCount = stack.Count - (ra1 - 1);
- }
- if (0 < retCount)
- {
- stack.GetBuffer().Slice(ra1 - 1, retCount).CopyTo(context.Buffer.Span);
- }
- context.ResultCount = retCount;
- goto End;
- case OpCode.ForLoop:
- ref var indexRef = ref stack.Get(instructionRef.A + frameBase);
- var limit = Unsafe.Add(ref indexRef, 1).UnsafeReadDouble();
- var step = Unsafe.Add(ref indexRef, 2).UnsafeReadDouble();
- var index = indexRef.UnsafeReadDouble() + step;
- if (step >= 0 ? index <= limit : limit <= index)
- {
- context.Pc += instructionRef.SBx;
- indexRef = index;
- Unsafe.Add(ref indexRef, 3) = index;
- stack.NotifyTop(instructionRef.A + frameBase + 4);
- continue;
- }
- stack.NotifyTop(instructionRef.A + frameBase + 1);
- continue;
- case OpCode.ForPrep:
- indexRef = ref stack.Get(instructionRef.A + frameBase);
- if (!indexRef.TryReadDouble(out var init))
- {
- ThrowLuaRuntimeException(ref context, "'for' initial value must be a number");
- return true;
- }
- if (!LuaValue.TryReadOrSetDouble(ref Unsafe.Add(ref indexRef, 1), out _))
- {
- ThrowLuaRuntimeException(ref context, "'for' limit must be a number");
- return true;
- }
- if (!LuaValue.TryReadOrSetDouble(ref Unsafe.Add(ref indexRef, 2), out step))
- {
- ThrowLuaRuntimeException(ref context, "'for' step must be a number");
- return true;
- }
- indexRef = init - step;
- stack.NotifyTop(instructionRef.A + frameBase + 1);
- context.Pc += instructionRef.SBx;
- continue;
- case OpCode.TForCall:
- if (TForCall(ref context, out doRestart))
- {
- if (doRestart) goto Restart;
- continue;
- }
- postOperation = PostOperationType.TForCall;
- return true;
- case OpCode.TForLoop:
- instruction = instructionRef;
- iA = instruction.A;
- ra1 = iA + frameBase + 1;
- ref var forState = ref stack.Get(ra1);
- if (forState.Type is not LuaValueType.Nil)
- {
- Unsafe.Add(ref forState, -1) = forState;
- context.Pc += instruction.SBx;
- }
- continue;
- case OpCode.SetList:
- SetList(ref context);
- continue;
- case OpCode.Closure:
- instruction = instructionRef;
- iA = instruction.A;
- ra1 = iA + frameBase + 1;
- stack.EnsureCapacity(ra1);
- stack.Get(ra1 - 1) = new Closure(context.State, context.Chunk.Functions[instruction.SBx]);
- stack.NotifyTop(ra1);
- continue;
- case OpCode.VarArg:
- instruction = instructionRef;
- iA = instruction.A;
- ra1 = iA + frameBase + 1;
- var frameVariableArgumentCount = context.VariableArgumentCount;
- var count = instruction.B == 0
- ? frameVariableArgumentCount
- : instruction.B - 1;
- var ra = ra1 - 1;
- stack.EnsureCapacity(ra + count);
- stackHead = ref stack.Get(0);
- for (int i = 0; i < count; i++)
- {
- Unsafe.Add(ref stackHead, ra + i) = frameVariableArgumentCount > i
- ? Unsafe.Add(ref stackHead, frameBase - (frameVariableArgumentCount - i))
- : default;
- }
- stack.NotifyTop(ra + count);
- continue;
- case OpCode.ExtraArg:
- default:
- ThrowLuaNotImplementedException(ref context, context.Instruction.OpCode);
- return true;
- }
- }
- End:
- postOperation = PostOperationType.None;
- LuaValueArrayPool.Return1024(context.ResultsBuffer);
- return false;
- }
- catch (Exception e)
- {
- context.PopOnTopCallStackFrames();
- context.State.CloseUpValues(context.Thread, context.FrameBase);
- LuaValueArrayPool.Return1024(context.ResultsBuffer, true);
- if (e is not LuaRuntimeException)
- {
- var newException = new LuaRuntimeException(GetTracebacks(ref context), e);
- context = default;
- throw newException;
- }
- throw;
- }
- }
- static void ThrowLuaRuntimeException(ref VirtualMachineExecutionContext context, string message)
- {
- throw new LuaRuntimeException(context.State.GetTraceback(), message);
- }
- static void ThrowLuaNotImplementedException(ref VirtualMachineExecutionContext context, OpCode opcode)
- {
- throw new LuaRuntimeException(context.State.GetTraceback(), $"OpCode {opcode} is not implemented");
- }
- static void SelfPostOperation(ref VirtualMachineExecutionContext context)
- {
- var stack = context.Stack;
- var instruction = context.Instruction;
- var RA = instruction.A + context.FrameBase;
- var RB = instruction.B + context.FrameBase;
- ref var stackHead = ref stack.Get(0);
- var table = Unsafe.Add(ref stackHead, RB);
- Unsafe.Add(ref stackHead, RA + 1) = table;
- Unsafe.Add(ref stackHead, RA) = context.TaskResult == 0 ? LuaValue.Nil : context.ResultsBuffer[0];
- stack.NotifyTop(RA + 2);
- context.ClearResultsBuffer();
- }
- static bool Concat(ref VirtualMachineExecutionContext context, out bool doRestart)
- {
- var instruction = context.Instruction;
- var stack = context.Stack;
- var RA = instruction.A + context.FrameBase;
- stack.EnsureCapacity(RA + 1);
- ref var stackHead = ref stack.Get(context.FrameBase);
- ref var constHead = ref MemoryMarshalEx.UnsafeElementAt(context.Chunk.Constants, 0);
- var vb = RKB(ref stackHead, ref constHead, instruction);
- var vc = RKC(ref stackHead, ref constHead, instruction);
- var bIsValid = vb.TryReadString(out var strB);
- var cIsValid = vc.TryReadString(out var strC);
- if (!bIsValid && vb.TryReadDouble(out var numB))
- {
- strB = numB.ToString(CultureInfo.InvariantCulture);
- bIsValid = true;
- }
- if (!cIsValid && vc.TryReadDouble(out var numC))
- {
- strC = numC.ToString(CultureInfo.InvariantCulture);
- cIsValid = true;
- }
- if (bIsValid && cIsValid)
- {
- stack.Get(RA) = strB + strC;
- stack.NotifyTop(RA + 1);
- doRestart = false;
- return true;
- }
- return ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Concat, "concat", out doRestart);
- }
- static bool Call(ref VirtualMachineExecutionContext context, out bool doRestart)
- {
- var instruction = context.Instruction;
- var RA = instruction.A + context.FrameBase;
- var va = context.Stack.Get(RA);
- if (!va.TryReadFunction(out var func))
- {
- if (va.TryGetMetamethod(context.State, Metamethods.Call, out var metamethod) &&
- metamethod.TryReadFunction(out func))
- {
- }
- else
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", va);
- }
- }
- var thread = context.Thread;
- var (newBase, argumentCount, variableArgumentCount) = PrepareForFunctionCall(thread, func, instruction, RA);
- var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount);
- thread.PushCallStackFrame(newFrame);
- if (func is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- return true;
- }
- doRestart = false;
- return FuncCall(ref context, in newFrame, func, newBase, argumentCount);
- static bool FuncCall(ref VirtualMachineExecutionContext context, in CallStackFrame newFrame, LuaFunction func, int newBase, int argumentCount)
- {
- var task = func.Invoke(ref context, newFrame, argumentCount);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- var awaiter = task.GetAwaiter();
- context.Thread.PopCallStackFrameUnsafe(newBase);
- context.TaskResult = awaiter.GetResult();
- var instruction = context.Instruction;
- var rawResultCount = context.TaskResult;
- var resultCount = rawResultCount;
- var ic = instruction.C;
- if (ic != 0)
- {
- resultCount = ic - 1;
- }
- if (resultCount == 0)
- {
- context.Stack.Pop();
- }
- else
- {
- var stack = context.Stack;
- var RA = instruction.A + context.FrameBase;
- stack.EnsureCapacity(RA + resultCount);
- ref var stackHead = ref stack.Get(RA);
- var results = context.ResultsBuffer.AsSpan(0, rawResultCount);
- for (int i = 0; i < resultCount; i++)
- {
- Unsafe.Add(ref stackHead, i) = i >= rawResultCount
- ? default
- : results[i];
- }
- stack.NotifyTop(RA + resultCount);
- results.Clear();
- }
- return true;
- }
- }
- static void CallPostOperation(ref VirtualMachineExecutionContext context)
- {
- var instruction = context.Instruction;
- var rawResultCount = context.TaskResult;
- var resultCount = rawResultCount;
- var ic = instruction.C;
- if (ic != 0)
- {
- resultCount = ic - 1;
- }
- if (resultCount == 0)
- {
- context.Stack.Pop();
- }
- else
- {
- var stack = context.Stack;
- var RA = instruction.A + context.FrameBase;
- stack.EnsureCapacity(RA + resultCount);
- ref var stackHead = ref stack.Get(RA);
- var results = context.ResultsBuffer.AsSpan(0, rawResultCount);
- for (int i = 0; i < resultCount; i++)
- {
- Unsafe.Add(ref stackHead, i) = i >= rawResultCount
- ? default
- : results[i];
- }
- stack.NotifyTop(RA + resultCount);
- results.Clear();
- }
- }
- static bool TailCall(ref VirtualMachineExecutionContext context, out bool doRestart)
- {
- var instruction = context.Instruction;
- var stack = context.Stack;
- var RA = instruction.A + context.FrameBase;
- var state = context.State;
- var thread = context.Thread;
- state.CloseUpValues(thread, context.FrameBase);
- var va = stack.Get(RA);
- if (!va.TryReadFunction(out var func))
- {
- if (!va.TryGetMetamethod(state, Metamethods.Call, out var metamethod) &&
- !metamethod.TryReadFunction(out func))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", metamethod);
- }
- }
- var (newBase, argumentCount, variableArgumentCount) = PrepareForFunctionTailCall(thread, func, instruction, RA);
- var newFrame = func.CreateNewFrame(ref context, newBase, variableArgumentCount);
- thread.PushCallStackFrame(newFrame);
- context.Push(newFrame);
- if (func is Closure)
- {
- doRestart = true;
- return true;
- }
- doRestart = false;
- var task = func.Invoke(ref context, newFrame, argumentCount);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- context.Thread.PopCallStackFrame();
- doRestart = true;
- var awaiter = task.GetAwaiter();
- var resultCount = awaiter.GetResult();
- var resultsSpan = context.ResultsBuffer.AsSpan(0, resultCount);
- if (!context.PopFromBuffer(resultsSpan))
- {
- doRestart = false;
- context.ResultCount = resultCount;
- resultsSpan.CopyTo(context.Buffer.Span);
- }
- resultsSpan.Clear();
- return true;
- }
- static bool TForCall(ref VirtualMachineExecutionContext context, out bool doRestart)
- {
- doRestart = false;
- var instruction = context.Instruction;
- var stack = context.Stack;
- var RA = instruction.A + context.FrameBase;
- var iteratorRaw = stack.Get(RA);
- if (!iteratorRaw.TryReadFunction(out var iterator))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", iteratorRaw);
- }
- var newBase = RA + 3 + instruction.C;
- stack.Get(newBase) = stack.Get(RA + 1);
- stack.Get(newBase + 1) = stack.Get(RA + 2);
- stack.NotifyTop(newBase + 2);
- var newFrame = iterator.CreateNewFrame(ref context, newBase);
- context.Thread.PushCallStackFrame(newFrame);
- if (iterator is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- return true;
- }
- var task = iterator.Invoke(ref context, newFrame, 2);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- var awaiter = task.GetAwaiter();
- context.TaskResult = awaiter.GetResult();
- context.Thread.PopCallStackFrame();
- TForCallPostOperation(ref context);
- return true;
- }
- static void TForCallPostOperation(ref VirtualMachineExecutionContext context)
- {
- var stack = context.Stack;
- var instruction = context.Instruction;
- var RA = instruction.A + context.FrameBase;
- var resultBuffer = context.ResultsBuffer;
- var resultCount = context.TaskResult;
- stack.EnsureCapacity(RA + instruction.C + 3);
- for (int i = 1; i <= instruction.C; i++)
- {
- var index = i - 1;
- stack.Get(RA + 2 + i) = index >= resultCount
- ? LuaValue.Nil
- : resultBuffer[i - 1];
- }
- stack.NotifyTop(RA + instruction.C + 3);
- context.ClearResultsBuffer(resultCount);
- }
- static void SetList(ref VirtualMachineExecutionContext context)
- {
- var instruction = context.Instruction;
- var stack = context.Stack;
- var RA = instruction.A + context.FrameBase;
- if (!stack.Get(RA).TryReadTable(out var table))
- {
- throw new LuaException("internal error");
- }
- var count = instruction.B == 0
- ? stack.Count - (RA + 1)
- : instruction.B;
- table.EnsureArrayCapacity((instruction.C - 1) * 50 + count);
- stack.GetBuffer().Slice(RA + 1, count)
- .CopyTo(table.GetArraySpan()[((instruction.C - 1) * 50)..]);
- }
- static void ComparePostOperation(ref VirtualMachineExecutionContext context)
- {
- var compareResult = context.TaskResult != 0 && context.ResultsBuffer[0].ToBoolean();
- if (compareResult != (context.Instruction.A == 1))
- {
- context.Pc++;
- }
- context.ClearResultsBuffer();
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static ref readonly LuaValue RKB(ref LuaValue stack, ref LuaValue constants, Instruction instruction)
- {
- var index = instruction.UIntB;
- return ref (index >= 256 ? ref Unsafe.Add(ref constants, index - 256) : ref Unsafe.Add(ref stack, index));
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static ref readonly LuaValue RKC(ref LuaValue stack, ref LuaValue constants, Instruction instruction)
- {
- var index = instruction.UIntC;
- return ref (index >= 256 ? ref Unsafe.Add(ref constants, index - 256) : ref Unsafe.Add(ref stack, index));
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool TryGetValueWithSync(LuaValue table, LuaValue key, ref VirtualMachineExecutionContext context, out LuaValue value, out bool doRestart)
- {
- var targetTable = table;
- const int MAX_LOOP = 100;
- doRestart = false;
- var skip = targetTable.Type == LuaValueType.Table;
- for (int i = 0; i < MAX_LOOP; i++)
- {
- if (table.TryReadTable(out var luaTable))
- {
- if (!skip && luaTable.TryGetValue(key, out value))
- {
- return true;
- }
- skip = false;
- var metatable = luaTable.Metatable;
- if (metatable != null && metatable.TryGetValue(Metamethods.Index, out table))
- {
- goto Function;
- }
- value = default;
- return true;
- }
- if (!table.TryGetMetamethod(context.State, Metamethods.Index, out var metatableValue))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "index", table);
- }
- table = metatableValue;
- Function:
- if (table.TryReadFunction(out var function))
- {
- return CallGetTableFunc(targetTable, function, key, ref context, out value, out doRestart);
- }
- }
- throw new LuaRuntimeException(GetTracebacks(ref context), "loop in gettable");
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool CallGetTableFunc(LuaValue table, LuaFunction indexTable, LuaValue key, ref VirtualMachineExecutionContext context, out LuaValue result, out bool doRestart)
- {
- doRestart = false;
- var stack = context.Stack;
- stack.Push(table);
- stack.Push(key);
- var newFrame = indexTable.CreateNewFrame(ref context, stack.Count - 2);
- context.Thread.PushCallStackFrame(newFrame);
- if (indexTable is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- result = default;
- return true;
- }
- var task = indexTable.Invoke(ref context, newFrame, 2);
- if (!task.IsCompleted)
- {
- context.Task = task;
- result = default;
- return false;
- }
- var awaiter = task.GetAwaiter();
- context.Thread.PopCallStackFrame();
- var resultCount = awaiter.GetResult();
- result = resultCount == 0 ? default : context.ResultsBuffer[0];
- context.ClearResultsBuffer(resultCount);
- return true;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool TrySetMetaTableValueWithSync(LuaValue table, LuaValue key, LuaValue value,
- ref VirtualMachineExecutionContext context, out bool doRestart)
- {
- var targetTable = table;
- const int MAX_LOOP = 100;
- doRestart = false;
- var skip = targetTable.Type == LuaValueType.Table;
- for (int i = 0; i < MAX_LOOP; i++)
- {
- if (table.TryReadTable(out var luaTable))
- {
- ref var valueRef = ref (skip ? ref Unsafe.NullRef<LuaValue>() : ref luaTable.FindValue(key));
- skip = false;
- if (!Unsafe.IsNullRef(ref valueRef) && valueRef.Type != LuaValueType.Nil)
- {
- luaTable[key] = value;
- return true;
- }
- var metatable = luaTable.Metatable;
- if (metatable == null || !metatable.TryGetValue(Metamethods.NewIndex, out table))
- {
- if (Unsafe.IsNullRef(ref valueRef))
- {
- luaTable[key] = value;
- return true;
- }
- valueRef = value;
- return true;
- }
- goto Function;
- }
- if (!table.TryGetMetamethod(context.State, Metamethods.NewIndex, out var metatableValue))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "index", table);
- }
- table = metatableValue;
- Function:
- if (table.TryReadFunction(out var function))
- {
- return CallSetTableFunc(targetTable, function, key, value, ref context, out doRestart);
- }
- }
- throw new LuaRuntimeException(GetTracebacks(ref context), "loop in settable");
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool CallSetTableFunc(LuaValue table, LuaFunction newIndexFunction, LuaValue key, LuaValue value, ref VirtualMachineExecutionContext context, out bool doRestart)
- {
- doRestart = false;
- var thread = context.Thread;
- var stack = thread.Stack;
- stack.Push(table);
- stack.Push(key);
- stack.Push(value);
- var newFrame = newIndexFunction.CreateNewFrame(ref context, stack.Count - 3);
- context.Thread.PushCallStackFrame(newFrame);
- if (newIndexFunction is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- return true;
- }
- var task = newIndexFunction.Invoke(ref context, newFrame, 3);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- var resultCount = task.GetAwaiter().GetResult();
- if (0 < resultCount)
- {
- context.ClearResultsBuffer(resultCount);
- }
- thread.PopCallStackFrame();
- return true;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool ExecuteBinaryOperationMetaMethod(LuaValue vb, LuaValue vc,
- ref VirtualMachineExecutionContext context, string name, string description, out bool doRestart)
- {
- doRestart = false;
- if (vb.TryGetMetamethod(context.State, name, out var metamethod) ||
- vc.TryGetMetamethod(context.State, name, out metamethod))
- {
- if (!metamethod.TryReadFunction(out var func))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", metamethod);
- }
- var stack = context.Stack;
- stack.Push(vb);
- stack.Push(vc);
- var newFrame = func.CreateNewFrame(ref context, stack.Count - 2);
- context.Thread.PushCallStackFrame(newFrame);
- if (func is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- return true;
- }
- var task = func.Invoke(ref context, newFrame, 2);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- var resultCount = task.GetAwaiter().GetResult();
- context.Thread.PopCallStackFrame();
- var RA = context.Instruction.A + context.FrameBase;
- stack.Get(RA) = resultCount == 0 ? LuaValue.Nil : context.ResultsBuffer[0];
- context.ClearResultsBuffer(resultCount);
- return true;
- }
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), description, vb, vc);
- return false;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool ExecuteUnaryOperationMetaMethod(LuaValue vb, ref VirtualMachineExecutionContext context,
- string name, string description, bool isLen, out bool doRestart)
- {
- doRestart = false;
- var stack = context.Stack;
- if (vb.TryGetMetamethod(context.State, name, out var metamethod))
- {
- if (!metamethod.TryReadFunction(out var func))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", metamethod);
- }
- stack.Push(vb);
- var newFrame = func.CreateNewFrame(ref context, stack.Count - 1);
- context.Thread.PushCallStackFrame(newFrame);
- if (func is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- return true;
- }
- var task = func.Invoke(ref context, newFrame, 1);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- context.Thread.PopCallStackFrame();
- var RA = context.Instruction.A + context.FrameBase;
- var resultCount = task.GetAwaiter().GetResult();
- stack.Get(RA) = resultCount == 0 ? LuaValue.Nil : context.ResultsBuffer[0];
- context.ClearResultsBuffer(resultCount);
- return true;
- }
- if (isLen && vb.TryReadTable(out var table))
- {
- var RA = context.Instruction.A + context.FrameBase;
- stack.Get(RA) = table.ArrayLength;
- return true;
- }
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), description, vb);
- return true;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc,
- ref VirtualMachineExecutionContext context, string name, string? description, out bool doRestart)
- {
- doRestart = false;
- bool reverseLe = false;
- ReCheck:
- if (vb.TryGetMetamethod(context.State, name, out var metamethod) ||
- vc.TryGetMetamethod(context.State, name, out metamethod))
- {
- if (!metamethod.TryReadFunction(out var func))
- {
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", metamethod);
- }
- var stack = context.Stack;
- stack.Push(vb);
- stack.Push(vc);
- var newFrame = func.CreateNewFrame(ref context, stack.Count - 2);
- if (reverseLe) newFrame.Flags |= CallStackFrameFlags.ReversedLe;
- context.Thread.PushCallStackFrame(newFrame);
- if (func is Closure)
- {
- context.Push(newFrame);
- doRestart = true;
- return true;
- }
- var task = func.Invoke(ref context, newFrame, 2);
- if (!task.IsCompleted)
- {
- context.Task = task;
- return false;
- }
- context.Thread.PopCallStackFrame();
- var resultCount = task.GetAwaiter().GetResult();
- var compareResult = resultCount != 0 && context.ResultsBuffer[0].ToBoolean();
- compareResult = reverseLe ? !compareResult : compareResult;
- if (compareResult != (context.Instruction.A == 1))
- {
- context.Pc++;
- }
- context.ClearResultsBuffer(resultCount);
- return true;
- }
- if (name == Metamethods.Le)
- {
- reverseLe = true;
- name = Metamethods.Lt;
- (vb, vc) = (vc, vb);
- goto ReCheck;
- }
- if (description != null)
- {
- if (reverseLe)
- {
- (vb, vc) = (vc, vb);
- }
- LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), description, vb, vc);
- }
- else
- {
- if (context.Instruction.A == 1)
- {
- context.Pc++;
- }
- }
- return true;
- }
- // 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.
- // see: https://wubingzheng.github.io/build-lua-in-rust/en/ch08-02.arguments.html
- [MethodImpl(MethodImplOptions.NoInlining)]
- static (int FrameBase, int ArgumentCount, int VariableArgumentCount) PrepareVariableArgument(LuaStack stack, int newBase, int argumentCount,
- int variableArgumentCount)
- {
- var temp = newBase;
- newBase += variableArgumentCount;
- stack.EnsureCapacity(newBase + argumentCount);
- stack.NotifyTop(newBase + argumentCount);
- var stackBuffer = stack.GetBuffer()[temp..];
- stackBuffer[..argumentCount].CopyTo(stackBuffer[variableArgumentCount..]);
- stackBuffer.Slice(argumentCount, variableArgumentCount).CopyTo(stackBuffer);
- return (newBase, argumentCount, variableArgumentCount);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static (int FrameBase, int ArgumentCount, int VariableArgumentCount) PrepareForFunctionCall(LuaThread thread, LuaFunction function,
- Instruction instruction, int RA)
- {
- var argumentCount = instruction.B - 1;
- if (argumentCount == -1)
- {
- argumentCount = (ushort)(thread.Stack.Count - (RA + 1));
- }
- else
- {
- thread.Stack.NotifyTop(RA + 1 + argumentCount);
- }
- var newBase = RA + 1;
- var variableArgumentCount = function.GetVariableArgumentCount(argumentCount);
- if (variableArgumentCount <= 0)
- {
- return (newBase, argumentCount, 0);
- }
- return PrepareVariableArgument(thread.Stack, newBase, argumentCount, variableArgumentCount);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static (int FrameBase, int ArgumentCount, int VariableArgumentCount) PrepareForFunctionTailCall(LuaThread thread, LuaFunction function,
- Instruction instruction, int RA)
- {
- var stack = thread.Stack;
- var argumentCount = instruction.B - 1;
- if (instruction.B == 0)
- {
- argumentCount = (ushort)(stack.Count - (RA + 1));
- }
- else
- {
- thread.Stack.NotifyTop(RA + 1 + argumentCount);
- }
- var newBase = RA + 1;
- // In the case of tailcall, the local variables of the caller are immediately discarded, so there is no need to retain them.
- // Therefore, a call can be made without allocating new registers.
- var currentBase = thread.GetCurrentFrame().Base;
- {
- var stackBuffer = stack.GetBuffer();
- if (argumentCount > 0)
- stackBuffer.Slice(newBase, argumentCount).CopyTo(stackBuffer.Slice(currentBase, argumentCount));
- newBase = currentBase;
- }
- var variableArgumentCount = function.GetVariableArgumentCount(argumentCount);
- if (variableArgumentCount <= 0)
- {
- return (newBase, argumentCount, 0);
- }
- return PrepareVariableArgument(thread.Stack, newBase, argumentCount, variableArgumentCount);
- }
- static Traceback GetTracebacks(ref VirtualMachineExecutionContext context)
- {
- return GetTracebacks(context.State, context.Pc);
- }
- static Traceback GetTracebacks(LuaState state, int pc)
- {
- var frame = state.CurrentThread.GetCurrentFrame();
- state.CurrentThread.PushCallStackFrame(frame with
- {
- CallerInstructionIndex = pc
- });
- var tracebacks = state.GetTraceback();
- state.CurrentThread.PopCallStackFrame();
- return tracebacks;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static CallStackFrame CreateNewFrame(this LuaFunction function, ref VirtualMachineExecutionContext context, int newBase, int variableArgumentCount = 0)
- {
- return new()
- {
- Base = newBase,
- Function = function,
- VariableArgumentCount = variableArgumentCount,
- CallerInstructionIndex = context.Pc,
- };
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static ValueTask<int> Invoke(this LuaFunction function, ref VirtualMachineExecutionContext context, in CallStackFrame frame, int arguments)
- {
- return function.Func(new()
- {
- State = context.State,
- Thread = context.Thread,
- ArgumentCount = arguments,
- FrameBase = frame.Base,
- CallerInstructionIndex = frame.CallerInstructionIndex,
- }, context.ResultsBuffer, context.CancellationToken);
- }
- }
|