LUALIB_API int luaL_execresult(lua_State *L, int stat) { if (stat != -1) { #if LJ_TARGET_POSIX if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); setnilV(L->top++); lua_pushliteral(L, "signal"); } else { if (WIFEXITED(stat)) stat = WEXITSTATUS(stat); if (stat == 0) setboolV(L->top++, 1); else setnilV(L->top++); lua_pushliteral(L, "exit"); } #else if (stat == 0) setboolV(L->top++, 1); else setnilV(L->top++); lua_pushliteral(L, "exit"); #endif setintV(L->top++, stat); return 3; } return luaL_fileresult(L, 0, NULL); }
/* Setup call to metamethod to be run by Assembler VM. */ static TValue *mmcall(lua_State *L, ASMFunction cont, cTValue *mo, cTValue *a, cTValue *b) { /* ** |-- framesize -> top top+1 top+2 top+3 ** before: [func slots ...] ** mm setup: [func slots ...] [cont|?] [mo|tmtype] [a] [b] ** in asm: [func slots ...] [cont|PC] [mo|delta] [a] [b] ** ^-- func base ^-- mm base ** after mm: [func slots ...] [result] ** ^-- copy to base[PC_RA] --/ for lj_cont_ra ** istruecond + branch for lj_cont_cond* ** ignore for lj_cont_nop ** next PC: [func slots ...] */ TValue *top = L->top; if (curr_funcisL(L)) top = curr_topL(L); setcont(top++, cont); /* Assembler VM stores PC in upper word or FR2. */ if (LJ_FR2) setnilV(top++); copyTV(L, top++, mo); /* Store metamethod and two arguments. */ if (LJ_FR2) setnilV(top++); copyTV(L, top, a); copyTV(L, top+1, b); return top; /* Return new base. */ }
/* Clear hash part of table. */ static LJ_AINLINE void clearhpart(GCtab *t) { uint32_t i, hmask = t->hmask; Node *node = noderef(t->node); lua_assert(t->hmask != 0); for (i = 0; i <= hmask; i++) { Node *n = &node[i]; setmref(n->next, NULL); setnilV(&n->key); setnilV(&n->val); } }
static int io_file_close(lua_State *L, IOFileUD *iof) { int ok; if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) { ok = (fclose(iof->fp) == 0); } else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) { int stat = -1; #if LJ_TARGET_POSIX stat = pclose(iof->fp); #elif LJ_TARGET_WINDOWS && !defined(WP8) stat = _pclose(iof->fp); #else lua_assert(0); return 0; #endif #if LJ_52 iof->fp = NULL; return luaL_execresult(L, stat); #else ok = (stat != -1); #endif } else { lua_assert((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF); setnilV(L->top++); lua_pushliteral(L, "cannot close standard file"); return 2; } iof->fp = NULL; return luaL_fileresult(L, ok, NULL); }
/* Clear array part of table. */ static LJ_AINLINE void clearapart(GCtab *t) { uint32_t i, asize = t->asize; TValue *array = tvref(t->array); for (i = 0; i < asize; i++) setnilV(&array[i]); }
/* Create an empty and closed upvalue. */ static GCupval *func_emptyuv(lua_State *L) { GCupval *uv = (GCupval *)lj_mem_newgco(L, sizeof(GCupval)); uv->gct = ~LJ_TUPVAL; uv->closed = 1; setnilV(&uv->tv); setmref(uv->v, &uv->tv); return uv; }
static int io_file_readnum(lua_State *L, FILE *fp) { lua_Number d; if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) { setnumV(L->top++, d); return 1; } else { setnilV(L->top++); return 0; } }
static int os_pushresult(lua_State *L, int i, const char *filename) { int en = errno; /* calls to Lua API may change this value */ if (i) { setboolV(L->top-1, 1); return 1; } else { setnilV(L->top-1); lua_pushfstring(L, "%s: %s", filename, strerror(en)); lua_pushinteger(L, en); return 3; } }
/* Helper for equality comparisons. __eq metamethod. */ TValue *lj_meta_equal(lua_State *L, GCobj *o1, GCobj *o2, int ne) { /* Field metatable must be at same offset for GCtab and GCudata! */ cTValue *mo = lj_meta_fast(L, tabref(o1->gch.metatable), MM_eq); if (mo) { TValue *top; uint32_t it; if (tabref(o1->gch.metatable) != tabref(o2->gch.metatable)) { cTValue *mo2 = lj_meta_fast(L, tabref(o2->gch.metatable), MM_eq); if (mo2 == NULL || !lj_obj_equal(mo, mo2)) return (TValue *)(intptr_t)ne; } top = curr_top(L); setcont(top++, ne ? lj_cont_condf : lj_cont_condt); if (LJ_FR2) setnilV(top++); copyTV(L, top++, mo); if (LJ_FR2) setnilV(top++); it = ~(uint32_t)o1->gch.gct; setgcV(L, top, o1, it); setgcV(L, top+1, o2, it); return top; /* Trigger metamethod call. */ } return (TValue *)(intptr_t)ne; }
/* -- Luajittex needs this one because it overloads loadfile -- */ LUALIB_API int RESERVED_load_aux_JIT(lua_State *L, int status, int envarg) { if (status == 0) { if (tvistab(L->base+envarg-1)) { GCfunc *fn = funcV(L->top-1); GCtab *t = tabV(L->base+envarg-1); setgcref(fn->c.env, obj2gco(t)); lj_gc_objbarrier(L, fn, t); } return 1; } else { setnilV(L->top-2); return 2; } }
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname) { if (stat) { setboolV(L->top++, 1); return 1; } else { int en = errno; /* Lua API calls may change this value. */ setnilV(L->top++); if (fname) lua_pushfstring(L, "%s: %s", fname, strerror(en)); else lua_pushfstring(L, "%s", strerror(en)); setintV(L->top++, en); lj_trace_abort(G(L)); return 3; } }
static int io_file_readnum(lua_State *L, FILE *fp) { lua_Number d; if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) { if (LJ_DUALNUM) { int32_t i = lj_num2int(d); if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) { setintV(L->top++, i); return 1; } } setnumV(L->top++, d); return 1; } else { setnilV(L->top++); return 0; } }
/* Call dispatch. Used by call hooks, hot calls or when recording. */ ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns *pc) { GCfunc *fn = curr_func(L); BCOp op; global_State *g = G(L); #if LJ_HASJIT jit_State *J = G2J(g); #endif int missing = call_init(L, fn); #if LJ_HASJIT J->L = L; if ((uintptr_t)pc & 1) { /* Marker for hot call. */ pc = (const BCIns *)((uintptr_t)pc & ~(uintptr_t)1); lj_trace_hot(J, pc); goto out; } else if (J->state != LJ_TRACE_IDLE && !(g->hookmask & (HOOK_GC|HOOK_VMEVENT))) { /* Record the FUNC* bytecodes, too. */ lj_trace_ins(J, pc-1); /* The interpreter bytecode PC is offset by 1. */ } #endif if ((g->hookmask & LUA_MASKCALL)) { int i; for (i = 0; i < missing; i++) /* Add missing parameters. */ setnilV(L->top++); callhook(L, LUA_HOOKCALL, -1); /* Preserve modifications of missing parameters by lua_setlocal(). */ while (missing-- > 0 && tvisnil(L->top - 1)) L->top--; } #if LJ_HASJIT out: #endif op = bc_op(pc[-1]); /* Get FUNC* op. */ #if LJ_HASJIT /* Use the non-hotcounting variants if JIT is off or while recording. */ if ((!(J->flags & JIT_F_ON) || J->state != LJ_TRACE_IDLE) && (op == BC_FUNCF || op == BC_FUNCV)) op = (BCOp)((int)op+(int)BC_IFUNCF-(int)BC_FUNCF); #endif return makeasmfunc(lj_bc_ofs[op]); /* Return static dispatch target. */ }
/* 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+LJ_FR2, top-1); copyTV(L, top-1, errfunc); if (LJ_FR2) setnilV(top++); L->top = top+1; lj_vm_call(L, top, 1+1); /* Stack: |errfunc|msg| -> |msg| */ } lj_err_throw(L, LUA_ERRRUN); }
static int io_file_close(lua_State *L, IOFileUD *iof) { int ok; if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) { ok = (fclose(iof->fp) == 0); } else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) { #if LJ_TARGET_POSIX ok = (pclose(iof->fp) != -1); #elif LJ_TARGET_WINDOWS ok = (_pclose(iof->fp) != -1); #else ok = 0; #endif } else { lua_assert((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF); setnilV(L->top++); lua_pushliteral(L, "cannot close standard file"); return 2; } iof->fp = NULL; return io_pushresult(L, ok, NULL); }
static int io_file_read(lua_State *L, FILE *fp, int start) { int ok, n, nargs = cast_int(L->top - L->base) - start; clearerr(fp); if (nargs == 0) { ok = io_file_readline(L, fp); n = start+1; /* Return 1 result. */ } else { /* The results plus the buffers go on top of the args. */ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); ok = 1; for (n = start; nargs-- && ok; n++) { if (tvisstr(L->base+n)) { const char *p = strVdata(L->base+n); if (p[0] != '*') lj_err_arg(L, n+1, LJ_ERR_INVOPT); if (p[1] == 'n') ok = io_file_readnum(L, fp); else if (p[1] == 'l') ok = io_file_readline(L, fp); else if (p[1] == 'a') io_file_readchars(L, fp, ~((size_t)0)); else lj_err_arg(L, n+1, LJ_ERR_INVFMT); } else if (tvisnum(L->base+n)) { size_t len = (size_t)lj_lib_checkint(L, n+1); ok = len ? io_file_readchars(L, fp, len) : io_file_testeof(L, fp); } else { lj_err_arg(L, n+1, LJ_ERR_INVOPT); } } } if (ferror(fp)) return io_pushresult(L, 0, NULL); if (!ok) setnilV(L->top-1); /* Replace last result with nil. */ return n - start; }
/* Synchronous abort with error message. */ void lj_trace_err(jit_State *J, TraceError e) { setnilV(&J->errinfo); /* No error info. */ setintV(J->L->top++, (int32_t)e); lj_err_throw(J->L, LUA_ERRRUN); }
/* 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; }
/* Restore interpreter state from exit state with the help of a snapshot. */ void lj_snap_restore(jit_State *J, void *exptr) { ExitState *ex = (ExitState *)exptr; SnapNo snapno = J->exitno; /* For now, snapno == exitno. */ Trace *T = J->trace[J->parent]; SnapShot *snap = &T->snap[snapno]; BCReg s, nslots = snap->nslots; IRRef2 *map = &T->snapmap[snap->mapofs]; IRRef2 *flinks = map + nslots + snap->nframelinks; TValue *o, *newbase, *ntop; BloomFilter rfilt = snap_renamefilter(T, snapno); lua_State *L = J->L; /* Make sure the stack is big enough for the slots from the snapshot. */ if (L->base + nslots >= L->maxstack) { L->top = curr_topL(L); lj_state_growstack(L, nslots - curr_proto(L)->framesize); } /* Fill stack slots with data from the registers and spill slots. */ newbase = NULL; ntop = L->base; for (s = 0, o = L->base-1; s < nslots; s++, o++) { IRRef ref = snap_ref(map[s]); if (ref) { IRIns *ir = &T->ir[ref]; if (irref_isk(ref)) { /* Restore constant slot. */ lj_ir_kvalue(L, o, ir); } else { IRType1 t = ir->t; RegSP rs = ir->prev; if (LJ_UNLIKELY(bloomtest(rfilt, ref))) rs = snap_renameref(T, snapno, ref, rs); if (ra_hasspill(regsp_spill(rs))) { /* Restore from spill slot. */ int32_t *sps = &ex->spill[regsp_spill(rs)]; if (irt_isinteger(t)) { setintV(o, *sps); } else if (irt_isnum(t)) { o->u64 = *(uint64_t *)sps; } else { lua_assert(!irt_ispri(t)); /* PRI refs never have a spill slot. */ setgcrefi(o->gcr, *sps); setitype(o, irt_toitype(t)); } } else if (ra_hasreg(regsp_reg(rs))) { /* Restore from register. */ Reg r = regsp_reg(rs); if (irt_isinteger(t)) { setintV(o, ex->gpr[r-RID_MIN_GPR]); } else if (irt_isnum(t)) { setnumV(o, ex->fpr[r-RID_MIN_FPR]); } else { if (!irt_ispri(t)) setgcrefi(o->gcr, ex->gpr[r-RID_MIN_GPR]); setitype(o, irt_toitype(t)); } } else { /* Restore frame slot. */ lua_assert(ir->o == IR_FRAME); /* This works for both PTR and FUNC IR_FRAME. */ setgcrefp(o->fr.func, mref(T->ir[ir->op2].ptr, void)); if (s != 0) /* Do not overwrite link to previous frame. */ o->fr.tp.ftsz = (int32_t)*--flinks; if (irt_isfunc(ir->t)) { GCfunc *fn = gco2func(gcref(T->ir[ir->op2].gcr)); if (isluafunc(fn)) { TValue *fs; newbase = o+1; fs = newbase + funcproto(fn)->framesize; if (fs > ntop) ntop = fs; /* Update top for newly added frames. */ } } } } } else if (newbase) { setnilV(o); /* Clear unreferenced slots of newly added frames. */ } }