TValue * LJ_FASTCALL lj_meta_equal_cd(lua_State *L, BCIns ins) { ASMFunction cont = (bc_op(ins) & 1) ? lj_cont_condf : lj_cont_condt; int op = (int)bc_op(ins) & ~1; TValue tv; cTValue *mo, *o2, *o1 = &L->base[bc_a(ins)]; cTValue *o1mm = o1; if (op == BC_ISEQV) { o2 = &L->base[bc_d(ins)]; if (!tviscdata(o1mm)) o1mm = o2; } else if (op == BC_ISEQS) { setstrV(L, &tv, gco2str(proto_kgc(curr_proto(L), ~(ptrdiff_t)bc_d(ins)))); o2 = &tv; } else if (op == BC_ISEQN) { o2 = &mref(curr_proto(L)->k, cTValue)[bc_d(ins)]; } else { lua_assert(op == BC_ISEQP); setitype(&tv, ~bc_d(ins)); o2 = &tv; } mo = lj_meta_lookup(L, o1mm, MM_eq); if (LJ_LIKELY(!tvisnil(mo))) return mmcall(L, cont, mo, o1, o2); else return (TValue *)(intptr_t)(bc_op(ins) & 1); }
/* Out-of-memory error. */ LJ_NOINLINE void lj_err_mem(lua_State *L) { if (L->status == LUA_ERRERR+1) /* Don't touch the stack during lua_open. */ lj_vm_unwind_c(L->cframe, LUA_ERRMEM); setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRMEM)); lj_err_throw(L, LUA_ERRMEM); }
/* Win64 exception handler for interpreter frame. */ LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win64(EXCEPTION_RECORD *rec, void *cf, CONTEXT *ctx, UndocumentedDispatcherContext *dispatch) { lua_State *L = cframe_L(cf); int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ? LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN; if ((rec->ExceptionFlags & 6)) { /* EH_UNWINDING|EH_EXIT_UNWIND */ /* Unwind internal frames. */ err_unwind(L, cf, errcode); } else { void *cf2 = err_unwind(L, cf, 0); if (cf2) { /* We catch it, so start unwinding the upper frames. */ if (rec->ExceptionCode == LJ_MSVC_EXCODE || rec->ExceptionCode == LJ_GCC_EXCODE) { __DestructExceptionObject(rec, 1); setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP)); } else if (!LJ_EXCODE_CHECK(rec->ExceptionCode)) { /* Don't catch access violations etc. */ return ExceptionContinueSearch; } /* Unwind the stack and call all handlers for all lower C frames ** (including ourselves) again with EH_UNWINDING set. Then set ** rsp = cf, rax = errcode and jump to the specified target. */ RtlUnwindEx(cf, (void *)((cframe_unwind_ff(cf2) && errcode != LUA_YIELD) ? lj_vm_unwind_ff_eh : lj_vm_unwind_c_eh), rec, (void *)(uintptr_t)errcode, ctx, dispatch->HistoryTable); /* RtlUnwindEx should never return. */ } } return ExceptionContinueSearch; }
/* Push a string for every flag bit that is set. */ static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base, const char *str) { for (; *str; base <<= 1, str += 1+*str) if (flags & base) setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str)); }
/* DWARF2 personality handler referenced from interpreter .eh_frame. */ LJ_FUNCA int lj_err_unwind_dwarf(int version, int actions, uint64_t uexclass, _Unwind_Exception *uex, _Unwind_Context *ctx) { void *cf; lua_State *L; if (version != 1) return _URC_FATAL_PHASE1_ERROR; UNUSED(uexclass); cf = (void *)_Unwind_GetCFA(ctx); L = cframe_L(cf); if ((actions & _UA_SEARCH_PHASE)) { #if LJ_UNWIND_EXT if (err_unwind(L, cf, 0) == NULL) return _URC_CONTINUE_UNWIND; #endif if (!LJ_UEXCLASS_CHECK(uexclass)) { setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP)); } return _URC_HANDLER_FOUND; } if ((actions & _UA_CLEANUP_PHASE)) { int errcode; if (LJ_UEXCLASS_CHECK(uexclass)) { errcode = LJ_UEXCLASS_ERRCODE(uexclass); } else { if ((actions & _UA_HANDLER_FRAME)) _Unwind_DeleteException(uex); errcode = LUA_ERRRUN; } #if LJ_UNWIND_EXT cf = err_unwind(L, cf, errcode); if ((actions & _UA_FORCE_UNWIND)) { return _URC_CONTINUE_UNWIND; } else if (cf) { _Unwind_SetGR(ctx, LJ_TARGET_EHRETREG, errcode); _Unwind_SetIP(ctx, (uintptr_t)(cframe_unwind_ff(cf) ? lj_vm_unwind_ff_eh : lj_vm_unwind_c_eh)); return _URC_INSTALL_CONTEXT; } #if LJ_TARGET_X86ORX64 else if ((actions & _UA_HANDLER_FRAME)) { /* Workaround for ancient libgcc bug. Still present in RHEL 5.5. :-/ ** Real fix: http://gcc.gnu.org/viewcvs/trunk/gcc/unwind-dw2.c?r1=121165&r2=124837&pathrev=153877&diff_format=h */ _Unwind_SetGR(ctx, LJ_TARGET_EHRETREG, errcode); _Unwind_SetIP(ctx, (uintptr_t)lj_vm_unwind_rethrow); return _URC_INSTALL_CONTEXT; } #endif #else /* This is not the proper way to escape from the unwinder. We get away with ** it on non-x64 because the interpreter restores all callee-saved regs. */ lj_err_throw(L, errcode); #endif } return _URC_CONTINUE_UNWIND; }
/* Protection twiddling failed. Probably due to kernel security. */ static LJ_NOINLINE void mcode_protfail(jit_State *J) { lua_CFunction panic = J2G(J)->panic; if (panic) { lua_State *L = J->L; setstrV(L, L->top++, lj_err_str(L, LJ_ERR_JITPROT)); panic(L); } }
/* In-place coercion of a number to a string. */ static LJ_AINLINE int tostring(lua_State *L, TValue *o) { if (tvisstr(o)) { return 1; } else if (tvisnumber(o)) { setstrV(L, o, lj_str_fromnumber(L, o)); return 1; } else { return 0; } }
static void read_string(LexState *ls, int delim, TValue *tv) { save_and_next(ls); while (ls->current != delim) { switch (ls->current) { case END_OF_STREAM: lj_lex_error(ls, TK_eof, LJ_ERR_XSTR); continue; case '\n': case '\r': lj_lex_error(ls, TK_string, LJ_ERR_XSTR); continue; case '\\': { int c; next(ls); /* do not save the `\' */ switch (ls->current) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\n': case '\r': save(ls, '\n'); inclinenumber(ls); continue; case END_OF_STREAM: continue; /* will raise an error next loop */ default: if (!lj_ctype_isdigit(ls->current)) { save_and_next(ls); /* handles \\, \", \', and \? */ } else { /* \xxx */ int i = 0; c = 0; do { c = 10*c + (ls->current-'0'); next(ls); } while (++i<3 && lj_ctype_isdigit(ls->current)); if (c > UCHAR_MAX) lj_lex_error(ls, TK_string, LJ_ERR_XESC); save(ls, c); } continue; } save(ls, c); next(ls); continue; } default: save_and_next(ls); break; } } save_and_next(ls); /* skip delimiter */ setstrV(ls->L, tv, lj_parse_keepstr(ls, ls->sb.buf + 1, ls->sb.n - 2)); }
static void io_file_readall(lua_State *L, FILE *fp) { MSize m, n; for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) { char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, m); n += (MSize)fread(buf+n, 1, m-n, fp); if (n != m) { setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); lj_gc_check(L); return; } } }
GCstr *lj_lib_checkstr(lua_State *L, int narg) { TValue *o = L->base + narg-1; if (o < L->top) { if (LJ_LIKELY(tvisstr(o))) { return strV(o); } else if (tvisnumber(o)) { GCstr *s = lj_str_fromnumber(L, o); setstrV(L, o, s); return s; } } lj_err_argt(L, narg, LUA_TSTRING); return NULL; /* unreachable */ }
static int io_file_readline(lua_State *L, FILE *fp, MSize chop) { MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0; char *buf; for (;;) { buf = lj_str_needbuf(L, &G(L)->tmpbuf, m); if (fgets(buf+n, m-n, fp) == NULL) break; n += (MSize)strlen(buf+n); ok |= n; if (n && buf[n-1] == '\n') { n -= chop; break; } if (n >= m - 64) m += m; } setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); lj_gc_check(L); return (int)ok; }
/* Get runtime value of string argument. */ static GCstr *argv2str(jit_State *J, TValue *o) { if (LJ_LIKELY(tvisstr(o))) { return strV(o); } else { GCstr *s; if (!tvisnumber(o)) lj_trace_err(J, LJ_TRERR_BADTYPE); if (tvisint(o)) s = lj_str_fromint(J->L, intV(o)); else s = lj_str_fromnum(J->L, &o->n); setstrV(J->L, o, s); return s; } }
/* Setup lexer state. */ int lj_lex_setup(lua_State *L, LexState *ls) { int header = 0; ls->L = L; ls->fs = NULL; ls->n = 0; ls->p = NULL; ls->vstack = NULL; ls->sizevstack = 0; ls->vtop = 0; ls->bcstack = NULL; ls->sizebcstack = 0; ls->lookahead = TK_eof; /* No look-ahead token. */ ls->linenumber = 1; ls->lastline = 1; lj_str_resizebuf(ls->L, &ls->sb, LJ_MIN_SBUF); next(ls); /* Read-ahead first char. */ if (ls->current == 0xef && ls->n >= 2 && char2int(ls->p[0]) == 0xbb && char2int(ls->p[1]) == 0xbf) { /* Skip UTF-8 BOM (if buffered). */ ls->n -= 2; ls->p += 2; next(ls); header = 1; } if (ls->current == '#') { /* Skip POSIX #! header line. */ do { next(ls); if (ls->current == END_OF_STREAM) return 0; } while (!currIsNewline(ls)); inclinenumber(ls); header = 1; } if (ls->current == LUA_SIGNATURE[0]) { /* Bytecode dump. */ if (header) { /* ** Loading bytecode with an extra header is disabled for security ** reasons. This may circumvent the usual check for bytecode vs. ** Lua code by looking at the first char. Since this is a potential ** security violation no attempt is made to echo the chunkname either. */ setstrV(L, L->top++, lj_err_str(L, LJ_ERR_BCBAD)); lj_err_throw(L, LUA_ERRSYNTAX); } return 1; } return 0; }
/* Runtime error. */ LJ_NOINLINE void lj_err_run(lua_State *L) { ptrdiff_t ef = finderrfunc(L); if (ef) { TValue *errfunc = restorestack(L, ef); TValue *top = L->top; lj_trace_abort(G(L)); if (!tvisfunc(errfunc) || L->status == LUA_ERRERR) { setstrV(L, top-1, lj_err_str(L, LJ_ERR_ERRERR)); lj_err_throw(L, LUA_ERRERR); } L->status = LUA_ERRERR; copyTV(L, top, top-1); copyTV(L, top-1, errfunc); L->top = top+1; lj_vm_call(L, top, 1+1); /* Stack: |errfunc|msg| -> |msg| */ } lj_err_throw(L, LUA_ERRRUN); }
static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud) { LexState *ls = (LexState *)ud; GCproto *pt; GCfunc *fn; int bc; UNUSED(dummy); cframe_errfunc(L->cframe) = -1; /* Inherit error function. */ bc = lj_lex_setup(L, ls); if (ls->mode && !strchr(ls->mode, bc ? 'b' : 't')) { setstrV(L, L->top++, lj_err_str(L, LJ_ERR_XMODE)); lj_err_throw(L, LUA_ERRSYNTAX); } pt = bc ? lj_bcread(ls) : lj_parse(ls); fn = lj_func_newL_empty(L, pt, tabref(L->env)); /* Don't combine above/below into one statement. */ setfuncV(L, L->top++, fn); return NULL; }
/* ARM unwinder personality handler referenced from interpreter .ARM.extab. */ LJ_FUNCA int lj_err_unwind_arm(int state, void *ucb, _Unwind_Context *ctx) { void *cf = (void *)_Unwind_GetGR(ctx, 13); lua_State *L = cframe_L(cf); if ((state & _US_ACTION_MASK) == _US_VIRTUAL_UNWIND_FRAME) { setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRCPP)); return _URC_HANDLER_FOUND; } if ((state&(_US_ACTION_MASK|_US_FORCE_UNWIND)) == _US_UNWIND_FRAME_STARTING) { _Unwind_DeleteException(ucb); _Unwind_SetGR(ctx, 15, (uint32_t)(void *)lj_err_throw); _Unwind_SetGR(ctx, 0, (uint32_t)L); _Unwind_SetGR(ctx, 1, (uint32_t)LUA_ERRRUN); return _URC_INSTALL_CONTEXT; } if (__gnu_unwind_frame(ucb, ctx) != _URC_OK) return _URC_FAILURE; return _URC_CONTINUE_UNWIND; }
static void jit_profile_callback(lua_State *L2, lua_State *L, int samples, int vmstate) { TValue key; cTValue *tv; setlightudV(&key, (void *)&KEY_PROFILE_FUNC); tv = lj_tab_get(L, tabV(registry(L)), &key); if (tvisfunc(tv)) { char vmst = (char)vmstate; int status; setfuncV(L2, L2->top++, funcV(tv)); setthreadV(L2, L2->top++, L); setintV(L2->top++, samples); setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1)); status = lua_pcall(L2, 3, 0, 0); /* callback(thread, samples, vmstate) */ if (status) { if (G(L2)->panic) G(L2)->panic(L2); exit(EXIT_FAILURE); } lj_trace_abort(G(L2)); } }
static int llex(LexState *ls, TValue *tv) { lj_str_resetbuf(&ls->sb); for (;;) { if (lj_char_isident(ls->current)) { GCstr *s; if (lj_char_isdigit(ls->current)) { /* Numeric literal. */ lex_number(ls, tv); return TK_number; } /* Identifier or reserved word. */ do { 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; return TK_name; } switch (ls->current) { case '\n': case '\r': inclinenumber(ls); continue; case ' ': case '\t': case '\v': case '\f': next(ls); continue; case '-': next(ls); if (ls->current != '-') return '-'; /* else is a comment */ next(ls); if (ls->current == '[') { int sep = skip_sep(ls); lj_str_resetbuf(&ls->sb); /* `skip_sep' may dirty the buffer */ if (sep >= 0) { read_long_string(ls, NULL, sep); /* long comment */ lj_str_resetbuf(&ls->sb); continue; } } /* else short comment */ while (!currIsNewline(ls) && ls->current != END_OF_STREAM) next(ls); continue; case '[': { int sep = skip_sep(ls); if (sep >= 0) { read_long_string(ls, tv, sep); return TK_string; } else if (sep == -1) { return '['; } else { lj_lex_error(ls, TK_string, LJ_ERR_XLDELIM); continue; } } case '=': next(ls); if (ls->current != '=') return '='; else { next(ls); return TK_eq; } case '<': next(ls); if (ls->current != '=') return '<'; else { next(ls); return TK_le; } case '>': next(ls); if (ls->current != '=') return '>'; else { next(ls); return TK_ge; } 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); return TK_string; case '.': save_and_next(ls); if (ls->current == '.') { next(ls); if (ls->current == '.') { next(ls); return TK_dots; /* ... */ } return TK_concat; /* .. */ } else if (!lj_char_isdigit(ls->current)) { return '.'; } else { lex_number(ls, tv); return TK_number; } case END_OF_STREAM: return TK_eof; default: { int c = ls->current; next(ls); return c; /* Single-char tokens (+ - / ...). */ } } } }
static void read_string(LexState *ls, int delim, TValue *tv) { save_and_next(ls); while (ls->current != delim) { switch (ls->current) { case END_OF_STREAM: lj_lex_error(ls, TK_eof, LJ_ERR_XSTR); continue; case '\n': case '\r': lj_lex_error(ls, TK_string, LJ_ERR_XSTR); continue; case '\\': { int c = next(ls); /* Skip the '\\'. */ switch (c) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'x': /* Hexadecimal escape '\xXX'. */ c = (next(ls) & 15u) << 4; if (!lj_char_isdigit(ls->current)) { if (!lj_char_isxdigit(ls->current)) goto err_xesc; c += 9 << 4; } c += (next(ls) & 15u); if (!lj_char_isdigit(ls->current)) { if (!lj_char_isxdigit(ls->current)) goto err_xesc; c += 9; } break; case 'z': /* Skip whitespace. */ next(ls); while (lj_char_isspace(ls->current)) if (currIsNewline(ls)) inclinenumber(ls); else next(ls); continue; case '\n': case '\r': save(ls, '\n'); inclinenumber(ls); continue; case '\\': case '\"': case '\'': break; case END_OF_STREAM: continue; default: if (!lj_char_isdigit(c)) goto err_xesc; c -= '0'; /* Decimal escape '\ddd'. */ if (lj_char_isdigit(next(ls))) { c = c*10 + (ls->current - '0'); if (lj_char_isdigit(next(ls))) { c = c*10 + (ls->current - '0'); if (c > 255) { err_xesc: lj_lex_error(ls, TK_string, LJ_ERR_XESC); } next(ls); } } save(ls, c); continue; } save(ls, c); next(ls); continue; } default: save_and_next(ls); break; } } save_and_next(ls); /* skip delimiter */ setstrV(ls->L, tv, lj_parse_keepstr(ls, ls->sb.buf + 1, ls->sb.n - 2)); }
/* Helper for CAT. Coercion, iterative concat, __concat metamethod. */ TValue *lj_meta_cat(lua_State *L, TValue *top, int left) { do { int n = 1; if (!(tvisstr(top-1) || tvisnumber(top-1)) || !tostring(L, top)) { cTValue *mo = lj_meta_lookup(L, top-1, MM_concat); if (tvisnil(mo)) { mo = lj_meta_lookup(L, top, MM_concat); if (tvisnil(mo)) { if (tvisstr(top-1) || tvisnumber(top-1)) top++; lj_err_optype(L, top-1, LJ_ERR_OPCAT); return NULL; /* unreachable */ } } /* One of the top two elements is not a string, call __cat metamethod: ** ** before: [...][CAT stack .........................] ** top-1 top top+1 top+2 ** pick two: [...][CAT stack ...] [o1] [o2] ** setup mm: [...][CAT stack ...] [cont|?] [mo|tmtype] [o1] [o2] ** in asm: [...][CAT stack ...] [cont|PC] [mo|delta] [o1] [o2] ** ^-- func base ^-- mm base ** after mm: [...][CAT stack ...] <--push-- [result] ** next step: [...][CAT stack .............] */ copyTV(L, top+2, top); /* Careful with the order of stack copies! */ copyTV(L, top+1, top-1); copyTV(L, top, mo); setcont(top-1, lj_cont_cat); return top+1; /* Trigger metamethod call. */ } else if (strV(top)->len == 0) { /* Shortcut. */ (void)tostring(L, top-1); } else { /* Pick as many strings as possible from the top and concatenate them: ** ** before: [...][CAT stack ...........................] ** pick str: [...][CAT stack ...] [...... strings ......] ** concat: [...][CAT stack ...] [result] ** next step: [...][CAT stack ............] */ MSize tlen = strV(top)->len; char *buffer; int i; for (n = 1; n <= left && tostring(L, top-n); n++) { MSize len = strV(top-n)->len; if (len >= LJ_MAX_STR - tlen) lj_err_msg(L, LJ_ERR_STROV); tlen += len; } buffer = lj_str_needbuf(L, &G(L)->tmpbuf, tlen); n--; tlen = 0; for (i = n; i >= 0; i--) { MSize len = strV(top-i)->len; memcpy(buffer + tlen, strVdata(top-i), len); tlen += len; } setstrV(L, top-n, lj_str_new(L, buffer, tlen)); } left -= n; top -= n; } while (left >= 1); lj_gc_check_fixtop(L); return NULL; }
void lj_lib_register(lua_State *L, const char *libname, const uint8_t *p, const lua_CFunction *cf) { GCtab *env = tabref(L->env); GCfunc *ofn = NULL; int ffid = *p++; BCIns *bcff = &L2GG(L)->bcff[*p++]; GCtab *tab = lib_create_table(L, libname, *p++); ptrdiff_t tpos = L->top - L->base; /* Avoid barriers further down. */ lj_gc_anybarriert(L, tab); tab->nomm = 0; for (;;) { uint32_t tag = *p++; MSize len = tag & LIBINIT_LENMASK; tag &= LIBINIT_TAGMASK; if (tag != LIBINIT_STRING) { const char *name; MSize nuv = (MSize)(L->top - L->base - tpos); GCfunc *fn = lj_func_newC(L, nuv, env); if (nuv) { L->top = L->base + tpos; memcpy(fn->c.upvalue, L->top, sizeof(TValue)*nuv); } fn->c.ffid = (uint8_t)(ffid++); name = (const char *)p; p += len; if (tag == LIBINIT_CF) setmref(fn->c.pc, &G(L)->bc_cfunc_int); else setmref(fn->c.pc, bcff++); if (tag == LIBINIT_ASM_) fn->c.f = ofn->c.f; /* Copy handler from previous function. */ else fn->c.f = *cf++; /* Get cf or handler from C function table. */ if (len) { /* NOBARRIER: See above for common barrier. */ setfuncV(L, lj_tab_setstr(L, tab, lj_str_new(L, name, len)), fn); } ofn = fn; } else { switch (tag | len) { case LIBINIT_SET: L->top -= 2; if (tvisstr(L->top+1) && strV(L->top+1)->len == 0) env = tabV(L->top); else /* NOBARRIER: See above for common barrier. */ copyTV(L, lj_tab_set(L, tab, L->top+1), L->top); break; case LIBINIT_NUMBER: memcpy(&L->top->n, p, sizeof(double)); L->top++; p += sizeof(double); break; case LIBINIT_COPY: copyTV(L, L->top, L->top - *p++); L->top++; break; case LIBINIT_LASTCL: setfuncV(L, L->top++, ofn); break; case LIBINIT_FFID: ffid++; break; case LIBINIT_END: return; default: setstrV(L, L->top++, lj_str_new(L, (const char *)p, len)); p += len; break; } } } }
/* Helper for CAT. Coercion, iterative concat, __concat metamethod. */ TValue *lj_meta_cat(lua_State *L, TValue *top, int left) { int fromc = 0; if (left < 0) { left = -left; fromc = 1; } do { if (!(tvisstr(top) || tvisnumber(top)) || !(tvisstr(top-1) || tvisnumber(top-1))) { cTValue *mo = lj_meta_lookup(L, top-1, MM_concat); if (tvisnil(mo)) { mo = lj_meta_lookup(L, top, MM_concat); if (tvisnil(mo)) { if (tvisstr(top-1) || tvisnumber(top-1)) top++; lj_err_optype(L, top-1, LJ_ERR_OPCAT); return NULL; /* unreachable */ } } /* One of the top two elements is not a string, call __cat metamethod: ** ** before: [...][CAT stack .........................] ** top-1 top top+1 top+2 ** pick two: [...][CAT stack ...] [o1] [o2] ** setup mm: [...][CAT stack ...] [cont|?] [mo|tmtype] [o1] [o2] ** in asm: [...][CAT stack ...] [cont|PC] [mo|delta] [o1] [o2] ** ^-- func base ^-- mm base ** after mm: [...][CAT stack ...] <--push-- [result] ** next step: [...][CAT stack .............] */ copyTV(L, top+2*LJ_FR2+2, top); /* Carefully ordered stack copies! */ copyTV(L, top+2*LJ_FR2+1, top-1); copyTV(L, top+LJ_FR2, mo); setcont(top-1, lj_cont_cat); if (LJ_FR2) { setnilV(top); setnilV(top+2); top += 2; } return top+1; /* Trigger metamethod call. */ } else { /* Pick as many strings as possible from the top and concatenate them: ** ** before: [...][CAT stack ...........................] ** pick str: [...][CAT stack ...] [...... strings ......] ** concat: [...][CAT stack ...] [result] ** next step: [...][CAT stack ............] */ TValue *e, *o = top; uint64_t tlen = tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM; char *p, *buf; do { o--; tlen += tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM; } while (--left > 0 && (tvisstr(o-1) || tvisnumber(o-1))); if (tlen >= LJ_MAX_STR) lj_err_msg(L, LJ_ERR_STROV); p = buf = lj_buf_tmp(L, (MSize)tlen); for (e = top, top = o; o <= e; o++) { if (tvisstr(o)) { GCstr *s = strV(o); MSize len = s->len; p = lj_buf_wmem(p, strdata(s), len); } else if (tvisint(o)) { p = lj_strfmt_wint(p, intV(o)); } else { lua_assert(tvisnum(o)); p = lj_strfmt_wnum(p, o); } } setstrV(L, top, lj_str_new(L, buf, (size_t)(p-buf))); } } while (left >= 1); if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) { if (!fromc) L->top = curr_topL(L); lj_gc_step(L); } return NULL; }