From 55b16959717084884fd4a0cbae6d19e3786c20c7 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 8 Dec 2009 19:46:35 +0100 Subject: RELEASE LuaJIT-2.0.0-beta1 --- src/lj_parse.c | 2198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2198 insertions(+) create mode 100644 src/lj_parse.c (limited to 'src/lj_parse.c') diff --git a/src/lj_parse.c b/src/lj_parse.c new file mode 100644 index 00000000..663525ab --- /dev/null +++ b/src/lj_parse.c @@ -0,0 +1,2198 @@ +/* +** Lua parser (source code -> bytecode). +** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h +** +** Major portions taken verbatim or adapted from the Lua interpreter. +** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h +*/ + +#define lj_parse_c +#define LUA_CORE + +#include "lj_obj.h" +#include "lj_gc.h" +#include "lj_err.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_func.h" +#include "lj_state.h" +#include "lj_bc.h" +#include "lj_lex.h" +#include "lj_parse.h" +#include "lj_vm.h" +#include "lj_vmevent.h" + +/* -- Parser structures and definitions ----------------------------------- */ + +/* Expression kinds. */ +typedef enum { + /* Constant expressions must be first and in this order: */ + VKNIL, + VKFALSE, + VKTRUE, + VKSTR, /* sval = string value */ + VKNUM, /* nval = numerical value */ + VKLAST = VKNUM, + /* Non-constant expressions follow: */ + VLOCAL, /* info = local register */ + VUPVAL, /* info = upvalue index */ + VGLOBAL, /* sval = string value */ + VINDEXED, /* info = table register, aux = index reg/byte/string const */ + VJMP, /* info = instruction PC */ + VRELOCABLE, /* info = instruction PC */ + VNONRELOC, /* info = result register */ + VCALL, /* info = instruction PC, aux = base */ + VVOID +} ExpKind; + +/* Expression descriptor. */ +typedef struct ExpDesc { + union { + struct { uint32_t info, aux; } s; + TValue nval; + GCstr *sval; + } u; + ExpKind k; + BCPos t; /* true condition exit list */ + BCPos f; /* false condition exit list */ +} ExpDesc; + +/* Tests for expression types */ +#define isK(e) ((uint32_t)((e)->k) <= VKLAST) +#define isnumK(e) ((e)->k == VKNUM) +#define isstrK(e) ((e)->k == VKSTR) +#define expnumV(e) check_exp(isnumK((e)), numV(&(e)->u.nval)) + +#define hasjumps(e) ((e)->t != (e)->f) +#define isKexp(e) (isK(e) && !hasjumps(e)) +#define isnumKexp(e) (isnumK(e) && !hasjumps(e)) + +#define priKk(k) check_exp((k) <= VKTRUE, (k) - VKNIL) +#define priK(e) priKk((e)->k) + +/* Per-function linked list of blocks. */ +typedef struct FuncBlock { + struct FuncBlock *previous; /* chain */ + BCPos breaklist; /* list of jumps out of this loop */ + uint8_t nactvar; /* # active locals outside the breakable structure */ + uint8_t upval; /* true if some variable in the block is an upvalue */ + uint8_t isbreakable; /* true if `block' is a loop */ +} FuncBlock; + +typedef struct UpValDesc { + uint8_t k; + uint8_t info; +} UpValDesc; + +/* Per-function state. */ +typedef struct FuncState { + GCproto *pt; /* current function header */ + GCtab *kt; /* table to find (and reuse) elements in `k' */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct lua_State *L; /* copy of the Lua state */ + struct FuncBlock *bl; /* chain of current blocks */ + BCPos pc; /* next bytecode position */ + BCPos lasttarget; /* PC of last jump target */ + BCPos jpc; /* list of pending jumps to PC */ + BCReg freereg; /* first free register */ + BCReg nkn, nkgc; /* number of lua_Number/GCobj constants */ + uint16_t nlocvars; /* number of elements in `locvars' */ + uint8_t nactvar; /* number of active local variables */ + uint8_t nuv; /* number of upvalues */ + UpValDesc upvalues[LJ_MAX_UPVAL]; /* upvalues */ + uint16_t actvar[LJ_MAX_LOCVAR]; /* declared-variable stack */ +} FuncState; + +/* Binary and unary operators. ORDER OPR */ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, /* ORDER ARITH */ + OPR_CONCAT, + OPR_NE, OPR_EQ, + OPR_LT, OPR_GE, OPR_LE, OPR_GT, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + +LJ_STATIC_ASSERT((int)BC_ISGE-(int)BC_ISLT == (int)OPR_GE-(int)OPR_LT); +LJ_STATIC_ASSERT((int)BC_ISLE-(int)BC_ISLT == (int)OPR_LE-(int)OPR_LT); +LJ_STATIC_ASSERT((int)BC_ISGT-(int)BC_ISLT == (int)OPR_GT-(int)OPR_LT); +LJ_STATIC_ASSERT((int)BC_SUBVV-(int)BC_ADDVV == (int)OPR_SUB-(int)OPR_ADD); +LJ_STATIC_ASSERT((int)BC_MULVV-(int)BC_ADDVV == (int)OPR_MUL-(int)OPR_ADD); +LJ_STATIC_ASSERT((int)BC_DIVVV-(int)BC_ADDVV == (int)OPR_DIV-(int)OPR_ADD); +LJ_STATIC_ASSERT((int)BC_MODVV-(int)BC_ADDVV == (int)OPR_MOD-(int)OPR_ADD); + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + +/* -- Error handling ------------------------------------------------------ */ + +LJ_NORET LJ_NOINLINE static void err_syntax(LexState *ls, ErrMsg em) +{ + lj_lex_error(ls, ls->token, em); +} + +LJ_NORET LJ_NOINLINE static void err_token(LexState *ls, LexToken token) +{ + lj_lex_error(ls, ls->token, LJ_ERR_XTOKEN, lj_lex_token2str(ls, token)); +} + +LJ_NORET static void err_limit(FuncState *fs, uint32_t limit, const char *what) +{ + if (fs->pt->linedefined == 0) + lj_lex_error(fs->ls, 0, LJ_ERR_XLIMM, limit, what); + else + lj_lex_error(fs->ls, 0, LJ_ERR_XLIMF, fs->pt->linedefined, limit, what); +} + +#define checklimit(fs, v, l, m) if ((v) >= (l)) err_limit(fs, l, m) +#define checklimitgt(fs, v, l, m) if ((v) > (l)) err_limit(fs, l, m) +#define checkcond(ls, c, em) { if (!(c)) err_syntax(ls, em); } + +/* -- Code emitter: branches ---------------------------------------------- */ + +static BCPos getjump(FuncState *fs, BCPos pc) +{ + ptrdiff_t delta = bc_j(fs->pt->bc[pc]); + if ((BCPos)delta == NO_JMP) + return NO_JMP; + else + return (BCPos)(((ptrdiff_t)pc+1)+delta); +} + +static int need_value(FuncState *fs, BCPos list) +{ + for (; list != NO_JMP; list = getjump(fs, list)) { + BCOp op = bc_op(fs->pt->bc[list >= 1 ? list-1 : list]); + if (!(op == BC_ISTC || op == BC_ISFC)) return 1; + } + return 0; /* Not found. */ +} + +static int patchtestreg(FuncState *fs, BCPos pc, BCReg reg) +{ + BCIns *i = &fs->pt->bc[pc >= 1 ? pc-1 : pc]; + BCOp op = bc_op(*i); + if (!(op == BC_ISTC || op == BC_ISFC)) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != bc_d(*i)) { + setbc_a(i, reg); + } else { /* no register to put value or register already has the value */ + setbc_op(i, op+(BC_IST-BC_ISTC)); + setbc_a(i, 0); + } + return 1; +} + +static void removevalues(FuncState *fs, BCPos list) +{ + for (; list != NO_JMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + +static void fixjump(FuncState *fs, BCPos pc, BCPos dest) +{ + BCIns *jmp = &fs->pt->bc[pc]; + BCPos offset = dest-(pc+1)+BCBIAS_J; + lua_assert(dest != NO_JMP); + if (offset > BCMAX_D) + err_syntax(fs->ls, LJ_ERR_XJUMP); + setbc_d(jmp, offset); +} + +static void concatjumps(FuncState *fs, BCPos *l1, BCPos l2) +{ + if (l2 == NO_JMP) return; + else if (*l1 == NO_JMP) { + *l1 = l2; + } else { + BCPos list = *l1; + BCPos next; + while ((next = getjump(fs, list)) != NO_JMP) /* find last element */ + list = next; + fixjump(fs, list, l2); + } +} + +static void patchlistaux(FuncState *fs, BCPos list, BCPos vtarget, + BCReg reg, BCPos dtarget) +{ + while (list != NO_JMP) { + BCPos next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + +static void patchtohere(FuncState *fs, BCPos list) +{ + fs->lasttarget = fs->pc; + concatjumps(fs, &fs->jpc, list); +} + +static void patchlist(FuncState *fs, BCPos list, BCPos target) +{ + if (target == fs->pc) { + patchtohere(fs, list); + } else { + lua_assert(target < fs->pc); + patchlistaux(fs, list, target, NO_REG, target); + } +} + +/* -- Code emitter: instructions ------------------------------------------ */ + +static BCPos emitINS(FuncState *fs, BCIns i) +{ + GCproto *pt; + patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); + fs->jpc = NO_JMP; + pt = fs->pt; + if (LJ_UNLIKELY(fs->pc >= pt->sizebc)) { + checklimit(fs, fs->pc, LJ_MAX_BCINS, "bytecode instructions"); + lj_mem_growvec(fs->L, pt->bc, pt->sizebc, LJ_MAX_BCINS, BCIns); + lj_mem_growvec(fs->L, pt->lineinfo, pt->sizelineinfo, LJ_MAX_BCINS, BCLine); + } + pt->bc[fs->pc] = i; + pt->lineinfo[fs->pc] = fs->ls->lastline; + return fs->pc++; +} + +#define emitABC(fs, o, a, b, c) emitINS(fs, BCINS_ABC(o, a, b, c)) +#define emitAD(fs, o, a, d) emitINS(fs, BCINS_AD(o, a, d)) +#define emitAJ(fs, o, a, j) emitINS(fs, BCINS_AJ(o, a, j)) + +#define bcptr(fs, e) (&(fs)->pt->bc[(e)->u.s.info]) + +static BCPos emit_jump(FuncState *fs) +{ + BCPos jpc = fs->jpc; /* save list of jumps to here */ + BCPos j = fs->pc - 1; + fs->jpc = NO_JMP; + if ((int32_t)j >= (int32_t)fs->lasttarget && bc_op(fs->pt->bc[j]) == BC_UCLO) + setbc_j(&fs->pt->bc[j], NO_JMP); + else + j = emitAJ(fs, BC_JMP, fs->freereg, NO_JMP); + concatjumps(fs, &j, jpc); /* keep them on hold */ + return j; +} + +/* -- Code emitter: constants --------------------------------------------- */ + +static BCReg numK(FuncState *fs, ExpDesc *e) +{ + lua_State *L = fs->L; + TValue *val; + lua_assert(isnumK(e)); + val = lj_tab_set(L, fs->kt, &e->u.nval); + if (tvisnum(val)) + return val->u32.lo; + val->u64 = fs->nkn; + return fs->nkn++; +} + +static BCReg gcK(FuncState *fs, GCobj *gc, int itype) +{ + lua_State *L = fs->L; + TValue o, *val; + setgcV(L, &o, &gc->gch, itype); + val = lj_tab_set(L, fs->kt, &o); + if (tvisnum(val)) + return val->u32.lo; + val->u64 = fs->nkgc; + return fs->nkgc++; +} + +static BCReg strK(FuncState *fs, ExpDesc *e) +{ + lua_assert(isstrK(e) || e->k == VGLOBAL); + return gcK(fs, obj2gco(e->u.sval), LJ_TSTR); +} + +GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t len) +{ + lua_State *L = ls->L; + GCstr *s = lj_str_new(L, str, len); + TValue *tv = lj_tab_setstr(L, ls->fs->kt, s); + if (tvisnil(tv)) setboolV(tv, 1); /* Anchor string to avoid GC. */ + return s; +} + +static void keep_token(LexState *ls) +{ + if (ls->token == TK_name || ls->token == TK_string) { + TValue *tv = lj_tab_setstr(ls->L, ls->fs->kt, strV(&ls->tokenval)); + if (tvisnil(tv)) setboolV(tv, 1); /* Anchor string to avoid GC. */ + } +} + +static void nilK(FuncState *fs, BCReg from, BCReg n) +{ + BCIns *pr; + if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ + BCReg pfrom, pto; + pr = &fs->pt->bc[fs->pc-1]; + pfrom = bc_a(*pr); + switch (bc_op(*pr)) { + case BC_KPRI: + if (bc_d(*pr) != ~LJ_TNIL) break; + if (from == pfrom) { + if (n == 1) return; + } else if (from == pfrom+1) { + from = pfrom; + n++; + } else { + break; + } + fs->pc--; + break; + case BC_KNIL: + pto = bc_d(*pr); + if (pfrom <= from && from <= pto+1) { /* can connect both? */ + if (from+n-1 > pto) + setbc_d(pr, from+n-1); + return; + } + break; + default: + break; + } + } + emitINS(fs, n == 1 ? BCINS_AD(BC_KPRI, from, priKk(VKNIL)) + : BCINS_AD(BC_KNIL, from, from+n-1)); +} + +/* -- Code emitter: registers --------------------------------------------- */ + +static void checkframe(FuncState *fs, BCReg n) +{ + BCReg sz = fs->freereg + n; + if (sz > fs->pt->framesize) { + if (sz >= LJ_MAX_SLOTS) + err_syntax(fs->ls, LJ_ERR_XSLOTS); + fs->pt->framesize = cast_byte(sz); + } +} + +static void reserveregs(FuncState *fs, BCReg n) +{ + checkframe(fs, n); + fs->freereg += n; +} + +static void freereg(FuncState *fs, BCReg reg) +{ + if (reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + +static void freeexp(FuncState *fs, ExpDesc *e) +{ + if (e->k == VNONRELOC) + freereg(fs, e->u.s.info); +} + +/* -- Code emitter: expressions ------------------------------------------- */ + +static void dischargevars(FuncState *fs, ExpDesc *e) +{ + BCIns ins; + switch (e->k) { + case VUPVAL: + ins = BCINS_AD(BC_UGET, 0, e->u.s.info); + break; + case VGLOBAL: + ins = BCINS_AD(BC_GGET, 0, strK(fs, e)); + break; + case VINDEXED: { + /* TGET[VSB] key = reg, string const or byte const */ + BCReg rc = e->u.s.aux; + if ((int32_t)rc < 0) { + ins = BCINS_ABC(BC_TGETS, 0, e->u.s.info, ~rc); + } else if (rc > BCMAX_C) { + ins = BCINS_ABC(BC_TGETB, 0, e->u.s.info, rc-(BCMAX_C+1)); + } else { + freereg(fs, rc); + ins = BCINS_ABC(BC_TGETV, 0, e->u.s.info, rc); + } + freereg(fs, e->u.s.info); + break; + } + case VCALL: + e->u.s.info = e->u.s.aux; + /* fallthrough */ + case VLOCAL: + e->k = VNONRELOC; + /* fallthrough */ + default: + return; + } + e->u.s.info = emitINS(fs, ins); + e->k = VRELOCABLE; +} + +static void discharge2reg(FuncState *fs, ExpDesc *e, BCReg reg) +{ + BCIns ins; + dischargevars(fs, e); + switch (e->k) { + case VKNIL: case VKFALSE: case VKTRUE: + ins = BCINS_AD(BC_KPRI, reg, priK(e)); + break; + case VKSTR: + ins = BCINS_AD(BC_KSTR, reg, strK(fs, e)); + break; + case VKNUM: { + lua_Number n = expnumV(e); + int32_t k = lj_num2int(n); + if (checki16(k) && n == cast_num(k)) + ins = BCINS_AD(BC_KSHORT, reg, (BCReg)(uint16_t)k); + else + ins = BCINS_AD(BC_KNUM, reg, numK(fs, e)); + break; + } + case VRELOCABLE: + setbc_a(bcptr(fs, e), reg); + goto noins; + case VNONRELOC: + if (reg == e->u.s.info) + goto noins; + ins = BCINS_AD(BC_MOV, reg, e->u.s.info); + break; + default: + lua_assert(e->k == VVOID || e->k == VJMP); + return; /* nothing to do... */ + } + emitINS(fs, ins); +noins: + e->u.s.info = reg; + e->k = VNONRELOC; +} + +static void exp2reg(FuncState *fs, ExpDesc *e, BCReg reg) +{ + discharge2reg(fs, e, reg); + if (e->k == VJMP) + concatjumps(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ + if (hasjumps(e)) { + BCPos final; /* position after whole expression */ + BCPos p_f = NO_JMP; /* position of an eventual LOAD false */ + BCPos p_t = NO_JMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + BCPos fj = (e->k == VJMP) ? NO_JMP : emit_jump(fs); + p_f = emitAD(fs, BC_KPRI, reg, priKk(VKFALSE)); + emitAJ(fs, BC_JMP, fs->freereg, 1); + p_t = emitAD(fs, BC_KPRI, reg, priKk(VKTRUE)); + patchtohere(fs, fj); + } + final = fs->pc; + fs->lasttarget = final; + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JMP; + e->u.s.info = reg; + e->k = VNONRELOC; +} + +static void exp2nextreg(FuncState *fs, ExpDesc *e) +{ + dischargevars(fs, e); + freeexp(fs, e); + reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + +static BCReg exp2anyreg(FuncState *fs, ExpDesc *e) +{ + dischargevars(fs, e); + if (e->k == VNONRELOC) { + if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ + if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.s.info); /* put value on it */ + return e->u.s.info; + } + } + exp2nextreg(fs, e); /* default */ + return e->u.s.info; +} + +static void exp2val(FuncState *fs, ExpDesc *e) +{ + if (hasjumps(e)) + exp2anyreg(fs, e); + else + dischargevars(fs, e); +} + +static void storevar(FuncState *fs, ExpDesc *var, ExpDesc *e) +{ + BCIns ins; + switch (var->k) { + case VLOCAL: + freeexp(fs, e); + exp2reg(fs, e, var->u.s.info); + return; + case VUPVAL: + exp2val(fs, e); + switch (e->k) { + case VKNIL: case VKFALSE: case VKTRUE: + ins = BCINS_AD(BC_USETP, var->u.s.info, priK(e)); + break; + case VKSTR: + ins = BCINS_AD(BC_USETS, var->u.s.info, strK(fs, e)); + break; + case VKNUM: + ins = BCINS_AD(BC_USETN, var->u.s.info, numK(fs, e)); + break; + default: + ins = BCINS_AD(BC_USETV, var->u.s.info, exp2anyreg(fs, e)); + break; + } + break; + case VGLOBAL: { + BCReg ra = exp2anyreg(fs, e); + ins = BCINS_AD(BC_GSET, ra, strK(fs, var)); + break; + } + case VINDEXED: { + /* TSET[VSB] key = reg, string const or byte const */ + BCReg ra = exp2anyreg(fs, e); + BCReg rc = var->u.s.aux; + if ((int32_t)rc < 0) { + ins = BCINS_ABC(BC_TSETS, ra, var->u.s.info, ~rc); + } else if (rc > BCMAX_C) { + ins = BCINS_ABC(BC_TSETB, ra, var->u.s.info, rc-(BCMAX_C+1)); + } else { + /* Free late alloced key reg to avoid assert on free of value reg. */ + /* This can only happen when called from constructor(). */ + lua_assert(e->k != VNONRELOC || ra < fs->nactvar || + rc < ra || (freereg(fs, rc),1)); + ins = BCINS_ABC(BC_TSETV, ra, var->u.s.info, rc); + } + break; + } + default: + lua_assert(0); /* invalid var kind to store */ + return; + } + emitINS(fs, ins); + freeexp(fs, e); +} + +static void indexexp(FuncState *fs, ExpDesc *t, ExpDesc *e) +{ + /* already called: exp2val(fs, e) */ + t->k = VINDEXED; + if (isnumK(e)) { + lua_Number n = expnumV(e); + int32_t k = lj_num2int(n); + if (checku8(k) && n == cast_num(k)) { + t->u.s.aux = BCMAX_C+1+(uint32_t)k; /* 256..511: const byte key */ + return; + } + } else if (isstrK(e)) { + BCReg idx = strK(fs, e); + if (idx <= BCMAX_C) { + t->u.s.aux = ~idx; /* -256..-1: const string key */ + return; + } + } + t->u.s.aux = exp2anyreg(fs, e); /* 0..255: register */ +} + +static void methodexp(FuncState *fs, ExpDesc *e, ExpDesc *key) +{ + BCReg idx, func, tab = exp2anyreg(fs, e); + freeexp(fs, e); + func = fs->freereg; + emitAD(fs, BC_MOV, func+1, tab); + lua_assert(isstrK(key)); + idx = strK(fs, key); + if (idx <= BCMAX_C) { + reserveregs(fs, 2); + emitABC(fs, BC_TGETS, func, tab, idx); + } else { + reserveregs(fs, 3); + emitAD(fs, BC_KSTR, func+2, idx); + emitABC(fs, BC_TGETV, func, tab, func+2); + fs->freereg--; + } + e->u.s.info = func; + e->k = VNONRELOC; +} + +/* -- Code emitter: conditionals ------------------------------------------ */ + +static void invertjump(FuncState *fs, ExpDesc *e) +{ + BCIns *i = bcptr(fs, e) - 1; + setbc_op(i, bc_op(*i)^1); +} + +static BCPos jumponcond(FuncState *fs, ExpDesc *e, int cond) +{ + if (e->k == VRELOCABLE) { + BCIns *i = bcptr(fs, e); + if (bc_op(*i) == BC_NOT) { + *i = BCINS_AD(cond ? BC_ISF : BC_IST, 0, bc_d(*i)); + return emit_jump(fs); + } + /* else go through */ + } + if (e->k != VNONRELOC) { + reserveregs(fs, 1); + discharge2reg(fs, e, fs->freereg-1); + } + freeexp(fs, e); + emitAD(fs, cond ? BC_ISTC : BC_ISFC, NO_REG, e->u.s.info); + return emit_jump(fs); +} + +static void goiftrue(FuncState *fs, ExpDesc *e) +{ + BCPos pc; /* PC of last jump. */ + dischargevars(fs, e); + switch (e->k) { + case VKSTR: case VKNUM: case VKTRUE: + pc = NO_JMP; /* always true; do nothing */ + break; + case VJMP: + invertjump(fs, e); + pc = e->u.s.info; + break; + case VKFALSE: + if (!hasjumps(e)) { + pc = emit_jump(fs); /* always jump */ + break; + } + /* fallthrough */ + default: + pc = jumponcond(fs, e, 0); + break; + } + concatjumps(fs, &e->f, pc); /* insert last jump in `f' list */ + patchtohere(fs, e->t); + e->t = NO_JMP; +} + +static void goiffalse(FuncState *fs, ExpDesc *e) +{ + BCPos pc; /* PC of last jump. */ + dischargevars(fs, e); + switch (e->k) { + case VKNIL: case VKFALSE: + pc = NO_JMP; /* always false; do nothing */ + break; + case VJMP: + pc = e->u.s.info; + break; + case VKTRUE: + if (!hasjumps(e)) { + pc = emit_jump(fs); /* always jump */ + break; + } + /* fallthrough */ + default: + pc = jumponcond(fs, e, 1); + break; + } + concatjumps(fs, &e->t, pc); /* insert last jump in `t' list */ + patchtohere(fs, e->f); + e->f = NO_JMP; +} + +/* -- Code emitter: operators --------------------------------------------- */ + +static int foldarith(BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + TValue o; + if (!isnumKexp(e1) || !isnumKexp(e2)) return 0; + setnumV(&o, lj_vm_foldarith(expnumV(e1), expnumV(e2), (int)opr-OPR_ADD)); + if (tvisnan(&o) || tvismzero(&o)) return 0; /* Avoid NaN and -0 as consts. */ + setnumV(&e1->u.nval, numV(&o)); + return 1; +} + +static void codearith(FuncState *fs, BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + BCReg rb, rc, t; + uint32_t op; + if (foldarith(opr, e1, e2)) + return; + if (opr == OPR_POW) { + op = BC_POW; + rc = exp2anyreg(fs, e2); + rb = exp2anyreg(fs, e1); + } else { + op = opr-OPR_ADD+BC_ADDVV; + /* must discharge 2nd operand first since VINDEXED might free regs */ + exp2val(fs, e2); + if (isnumK(e2) && (rc = numK(fs, e2)) <= BCMAX_C) + op -= BC_ADDVV-BC_ADDVN; + else + rc = exp2anyreg(fs, e2); + /* emit_prebinop discharges 1st operand, but may need to use KNUM/KSHORT */ + lua_assert(isnumK(e1) || e1->k == VNONRELOC); + exp2val(fs, e1); + /* avoid two consts to satisfy bytecode constraints */ + if (isnumK(e1) && !isnumK(e2) && (t = numK(fs, e1)) <= BCMAX_B) { + rb = rc; rc = t; op -= BC_ADDVV-BC_ADDNV; + } else { + rb = exp2anyreg(fs, e1); + } + } + /* using freeexp might cause asserts if the order is wrong */ + if (e1->k == VNONRELOC && e1->u.s.info >= fs->nactvar) fs->freereg--; + if (e2->k == VNONRELOC && e2->u.s.info >= fs->nactvar) fs->freereg--; + e1->u.s.info = emitABC(fs, op, 0, rb, rc); + e1->k = VRELOCABLE; +} + +static void codecomp(FuncState *fs, BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + ExpDesc *eret = e1; + BCIns ins; + exp2val(fs, e1); + if (opr == OPR_EQ || opr == OPR_NE) { + BCOp op = opr == OPR_EQ ? BC_ISEQV : BC_ISNEV; + BCReg ra; + if (isK(e1)) { e1 = e2; e2 = eret; } /* need constant in 2nd arg */ + ra = exp2anyreg(fs, e1); /* first arg must be in a reg */ + exp2val(fs, e2); + switch (e2->k) { + case VKNIL: case VKFALSE: case VKTRUE: + ins = BCINS_AD(op+(BC_ISEQP-BC_ISEQV), ra, priK(e2)); + break; + case VKSTR: + ins = BCINS_AD(op+(BC_ISEQS-BC_ISEQV), ra, strK(fs, e2)); + break; + case VKNUM: + ins = BCINS_AD(op+(BC_ISEQN-BC_ISEQV), ra, numK(fs, e2)); + break; + default: + ins = BCINS_AD(op, ra, exp2anyreg(fs, e2)); + break; + } + } else { + uint32_t op = opr-OPR_LT+BC_ISLT; + BCReg ra; + if ((op-BC_ISLT) & 1) { /* GT -> LT, GE -> LE */ + e1 = e2; e2 = eret; /* swap operands */ + op = ((op-BC_ISLT)^3)+BC_ISLT; + } + ra = exp2anyreg(fs, e1); + ins = BCINS_AD(op, ra, exp2anyreg(fs, e2)); + } + /* using freeexp might cause asserts if the order is wrong */ + if (e1->k == VNONRELOC && e1->u.s.info >= fs->nactvar) fs->freereg--; + if (e2->k == VNONRELOC && e2->u.s.info >= fs->nactvar) fs->freereg--; + emitINS(fs, ins); + eret->u.s.info = emit_jump(fs); + eret->k = VJMP; +} + +static void emit_unop(FuncState *fs, UnOpr uop, ExpDesc *e) +{ + BCOp op = BC_LEN; + switch (uop) { + case OPR_MINUS: + if (isnumKexp(e) && expnumV(e) != 0) { /* Avoid const-folding to -0. */ + setnumV(&e->u.nval, -expnumV(e)); + return; + } + op = BC_UNM; + /* fallthrough */ + case OPR_LEN: + exp2anyreg(fs, e); + break; + case OPR_NOT: + /* interchange true and false lists */ + { BCPos temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); + removevalues(fs, e->t); + dischargevars(fs, e); + switch (e->k) { + case VKNIL: case VKFALSE: + e->k = VKTRUE; + return; + case VKSTR: case VKNUM: case VKTRUE: + e->k = VKFALSE; + return; + case VJMP: + invertjump(fs, e); + return; + case VRELOCABLE: + reserveregs(fs, 1); + setbc_a(bcptr(fs, e), fs->freereg-1); + e->u.s.info = fs->freereg-1; + e->k = VNONRELOC; + break; + case VNONRELOC: + break; + default: lua_assert(0); return; + } + op = BC_NOT; + break; + default: lua_assert(0); return; + } + freeexp(fs, e); + e->u.s.info = emitAD(fs, op, 0, e->u.s.info); + e->k = VRELOCABLE; +} + +static void prepare_binop(FuncState *fs, BinOpr op, ExpDesc *e) +{ + switch (op) { + case OPR_AND: + goiftrue(fs, e); + break; + case OPR_OR: + goiffalse(fs, e); + break; + case OPR_CONCAT: + exp2nextreg(fs, e); /* operand must be on the `stack' */ + break; + case OPR_EQ: case OPR_NE: + if (!isKexp(e)) exp2anyreg(fs, e); + break; + default: + if (!isnumKexp(e)) exp2anyreg(fs, e); + break; + } +} + +static void emit_binop(FuncState *fs, BinOpr op, ExpDesc *e1, ExpDesc *e2) +{ + switch (op) { + case OPR_AND: + lua_assert(e1->t == NO_JMP); /* list must be closed */ + dischargevars(fs, e2); + concatjumps(fs, &e2->f, e1->f); + *e1 = *e2; + break; + case OPR_OR: + lua_assert(e1->f == NO_JMP); /* list must be closed */ + dischargevars(fs, e2); + concatjumps(fs, &e2->t, e1->t); + *e1 = *e2; + break; + case OPR_CONCAT: + exp2val(fs, e2); + if (e2->k == VRELOCABLE && bc_op(*bcptr(fs, e2)) == BC_CAT) { + lua_assert(e1->u.s.info == bc_b(*bcptr(fs, e2))-1); + freeexp(fs, e1); + setbc_b(bcptr(fs, e2), e1->u.s.info); + e1->u.s.info = e2->u.s.info; + } else { + exp2nextreg(fs, e2); + freeexp(fs, e2); + freeexp(fs, e1); + e1->u.s.info = emitABC(fs, BC_CAT, 0, e1->u.s.info, e2->u.s.info); + } + e1->k = VRELOCABLE; + break; + case OPR_ADD: case OPR_SUB: case OPR_MUL: + case OPR_DIV: case OPR_MOD: case OPR_POW: + codearith(fs, op, e1, e2); + break; + case OPR_EQ: case OPR_NE: + case OPR_LT: case OPR_LE: case OPR_GT: case OPR_GE: + codecomp(fs, op, e1, e2); + break; + default: lua_assert(0); break; + } +} + +/* -- Lexer support ------------------------------------------------------- */ + +static int testnext(LexState *ls, LexToken tok) +{ + if (ls->token == tok) { + lj_lex_next(ls); + return 1; + } + return 0; +} + +static void checknext(LexState *ls, LexToken tok) +{ + if (ls->token != tok) + err_token(ls, tok); + lj_lex_next(ls); +} + +static void checkmatch(LexState *ls, LexToken what, LexToken who, BCLine line) +{ + if (!testnext(ls, what)) { + if (line == ls->linenumber) { + err_token(ls, what); + } else { + const char *swhat = lj_lex_token2str(ls, what); + const char *swho = lj_lex_token2str(ls, who); + lj_lex_error(ls, ls->token, LJ_ERR_XMATCH, swhat, swho, line); + } + } +} + +static GCstr *str_checkname(LexState *ls) +{ + GCstr *s; + if (ls->token != TK_name) + err_token(ls, TK_name); + s = strV(&ls->tokenval); + lj_lex_next(ls); + return s; +} + +static void init_exp(ExpDesc *e, ExpKind k, uint32_t info) +{ + e->k = k; + e->u.s.info = info; + e->f = e->t = NO_JMP; +} + +static void checkname(LexState *ls, ExpDesc *e) +{ + init_exp(e, VKSTR, 0); + e->u.sval = str_checkname(ls); +} + +/* -- Variable handling --------------------------------------------------- */ + +#define getlocvar(fs, i) ((fs)->pt->varinfo[(fs)->actvar[(i)]]) + +static BCReg registerlocalvar(LexState *ls, GCstr *name) +{ + FuncState *fs = ls->fs; + GCproto *pt = fs->pt; + if (LJ_UNLIKELY(fs->nlocvars >= pt->sizevarinfo)) { + MSize oldsize = pt->sizevarinfo; + checklimit(fs, fs->nlocvars, 32767, "local variables"); + lj_mem_growvec(fs->L, pt->varinfo, pt->sizevarinfo, 32767, VarInfo); + while (oldsize < pt->sizevarinfo) pt->varinfo[oldsize++].name = NULL; + } + pt->varinfo[fs->nlocvars].name = name; + lj_gc_objbarrier(ls->L, pt, name); + return fs->nlocvars++; +} + +static void new_localvar(LexState *ls, GCstr *name, BCReg n) +{ + FuncState *fs = ls->fs; + checklimit(fs, fs->nactvar+n, LJ_MAX_LOCVAR, "local variables"); + fs->actvar[fs->nactvar+n] = cast(uint16_t, registerlocalvar(ls, name)); +} + +#define new_localvarliteral(ls,v,n) \ + new_localvar(ls, lj_parse_keepstr(ls, "" v, sizeof(v)-1), n) + +static void adjustlocalvars(LexState *ls, BCReg nvars) +{ + FuncState *fs = ls->fs; + fs->nactvar = cast_byte(fs->nactvar + nvars); + for (; nvars; nvars--) + getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; +} + +static void removevars(LexState *ls, BCReg tolevel) +{ + FuncState *fs = ls->fs; + while (fs->nactvar > tolevel) + getlocvar(fs, --fs->nactvar).endpc = fs->pc; +} + +static uint32_t indexupvalue(FuncState *fs, GCstr *name, ExpDesc *v) +{ + uint32_t i; + GCproto *pt = fs->pt; + for (i = 0; i < fs->nuv; i++) { + if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { + lua_assert(pt->uvname[i] == name); + return i; + } + } + /* Not found, create a new upvalue for this name. */ + if (LJ_UNLIKELY(fs->nuv >= pt->sizeuvname)) { + MSize oldsize = pt->sizeuvname; + checklimit(fs, fs->nuv, LJ_MAX_UPVAL, "upvalues"); + lj_mem_growvec(fs->L, pt->uvname, pt->sizeuvname, LJ_MAX_UPVAL, GCstr *); + while (oldsize < pt->sizeuvname) pt->uvname[oldsize++] = NULL; + } + pt->uvname[fs->nuv] = name; + lj_gc_objbarrier(fs->L, pt, name); + lua_assert(v->k == VLOCAL || v->k == VUPVAL); + fs->upvalues[fs->nuv].k = cast_byte(v->k); + fs->upvalues[fs->nuv].info = cast_byte(v->u.s.info); + return fs->nuv++; +} + +static BCReg searchvar(FuncState *fs, GCstr *n) +{ + int i; + for (i = fs->nactvar-1; i >= 0; i--) { + if (n == getlocvar(fs, i).name) + return (BCReg)i; + } + return (BCReg)-1; /* Not found. */ +} + +static void markupval(FuncState *fs, BCReg level) +{ + FuncBlock *bl = fs->bl; + while (bl && bl->nactvar > level) bl = bl->previous; + if (bl) bl->upval = 1; +} + +static int singlevaraux(FuncState *fs, GCstr *name, ExpDesc *e, int first) +{ + if (fs == NULL) { /* no more levels? */ + init_exp(e, VGLOBAL, 0); /* default is global variable */ + e->u.sval = name; + return 1; + } else { + BCReg reg = searchvar(fs, name); /* look up at current level */ + if ((int32_t)reg >= 0) { + init_exp(e, VLOCAL, reg); + if (!first) + markupval(fs, reg); /* local will be used as an upval */ + return 0; + } else { /* not found at current level; try upper one */ + if (singlevaraux(fs->prev, name, e, 0)) /* global? */ + return 1; + e->u.s.info = indexupvalue(fs, name, e); /* else was local or upvalue */ + e->k = VUPVAL; /* upvalue in this level */ + return 0; + } + } +} + +#define singlevar(ls, e) singlevaraux((ls)->fs, str_checkname(ls), (e), 1) + +static void adjust_assign(LexState *ls, BCReg nvars, BCReg nexps, ExpDesc *e) +{ + FuncState *fs = ls->fs; + int32_t extra = (int32_t)nvars - (int32_t)nexps; + if (e->k == VCALL) { + extra++; /* includes call itself */ + if (extra < 0) extra = 0; + setbc_b(bcptr(fs, e), extra+1); + if (extra > 1) reserveregs(fs, (BCReg)extra-1); + } else { + if (e->k != VVOID) exp2nextreg(fs, e); /* close last expression */ + if (extra > 0) { + BCReg reg = fs->freereg; + reserveregs(fs, (BCReg)extra); + nilK(fs, reg, (BCReg)extra); + } + } +} + +/* -- Function handling --------------------------------------------------- */ + +/* Forward declaration. */ +static void chunk(LexState *ls); + +static void open_func(LexState *ls, FuncState *fs) +{ + lua_State *L = ls->L; + GCproto *pt = lj_func_newproto(L); + fs->pt = pt; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + fs->L = L; + ls->fs = fs; + fs->pc = 0; + fs->lasttarget = 0; + fs->jpc = NO_JMP; + fs->freereg = 0; + fs->nkgc = 0; + fs->nkn = 0; + fs->nlocvars = 0; + fs->nactvar = 0; + fs->nuv = 0; + fs->bl = NULL; + pt->chunkname = ls->chunkname; + pt->framesize = 2; /* registers 0/1 are always valid */ + fs->kt = lj_tab_new(L, 0, 0); + /* anchor table of constants and prototype (to avoid being collected) */ + settabV(L, L->top, fs->kt); + incr_top(L); + setprotoV(L, L->top, pt); + incr_top(L); +} + +static void collectk(FuncState *fs, GCproto *pt) +{ + GCtab *kt; + TValue *array; + Node *node; + BCReg nkgc; + MSize i, hmask, sizek; + GCRef *kstart; + checklimitgt(fs, fs->nkn, BCMAX_D+1, "constants"); + checklimitgt(fs, fs->nkgc, BCMAX_D+1, "constants"); + nkgc = round_nkgc(fs->nkgc); + sizek = (MSize)(nkgc*sizeof(MRef) + fs->nkn*sizeof(lua_Number)); + kstart = lj_mem_newt(fs->L, sizek, GCRef); + if (nkgc) setgcrefnull(kstart[0]); /* May be uninitialized otherwise. */ + pt->k.gc = kstart + nkgc; + pt->sizekn = fs->nkn; + pt->sizekgc = fs->nkgc; + kt = fs->kt; + array = tvref(kt->array); + for (i = 0; i < kt->asize; i++) + if (tvisnum(&array[i])) + pt->k.n[array[i].u32.lo] = cast_num(i); + node = noderef(kt->node); + hmask = kt->hmask; + for (i = 0; i <= hmask; i++) { + Node *n = &node[i]; + if (tvisnum(&n->val)) { + ptrdiff_t kidx = (ptrdiff_t)n->val.u32.lo; + if (tvisnum(&n->key)) { + pt->k.n[kidx] = numV(&n->key); + } else { + GCobj *o = gcV(&n->key); + setgcref(pt->k.gc[~kidx], o); + lj_gc_objbarrier(fs->L, pt, o); + } + } + } +} + +static void collectuv(FuncState *fs, GCproto *pt) +{ + uint32_t i; + pt->uv = lj_mem_newvec(fs->L, fs->nuv, int16_t); + pt->sizeuv = fs->nuv; + for (i = 0; i < pt->sizeuv; i++) { + uint32_t v = fs->upvalues[i].info; + if (fs->upvalues[i].k == VUPVAL) v = ~v; + pt->uv[i] = (int16_t)v; + } +} + +static void finalret(FuncState *fs, GCproto *pt) +{ + BCPos lastpc = fs->pc; + if (lastpc > fs->lasttarget) { + switch (bc_op(pt->bc[lastpc-1])) { + case BC_CALLMT: case BC_CALLT: + case BC_RETM: case BC_RET: case BC_RET0: case BC_RET1: + goto suppress_return; /* already got a return */ + default: break; + } + } + if (fs->pt->flags & PROTO_HAS_FNEW) + emitAJ(fs, BC_UCLO, 0, 0); + emitAD(fs, BC_RET0, 0, 1); /* final return */ +suppress_return: + /* may need to fixup returns encoded before first function was created */ + if (fs->pt->flags & PROTO_FIXUP_RETURN) { + BCPos pc; + for (pc = 0; pc < lastpc; pc++) { + BCIns i = pt->bc[pc]; + BCPos offset; + switch (bc_op(i)) { + case BC_CALLMT: case BC_CALLT: + case BC_RETM: case BC_RET: case BC_RET0: case BC_RET1: + offset = emitINS(fs, i)-(pc+1)+BCBIAS_J; /* copy return ins */ + if (offset > BCMAX_D) + err_syntax(fs->ls, LJ_ERR_XFIXUP); + pt->bc[pc] = BCINS_AD(BC_UCLO, 0, offset); /* replace w/ UCLO+branch */ + break; + case BC_UCLO: return; /* we're done */ + default: break; + } + } + } +} + +static void close_func(LexState *ls) +{ + lua_State *L = ls->L; + FuncState *fs = ls->fs; + GCproto *pt = fs->pt; + removevars(ls, 0); + finalret(fs, pt); + lj_mem_reallocvec(L, pt->bc, pt->sizebc, fs->pc, BCIns); + pt->sizebc = fs->pc; + collectk(fs, pt); + collectuv(fs, pt); + lj_mem_reallocvec(L, pt->lineinfo, pt->sizelineinfo, fs->pc, BCLine); + pt->sizelineinfo = fs->pc; + lj_mem_reallocvec(L, pt->varinfo, pt->sizevarinfo, fs->nlocvars, VarInfo); + pt->sizevarinfo = fs->nlocvars; + lj_mem_reallocvec(L, pt->uvname, pt->sizeuvname, fs->nuv, GCstr *); + pt->sizeuvname = fs->nuv; + lua_assert(fs->bl == NULL); + lj_vmevent_send(L, BC, + setprotoV(L, L->top++, pt); + ); + ls->fs = fs->prev; + L->top -= 2; /* Remove table and prototype from the stack. */ + lua_assert(ls->fs != NULL || ls->token == TK_eof); + keep_token(ls); /* Re-anchor last token. */ +} + +GCproto *lj_parse(LexState *ls) +{ + struct FuncState fs; + ls->level = 0; + open_func(ls, &fs); + fs.pt->flags |= PROTO_IS_VARARG; /* Main chunk is always a vararg func. */ + lj_lex_next(ls); /* Read-ahead first token. */ + chunk(ls); + if (ls->token != TK_eof) + err_token(ls, TK_eof); + fs.pt->lastlinedefined = ls->linenumber; + close_func(ls); + lua_assert(fs.prev == NULL); + lua_assert(fs.pt->sizeuv == 0); + lua_assert(ls->fs == NULL); + return fs.pt; +} + +/* -- Expressions --------------------------------------------------------- */ + +/* forward declaration */ +static void expr(LexState *ls, ExpDesc *v); + +static void field(LexState *ls, ExpDesc *v) +{ + /* field -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + ExpDesc key; + exp2anyreg(fs, v); + lj_lex_next(ls); /* skip the dot or colon */ + checkname(ls, &key); + indexexp(fs, v, &key); +} + +static void yindex(LexState *ls, ExpDesc *v) +{ + /* index -> '[' expr ']' */ + lj_lex_next(ls); /* skip the '[' */ + expr(ls, v); + exp2val(ls->fs, v); + checknext(ls, ']'); +} + +static void kexp2tv(TValue *v, ExpDesc *e) +{ + switch (e->k) { + case VKNIL: case VKFALSE: case VKTRUE: v->it = ~(int32_t)e->k; break; + case VKSTR: + setgcref(v->gcr, obj2gco(e->u.sval)); v->it = LJ_TSTR; break; + case VKNUM: setnumV(v, expnumV(e)); break; + default: lua_assert(0); break; + } +} + +static void constructor(LexState *ls, ExpDesc *e) +{ + FuncState *fs = ls->fs; + BCLine line = ls->linenumber; + GCtab *t = NULL; + int vcall = 0, needarr = 0; + int32_t narr = 1; /* first array index */ + uint32_t nhash = 0; /* number of hash entries */ + BCReg freg = fs->freereg; + BCPos pc = emitAD(fs, BC_TNEW, freg, 0); + init_exp(e, VNONRELOC, freg); + reserveregs(fs, 1); + freg++; + checknext(ls, '{'); + while (ls->token != '}') { + ExpDesc key, val; + vcall = 0; + if (ls->token == '[') { + yindex(ls, &key); /* already calls exp2val */ + if (!isK(&key)) indexexp(fs, e, &key); + if (isnumK(&key) && expnumV(&key) == 0) needarr = 1; else nhash++; + checknext(ls, '='); + } else if (ls->token == TK_name && lj_lex_lookahead(ls) == '=') { + checkname(ls, &key); + checknext(ls, '='); + nhash++; + } else { + init_exp(&key, VKNUM, 0); + setintV(&key.u.nval, narr); + narr++; + needarr = vcall = 1; + } + expr(ls, &val); + if (isKexp(&val) && isK(&key) && key.k != VKNIL) { + TValue k; + if (!t) { /* create template table on demand */ + BCReg kidx; + t = lj_tab_new(fs->L, 0, 0); + kidx = gcK(fs, obj2gco(t), LJ_TTAB); + fs->pt->bc[pc] = BCINS_AD(BC_TDUP, freg-1, kidx); + } + vcall = 0; + kexp2tv(&k, &key); + kexp2tv(lj_tab_set(fs->L, t, &k), &val); + if (val.k == VKSTR) + lj_gc_objbarriert(fs->L, t, val.u.sval); + } else { + if (isK(&key)) indexexp(fs, e, &key); + if (val.k != VCALL) vcall = 0; + storevar(fs, e, &val); + } + fs->freereg = freg; + if (!testnext(ls, ',') && !testnext(ls, ';')) break; + } + checkmatch(ls, '}', '{', line); + if (vcall) { + BCIns *i = &fs->pt->bc[fs->pc-1]; + ExpDesc en; + lua_assert(bc_a(*i)==freg && bc_op(*i) == (narr>256?BC_TSETV:BC_TSETB)); + init_exp(&en, VKNUM, 0); + setintV(&en.u.nval, narr-1); + if (narr > 256) { fs->pc--; i--; } + *i = BCINS_AD(BC_TSETM, freg, numK(fs, &en)); + setbc_b(i-1, 0); + } + if (pc == fs->pc-1) { /* make expr relocable if possible */ + e->u.s.info = pc; + fs->freereg--; + e->k = VRELOCABLE; + } else { + e->k = VNONRELOC; /* indexexp may have changed it */ + } + if (!t) { /* Construct TNEW RD: hhhhhaaaaaaaaaaa. */ + if (!needarr) narr = 0; + else if (narr < 3) narr = 3; + else if (narr > 0x7ff) narr = 0x7ff; + setbc_d(&fs->pt->bc[pc], (uint32_t)narr | (hsize2hbits(nhash) << 11)); + } +} + +static void parlist(LexState *ls) +{ + /* parlist -> [ param { `,' param } ] */ + FuncState *fs = ls->fs; + GCproto *pt = fs->pt; + BCReg nparams = 0; + if (ls->token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->token) { + case TK_name: /* param -> NAME */ + new_localvar(ls, str_checkname(ls), nparams++); + break; + case TK_dots: /* param -> `...' */ + lj_lex_next(ls); + pt->flags |= PROTO_IS_VARARG; + break; + default: + err_syntax(ls, LJ_ERR_XPARAM); + break; + } + } while (!(pt->flags & PROTO_IS_VARARG) && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + pt->numparams = cast_byte(fs->nactvar); + reserveregs(fs, fs->nactvar); /* reserve register for parameters */ +} + +static void body(LexState *ls, ExpDesc *e, int needself, BCLine line) +{ + /* body -> `(' parlist `)' chunk END */ + FuncState *fs, new_fs; + BCReg kidx; + open_func(ls, &new_fs); + new_fs.pt->linedefined = line; + checknext(ls, '('); + if (needself) { + new_localvarliteral(ls, "self", 0); + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + chunk(ls); + new_fs.pt->lastlinedefined = ls->linenumber; + checkmatch(ls, TK_end, TK_function, line); + close_func(ls); + fs = ls->fs; + kidx = gcK(fs, obj2gco(new_fs.pt), LJ_TPROTO); + init_exp(e, VRELOCABLE, emitAD(fs, BC_FNEW, 0, kidx)); + if (!(fs->pt->flags & PROTO_HAS_FNEW)) { + if (fs->pt->flags & PROTO_HAS_RETURN) + fs->pt->flags |= PROTO_FIXUP_RETURN; + fs->pt->flags |= PROTO_HAS_FNEW; + } +} + +static BCReg explist1(LexState *ls, ExpDesc *v) +{ + /* explist1 -> expr { `,' expr } */ + BCReg n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + +static void funcargs(LexState *ls, ExpDesc *e) +{ + FuncState *fs = ls->fs; + ExpDesc args; + BCIns ins; + BCReg base; + BCLine line = ls->linenumber; + switch (ls->token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != ls->lastline) + err_syntax(ls, LJ_ERR_XAMBIG); + lj_lex_next(ls); + if (ls->token == ')') { /* arg list is empty? */ + args.k = VVOID; + } else { + explist1(ls, &args); + if (args.k == VCALL) + setbc_b(bcptr(fs, &args), 0); + } + checkmatch(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_string: { /* funcargs -> STRING */ + init_exp(&args, VKSTR, 0); + args.u.sval = strV(&ls->tokenval); + lj_lex_next(ls); /* must use `seminfo' before `next' */ + break; + } + default: { + err_syntax(ls, LJ_ERR_XFUNARG); + return; + } + } + lua_assert(e->k == VNONRELOC); + base = e->u.s.info; /* base register for call */ + if (args.k == VCALL) { + ins = BCINS_ABC(BC_CALLM, base, 2, args.u.s.aux - base - 1); + } else { + if (args.k != VVOID) + exp2nextreg(fs, &args); /* close last argument */ + ins = BCINS_ABC(BC_CALL, base, 2, fs->freereg - base); + } + init_exp(e, VCALL, emitINS(fs, ins)); + e->u.s.aux = base; + fs->pt->lineinfo[fs->pc - 1] = line; + fs->freereg = base+1; /* call removes function and arguments and leaves + (unless changed) one result */ +} + +static void prefixexp(LexState *ls, ExpDesc *v) +{ + /* prefixexp -> NAME | '(' expr ')' */ + switch (ls->token) { + case '(': { + BCLine line = ls->linenumber; + lj_lex_next(ls); + expr(ls, v); + checkmatch(ls, ')', '(', line); + dischargevars(ls->fs, v); + return; + } + case TK_name: { + singlevar(ls, v); + return; + } + default: { + err_syntax(ls, LJ_ERR_XSYMBOL); + return; + } + } +} + +static void primaryexp(LexState *ls, ExpDesc *v) +{ + /* primaryexp -> + prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + prefixexp(ls, v); + for (;;) { + switch (ls->token) { + case '.': /* field */ + field(ls, v); + break; + case '[': { /* `[' exp1 `]' */ + ExpDesc key; + exp2anyreg(fs, v); + yindex(ls, &key); + indexexp(fs, v, &key); + break; + } + case ':': { /* `:' NAME funcargs */ + ExpDesc key; + lj_lex_next(ls); + checkname(ls, &key); + methodexp(fs, v, &key); + funcargs(ls, v); + break; + } + case '(': case TK_string: case '{': /* funcargs */ + exp2nextreg(fs, v); + funcargs(ls, v); + break; + default: return; + } + } +} + +static void simpleexp(LexState *ls, ExpDesc *v) +{ + /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | + constructor | FUNCTION body | primaryexp */ + switch (ls->token) { + case TK_number: + init_exp(v, VKNUM, 0); + setnumV(&v->u.nval, numV(&ls->tokenval)); + break; + case TK_string: + init_exp(v, VKSTR, 0); + v->u.sval = strV(&ls->tokenval); + break; + case TK_nil: + init_exp(v, VKNIL, 0); + break; + case TK_true: + init_exp(v, VKTRUE, 0); + break; + case TK_false: + init_exp(v, VKFALSE, 0); + break; + case TK_dots: { /* vararg */ + FuncState *fs = ls->fs; + BCReg base; + checkcond(ls, fs->pt->flags & PROTO_IS_VARARG, LJ_ERR_XDOTS); + reserveregs(fs, 1); + base = fs->freereg-1; + init_exp(v, VCALL, emitABC(fs, BC_VARG, base, 2, 1)); + v->u.s.aux = base; + break; + } + case '{': /* constructor */ + constructor(ls, v); + return; + case TK_function: + lj_lex_next(ls); + body(ls, v, 0, ls->linenumber); + return; + default: + primaryexp(ls, v); + return; + } + lj_lex_next(ls); +} + +static void enterlevel(LexState *ls) +{ + if (++ls->level >= LJ_MAX_XLEVEL) + lj_lex_error(ls, 0, LJ_ERR_XLEVELS); +} + +#define leavelevel(ls) ((ls)->level--) + +static UnOpr getunopr(LexToken tok) +{ + switch (tok) { + case TK_not: return OPR_NOT; + case '-': return OPR_MINUS; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + +static BinOpr getbinopr(LexToken tok) +{ + switch (tok) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_concat: return OPR_CONCAT; + case TK_ne: return OPR_NE; + case TK_eq: return OPR_EQ; + case '<': return OPR_LT; + case TK_le: return OPR_LE; + case '>': return OPR_GT; + case TK_ge: return OPR_GE; + case TK_and: return OPR_AND; + case TK_or: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + +static const struct { + uint8_t left; /* left priority for each binary operator */ + uint8_t right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {6,6}, {6,6}, {7,7}, {7,7}, {7,7}, /* ADD SUB MUL DIV MOD */ + {10,9}, {5,4}, /* POW CONCAT (right associative) */ + {3,3}, {3,3}, /* EQ NE */ + {3,3}, {3,3}, {3,3}, {3,3}, /* LT GE GT LE */ + {2,2}, {1,1} /* AND OR */ +}; + +#define UNARY_PRIORITY 8 /* priority for unary operators */ + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr subexpr(LexState *ls, ExpDesc *v, uint32_t limit) +{ + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->token); + if (uop != OPR_NOUNOPR) { + lj_lex_next(ls); + subexpr(ls, v, UNARY_PRIORITY); + emit_unop(ls->fs, uop, v); + } else { + simpleexp(ls, v); + } + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(ls->token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + ExpDesc v2; + BinOpr nextop; + lj_lex_next(ls); + prepare_binop(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + emit_binop(ls->fs, op, v, &v2); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + +static void expr(LexState *ls, ExpDesc *v) +{ + subexpr(ls, v, 0); +} + +static BCPos condexpr(LexState *ls) +{ + /* cond -> exp */ + ExpDesc v; + expr(ls, &v); /* read condition */ + if (v.k == VKNIL) v.k = VKFALSE; /* `falses' are all equal here */ + goiftrue(ls->fs, &v); + return v.f; +} + +/* -- Scope handling ------------------------------------------------------ */ + +static void enterblock(FuncState *fs, FuncBlock *bl, int isbreakable) +{ + bl->breaklist = NO_JMP; + bl->isbreakable = (uint8_t)isbreakable; + bl->nactvar = fs->nactvar; + bl->upval = 0; + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + +static void leaveblock(FuncState *fs) +{ + FuncBlock *bl = fs->bl; + fs->bl = bl->previous; + removevars(fs->ls, bl->nactvar); + fs->freereg = fs->nactvar; /* free registers */ + lua_assert(bl->nactvar == fs->nactvar); + /* a block either controls scope or breaks (never both) */ + lua_assert(!bl->isbreakable || !bl->upval); + if (bl->upval) + emitAJ(fs, BC_UCLO, bl->nactvar, 0); + else /* avoid in upval case, it clears lasttarget and kills UCLO+JMP join */ + patchtohere(fs, bl->breaklist); +} + +static void block(LexState *ls) +{ + /* block -> chunk */ + FuncState *fs = ls->fs; + FuncBlock bl; + enterblock(fs, &bl, 0); + chunk(ls); + lua_assert(bl.breaklist == NO_JMP); + leaveblock(fs); +} + +/* -- Statements ---------------------------------------------------------- */ + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + ExpDesc v; /* variable (global, local, upvalue, or indexed) */ + struct LHS_assign *prev; +}; + +/* +** check whether, in an assignment to a local variable, the local variable +** is needed in a previous assignment (to a table). If so, save original +** local value in a safe place and use this safe copy in the previous +** assignment. +*/ +static void check_conflict(LexState *ls, struct LHS_assign *lh, + const ExpDesc *v) +{ + FuncState *fs = ls->fs; + BCReg reg = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { + if (lh->v.k == VINDEXED) { + if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.info = reg; /* previous assignment will use safe copy */ + } + if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.aux = reg; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + emitAD(fs, BC_MOV, reg, v->u.s.info); /* make copy */ + reserveregs(fs, 1); + } +} + +static void assignment(LexState *ls, struct LHS_assign *lh, BCReg nvars) +{ + ExpDesc e; + checkcond(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, LJ_ERR_XSYNTAX); + if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ + struct LHS_assign nv; + nv.prev = lh; + primaryexp(ls, &nv.v); + if (nv.v.k == VLOCAL) + check_conflict(ls, lh, &nv.v); + checklimit(ls->fs, ls->level + nvars, LJ_MAX_XLEVEL, "variable names"); + assignment(ls, &nv, nvars+1); + } else { /* assignment -> `=' explist1 */ + BCReg nexps; + checknext(ls, '='); + nexps = explist1(ls, &e); + if (nexps == nvars) { + if (e.k == VCALL) { + if (bc_op(*bcptr(ls->fs, &e)) == BC_VARG) { + ls->fs->freereg--; + e.k = VRELOCABLE; + } else { + e.u.s.info = e.u.s.aux; + e.k = VNONRELOC; + } + } + storevar(ls->fs, &lh->v, &e); + return; + } + adjust_assign(ls, nvars, nexps, &e); + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* remove extra values */ + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + storevar(ls->fs, &lh->v, &e); +} + +static void breakstat(LexState *ls) +{ + FuncState *fs = ls->fs; + FuncBlock *bl = fs->bl; + int upval = 0; + while (bl && !bl->isbreakable) { + upval |= bl->upval; + bl = bl->previous; + } + if (!bl) + err_syntax(ls, LJ_ERR_XBREAK); + if (upval) + emitAJ(fs, BC_UCLO, bl->nactvar, 0); + concatjumps(fs, &bl->breaklist, emit_jump(fs)); +} + +static void whilestat(LexState *ls, BCLine line) +{ + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + BCPos start, loop, condexit; + FuncBlock bl; + lj_lex_next(ls); /* skip WHILE */ + start = fs->lasttarget = fs->pc; + condexit = condexpr(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_do); + loop = emitAD(fs, BC_LOOP, fs->nactvar, 0); + block(ls); + patchlist(fs, emit_jump(fs), start); + checkmatch(ls, TK_end, TK_while, line); + leaveblock(fs); + patchtohere(fs, condexit); /* false conditions finish the loop */ + fixjump(fs, loop, fs->pc); +} + +static void repeatstat(LexState *ls, BCLine line) +{ + /* repeatstat -> REPEAT block UNTIL cond */ + FuncState *fs = ls->fs; + BCPos loop = fs->lasttarget = fs->pc; + BCPos condexit; + FuncBlock bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + lj_lex_next(ls); /* skip REPEAT */ + emitAD(fs, BC_LOOP, fs->nactvar, 0); + chunk(ls); + checkmatch(ls, TK_until, TK_repeat, line); + condexit = condexpr(ls); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + leaveblock(fs); /* finish scope */ + } else { /* complete semantics when there are upvalues */ + breakstat(ls); /* if condition then break */ + patchtohere(fs, condexit); /* else... */ + leaveblock(fs); /* finish scope... */ + condexit = emit_jump(fs); /* and repeat */ + } + patchlist(fs, condexit, loop); /* close the loop */ + fixjump(fs, loop, fs->pc); + leaveblock(fs); /* finish loop */ +} + +static void exp1(LexState *ls) +{ + ExpDesc e; + expr(ls, &e); + exp2nextreg(ls->fs, &e); +} + +static void forbody(LexState *ls, BCReg base, BCLine line, BCReg nvars, + int isnum) +{ + /* forbody -> DO block */ + FuncBlock bl; + FuncState *fs = ls->fs; + BCPos loop, loopend; + adjustlocalvars(ls, 3); /* control variables */ + checknext(ls, TK_do); + loop = isnum ? emitAJ(fs, BC_FORI, base, NO_JMP) : + emitAJ(fs, BC_JMP, fs->freereg, NO_JMP); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + if (isnum) { + loopend = emitAJ(fs, BC_FORL, base, NO_JMP); + fixjump(fs, loop, fs->pc); + } else { + fixjump(fs, loop, fs->pc); + emitABC(fs, BC_ITERC, base+3, nvars+1, 2+1); + loopend = emitAJ(fs, BC_ITERL, base+3, NO_JMP); + fs->pt->lineinfo[loopend-1] = line; + } + fs->pt->lineinfo[loopend] = line; /* pretend last op starts the loop */ + fixjump(fs, loopend, loop+1); +} + +static void fornum(LexState *ls, GCstr *varname, BCLine line) +{ + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + BCReg base = fs->freereg; + new_localvarliteral(ls, "(for index)", FORL_IDX); + new_localvarliteral(ls, "(for limit)", FORL_STOP); + new_localvarliteral(ls, "(for step)", FORL_STEP); + new_localvar(ls, varname, FORL_EXT); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) { + exp1(ls); /* optional step */ + } else { /* default step = 1 */ + emitAD(fs, BC_KSHORT, fs->freereg, 1); + reserveregs(fs, 1); + } + forbody(ls, base, line, 1, 1); +} + +static void forlist(LexState *ls, GCstr *indexname) +{ + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState *fs = ls->fs; + ExpDesc e; + BCReg nvars = 0; + BCLine line; + BCReg base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)", nvars++); + new_localvarliteral(ls, "(for state)", nvars++); + new_localvarliteral(ls, "(for control)", nvars++); + /* create declared variables */ + new_localvar(ls, indexname, nvars++); + while (testnext(ls, ',')) + new_localvar(ls, str_checkname(ls), nvars++); + checknext(ls, TK_in); + line = ls->linenumber; + adjust_assign(ls, 3, explist1(ls, &e), &e); + checkframe(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); +} + +static void forstat(LexState *ls, BCLine line) +{ + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + GCstr *varname; + FuncBlock bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + lj_lex_next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_in: forlist(ls, varname); break; + default: err_syntax(ls, LJ_ERR_XFOR); + } + checkmatch(ls, TK_end, TK_for, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + +static BCPos test_then_block(LexState *ls) +{ + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + BCPos condexit; + lj_lex_next(ls); /* skip IF or ELSEIF */ + condexit = condexpr(ls); + checknext(ls, TK_then); + block(ls); /* `then' part */ + return condexit; +} + +static void ifstat(LexState *ls, BCLine line) +{ + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + BCPos flist; + BCPos escapelist = NO_JMP; + flist = test_then_block(ls); /* IF cond THEN block */ + while (ls->token == TK_elseif) { + concatjumps(fs, &escapelist, emit_jump(fs)); + patchtohere(fs, flist); + flist = test_then_block(ls); /* ELSEIF cond THEN block */ + } + if (ls->token == TK_else) { + concatjumps(fs, &escapelist, emit_jump(fs)); + patchtohere(fs, flist); + lj_lex_next(ls); /* skip ELSE (after patch, for correct line info) */ + block(ls); /* `else' part */ + } else { + concatjumps(fs, &escapelist, flist); + } + patchtohere(fs, escapelist); + checkmatch(ls, TK_end, TK_if, line); +} + +static void localfunc(LexState *ls) +{ + ExpDesc v, b; + FuncState *fs = ls->fs; + new_localvar(ls, str_checkname(ls), 0); + init_exp(&v, VLOCAL, fs->freereg); + reserveregs(fs, 1); + adjustlocalvars(ls, 1); + body(ls, &b, 0, ls->linenumber); + storevar(fs, &v, &b); + /* debug information will only see the variable after this point! */ + getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; +} + +static void localstat(LexState *ls) +{ + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + BCReg nvars = 0; + BCReg nexps; + ExpDesc e; + do { + new_localvar(ls, str_checkname(ls), nvars++); + } while (testnext(ls, ',')); + if (testnext(ls, '=')) { + nexps = explist1(ls, &e); + } else { + e.k = VVOID; + nexps = 0; + } + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); +} + +static int func_name(LexState *ls, ExpDesc *v) +{ + /* func_name -> NAME {field} [`:' NAME] */ + int needself = 0; + singlevar(ls, v); + while (ls->token == '.') + field(ls, v); + if (ls->token == ':') { + needself = 1; + field(ls, v); + } + return needself; +} + +static void funcstat(LexState *ls, BCLine line) +{ + /* funcstat -> FUNCTION func_name body */ + FuncState *fs; + int needself; + ExpDesc v, b; + lj_lex_next(ls); /* skip FUNCTION */ + needself = func_name(ls, &v); + body(ls, &b, needself, line); + fs = ls->fs; + storevar(fs, &v, &b); + fs->pt->lineinfo[fs->pc - 1] = line; +} + +static void exprstat(LexState *ls) +{ + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + primaryexp(ls, &v.v); + if (v.v.k == VCALL) { /* stat -> func */ + setbc_b(bcptr(fs, &v.v), 1); /* call statement uses no results */ + } else { /* stat -> assignment */ + v.prev = NULL; + assignment(ls, &v, 1); + } +} + +static int block_follow(LexToken token) +{ + switch (token) { + case TK_else: case TK_elseif: case TK_end: case TK_until: case TK_eof: + return 1; + default: + return 0; + } +} + +static void retstat(LexState *ls) +{ + /* stat -> RETURN explist */ + BCIns ins; + FuncState *fs = ls->fs; + lj_lex_next(ls); /* skip RETURN */ + fs->pt->flags |= PROTO_HAS_RETURN; + if (block_follow(ls->token) || ls->token == ';') { + ins = BCINS_AD(BC_RET0, 0, 1); /* return no values */ + } else { + ExpDesc e; + BCReg nret = explist1(ls, &e); /* optional return values */ + if (nret == 1) { + if (e.k == VCALL) { + BCIns *i = bcptr(fs, &e); + /* It doesn't pay off to add BC_VARGT just for 'return ...'. */ + if (bc_op(*i) == BC_VARG) goto notailcall; + fs->pc--; + ins = BCINS_AD(bc_op(*i)-BC_CALL+BC_CALLT, bc_a(*i), bc_c(*i)); + } else { + ins = BCINS_AD(BC_RET1, exp2anyreg(fs, &e), 2); + } + } else { + if (e.k == VCALL) { + notailcall: + setbc_b(bcptr(fs, &e), 0); + ins = BCINS_AD(BC_RETM, fs->nactvar, e.u.s.aux - fs->nactvar); + } else { + exp2nextreg(fs, &e); /* values must go to the `stack' */ + ins = BCINS_AD(BC_RET, fs->nactvar, nret+1); + } + } + } + if (fs->pt->flags & PROTO_HAS_FNEW) + emitAJ(fs, BC_UCLO, 0, 0); + emitINS(fs, ins); +} + +static int statement(LexState *ls) +{ + BCLine line = ls->linenumber; /* may be needed for error messages */ + switch (ls->token) { + case TK_if: + ifstat(ls, line); + return 0; + case TK_while: + whilestat(ls, line); + return 0; + case TK_do: + lj_lex_next(ls); /* skip DO */ + block(ls); + checkmatch(ls, TK_end, TK_do, line); + return 0; + case TK_for: + forstat(ls, line); + return 0; + case TK_repeat: + repeatstat(ls, line); + return 0; + case TK_function: + funcstat(ls, line); + return 0; + case TK_local: + lj_lex_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_function)) /* local function? */ + localfunc(ls); + else + localstat(ls); + return 0; + case TK_return: + retstat(ls); + return 1; /* must be last statement */ + case TK_break: + lj_lex_next(ls); /* skip BREAK */ + breakstat(ls); + return 1; /* must be last statement */ + default: + exprstat(ls); + return 0; + } +} + +static void chunk(LexState *ls) +{ + /* chunk -> { stat [`;'] } */ + int islast = 0; + enterlevel(ls); + while (!islast && !block_follow(ls->token)) { + islast = statement(ls); + testnext(ls, ';'); + lua_assert(ls->fs->pt->framesize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* free registers */ + } + leavelevel(ls); +} + -- cgit v1.2.3-55-g6feb