/*
** $Id: lvm.c,v 2.155.1.1 2013/04/12 18:48:47 roberto Exp $
** Lua virtual machine
** See Copyright Notice in lua.h
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define lvm_c
#define LUA_CORE

#include "lua.h"

#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"

/* limit for table tag-method chains (to avoid loops) */
#define MAXTAGLOOP 100

const TValue *luaV_tonumber(const TValue *obj, TValue *n)
{
	lua_Number num;
	if (ttisnumber(obj)) return obj;
	if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num))
	{
		setnvalue(n, num);
		return n;
	}
	else
		return NULL;
}

int luaV_tostring(lua_State *L, StkId obj)
{
	if (!ttisnumber(obj))
		return 0;
	else
	{
		char s[LUAI_MAXNUMBER2STR];
		lua_Number n = nvalue(obj);
		int l = lua_number2str(s, n);
		setsvalue2s(L, obj, luaS_newlstr(L, s, l));
		return 1;
	}
}

static void traceexec(lua_State *L)
{
	CallInfo *ci = L->ci;
	lu_byte mask = L->hookmask;
	int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0);
	if (counthook)
		resethookcount(L); /* reset count */
	if (ci->callstatus & CIST_HOOKYIELD)
	{                                      /* called hook last time? */
		ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
		return;                            /* do not call hook again (VM yielded, so it did not move) */
	}
	if (counthook)
		luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */
	if (mask & LUA_MASKLINE)
	{
		Proto *p = ci_func(ci)->p;
		int npc = pcRel(ci->u.l.savedpc, p);
		int newline = getfuncline(p, npc);
		if (npc == 0 ||                                    /* call linehook when enter a new function, */
			ci->u.l.savedpc <= L->oldpc ||                 /* when jump back (loop), or when */
			newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */
			luaD_hook(L, LUA_HOOKLINE, newline);           /* call line hook */
	}
	L->oldpc = ci->u.l.savedpc;
	if (L->status == LUA_YIELD)
	{ /* did hook yield? */
		if (counthook)
			L->hookcount = 1;             /* undo decrement to zero */
		ci->u.l.savedpc--;                /* undo increment (resume will increment it again) */
		ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
		ci->func = L->top - 1;            /* protect stack below results */
		luaD_throw(L, LUA_YIELD);
	}
}

static void callTM(lua_State *L, const TValue *f, const TValue *p1,
				   const TValue *p2, TValue *p3, int hasres)
{
	ptrdiff_t result = savestack(L, p3);
	setobj2s(L, L->top++, f);      /* push function */
	setobj2s(L, L->top++, p1);     /* 1st argument */
	setobj2s(L, L->top++, p2);     /* 2nd argument */
	if (!hasres)                   /* no result? 'p3' is third argument */
		setobj2s(L, L->top++, p3); /* 3rd argument */
	/* metamethod may yield only when called from Lua code */
	luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci));
	if (hasres)
	{ /* if has result, move it to its place */
		p3 = restorestack(L, result);
		setobjs2s(L, p3, --L->top);
	}
}

void luaV_gettable(lua_State *L, const TValue *t, TValue *key, StkId val)
{
	int loop;
	for (loop = 0; loop < MAXTAGLOOP; loop++)
	{
		const TValue *tm;
		if (ttistable(t))
		{ /* `t' is a table? */
			Table *h = hvalue(t);
			const TValue *res = luaH_get(h, key); /* do a primitive get */
			if (!ttisnil(res) ||                  /* result is not nil? */
				(tm = fasttm(L, h->metatable, TM_INDEX)) == NULL)
			{ /* or no TM? */
				setobj2s(L, val, res);
				return;
			}
			/* else will try the tag method */
		}
		else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
			luaG_typeerror(L, t, "index");
		if (ttisfunction(tm))
		{
			callTM(L, tm, t, key, val, 1);
			return;
		}
		t = tm; /* else repeat with 'tm' */
	}
	luaG_runerror(L, "loop in gettable");
}

void luaV_settable(lua_State *L, const TValue *t, TValue *key, StkId val)
{
	int loop;
	for (loop = 0; loop < MAXTAGLOOP; loop++)
	{
		const TValue *tm;
		if (ttistable(t))
		{ /* `t' is a table? */
			Table *h = hvalue(t);
			TValue *oldval = cast(TValue *, luaH_get(h, key));
			/* if previous value is not nil, there must be a previous entry
         in the table; moreover, a metamethod has no relevance */
			if (!ttisnil(oldval) ||
				/* previous value is nil; must check the metamethod */
				((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL &&
				 /* no metamethod; is there a previous entry in the table? */
				 (oldval != luaO_nilobject ||
				  /* no previous entry; must create one. (The next test is
            always true; we only need the assignment.) */
				  (oldval = luaH_newkey(L, h, key), 1))))
			{
				/* no metamethod and (now) there is an entry with given key */
				setobj2t(L, oldval, val); /* assign new value to that entry */
				invalidateTMcache(h);
				luaC_barrierback(L, obj2gco(h), val);
				return;
			}
			/* else will try the metamethod */
		}
		else /* not a table; check metamethod */
			if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
			luaG_typeerror(L, t, "index");
		/* there is a metamethod */
		if (ttisfunction(tm))
		{
			callTM(L, tm, t, key, val, 0);
			return;
		}
		t = tm; /* else repeat with 'tm' */
	}
	luaG_runerror(L, "loop in settable");
}

static int call_binTM(lua_State *L, const TValue *p1, const TValue *p2,
					  StkId res, TMS event)
{
	const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
	if (ttisnil(tm))
		tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
	if (ttisnil(tm)) return 0;
	callTM(L, tm, p1, p2, res, 1);
	return 1;
}

static const TValue *get_equalTM(lua_State *L, Table *mt1, Table *mt2,
								 TMS event)
{
	const TValue *tm1 = fasttm(L, mt1, event);
	const TValue *tm2;
	if (tm1 == NULL) return NULL; /* no metamethod */
	if (mt1 == mt2) return tm1;   /* same metatables => same metamethods */
	tm2 = fasttm(L, mt2, event);
	if (tm2 == NULL) return NULL;   /* no metamethod */
	if (luaV_rawequalobj(tm1, tm2)) /* same metamethods? */
		return tm1;
	return NULL;
}

static int call_orderTM(lua_State *L, const TValue *p1, const TValue *p2,
						TMS event)
{
	if (!call_binTM(L, p1, p2, L->top, event))
		return -1; /* no metamethod */
	else
		return !l_isfalse(L->top);
}

static int l_strcmp(const TString *ls, const TString *rs)
{
	const char *l = getstr(ls);
	size_t ll = ls->tsv.len;
	const char *r = getstr(rs);
	size_t lr = rs->tsv.len;
	for (;;)
	{
		int temp = strcoll(l, r);
		if (temp != 0)
			return temp;
		else
		{                           /* strings are equal up to a `\0' */
			size_t len = strlen(l); /* index of first `\0' in both strings */
			if (len == lr)          /* r is finished? */
				return (len == ll) ? 0 : 1;
			else if (len == ll) /* l is finished? */
				return -1;      /* l is smaller than r (because r is not finished) */
			/* both strings longer than `len'; go on comparing (after the `\0') */
			len++;
			l += len;
			ll -= len;
			r += len;
			lr -= len;
		}
	}
}

int luaV_lessthan(lua_State *L, const TValue *l, const TValue *r)
{
	int res;
	if (ttisnumber(l) && ttisnumber(r))
		return luai_numlt(L, nvalue(l), nvalue(r));
	else if (ttisstring(l) && ttisstring(r))
		return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
	else if ((res = call_orderTM(L, l, r, TM_LT)) < 0)
		luaG_ordererror(L, l, r);
	return res;
}

int luaV_lessequal(lua_State *L, const TValue *l, const TValue *r)
{
	int res;
	if (ttisnumber(l) && ttisnumber(r))
		return luai_numle(L, nvalue(l), nvalue(r));
	else if (ttisstring(l) && ttisstring(r))
		return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
	else if ((res = call_orderTM(L, l, r, TM_LE)) >= 0) /* first try `le' */
		return res;
	else if ((res = call_orderTM(L, r, l, TM_LT)) < 0) /* else try `lt' */
		luaG_ordererror(L, l, r);
	return !res;
}

/*
** equality of Lua values. L == NULL means raw equality (no metamethods)
*/
int luaV_equalobj_(lua_State *L, const TValue *t1, const TValue *t2)
{
	const TValue *tm;
	lua_assert(ttisequal(t1, t2));
	switch (ttype(t1))
	{
		case LUA_TNIL:
			return 1;
		case LUA_TNUMBER:
			return luai_numeq(nvalue(t1), nvalue(t2));
		case LUA_TBOOLEAN:
			return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
		case LUA_TLIGHTUSERDATA:
			return pvalue(t1) == pvalue(t2);
		case LUA_TLCF:
			return fvalue(t1) == fvalue(t2);
		case LUA_TSHRSTR:
			return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
		case LUA_TLNGSTR:
			return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
		case LUA_TUSERDATA:
		{
			if (uvalue(t1) == uvalue(t2))
				return 1;
			else if (L == NULL)
				return 0;
			tm = get_equalTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ);
			break; /* will try TM */
		}
		case LUA_TTABLE:
		{
			if (hvalue(t1) == hvalue(t2))
				return 1;
			else if (L == NULL)
				return 0;
			tm = get_equalTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
			break; /* will try TM */
		}
		default:
			lua_assert(iscollectable(t1));
			return gcvalue(t1) == gcvalue(t2);
	}
	if (tm == NULL) return 0;         /* no TM? */
	callTM(L, tm, t1, t2, L->top, 1); /* call TM */
	return !l_isfalse(L->top);
}

void luaV_concat(lua_State *L, int total)
{
	lua_assert(total >= 2);
	do
	{
		StkId top = L->top;
		int n = 2; /* number of elements handled in this pass (at least 2) */
		if (!(ttisstring(top - 2) || ttisnumber(top - 2)) || !tostring(L, top - 1))
		{
			if (!call_binTM(L, top - 2, top - 1, top - 2, TM_CONCAT))
				luaG_concaterror(L, top - 2, top - 1);
		}
		else if (tsvalue(top - 1)->len == 0) /* second operand is empty? */
			(void)tostring(L, top - 2);      /* result is first operand */
		else if (ttisstring(top - 2) && tsvalue(top - 2)->len == 0)
		{
			setobjs2s(L, top - 2, top - 1); /* result is second op. */
		}
		else
		{
			/* at least two non-empty string values; get as many as possible */
			size_t tl = tsvalue(top - 1)->len;
			char *buffer;
			int i;
			/* collect total length */
			for (i = 1; i < total && tostring(L, top - i - 1); i++)
			{
				size_t l = tsvalue(top - i - 1)->len;
				if (l >= (MAX_SIZET / sizeof(char)) - tl)
					luaG_runerror(L, "string length overflow");
				tl += l;
			}
			buffer = luaZ_openspace(L, &G(L)->buff, tl);
			tl = 0;
			n = i;
			do
			{ /* concat all strings */
				size_t l = tsvalue(top - i)->len;
				memcpy(buffer + tl, svalue(top - i), l * sizeof(char));
				tl += l;
			} while (--i > 0);
			setsvalue2s(L, top - n, luaS_newlstr(L, buffer, tl));
		}
		total -= n - 1;  /* got 'n' strings to create 1 new */
		L->top -= n - 1; /* popped 'n' strings and pushed one */
	} while (total > 1); /* repeat until only 1 result left */
}

void luaV_objlen(lua_State *L, StkId ra, const TValue *rb)
{
	const TValue *tm;
	switch (ttypenv(rb))
	{
		case LUA_TTABLE:
		{
			Table *h = hvalue(rb);
			tm = fasttm(L, h->metatable, TM_LEN);
			if (tm) break;                         /* metamethod? break switch to call it */
			setnvalue(ra, cast_num(luaH_getn(h))); /* else primitive len */
			return;
		}
		case LUA_TSTRING:
		{
			setnvalue(ra, cast_num(tsvalue(rb)->len));
			return;
		}
		default:
		{ /* try metamethod */
			tm = luaT_gettmbyobj(L, rb, TM_LEN);
			if (ttisnil(tm)) /* no metamethod? */
				luaG_typeerror(L, rb, "get length of");
			break;
		}
	}
	callTM(L, tm, rb, rb, ra, 1);
}

void luaV_arith(lua_State *L, StkId ra, const TValue *rb,
				const TValue *rc, TMS op)
{
	TValue tempb, tempc;
	const TValue *b, *c;
	if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
		(c = luaV_tonumber(rc, &tempc)) != NULL)
	{
		lua_Number res = luaO_arith(op - TM_ADD + LUA_OPADD, nvalue(b), nvalue(c));
		setnvalue(ra, res);
	}
	else if (!call_binTM(L, rb, rc, ra, op))
		luaG_aritherror(L, rb, rc);
}

/*
** check whether cached closure in prototype 'p' may be reused, that is,
** whether there is a cached closure with the same upvalues needed by
** new closure to be created.
*/
static Closure *getcached(Proto *p, UpVal **encup, StkId base)
{
	Closure *c = p->cache;
	if (c != NULL)
	{ /* is there a cached closure? */
		int nup = p->sizeupvalues;
		Upvaldesc *uv = p->upvalues;
		int i;
		for (i = 0; i < nup; i++)
		{ /* check whether it has right upvalues */
			TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v;
			if (c->l.upvals[i]->v != v)
				return NULL; /* wrong upvalue; cannot reuse closure */
		}
	}
	return c; /* return cached closure (or NULL if no cached closure) */
}

/*
** create a new Lua closure, push it in the stack, and initialize
** its upvalues. Note that the call to 'luaC_barrierproto' must come
** before the assignment to 'p->cache', as the function needs the
** original value of that field.
*/
static void pushclosure(lua_State *L, Proto *p, UpVal **encup, StkId base,
						StkId ra)
{
	int nup = p->sizeupvalues;
	Upvaldesc *uv = p->upvalues;
	int i;
	Closure *ncl = luaF_newLclosure(L, nup);
	ncl->l.p = p;
	setclLvalue(L, ra, ncl); /* anchor new closure in stack */
	for (i = 0; i < nup; i++)
	{                      /* fill in its upvalues */
		if (uv[i].instack) /* upvalue refers to local variable? */
			ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx);
		else /* get upvalue from enclosing function */
			ncl->l.upvals[i] = encup[uv[i].idx];
	}
	luaC_barrierproto(L, p, ncl);
	p->cache = ncl; /* save it on cache for reuse */
}

/*
** finish execution of an opcode interrupted by an yield
*/
void luaV_finishOp(lua_State *L)
{
	CallInfo *ci = L->ci;
	StkId base = ci->u.l.base;
	Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
	OpCode op = GET_OPCODE(inst);
	switch (op)
	{ /* finish its execution */
		case OP_ADD:
		case OP_SUB:
		case OP_MUL:
		case OP_DIV:
		case OP_MOD:
		case OP_POW:
		case OP_UNM:
		case OP_LEN:
		case OP_GETTABUP:
		case OP_GETTABLE:
		case OP_SELF:
		{
			setobjs2s(L, base + GETARG_A(inst), --L->top);
			break;
		}
		case OP_LE:
		case OP_LT:
		case OP_EQ:
		{
			int res = !l_isfalse(L->top - 1);
			L->top--;
			/* metamethod should not be called when operand is K */
			lua_assert(!ISK(GETARG_B(inst)));
			if (op == OP_LE && /* "<=" using "<" instead? */
				ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE)))
				res = !res; /* invert result */
			lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
			if (res != GETARG_A(inst)) /* condition failed? */
				ci->u.l.savedpc++;     /* skip jump instruction */
			break;
		}
		case OP_CONCAT:
		{
			StkId top = L->top - 1;                     /* top when 'call_binTM' was called */
			int b = GETARG_B(inst);                     /* first element to concatenate */
			int total = cast_int(top - 1 - (base + b)); /* yet to concatenate */
			setobj2s(L, top - 2, top);                  /* put TM result in proper position */
			if (total > 1)
			{                          /* are there elements to concat? */
				L->top = top - 1;      /* top is one after last element (at top-2) */
				luaV_concat(L, total); /* concat them (may yield again) */
			}
			/* move final result to final position */
			setobj2s(L, ci->u.l.base + GETARG_A(inst), L->top - 1);
			L->top = ci->top; /* restore top */
			break;
		}
		case OP_TFORCALL:
		{
			lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_TFORLOOP);
			L->top = ci->top; /* correct top */
			break;
		}
		case OP_CALL:
		{
			if (GETARG_C(inst) - 1 >= 0) /* nresults >= 0? */
				L->top = ci->top;        /* adjust results */
			break;
		}
		case OP_TAILCALL:
		case OP_SETTABUP:
		case OP_SETTABLE:
			break;
		default:
			lua_assert(0);
	}
}

/*
** some macros for common tasks in `luaV_execute'
*/

#if !defined luai_runtimecheck
#define luai_runtimecheck(L, c) /* void */
#endif

#define RA(i) (base + GETARG_A(i))
/* to be used after possible stack reallocation */
#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base + GETARG_B(i))
#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base + GETARG_C(i))
#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
						 ISK(GETARG_B(i)) ? k + INDEXK(GETARG_B(i)) : base + GETARG_B(i))
#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
						 ISK(GETARG_C(i)) ? k + INDEXK(GETARG_C(i)) : base + GETARG_C(i))
#define KBx(i) \
	(k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++)))

/* execute a jump instruction */
#define dojump(ci, i, e)                                \
	{                                                   \
		int a = GETARG_A(i);                            \
		if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \
		ci->u.l.savedpc += GETARG_sBx(i) + e;           \
	}

/* for test instructions, execute the jump instruction that follows it */
#define donextjump(ci)        \
	{                         \
		i = *ci->u.l.savedpc; \
		dojump(ci, i, 1);     \
	}

#define Protect(x)           \
	{                        \
		{                    \
			x;               \
		};                   \
		base = ci->u.l.base; \
	}

#define checkGC(L, c)                             \
	Protect(luaC_condGC(L, {L->top = (c);  /* limit of live values */ \
                          luaC_step(L); \
                          L->top = ci->top; }) /* restore top */ \
			luai_threadyield(L);)

#define arith_op(op, tm)                                 \
	{                                                    \
		TValue *rb = RKB(i);                             \
		TValue *rc = RKC(i);                             \
		if (ttisnumber(rb) && ttisnumber(rc))            \
		{                                                \
			lua_Number nb = nvalue(rb), nc = nvalue(rc); \
			setnvalue(ra, op(L, nb, nc));                \
		}                                                \
		else                                             \
		{                                                \
			Protect(luaV_arith(L, ra, rb, rc, tm));      \
		}                                                \
	}

#define vmdispatch(o) switch (o)
#define vmcase(l, b) \
	case l:          \
	{                \
		b            \
	}                \
	break;
#define vmcasenb(l, b) \
	case l:            \
	{                  \
		b              \
	} /* nb = no break */

void luaV_execute(lua_State *L)
{
	CallInfo *ci = L->ci;
	LClosure *cl;
	TValue *k;
	StkId base;
newframe: /* reentry point when frame changes (call/return) */
	lua_assert(ci == L->ci);
	cl = clLvalue(ci->func);
	k = cl->p->k;
	base = ci->u.l.base;
	/* main loop of interpreter */
	for (;;)
	{
		Instruction i = *(ci->u.l.savedpc++);
		StkId ra;
		if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
			(--L->hookcount == 0 || L->hookmask & LUA_MASKLINE))
		{
			Protect(traceexec(L));
		}
		/* WARNING: several calls may realloc the stack and invalidate `ra' */
		ra = RA(i);
		lua_assert(base == ci->u.l.base);
		lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
		vmdispatch(GET_OPCODE(i))
		{
			vmcase(OP_MOVE,
				   setobjs2s(L, ra, RB(i));)
				vmcase(OP_LOADK,
					   TValue *rb = k + GETARG_Bx(i);
					   setobj2s(L, ra, rb);)
					vmcase(OP_LOADKX,
						   TValue * rb;
						   lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG);
						   rb = k + GETARG_Ax(*ci->u.l.savedpc++);
						   setobj2s(L, ra, rb);)
						vmcase(OP_LOADBOOL,
							   setbvalue(ra, GETARG_B(i));
							   if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */
							   )
							vmcase(OP_LOADNIL,
								   int b = GETARG_B(i);
								   do {
									   setnilvalue(ra++);
								   } while (b--);) vmcase(OP_GETUPVAL, int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v);) vmcase(OP_GETTABUP, int b = GETARG_B(i); Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra));) vmcase(OP_GETTABLE, Protect(luaV_gettable(L, RB(i), RKC(i), ra));) vmcase(OP_SETTABUP, int a = GETARG_A(i); Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i)));) vmcase(OP_SETUPVAL, UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra);) vmcase(OP_SETTABLE, Protect(luaV_settable(L, ra, RKB(i), RKC(i)));) vmcase(OP_NEWTABLE, int b = GETARG_B(i); int c = GETARG_C(i); Table *t = luaH_new(L); sethvalue(L, ra, t); if (b != 0 || c != 0) luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); checkGC(L, ra + 1);) vmcase(OP_SELF, StkId rb = RB(i); setobjs2s(L, ra + 1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra));) vmcase(OP_ADD, arith_op(luai_numadd, TM_ADD);) vmcase(OP_SUB, arith_op(luai_numsub, TM_SUB);) vmcase(OP_MUL, arith_op(luai_nummul, TM_MUL);) vmcase(OP_DIV, arith_op(luai_numdiv, TM_DIV);) vmcase(OP_MOD, arith_op(luai_nummod, TM_MOD);) vmcase(OP_POW, arith_op(luai_numpow, TM_POW);) vmcase(OP_UNM, TValue *rb = RB(i); if (ttisnumber(rb)) {
          lua_Number nb = nvalue(rb);
          setnvalue(ra, luai_numunm(L, nb)); } else {
								Protect(luaV_arith(L, ra, rb, rb, TM_UNM));
							}) vmcase(OP_NOT, TValue *rb = RB(i); int res = l_isfalse(rb);                                                                                                                         /* next assignment may change this value */
									  setbvalue(ra, res);) vmcase(OP_LEN, Protect(luaV_objlen(L, ra, RB(i)));) vmcase(OP_CONCAT, int b = GETARG_B(i); int c = GETARG_C(i); StkId rb; L->top = base + c + 1;        /* mark the end of concat operands */
																													  Protect(luaV_concat(L, c - b + 1)); ra = RA(i);                                              /* 'luav_concat' may invoke TMs and move the stack */
																													  rb = b + base; setobjs2s(L, ra, rb); checkGC(L, (ra >= rb ? ra + 1 : rb)); L->top = ci->top; /* restore top */
																													  ) vmcase(OP_JMP, dojump(ci, i, 0);) vmcase(OP_EQ, TValue *rb = RKB(i); TValue *rc = RKC(i); Protect(if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci);)) vmcase(OP_LT, Protect(if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci);)) vmcase(OP_LE, Protect(if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci);)) vmcase(OP_TEST, if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra)) ci->u.l.savedpc++; else donextjump(ci);) vmcase(OP_TESTSET, TValue *rb = RB(i); if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb)) ci->u.l.savedpc++; else {
								setobjs2s(L, ra, rb);
								donextjump(ci);
							}) vmcase(OP_CALL, int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra + b; /* else previous instruction set top */
									  if (luaD_precall(L, ra, nresults)) {                                                       /* C function? */
																		   if (nresults >= 0) L->top = ci->top;                  /* adjust results */
																		   base = ci->u.l.base;
									  } else { /* Lua function */
											   ci = L->ci;
											   ci->callstatus |= CIST_REENTRY;
											   goto newframe;                                                                     /* restart luaV_execute over new Lua function */
									  }) vmcase(OP_TAILCALL, int b = GETARG_B(i); if (b != 0) L->top = ra + b;                    /* else previous instruction set top */
												lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */
																							base = ci->u.l.base;
												else {
													/* tail call: put called frame (n) in place of caller one (o) */
													CallInfo *nci = L->ci;         /* called frame */
													CallInfo *oci = nci->previous; /* caller frame */
													StkId nfunc = nci->func;       /* called function */
													StkId ofunc = oci->func;       /* caller function */
													/* last stack slot filled by 'precall' */
													StkId lim = nci->u.l.base + getproto(nfunc)->numparams;
													int aux;
													/* close all upvalues from previous call */
													if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base);
													/* move new frame into old one */
													for (aux = 0; nfunc + aux < lim; aux++)
														setobjs2s(L, ofunc + aux, nfunc + aux);
													oci->u.l.base = ofunc + (nci->u.l.base - nfunc); /* correct base */
													oci->top = L->top = ofunc + (L->top - nfunc);    /* correct top */
													oci->u.l.savedpc = nci->u.l.savedpc;
													oci->callstatus |= CIST_TAIL; /* function was tail called */
													ci = L->ci = oci;             /* remove new frame */
													lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize);
													goto newframe;                                                                                                                                                                     /* restart luaV_execute over new Lua function */
												}) vmcasenb(OP_RETURN, int b = GETARG_B(i); if (b != 0) L->top = ra + b - 1; if (cl->p->sizep > 0) luaF_close(L, base); b = luaD_poscall(L, ra); if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */
															return;                                                                                                                                                                    /* external invocation: return */
															else {                                                                                                                                                                     /* invocation via reentry: continue execution */
																   ci = L->ci;
																   if (b) L->top = ci->top;
																   lua_assert(isLua(ci));
																   lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL);
																   goto newframe;                                                                                      /* restart luaV_execute over new Lua function */
															}) vmcase(OP_FORLOOP, lua_Number step = nvalue(ra + 2); lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */
																	  lua_Number limit = nvalue(ra + 1); if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit) : luai_numle(L, limit, idx)) {
																		  ci->u.l.savedpc += GETARG_sBx(i);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        /* jump back */
																		  setnvalue(ra, idx);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      /* update internal index... */
																		  setnvalue(ra + 3, idx);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  /* ...and external index */
																	  }) vmcase(OP_FORPREP, const TValue *init = ra; const TValue *plimit = ra + 1; const TValue *pstep = ra + 2; if (!tonumber(init, ra)) luaG_runerror(L, LUA_QL("for") " initial value must be a number"); else if (!tonumber(plimit, ra + 1)) luaG_runerror(L, LUA_QL("for") " limit must be a number"); else if (!tonumber(pstep, ra + 2)) luaG_runerror(L, LUA_QL("for") " step must be a number"); setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep))); ci->u.l.savedpc += GETARG_sBx(i);) vmcasenb(OP_TFORCALL, StkId cb = ra + 3;                                                                    /* call base */
																																																																																																																																												setobjs2s(L, cb + 2, ra + 2); setobjs2s(L, cb + 1, ra + 1); setobjs2s(L, cb, ra); L->top = cb + 3; /* func. + 2 args (state and index) */
																																																																																																																																												Protect(luaD_call(L, cb, GETARG_C(i), 1)); L->top = ci->top; i = *(ci->u.l.savedpc++);             /* go to next instruction */
																																																																																																																																												ra = RA(i); lua_assert(GET_OPCODE(i) == OP_TFORLOOP); goto l_tforloop;) vmcase(OP_TFORLOOP, l_tforloop
																																																																																																																																																															   : if (!ttisnil(ra + 1)) {                                                                                                                                                                                                                                               /* continue loop? */
																																																																																																																																																																						 setobjs2s(L, ra, ra + 1);                                                                                                                                                                                                                     /* save control variable */
																																																																																																																																																																						 ci->u.l.savedpc += GETARG_sBx(i);                                                                                                                                                                                                             /* jump back */
																																																																																																																																																															   }) vmcase(OP_SETLIST, int n = GETARG_B(i); int c = GETARG_C(i); int last; Table * h; if (n == 0) n = cast_int(L->top - ra) - 1; if (c == 0) {
          lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG);
          c = GETARG_Ax(*ci->u.l.savedpc++); } luai_runtimecheck(L, ttistable(ra)); h = hvalue(ra); last = ((c - 1) * LFIELDS_PER_FLUSH) + n; if (last > h->sizearray) /* needs more space? */
																																																																																																																																																																		 luaH_resizearray(L, h, last);                                                                                                                                                                                                                                 /* pre-allocate it at once */
																																																																																																																																																																		 for (; n > 0; n--) {
																																																																																																																																																																			 TValue *val = ra + n;
																																																																																																																																																																			 luaH_setint(L, h, last--, val);
																																																																																																																																																																			 luaC_barrierback(L, obj2gco(h), val);
																																																																																																																																																																		 } L->top = ci->top;                                                                                                                                           /* correct top (in case of previous open call) */
																																																																																																																																																																		 ) vmcase(OP_CLOSURE, Proto *p = cl->p->p[GETARG_Bx(i)]; Closure *ncl = getcached(p, cl->upvals, base);                                                        /* cached closure */
																																																																																																																																																																				  if (ncl == NULL)                                                                                                                                     /* no match? */
																																																																																																																																																																				  pushclosure(L, p, cl->upvals, base, ra);                                                                                                             /* create a new one */
																																																																																																																																																																				  else setclLvalue(L, ra, ncl);                                                                                                                        /* push cashed closure */
																																																																																																																																																																				  checkGC(L, ra + 1);) vmcase(OP_VARARG, int b = GETARG_B(i) - 1; int j; int n = cast_int(base - ci->func) - cl->p->numparams - 1; if (b < 0) {        /* B == 0? */
																																																																																																																																																																																																								b = n; /* get all var. arguments */
																																																																																																																																																																																																								Protect(luaD_checkstack(L, n));
																																																																																																																																																																																																								ra = RA(i); /* previous call may change the stack */
																																																																																																																																																																																																								L->top = ra + n;
							} for (j = 0; j < b; j++) {
								if (j < n)
								{
									setobjs2s(L, ra + j, base - n + j);
								}
								else
								{
									setnilvalue(ra + j);
								}
							}) vmcase(OP_EXTRAARG, lua_assert(0);)
		}
	}
}
