public void CallArgument(int frameBase, int argument, LuaValue value) { Stack[frameBase + 1 + argument] = value; }
void Dispatch(LuaThread thread, int frameBase, int resultCount, int fp, int ip) { try { while (true) { // Suspend coroutine (support for suspending at arbitrary instructions). /* * if ( thread.UnwoundFrames.Count > 0 ) * { * thread.UnwoundFrames.Add( new Frame( frameBase, resultCount, fp, ip ) ); * return; * } */ // Dispatch instructions. Instruction i = prototype.Instructions[ip++]; switch (i.Opcode) { case Opcode.Move: { // R( A ) := R( B ) thread.Stack[fp + i.A] = thread.Stack[fp + i.B]; continue; } case Opcode.LoadK: { // R( A ) := K( Bx ) thread.Stack[fp + i.A] = K(i.Bx); continue; } case Opcode.LoadBool: { // R( A ) := (bool)B if (i.B != 0) { thread.Stack[fp + i.A] = true; } else { thread.Stack[fp + i.A] = false; } // if C skip next instruction if (i.C != 0) { ip += 1; } continue; } case Opcode.LoadNil: { // R( A ) ... R( B ) := nil for (int r = i.A; r <= i.B; ++r) { thread.Stack[fp + r] = null; } continue; } case Opcode.GetUpVal: { // R( A ) := U( B ) thread.Stack[fp + i.A] = upVals[i.B].Value; continue; } case Opcode.GetGlobal: { // R( A ) := G[ K( Bx ) ] thread.Stack[fp + i.A] = Environment.Index(K(i.Bx)); continue; } case Opcode.GetTable: { // R( A ) := R( B )[ RK( C ) ] thread.Stack[fp + i.A] = thread.Stack[fp + i.B].Index(RK(thread.Stack, fp, i.C)); continue; } case Opcode.SetGlobal: { // G[ K( Bx ) ] := R( A ) Environment.NewIndex(K(i.Bx), thread.Stack[fp + i.A]); continue; } case Opcode.SetUpVal: { // U( B ) := R( A ) upVals[i.B].Value = thread.Stack[fp + i.A]; continue; } case Opcode.SetTable: { // R( A )[ RK( B ) ] = RK( C ) thread.Stack[fp + i.A].NewIndex(RK(thread.Stack, fp, i.B), RK(thread.Stack, fp, i.C)); continue; } case Opcode.NewTable: { // R( A ) := {} ( B is array size hint, C is hash size hint ) thread.Stack[fp + i.A] = new LuaTable(i.B, i.C); continue; } case Opcode.Self: { LuaValue self = thread.Stack[fp + i.B]; // R( A + 1 ) := R( B ) thread.Stack[fp + i.A + 1] = self; // R( A ) = R( B )[ RK( C ) ] thread.Stack[fp + i.A] = self.Index(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Add: { // R( A ) := RK( B ) + RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).Add(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Sub: { // R( A ) := RK( B ) - RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).Subtract(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Mul: { // R( A ) := RK( B ) * RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).Multiply(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Div: { // R( A ) := RK( B ) / RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).Divide(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Mod: { // R( A ) := RK( B ) % RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).Modulus(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Pow: { // R( A ) := RK( B ) ^ RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).RaiseToPower(RK(thread.Stack, fp, i.C)); continue; } case Opcode.Unm: { // R( A ) := -R( B ) thread.Stack[fp + i.A] = thread.Stack[fp + i.B].UnaryMinus(); continue; } case Opcode.Not: { // R( A ) := not R( B ) LuaValue B = thread.Stack[fp + i.B]; thread.Stack[fp + i.A] = !(B != null && B.IsTrue()); continue; } case Opcode.Len: { // R( A ) := length of R( B ) thread.Stack[fp + i.A] = thread.Stack[fp + i.B].Length(); continue; } case Opcode.Concat: { // R( A ) := R( B ) .. ... .. R( C ), concatenating whole list int listTop = fp + i.C; int count = i.C - i.B + 1; while (count > 1) { LuaValue left = thread.Stack[listTop - 1]; LuaValue right = thread.Stack[listTop - 0]; if (left.SupportsSimpleConcatenation() && right.SupportsSimpleConcatenation()) { // Count how many we can concatenate in this pass. int concatCount = 2; for (concatCount = 2; concatCount < count; ++concatCount) { LuaValue operand = thread.Stack[listTop - concatCount]; if (!operand.SupportsSimpleConcatenation()) { break; } } // Concatenate them. StringBuilder s = new StringBuilder(); for (int r = listTop - (concatCount - 1); r <= listTop; ++r) { s.Append(thread.Stack[r].ToString()); } // Modify the thread.Stack top and continue. thread.Stack[listTop - (concatCount - 1)] = s.ToString(); listTop -= concatCount - 1; count -= concatCount - 1; } else { // Perform meta concatenation. thread.Stack[listTop - 1] = left.Concatenate(right); listTop -= 1; count -= 1; } } thread.Stack[fp + i.A] = thread.Stack[listTop]; continue; } case Opcode.Jmp: { // relative jump sBx ( relative to next instruction ) ip += i.sBx; continue; } case Opcode.Eq: { // if ( RK( B ) == RK( C ) ) ~= A then skip associated jump LuaValue B = RK(thread.Stack, fp, i.B); LuaValue C = RK(thread.Stack, fp, i.C); bool equals = B != null?B.Equals(C) : C == null; if (equals == (i.A != 0)) { i = prototype.Instructions[ip++]; ip += i.sBx; } else { ip += 1; } continue; } case Opcode.Lt: { // if ( RK( B ) < RK( C ) ) ~= A then skip associated jump if (RK(thread.Stack, fp, i.B).LessThan(RK(thread.Stack, fp, i.C)) == (i.A != 0)) { i = prototype.Instructions[ip++]; ip += i.sBx; } else { ip += 1; } continue; } case Opcode.Le: { // if ( RK( B ) <= RK( C ) ) ~= A then skip associated jump if (RK(thread.Stack, fp, i.B).LessThanOrEquals(RK(thread.Stack, fp, i.C)) == (i.A != 0)) { i = prototype.Instructions[ip++]; ip += i.sBx; } else { ip += 1; } continue; } case Opcode.Test: { // if not ( R( A ) <=> C ) then skip associated jump LuaValue A = thread.Stack[fp + i.A]; if ((A != null && A.IsTrue()) == (i.C != 0)) { i = prototype.Instructions[ip++]; ip += i.sBx; } else { ip += 1; } continue; } case Opcode.TestSet: { // if ( R( B ) <=> C ) then R( A ) := R( B ) else skip associated jump LuaValue B = thread.Stack[fp + i.B]; if ((B != null && B.IsTrue()) == (i.C != 0)) { // Set. thread.Stack[fp + i.A] = thread.Stack[fp + i.B]; // Perform associated jump. i = prototype.Instructions[ip++]; ip += i.sBx; } else { ip += 1; } continue; } case Opcode.Call: { // R( A ), ... , R( A + C - 2 ) := R( A )( R( A + 1 ), ... , R( A + B - 1 ) ) // Count arguments. int callArgumentCount; if (i.B != 0) { callArgumentCount = i.B - 1; } else { callArgumentCount = thread.Top - fp - i.A; thread.Top = -1; } LuaValue function = thread.Stack[fp + i.A]; function.Call(thread, fp + i.A, callArgumentCount, i.C - 1); if (thread.UnwoundFrames.Count > 0) { thread.UnwoundFrames.Add(new Frame(frameBase, resultCount, fp, ip)); return; } if (i.C != 0) { thread.StackWatermark(fp + prototype.StackSize); } else { thread.StackWatermark(Math.Max(fp + prototype.StackSize, thread.Top + 1)); } continue; } case Opcode.TailCall: { // return R( A )( R( A + 1 ), ... , R( A + B - 1 ) ) thread.CloseUpVals(fp); thread.StackLevels.RemoveAt(thread.StackLevels.Count - 1); int callArgumentCount; if (i.B != 0) { callArgumentCount = i.B - 1; } else { callArgumentCount = thread.Top - fp - i.A; thread.Top = -1; } LuaValue function = thread.Stack[fp + i.A]; thread.Stack[frameBase] = function; for (int argument = 0; argument < callArgumentCount; ++argument) { thread.Stack[frameBase + 1 + argument] = thread.Stack[fp + i.A + 1 + argument]; } function.Call(thread, frameBase, callArgumentCount, resultCount); return; } case Opcode.Return: { // return R( A ), ... R( A + B - 2 ) thread.CloseUpVals(fp); thread.StackLevels.RemoveAt(thread.StackLevels.Count - 1); // Find number of results we have. int returnResultCount; if (i.B != 0) { returnResultCount = i.B - 1; } else { returnResultCount = thread.Top + 1 - fp - i.A; thread.Top = -1; } // Calculate number of results we want. int copyCount; if (resultCount != -1) { copyCount = Math.Min(resultCount, returnResultCount); } else { copyCount = returnResultCount; thread.Top = frameBase + returnResultCount - 1; } // Copy results. for (int result = 0; result < copyCount; ++result) { thread.Stack[frameBase + result] = thread.Stack[fp + i.A + result]; } for (int result = copyCount; result < resultCount; ++result) { thread.Stack[frameBase + result] = null; } return; } /* A + 0 Index * A + 1 Limit * A + 2 Step * A + 3 Var */ case Opcode.ForLoop: { LuaValue index = thread.Stack[fp + i.A + 0]; LuaValue limit = thread.Stack[fp + i.A + 1]; LuaValue step = thread.Stack[fp + i.A + 2]; // Index += Step index = index.Add(step); thread.Stack[fp + i.A + 0] = index; // if ( Step > 0 and Index <= Limit ) or ( Step < 0 and Index >= Limit ) then Var = Index, relative jump sBx if ((!step.LessThanOrEquals(zero) && index.LessThanOrEquals(limit)) || (step.LessThan(zero) && !index.LessThan(limit))) { thread.Stack[fp + i.A + 3] = index; ip += i.sBx; } continue; } case Opcode.ForPrep: { LuaValue index = thread.Stack[fp + i.A + 0]; LuaValue limit = thread.Stack[fp + i.A + 1]; LuaValue step = thread.Stack[fp + i.A + 2]; // Convert for control variables to numbers. if (!index.TryToNumberValue(out index)) { throw new InvalidOperationException("'for' initial value must be a number."); } if (!limit.TryToNumberValue(out limit)) { throw new InvalidOperationException("'for' limit must be a number."); } if (!step.TryToNumberValue(out step)) { throw new InvalidOperationException("'for' step must be a number."); } // Index -= Step index = index.Subtract(step); // Update thread.Stack. thread.Stack[fp + i.A + 0] = index; thread.Stack[fp + i.A + 1] = limit; thread.Stack[fp + i.A + 2] = step; // relative jump sBx ip += i.sBx; continue; } /* A + 0 Generator * A + 1 State * A + 2 Control * A + 3 Var_1 * ... * A + 3 + C Var_C */ case Opcode.TForLoop: { LuaValue generator = thread.Stack[fp + i.A + 0]; LuaValue state = thread.Stack[fp + i.A + 1]; LuaValue control = thread.Stack[fp + i.A + 2]; // Var_1, ..., Var_C := Generator( State, Control ) thread.Stack[fp + i.A + 3] = generator; thread.Stack[fp + i.A + 4] = state; thread.Stack[fp + i.A + 5] = control; generator.Call(thread, fp + i.A + 3, 2, i.C); // if Var_1 ~= nil then Control = Var_1 else skip associated jump LuaValue var_1 = thread.Stack[fp + i.A + 3]; if (var_1 != null && var_1.IsTrue()) { // Set control. thread.Stack[fp + i.A + 2] = var_1; // Perform associated jump. i = prototype.Instructions[ip++]; ip += i.sBx; } else { ip += 1; } continue; } case Opcode.SetList: { // Special decoding. int keyBase; if (i.C != 0) { keyBase = i.C; } else { keyBase = prototype.Instructions[ip++].Index; } int lastKey; if (i.B != 0) { lastKey = i.B; } else { lastKey = thread.Top - fp - i.A; thread.Top = -1; } // R( A )[ ( C - 1 ) * 50 + i ] := R( A + i ), 1 <= i <= B for (int key = 1; key <= lastKey; ++key) { thread.Stack[fp + i.A].NewIndex((keyBase - 1) * Instruction.FieldsPerFlush + key, thread.Stack[fp + i.A + key]); } if (i.B == 0) { thread.StackWatermark(fp + prototype.StackSize); } continue; } case Opcode.Close: { // close all thread.Stack variables from R( A ) to the top thread.CloseUpVals(fp + i.A); continue; } case Opcode.Closure: { // R( A ) := function closure from P( Bx ) LuaFunction function = new LuaFunction(prototype.Prototypes[i.Bx], Environment); thread.Stack[fp + i.A] = function; // followed by upval initialization with Move or GetUpVal for (int upval = 0; upval < function.prototype.UpValCount; ++upval) { Instruction u = prototype.Instructions[ip++]; if (u.Opcode == Opcode.Move) { function.upVals[upval] = thread.MakeUpVal(fp + u.B); } else if (u.Opcode == Opcode.GetUpVal) { function.upVals[upval] = upVals[u.B]; } else { throw new InvalidProgramException("Malformed upval initialization bytecode."); } } continue; } case Opcode.Vararg: { // R( A ), ..., R( A + B - 1 ) = vararg /* frameBase --> Function * ^ null * argumentCount null * v argument (vararg) * framePointer --> argument * argument */ // Find varargs. int varargBase = frameBase + 1 + prototype.ParameterCount; int varargCount = Math.Max(fp - varargBase, 0); // Find how many we want. int copyCount; if (i.B != 0) { copyCount = Math.Min(i.B, varargCount); } else { copyCount = varargCount; thread.Top = fp + i.A + copyCount - 1; thread.StackWatermark(Math.Max(fp + prototype.StackSize, thread.Top + 1)); } // Copy into correct position. for (int vararg = 0; vararg < copyCount; ++vararg) { thread.Stack[fp + i.A + vararg] = thread.Stack[varargBase + vararg]; } for (int vararg = copyCount; vararg < i.B; ++vararg) { thread.Stack[fp + i.A + vararg] = null; } continue; } case Opcode.IntDiv: { // R( A ) := RK( B ) \ RK( C ) thread.Stack[fp + i.A] = RK(thread.Stack, fp, i.B).IntegerDivide(RK(thread.Stack, fp, i.C)); continue; } } } } catch (Exception e) { thread.UnwoundFrames.Add(new Frame(frameBase, resultCount, fp, ip)); throw e; } }