/* 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)); }
/* Check for a recognized ld script line. */ static const char *clib_check_lds(lua_State *L, const char *buf) { char *p, *e; if ((!strncmp(buf, "GROUP", 5) || !strncmp(buf, "INPUT", 5)) && (p = strchr(buf, '('))) { while (*++p == ' ') ; for (e = p; *e && *e != ' ' && *e != ')'; e++) ; return strdata(lj_str_new(L, p, e-p)); } return NULL; }
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; } } }
static int io_file_readlen(lua_State *L, FILE *fp, MSize m) { if (m) { char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, m); MSize n = (MSize)fread(buf, 1, m, fp); setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); lj_gc_check(L); return (n > 0 || m == 0); } else { int c = getc(fp); ungetc(c, fp); setstrV(L, L->top++, &G(L)->strempty); return (c != EOF); } }
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; }
/* String interning of metamethod names for fast indexing. */ void lj_meta_init(lua_State *L) { #define MMNAME(name) "__" #name const char *metanames = MMDEF(MMNAME); #undef MMNAME global_State *g = G(L); const char *p, *q; uint32_t mm; for (mm = 0, p = metanames; *p; mm++, p = q) { GCstr *s; for (q = p+2; *q && *q != '_'; q++) ; s = lj_str_new(L, p, (size_t)(q-p)); /* NOBARRIER: g->gcroot[] is a GC root. */ setgcref(g->gcroot[GCROOT_MMNAME+mm], obj2gco(s)); } }
static void *clib_loadlib(lua_State *L, const char *name, int global) { void *h = dlopen(clib_extname(L, name), RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); if (!h) { const char *e, *err = dlerror(); if (*err == '/' && (e = strchr(err, ':')) && (name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) { h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); if (h) return h; err = dlerror(); } lj_err_callermsg(L, err); } return h; }
static const uint8_t *lib_read_lfunc(lua_State *L, const uint8_t *p, GCtab *tab) { int len = *p++; GCstr *name = lj_str_new(L, (const char *)p, len); LexState ls; GCproto *pt; GCfunc *fn; memset(&ls, 0, sizeof(ls)); ls.L = L; ls.p = (const char *)(p+len); ls.pe = (const char *)~(uintptr_t)0; ls.c = -1; ls.level = (BCDUMP_F_STRIP|(LJ_BE*BCDUMP_F_BE)); ls.chunkname = name; pt = lj_bcread_proto(&ls); pt->firstline = ~(BCLine)0; fn = lj_func_newL_empty(L, pt, tabref(L->env)); /* NOBARRIER: See below for common barrier. */ setfuncV(L, lj_tab_setstr(L, tab, name), fn); return (const uint8_t *)ls.p; }
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)); } }
/* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */ static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd) { TRef trstr = lj_ir_tostr(J, J->base[0]); TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN); TRef tr0 = lj_ir_kint(J, 0); TRef trstart, trend; GCstr *str = argv2str(J, &rd->argv[0]); int32_t start, end; if (rd->data) { /* string.sub(str, start [,end]) */ start = argv2int(J, &rd->argv[1]); trstart = lj_opt_narrow_toint(J, J->base[1]); trend = J->base[2]; if (tref_isnil(trend)) { trend = lj_ir_kint(J, -1); end = -1; } else { trend = lj_opt_narrow_toint(J, trend); end = argv2int(J, &rd->argv[2]); } } else { /* string.byte(str, [,start [,end]]) */ if (J->base[1]) { start = argv2int(J, &rd->argv[1]); trstart = lj_opt_narrow_toint(J, J->base[1]); trend = J->base[2]; if (tref_isnil(trend)) { trend = trstart; end = start; } else { trend = lj_opt_narrow_toint(J, trend); end = argv2int(J, &rd->argv[2]); } } else { trend = trstart = lj_ir_kint(J, 1); end = start = 1; } } if (end < 0) { emitir(IRTGI(IR_LT), trend, tr0); trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend), lj_ir_kint(J, 1)); end = end+(int32_t)str->len+1; } else if ((MSize)end <= str->len) { emitir(IRTGI(IR_ULE), trend, trlen); } else { emitir(IRTGI(IR_GT), trend, trlen); end = (int32_t)str->len; trend = trlen; } if (start < 0) { emitir(IRTGI(IR_LT), trstart, tr0); trstart = emitir(IRTI(IR_ADD), trlen, trstart); start = start+(int32_t)str->len; emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), trstart, tr0); if (start < 0) { trstart = tr0; start = 0; } } else { if (start == 0) { emitir(IRTGI(IR_EQ), trstart, tr0); trstart = tr0; } else { trstart = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, -1)); emitir(IRTGI(IR_GE), trstart, tr0); start--; } } if (rd->data) { /* Return string.sub result. */ if (end - start >= 0) { /* Also handle empty range here, to avoid extra traces. */ TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart); emitir(IRTGI(IR_GE), trslen, tr0); trptr = emitir(IRT(IR_STRREF, IRT_P32), trstr, trstart); J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen); } else { /* Range underflow: return empty string. */ emitir(IRTGI(IR_LT), trend, trstart); J->base[0] = lj_ir_kstr(J, lj_str_new(J->L, strdata(str), 0)); } } else { /* Return string.byte result(s). */ ptrdiff_t i, len = end - start; if (len > 0) { TRef trslen = emitir(IRTI(IR_SUB), trend, trstart); emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len)); if (J->baseslot + len > LJ_MAX_JSLOTS) lj_trace_err_info(J, LJ_TRERR_STACKOV); rd->nres = len; for (i = 0; i < len; i++) { TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i)); tmp = emitir(IRT(IR_STRREF, IRT_P32), trstr, tmp); J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY); } } else { /* Empty range or range underflow: return no results. */ emitir(IRTGI(IR_LE), trend, trstart); rd->nres = 0; } } }
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; }
/* 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; }