diff --git a/src/lj_errmsg.h b/src/lj_errmsg.h index dd3eefa8..c07d4387 100644 --- a/src/lj_errmsg.h +++ b/src/lj_errmsg.h @@ -134,8 +134,11 @@ ERRDEF(XFUNARG, "function arguments expected") ERRDEF(XSYMBOL, "unexpected symbol") ERRDEF(XDOTS, "cannot use " LUA_QL("...") " outside a vararg function") ERRDEF(XSYNTAX, "syntax error") -ERRDEF(XBREAK, "no loop to break") ERRDEF(XFOR, LUA_QL("=") " or " LUA_QL("in") " expected") +ERRDEF(XBREAK, "no loop to break") +ERRDEF(XLUNDEF, "undefined label " LUA_QS) +ERRDEF(XLDUP, "duplicate label " LUA_QS) +ERRDEF(XGSCOPE, " jumps into the scope of local " LUA_QS) /* Bytecode reader errors. */ ERRDEF(BCFMT, "cannot load incompatible bytecode") diff --git a/src/lj_lex.c b/src/lj_lex.c index c85ea314..e464f420 100644 --- a/src/lj_lex.c +++ b/src/lj_lex.c @@ -272,9 +272,9 @@ static int llex(LexState *ls, TValue *tv) save_and_next(ls); } while (lj_char_isident(ls->current)); s = lj_parse_keepstr(ls, ls->sb.buf, ls->sb.n); + setstrV(ls->L, tv, s); if (s->reserved > 0) /* Reserved word? */ return TK_OFS + s->reserved; - setstrV(ls->L, tv, s); return TK_name; } switch (ls->current) { @@ -330,6 +330,9 @@ static int llex(LexState *ls, TValue *tv) case '~': next(ls); if (ls->current != '=') return '~'; else { next(ls); return TK_ne; } + case ':': + next(ls); + if (ls->current != ':') return ':'; else { next(ls); return TK_label; } case '"': case '\'': read_string(ls, ls->current, tv); diff --git a/src/lj_lex.h b/src/lj_lex.h index 1ddf4b59..16f35aa5 100644 --- a/src/lj_lex.h +++ b/src/lj_lex.h @@ -15,10 +15,11 @@ /* Lua lexer tokens. */ #define TKDEF(_, __) \ _(and) _(break) _(do) _(else) _(elseif) _(end) _(false) \ - _(for) _(function) _(if) _(in) _(local) _(nil) _(not) _(or) \ + _(for) _(function) _(goto) _(if) _(in) _(local) _(nil) _(not) _(or) \ _(repeat) _(return) _(then) _(true) _(until) _(while) \ __(concat, ..) __(dots, ...) __(eq, ==) __(ge, >=) __(le, <=) __(ne, ~=) \ - __(number, ) __(name, ) __(string, ) __(eof, ) + __(label, ::) __(number, ) __(name, ) __(string, ) \ + __(eof, ) enum { TK_OFS = 256, diff --git a/src/lj_parse.c b/src/lj_parse.c index 6c2f0d31..322a542d 100644 --- a/src/lj_parse.c +++ b/src/lj_parse.c @@ -95,17 +95,28 @@ static int expr_numiszero(ExpDesc *e) /* Per-function linked list of scope blocks. */ typedef struct FuncScope { struct FuncScope *prev; /* Link to outer scope. */ - BCPos breaklist; /* Jump list for loop breaks. */ + MSize vstart; /* Start of block-local variables. */ uint8_t nactvar; /* Number of active vars outside the scope. */ - uint8_t upval; /* Some variable in the scope is an upvalue. */ - uint8_t isbreakable; /* Scope is a loop and allows a break. */ + uint8_t flags; /* Scope flags. */ } FuncScope; +#define FSCOPE_LOOP 0x01 /* Scope is a (breakable) loop. */ +#define FSCOPE_BREAK 0x02 /* Break used in scope. */ +#define FSCOPE_GOLA 0x04 /* Goto or label used in scope. */ +#define FSCOPE_UPVAL 0x08 /* Upvalue in scope. */ +#define FSCOPE_NOCLOSE 0x10 /* Do not close upvalues. */ + +#define NAME_BREAK ((GCstr *)(uintptr_t)1) + /* Index into variable stack. */ typedef uint16_t VarIndex; #define LJ_MAX_VSTACK 65536 -#define VSTACK_VAR_RW 0x80000000 /* In endpc: R/W variable. */ +/* Flags stored in upper bits of endpc. */ +#define VSTACK_MASK 0x1fffffff /* Mask for actual endpc. */ +#define VSTACK_VAR_RW 0x80000000 /* R/W variable. */ +#define VSTACK_GOTO 0x40000000 /* Pending goto. */ +#define VSTACK_LABEL 0x20000000 /* Label. */ /* Upvalue map. */ typedef struct UVMap { @@ -680,8 +691,7 @@ static BCPos bcemit_jmp(FuncState *fs) BCPos j = fs->pc - 1; BCIns *ip = &fs->bcbase[j].ins; fs->jpc = NO_JMP; - if ((int32_t)j >= (int32_t)fs->lasttarget && - bc_op(*ip) == BC_UCLO) + if ((int32_t)j >= (int32_t)fs->lasttarget && bc_op(*ip) == BC_UCLO) setbc_j(ip, NO_JMP); else j = bcemit_AJ(fs, BC_JMP, fs->freereg, NO_JMP); @@ -1009,7 +1019,7 @@ static void lex_match(LexState *ls, LexToken what, LexToken who, BCLine line) static GCstr *lex_str(LexState *ls) { GCstr *s; - if (ls->token != TK_name) + if (ls->token != TK_name && (LJ_52 || ls->token != TK_goto)) err_token(ls, TK_name); s = strV(&ls->tokenval); lj_lex_next(ls); @@ -1094,7 +1104,7 @@ static MSize var_lookup_uv(FuncState *fs, MSize vidx, ExpDesc *e) } /* Forward declaration. */ -static void scope_uvmark(FuncState *fs, BCReg level); +static void fscope_uvmark(FuncState *fs, BCReg level); /* Recursively lookup variables in enclosing functions. */ static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first) @@ -1104,7 +1114,7 @@ static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first) if ((int32_t)reg >= 0) { /* Local in this function? */ expr_init(e, VLOCAL, reg); if (!first) - scope_uvmark(fs, reg); /* Scope now has an upvalue. */ + fscope_uvmark(fs, reg); /* Scope now has an upvalue. */ return (MSize)(e->u.s.aux = (uint32_t)fs->varmap[reg]); } else { MSize vidx = var_lookup_(fs->prev, name, e, 0); /* Var in outer func? */ @@ -1125,6 +1135,170 @@ static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first) #define var_lookup(ls, e) \ var_lookup_((ls)->fs, lex_str(ls), (e), 1) +/* -- Goto an label handling ---------------------------------------------- */ + +/* Add a new goto or label. */ +static MSize gola_new(LexState *ls, GCstr *name, uint32_t type, BCPos pc) +{ + FuncState *fs = ls->fs; + MSize vtop = ls->vtop; + if (LJ_UNLIKELY(vtop >= ls->sizevstack)) { + if (ls->sizevstack >= LJ_MAX_VSTACK) + lj_lex_error(ls, 0, LJ_ERR_XLIMC, LJ_MAX_VSTACK); + lj_mem_growvec(ls->L, ls->vstack, ls->sizevstack, LJ_MAX_VSTACK, VarInfo); + } + lua_assert(name == NAME_BREAK || lj_tab_getstr(fs->kt, name) != NULL); + /* NOBARRIER: name is anchored in fs->kt and ls->vstack is not a GCobj. */ + setgcref(ls->vstack[vtop].name, obj2gco(name)); + ls->vstack[vtop].startpc = pc; + ls->vstack[vtop].endpc = fs->nactvar | type; + ls->vtop = vtop+1; + return vtop; +} + +#define gola_nactvar(v) ((uint8_t)((v)->endpc)) +#define gola_isgoto(v) ((v)->endpc & VSTACK_GOTO) +#define gola_islabel(v) ((v)->endpc & VSTACK_LABEL) +#define gola_isgotolabel(v) ((v)->endpc & (VSTACK_GOTO|VSTACK_LABEL)) + +/* Patch goto to jump to label. */ +static void gola_patch(LexState *ls, VarInfo *vg, VarInfo *vl) +{ + FuncState *fs = ls->fs; + BCPos pc = vg->startpc; + setgcrefnull(vg->name); /* Invalidate pending goto. */ + setbc_a(&fs->bcbase[pc].ins, gola_nactvar(vl)); + jmp_patch(fs, pc, vl->startpc); +} + +/* Patch goto to close upvalues. */ +static void gola_close(LexState *ls, VarInfo *vg) +{ + FuncState *fs = ls->fs; + BCPos pc = vg->startpc; + BCIns *ip = &fs->bcbase[pc].ins; + lua_assert(gola_isgoto(vg)); + lua_assert(bc_op(*ip) == BC_JMP || bc_op(*ip) == BC_UCLO); + setbc_a(ip, gola_nactvar(vg)); + if (bc_op(*ip) == BC_JMP) { + BCPos next = jmp_next(fs, pc); + if (next != NO_JMP) jmp_patch(fs, next, pc); /* Jump to UCLO. */ + setbc_op(ip, BC_UCLO); /* Turn into UCLO. */ + setbc_j(ip, NO_JMP); + } +} + +/* Resolve pending forward gotos for label. */ +static void gola_resolve(LexState *ls, MSize idx) +{ + VarInfo *vg = ls->vstack + ls->fs->bl->vstart; + VarInfo *vl = ls->vstack + idx; + for (; vg < vl; vg++) + if (gcrefeq(vg->name, vl->name) && gola_isgoto(vg)) { + if (gola_nactvar(vg) < gola_nactvar(vl)) { + GCstr *name = strref(var_get(ls, ls->fs, gola_nactvar(vg)).name); + lua_assert((uintptr_t)name >= VARNAME__MAX); + ls->linenumber = ls->fs->bcbase[vg->startpc].line; + lj_lex_error(ls, 0, LJ_ERR_XGSCOPE, + strdata(strref(vg->name)), strdata(name)); + } + gola_patch(ls, vg, vl); + } +} + +/* Fixup remaining gotos and labels for scope. */ +static void gola_fixup(LexState *ls, FuncScope *bl) +{ + VarInfo *v = ls->vstack + bl->vstart; + VarInfo *ve = ls->vstack + ls->vtop; + for (; v < ve; v++) { + GCstr *name = strref(v->name); + if (name != NULL) { /* Only consider remaining valid gotos/labels. */ + if (gola_islabel(v)) { + VarInfo *vg; + setgcrefnull(v->name); /* Invalidate label that goes out of scope. */ + for (vg = v+1; vg < ve; vg++) /* Resolve pending backward gotos. */ + if (strref(vg->name) == name && gola_isgoto(vg)) { + if ((bl->flags&FSCOPE_UPVAL) && gola_nactvar(vg) > gola_nactvar(v)) + gola_close(ls, vg); + gola_patch(ls, vg, v); + } + } else if (gola_isgoto(v)) { + if (bl->prev) { /* Propagate goto or break to outer scope. */ + bl->prev->flags |= name == NAME_BREAK ? FSCOPE_BREAK : FSCOPE_GOLA; + v->endpc = bl->nactvar | VSTACK_GOTO; + if ((bl->flags & FSCOPE_UPVAL)) + gola_close(ls, v); + } else { /* No outer scope: undefined goto label or no loop. */ + ls->linenumber = ls->fs->bcbase[v->startpc].line; + if (name == NAME_BREAK) + lj_lex_error(ls, 0, LJ_ERR_XBREAK); + else + lj_lex_error(ls, 0, LJ_ERR_XLUNDEF, strdata(name)); + } + } + } + } +} + +/* Find existing label. */ +static VarInfo *gola_findlabel(LexState *ls, GCstr *name) +{ + VarInfo *v = ls->vstack + ls->fs->bl->vstart; + VarInfo *ve = ls->vstack + ls->vtop; + for (; v < ve; v++) + if (strref(v->name) == name && gola_islabel(v)) + return v; + return NULL; +} + +/* -- Scope handling ------------------------------------------------------ */ + +/* Begin a scope. */ +static void fscope_begin(FuncState *fs, FuncScope *bl, int flags) +{ + bl->nactvar = (uint8_t)fs->nactvar; + bl->flags = flags; + bl->vstart = fs->ls->vtop; + bl->prev = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + +/* End a scope. */ +static void fscope_end(FuncState *fs) +{ + FuncScope *bl = fs->bl; + LexState *ls = fs->ls; + fs->bl = bl->prev; + var_remove(ls, bl->nactvar); + fs->freereg = fs->nactvar; + lua_assert(bl->nactvar == fs->nactvar); + if ((bl->flags & (FSCOPE_UPVAL|FSCOPE_NOCLOSE)) == FSCOPE_UPVAL) + bcemit_AJ(fs, BC_UCLO, bl->nactvar, 0); + if ((bl->flags & FSCOPE_BREAK)) { + if ((bl->flags & FSCOPE_LOOP)) { + MSize idx = gola_new(ls, NAME_BREAK, VSTACK_LABEL, fs->pc); + ls->vtop = idx; /* Drop break label immediately. */ + gola_resolve(ls, idx); + return; + } /* else: need the fixup step to propagate the breaks. */ + } else if (!(bl->flags & FSCOPE_GOLA)) { + return; + } + gola_fixup(ls, bl); +} + +/* Mark scope as having an upvalue. */ +static void fscope_uvmark(FuncState *fs, BCReg level) +{ + FuncScope *bl; + for (bl = fs->bl; bl && bl->nactvar > level; bl = bl->prev) + ; + if (bl) + bl->flags |= FSCOPE_UPVAL; +} + /* -- Function state management ------------------------------------------- */ /* Fixup bytecode for prototype. */ @@ -1287,36 +1461,37 @@ static void fs_buf_uleb128(LexState *ls, uint32_t v) /* Prepare variable info for prototype. */ static size_t fs_prep_var(LexState *ls, FuncState *fs, size_t *ofsvar) { - VarInfo *vstack = fs->ls->vstack; + VarInfo *vs = fs->ls->vstack, *ve; MSize i, n; BCPos lastpc; lj_str_resetbuf(&ls->sb); /* Copy to temp. string buffer. */ /* Store upvalue names. */ for (i = 0, n = fs->nuv; i < n; i++) { - GCstr *s = strref(vstack[fs->uvloc[i].vidx].name); + GCstr *s = strref(vs[fs->uvloc[i].vidx].name); MSize len = s->len+1; fs_buf_need(ls, len); fs_buf_str(ls, strdata(s), len); } *ofsvar = ls->sb.n; - vstack += fs->vbase; lastpc = 0; /* Store local variable names and compressed ranges. */ - for (i = 0, n = ls->vtop - fs->vbase; i < n; i++) { - GCstr *s = strref(vstack[i].name); - BCPos startpc = vstack[i].startpc; - BCPos endpc = vstack[i].endpc & ~VSTACK_VAR_RW; - if ((uintptr_t)s < VARNAME__MAX) { - fs_buf_need(ls, 1 + 2*5); - ls->sb.buf[ls->sb.n++] = (uint8_t)(uintptr_t)s; - } else { - MSize len = s->len+1; - fs_buf_need(ls, len + 2*5); - fs_buf_str(ls, strdata(s), len); + for (ve = vs + ls->vtop, vs += fs->vbase; vs < ve; vs++) { + if (!gola_isgotolabel(vs)) { + GCstr *s = strref(vs->name); + BCPos startpc; + if ((uintptr_t)s < VARNAME__MAX) { + fs_buf_need(ls, 1 + 2*5); + ls->sb.buf[ls->sb.n++] = (uint8_t)(uintptr_t)s; + } else { + MSize len = s->len+1; + fs_buf_need(ls, len + 2*5); + fs_buf_str(ls, strdata(s), len); + } + startpc = vs->startpc; + fs_buf_uleb128(ls, startpc-lastpc); + fs_buf_uleb128(ls, (vs->endpc & VSTACK_MASK)-startpc); + lastpc = startpc; } - fs_buf_uleb128(ls, startpc-lastpc); - fs_buf_uleb128(ls, endpc-startpc); - lastpc = startpc; } fs_buf_need(ls, 1); ls->sb.buf[ls->sb.n++] = '\0'; /* Terminator for varinfo. */ @@ -1359,14 +1534,17 @@ static void fs_fixup_ret(FuncState *fs) { BCPos lastpc = fs->pc; if (lastpc <= fs->lasttarget || !bcopisret(bc_op(fs->bcbase[lastpc-1].ins))) { - if (fs->flags & PROTO_CHILD) + if ((fs->bl->flags & FSCOPE_UPVAL)) bcemit_AJ(fs, BC_UCLO, 0, 0); bcemit_AD(fs, BC_RET0, 0, 1); /* Need final return. */ } + fs->bl->flags |= FSCOPE_NOCLOSE; /* Handled above. */ + fscope_end(fs); + lua_assert(fs->bl == NULL); /* May need to fixup returns encoded before first function was created. */ if (fs->flags & PROTO_FIXUP_RETURN) { BCPos pc; - for (pc = 0; pc < lastpc; pc++) { + for (pc = 1; pc < lastpc; pc++) { BCIns ins = fs->bcbase[pc].ins; BCPos offset; switch (bc_op(ins)) { @@ -1397,9 +1575,7 @@ static GCproto *fs_finish(LexState *ls, BCLine line) GCproto *pt; /* Apply final fixups. */ - lua_assert(fs->bl == NULL); fs_fixup_ret(fs); - var_remove(ls, 0); /* Calculate total size of prototype including all colocated arrays. */ sizept = sizeof(GCproto) + fs->pc*sizeof(BCIns) + fs->nkgc*sizeof(GCRef); @@ -1564,7 +1740,8 @@ static void expr_table(LexState *ls, ExpDesc *e) if (!expr_isk(&key)) expr_index(fs, e, &key); if (expr_isnumk(&key) && expr_numiszero(&key)) needarr = 1; else nhash++; lex_check(ls, '='); - } else if (ls->token == TK_name && lj_lex_lookahead(ls) == '=') { + } else if ((ls->token == TK_name || (!LJ_52 && ls->token == TK_goto)) && + lj_lex_lookahead(ls) == '=') { expr_str(ls, &key); lex_check(ls, '='); nhash++; @@ -1657,7 +1834,7 @@ static BCReg parse_params(LexState *ls, int needself) var_new_lit(ls, nparams++, "self"); if (ls->token != ')') { do { - if (ls->token == TK_name) { + if (ls->token == TK_name || (!LJ_52 && ls->token == TK_goto)) { var_new(ls, nparams++, lex_str(ls)); } else if (ls->token == TK_dots) { lj_lex_next(ls); @@ -1682,9 +1859,11 @@ static void parse_chunk(LexState *ls); static void parse_body(LexState *ls, ExpDesc *e, int needself, BCLine line) { FuncState fs, *pfs = ls->fs; + FuncScope bl; GCproto *pt; ptrdiff_t oldbase = pfs->bcbase - ls->bcstack; fs_init(ls, &fs); + fscope_begin(&fs, &bl, 0); fs.linedefined = line; fs.numparams = (uint8_t)parse_params(ls, needself); fs.bcbase = pfs->bcbase + pfs->pc; @@ -1778,7 +1957,7 @@ static void expr_primary(LexState *ls, ExpDesc *v) expr(ls, v); lex_match(ls, ')', '(', line); expr_discharge(ls->fs, v); - } else if (ls->token == TK_name) { + } else if (ls->token == TK_name || (!LJ_52 && ls->token == TK_goto)) { var_lookup(ls, v); } else { err_syntax(ls, LJ_ERR_XSYMBOL); @@ -1964,125 +2143,6 @@ static BCPos expr_cond(LexState *ls) return v.f; } -/* -- Scope handling ------------------------------------------------------ */ - -/* Begin a scope. */ -static void scope_begin(FuncState *fs, FuncScope *bl, int isbreakable) -{ - bl->breaklist = NO_JMP; - bl->isbreakable = (uint8_t)isbreakable; - bl->nactvar = (uint8_t)fs->nactvar; - bl->upval = 0; - bl->prev = fs->bl; - fs->bl = bl; - lua_assert(fs->freereg == fs->nactvar); -} - -/* End a scope. */ -static void scope_end(FuncState *fs) -{ - FuncScope *bl = fs->bl; - fs->bl = bl->prev; - var_remove(fs->ls, bl->nactvar); - fs->freereg = fs->nactvar; - lua_assert(bl->nactvar == fs->nactvar); - /* A scope is either breakable or has upvalues. */ - lua_assert(!bl->isbreakable || !bl->upval); - if (bl->upval) - bcemit_AJ(fs, BC_UCLO, bl->nactvar, 0); - else /* Avoid in upval case, it clears lasttarget and kills UCLO+JMP join. */ - jmp_tohere(fs, bl->breaklist); -} - -/* Mark scope as having an upvalue. */ -static void scope_uvmark(FuncState *fs, BCReg level) -{ - FuncScope *bl; - for (bl = fs->bl; bl && bl->nactvar > level; bl = bl->prev) - ; - if (bl) - bl->upval = 1; -} - -/* Parse 'break' statement. */ -static void parse_break(LexState *ls) -{ - FuncState *fs = ls->fs; - FuncScope *bl; - BCReg savefr; - int upval = 0; - for (bl = fs->bl; bl && !bl->isbreakable; bl = bl->prev) - upval |= bl->upval; /* Collect upvalues in intervening scopes. */ - if (!bl) /* Error if no breakable scope found. */ - err_syntax(ls, LJ_ERR_XBREAK); - savefr = fs->freereg; - fs->freereg = bl->nactvar; /* Shrink slots to help data-flow analysis. */ - if (upval) - bcemit_AJ(fs, BC_UCLO, bl->nactvar, 0); /* Close upvalues. */ - jmp_append(fs, &bl->breaklist, bcemit_jmp(fs)); - fs->freereg = savefr; -} - -/* Check for end of block. */ -static int endofblock(LexToken token) -{ - switch (token) { - case TK_else: case TK_elseif: case TK_end: case TK_until: case TK_eof: - return 1; - default: - return 0; - } -} - -/* Parse 'return' statement. */ -static void parse_return(LexState *ls) -{ - BCIns ins; - FuncState *fs = ls->fs; - lj_lex_next(ls); /* Skip 'return'. */ - fs->flags |= PROTO_HAS_RETURN; - if (endofblock(ls->token) || ls->token == ';') { /* Bare return. */ - ins = BCINS_AD(BC_RET0, 0, 1); - } else { /* Return with one or more values. */ - ExpDesc e; /* Receives the _last_ expression in the list. */ - BCReg nret = expr_list(ls, &e); - if (nret == 1) { /* Return one result. */ - if (e.k == VCALL) { /* Check for tail call. */ - BCIns *ip = bcptr(fs, &e); - /* It doesn't pay off to add BC_VARGT just for 'return ...'. */ - if (bc_op(*ip) == BC_VARG) goto notailcall; - fs->pc--; - ins = BCINS_AD(bc_op(*ip)-BC_CALL+BC_CALLT, bc_a(*ip), bc_c(*ip)); - } else { /* Can return the result from any register. */ - ins = BCINS_AD(BC_RET1, expr_toanyreg(fs, &e), 2); - } - } else { - if (e.k == VCALL) { /* Append all results from a call. */ - notailcall: - setbc_b(bcptr(fs, &e), 0); - ins = BCINS_AD(BC_RETM, fs->nactvar, e.u.s.aux - fs->nactvar); - } else { - expr_tonextreg(fs, &e); /* Force contiguous registers. */ - ins = BCINS_AD(BC_RET, fs->nactvar, nret+1); - } - } - } - if (fs->flags & PROTO_CHILD) - bcemit_AJ(fs, BC_UCLO, 0, 0); /* May need to close upvalues first. */ - bcemit_INS(fs, ins); -} - -/* Parse a block. */ -static void parse_block(LexState *ls) -{ - FuncState *fs = ls->fs; - FuncScope bl; - scope_begin(fs, &bl, 0); - parse_chunk(ls); - lua_assert(bl.breaklist == NO_JMP); - scope_end(fs); -} - /* -- Assignments --------------------------------------------------------- */ /* List of LHS variables. */ @@ -2243,7 +2303,119 @@ static void parse_func(LexState *ls, BCLine line) fs->bcbase[fs->pc - 1].line = line; /* Set line for the store. */ } -/* -- Loop and conditional statements ------------------------------------- */ +/* -- Control transfer statements ----------------------------------------- */ + +/* Check for end of block. */ +static int endofblock(LexToken token) +{ + switch (token) { + case TK_else: case TK_elseif: case TK_end: case TK_until: case TK_eof: + return 1; + default: + return 0; + } +} + +/* Parse 'return' statement. */ +static void parse_return(LexState *ls) +{ + BCIns ins; + FuncState *fs = ls->fs; + lj_lex_next(ls); /* Skip 'return'. */ + fs->flags |= PROTO_HAS_RETURN; + if (endofblock(ls->token) || ls->token == ';') { /* Bare return. */ + ins = BCINS_AD(BC_RET0, 0, 1); + } else { /* Return with one or more values. */ + ExpDesc e; /* Receives the _last_ expression in the list. */ + BCReg nret = expr_list(ls, &e); + if (nret == 1) { /* Return one result. */ + if (e.k == VCALL) { /* Check for tail call. */ + BCIns *ip = bcptr(fs, &e); + /* It doesn't pay off to add BC_VARGT just for 'return ...'. */ + if (bc_op(*ip) == BC_VARG) goto notailcall; + fs->pc--; + ins = BCINS_AD(bc_op(*ip)-BC_CALL+BC_CALLT, bc_a(*ip), bc_c(*ip)); + } else { /* Can return the result from any register. */ + ins = BCINS_AD(BC_RET1, expr_toanyreg(fs, &e), 2); + } + } else { + if (e.k == VCALL) { /* Append all results from a call. */ + notailcall: + setbc_b(bcptr(fs, &e), 0); + ins = BCINS_AD(BC_RETM, fs->nactvar, e.u.s.aux - fs->nactvar); + } else { + expr_tonextreg(fs, &e); /* Force contiguous registers. */ + ins = BCINS_AD(BC_RET, fs->nactvar, nret+1); + } + } + } + if (fs->flags & PROTO_CHILD) + bcemit_AJ(fs, BC_UCLO, 0, 0); /* May need to close upvalues first. */ + bcemit_INS(fs, ins); +} + +/* Parse 'break' statement. */ +static void parse_break(LexState *ls) +{ + ls->fs->bl->flags |= FSCOPE_BREAK; + gola_new(ls, NAME_BREAK, VSTACK_GOTO, bcemit_jmp(ls->fs)); +} + +/* Parse 'goto' statement. */ +static void parse_goto(LexState *ls) +{ + FuncState *fs = ls->fs; + GCstr *name = lex_str(ls); + VarInfo *vl = gola_findlabel(ls, name); + if (vl) /* Treat backwards goto within same scope like a loop. */ + bcemit_AJ(fs, BC_LOOP, gola_nactvar(vl), -1); /* No BC range check. */ + fs->bl->flags |= FSCOPE_GOLA; + gola_new(ls, name, VSTACK_GOTO, bcemit_jmp(fs)); +} + +/* Parse label. */ +static void parse_label(LexState *ls) +{ + FuncState *fs = ls->fs; + GCstr *name; + MSize idx; + fs->lasttarget = fs->pc; + fs->bl->flags |= FSCOPE_GOLA; + lj_lex_next(ls); /* Skip '::'. */ + name = lex_str(ls); + if (gola_findlabel(ls, name)) + lj_lex_error(ls, 0, LJ_ERR_XLDUP, strdata(name)); + idx = gola_new(ls, name, VSTACK_LABEL, fs->pc); + lex_check(ls, TK_label); + /* Recursively parse trailing statements: labels and ';' (Lua 5.2 only). */ + for (;;) { + if (ls->token == TK_label) { + synlevel_begin(ls); + parse_label(ls); + synlevel_end(ls); + } else if (LJ_52 && ls->token == ';') { + lj_lex_next(ls); + } else { + break; + } + } + /* Trailing label is considered to be outside of scope. */ + if (endofblock(ls->token) && ls->token != TK_until) + ls->vstack[idx].endpc = fs->bl->nactvar | VSTACK_LABEL; + gola_resolve(ls, idx); +} + +/* -- Blocks, loops and conditional statements ---------------------------- */ + +/* Parse a block. */ +static void parse_block(LexState *ls) +{ + FuncState *fs = ls->fs; + FuncScope bl; + fscope_begin(fs, &bl, 0); + parse_chunk(ls); + fscope_end(fs); +} /* Parse 'while' statement. */ static void parse_while(LexState *ls, BCLine line) @@ -2254,13 +2426,13 @@ static void parse_while(LexState *ls, BCLine line) lj_lex_next(ls); /* Skip 'while'. */ start = fs->lasttarget = fs->pc; condexit = expr_cond(ls); - scope_begin(fs, &bl, 1); + fscope_begin(fs, &bl, FSCOPE_LOOP); lex_check(ls, TK_do); loop = bcemit_AD(fs, BC_LOOP, fs->nactvar, 0); parse_block(ls); jmp_patch(fs, bcemit_jmp(fs), start); lex_match(ls, TK_end, TK_while, line); - scope_end(fs); + fscope_end(fs); jmp_tohere(fs, condexit); jmp_patchins(fs, loop, fs->pc); } @@ -2272,24 +2444,24 @@ static void parse_repeat(LexState *ls, BCLine line) BCPos loop = fs->lasttarget = fs->pc; BCPos condexit; FuncScope bl1, bl2; - scope_begin(fs, &bl1, 1); /* Breakable loop scope. */ - scope_begin(fs, &bl2, 0); /* Inner scope. */ + fscope_begin(fs, &bl1, FSCOPE_LOOP); /* Breakable loop scope. */ + fscope_begin(fs, &bl2, 0); /* Inner scope. */ lj_lex_next(ls); /* Skip 'repeat'. */ bcemit_AD(fs, BC_LOOP, fs->nactvar, 0); parse_chunk(ls); lex_match(ls, TK_until, TK_repeat, line); condexit = expr_cond(ls); /* Parse condition (still inside inner scope). */ - if (!bl2.upval) { /* No upvalues? Just end inner scope. */ - scope_end(fs); + if (!(bl2.flags & FSCOPE_UPVAL)) { /* No upvalues? Just end inner scope. */ + fscope_end(fs); } else { /* Otherwise generate: cond: UCLO+JMP out, !cond: UCLO+JMP loop. */ parse_break(ls); /* Break from loop and close upvalues. */ jmp_tohere(fs, condexit); - scope_end(fs); /* End inner scope and close upvalues. */ + fscope_end(fs); /* End inner scope and close upvalues. */ condexit = bcemit_jmp(fs); } jmp_patch(fs, condexit, loop); /* Jump backwards if !cond. */ jmp_patchins(fs, loop, fs->pc); - scope_end(fs); /* End loop scope. */ + fscope_end(fs); /* End loop scope. */ } /* Parse numeric 'for'. */ @@ -2318,11 +2490,11 @@ static void parse_for_num(LexState *ls, GCstr *varname, BCLine line) var_add(ls, 3); /* Hidden control variables. */ lex_check(ls, TK_do); loop = bcemit_AJ(fs, BC_FORI, base, NO_JMP); - scope_begin(fs, &bl, 0); /* Scope for visible variables. */ + fscope_begin(fs, &bl, 0); /* Scope for visible variables. */ var_add(ls, 1); bcreg_reserve(fs, 1); parse_block(ls); - scope_end(fs); + fscope_end(fs); /* Perform loop inversion. Loop control instructions are at the end. */ loopend = bcemit_AJ(fs, BC_FORL, base, NO_JMP); fs->bcbase[loopend].line = line; /* Fix line for control ins. */ @@ -2389,11 +2561,11 @@ static void parse_for_iter(LexState *ls, GCstr *indexname) var_add(ls, 3); /* Hidden control variables. */ lex_check(ls, TK_do); loop = bcemit_AJ(fs, isnext ? BC_ISNEXT : BC_JMP, base, NO_JMP); - scope_begin(fs, &bl, 0); /* Scope for visible variables. */ + fscope_begin(fs, &bl, 0); /* Scope for visible variables. */ var_add(ls, nvars-3); bcreg_reserve(fs, nvars-3); parse_block(ls); - scope_end(fs); + fscope_end(fs); /* Perform loop inversion. Loop control instructions are at the end. */ jmp_patchins(fs, loop, fs->pc); bcemit_ABC(fs, isnext ? BC_ITERN : BC_ITERC, base, nvars-3+1, 2+1); @@ -2409,7 +2581,7 @@ static void parse_for(LexState *ls, BCLine line) FuncState *fs = ls->fs; GCstr *varname; FuncScope bl; - scope_begin(fs, &bl, 1); /* Breakable loop scope. */ + fscope_begin(fs, &bl, FSCOPE_LOOP); lj_lex_next(ls); /* Skip 'for'. */ varname = lex_str(ls); /* Get first variable name. */ if (ls->token == '=') @@ -2419,7 +2591,7 @@ static void parse_for(LexState *ls, BCLine line) else err_syntax(ls, LJ_ERR_XFOR); lex_match(ls, TK_end, TK_for, line); - scope_end(fs); /* Resolve break list. */ + fscope_end(fs); /* Resolve break list. */ } /* Parse condition and 'then' block. */ @@ -2500,6 +2672,15 @@ static int parse_stmt(LexState *ls) lj_lex_next(ls); break; #endif + case TK_label: + parse_label(ls); + break; + case TK_goto: + if (LJ_52 || lj_lex_lookahead(ls) == TK_name) { + lj_lex_next(ls); + parse_goto(ls); + break; + } /* else: fallthrough */ default: parse_call_assign(ls); break; @@ -2526,6 +2707,7 @@ static void parse_chunk(LexState *ls) GCproto *lj_parse(LexState *ls) { FuncState fs; + FuncScope bl; GCproto *pt; lua_State *L = ls->L; #ifdef LUAJIT_DISABLE_DEBUGINFO @@ -2542,6 +2724,7 @@ GCproto *lj_parse(LexState *ls) fs.bcbase = NULL; fs.bclim = 0; fs.flags |= PROTO_VARARG; /* Main chunk is always a vararg func. */ + fscope_begin(&fs, &bl, 0); bcemit_AD(&fs, BC_FUNCV, 0, 0); /* Placeholder. */ lj_lex_next(ls); /* Read-ahead first token. */ parse_chunk(ls); diff --git a/src/lj_record.c b/src/lj_record.c index 8718f8bb..7620ae4c 100644 --- a/src/lj_record.c +++ b/src/lj_record.c @@ -550,7 +550,7 @@ static void rec_loop_interp(jit_State *J, const BCIns *pc, LoopEvent ev) ** an inner loop even in a root trace. But it's better to be a bit ** more conservative here and only do it for very short loops. */ - if (!innerloopleft(J, pc)) + if (bc_j(*pc) != -1 && !innerloopleft(J, pc)) lj_trace_err(J, LJ_TRERR_LINNER); /* Root trace hit an inner loop. */ if ((ev != LOOPEV_ENTERLO && J->loopref && J->cur.nins - J->loopref > 24) || --J->loopunroll < 0)