|  | /* | 
|  | ** Metamethod handling. | 
|  | ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h | 
|  | ** | 
|  | ** 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_meta_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_meta.h" | 
|  | #include "lj_frame.h" | 
|  | #include "lj_bc.h" | 
|  | #include "lj_vm.h" | 
|  | #include "lj_strscan.h" | 
|  |  | 
|  | /* -- Metamethod handling ------------------------------------------------- */ | 
|  |  | 
|  | /* 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)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Negative caching of a few fast metamethods. See the lj_meta_fast() macro. */ | 
|  | cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name) | 
|  | { | 
|  | cTValue *mo = lj_tab_getstr(mt, name); | 
|  | lua_assert(mm <= MM_FAST); | 
|  | if (!mo || tvisnil(mo)) {  /* No metamethod? */ | 
|  | mt->nomm |= (uint8_t)(1u<<mm);  /* Set negative cache flag. */ | 
|  | return NULL; | 
|  | } | 
|  | return mo; | 
|  | } | 
|  |  | 
|  | /* Lookup metamethod for object. */ | 
|  | cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm) | 
|  | { | 
|  | GCtab *mt; | 
|  | if (tvistab(o)) | 
|  | mt = tabref(tabV(o)->metatable); | 
|  | else if (tvisudata(o)) | 
|  | mt = tabref(udataV(o)->metatable); | 
|  | else | 
|  | mt = tabref(basemt_obj(G(L), o)); | 
|  | if (mt) { | 
|  | cTValue *mo = lj_tab_getstr(mt, mmname_str(G(L), mm)); | 
|  | if (mo) | 
|  | return mo; | 
|  | } | 
|  | return niltv(L); | 
|  | } | 
|  |  | 
|  | #if LJ_HASFFI | 
|  | /* Tailcall from C function. */ | 
|  | int lj_meta_tailcall(lua_State *L, cTValue *tv) | 
|  | { | 
|  | TValue *base = L->base; | 
|  | TValue *top = L->top; | 
|  | const BCIns *pc = frame_pc(base-1);  /* Preserve old PC from frame. */ | 
|  | copyTV(L, base-1, tv);  /* Replace frame with new object. */ | 
|  | top->u32.lo = LJ_CONT_TAILCALL; | 
|  | setframe_pc(top, pc); | 
|  | setframe_gc(top+1, obj2gco(L));  /* Dummy frame object. */ | 
|  | setframe_ftsz(top+1, (int)((char *)(top+2) - (char *)base) + FRAME_CONT); | 
|  | L->base = L->top = top+2; | 
|  | /* | 
|  | ** before:   [old_mo|PC]    [... ...] | 
|  | **                         ^base     ^top | 
|  | ** after:    [new_mo|itype] [... ...] [NULL|PC] [dummy|delta] | 
|  | **                                                           ^base/top | 
|  | ** tailcall: [new_mo|PC]    [... ...] | 
|  | **                         ^base     ^top | 
|  | */ | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* 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. */ | 
|  | copyTV(L, top+1, mo);  /* Store metamethod and two arguments. */ | 
|  | copyTV(L, top+2, a); | 
|  | copyTV(L, top+3, b); | 
|  | return top+2;  /* Return new base. */ | 
|  | } | 
|  |  | 
|  | /* -- C helpers for some instructions, called from assembler VM ----------- */ | 
|  |  | 
|  | /* Helper for TGET*. __index chain and metamethod. */ | 
|  | cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k) | 
|  | { | 
|  | int loop; | 
|  | for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) { | 
|  | cTValue *mo; | 
|  | if (LJ_LIKELY(tvistab(o))) { | 
|  | GCtab *t = tabV(o); | 
|  | cTValue *tv = lj_tab_get(L, t, k); | 
|  | if (!tvisnil(tv) || | 
|  | !(mo = lj_meta_fast(L, tabref(t->metatable), MM_index))) | 
|  | return tv; | 
|  | } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_index))) { | 
|  | lj_err_optype(L, o, LJ_ERR_OPINDEX); | 
|  | return NULL;  /* unreachable */ | 
|  | } | 
|  | if (tvisfunc(mo)) { | 
|  | L->top = mmcall(L, lj_cont_ra, mo, o, k); | 
|  | return NULL;  /* Trigger metamethod call. */ | 
|  | } | 
|  | o = mo; | 
|  | } | 
|  | lj_err_msg(L, LJ_ERR_GETLOOP); | 
|  | return NULL;  /* unreachable */ | 
|  | } | 
|  |  | 
|  | /* Helper for TSET*. __newindex chain and metamethod. */ | 
|  | TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k) | 
|  | { | 
|  | TValue tmp; | 
|  | int loop; | 
|  | for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) { | 
|  | cTValue *mo; | 
|  | if (LJ_LIKELY(tvistab(o))) { | 
|  | GCtab *t = tabV(o); | 
|  | cTValue *tv = lj_tab_get(L, t, k); | 
|  | if (LJ_LIKELY(!tvisnil(tv))) { | 
|  | t->nomm = 0;  /* Invalidate negative metamethod cache. */ | 
|  | lj_gc_anybarriert(L, t); | 
|  | return (TValue *)tv; | 
|  | } else if (!(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) { | 
|  | t->nomm = 0;  /* Invalidate negative metamethod cache. */ | 
|  | lj_gc_anybarriert(L, t); | 
|  | if (tv != niltv(L)) | 
|  | return (TValue *)tv; | 
|  | if (tvisnil(k)) lj_err_msg(L, LJ_ERR_NILIDX); | 
|  | else if (tvisint(k)) { setnumV(&tmp, (lua_Number)intV(k)); k = &tmp; } | 
|  | else if (tvisnum(k) && tvisnan(k)) lj_err_msg(L, LJ_ERR_NANIDX); | 
|  | return lj_tab_newkey(L, t, k); | 
|  | } | 
|  | } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_newindex))) { | 
|  | lj_err_optype(L, o, LJ_ERR_OPINDEX); | 
|  | return NULL;  /* unreachable */ | 
|  | } | 
|  | if (tvisfunc(mo)) { | 
|  | L->top = mmcall(L, lj_cont_nop, mo, o, k); | 
|  | /* L->top+2 = v filled in by caller. */ | 
|  | return NULL;  /* Trigger metamethod call. */ | 
|  | } | 
|  | copyTV(L, &tmp, mo); | 
|  | o = &tmp; | 
|  | } | 
|  | lj_err_msg(L, LJ_ERR_SETLOOP); | 
|  | return NULL;  /* unreachable */ | 
|  | } | 
|  |  | 
|  | static cTValue *str2num(cTValue *o, TValue *n) | 
|  | { | 
|  | if (tvisnum(o)) | 
|  | return o; | 
|  | else if (tvisint(o)) | 
|  | return (setnumV(n, (lua_Number)intV(o)), n); | 
|  | else if (tvisstr(o) && lj_strscan_num(strV(o), n)) | 
|  | return n; | 
|  | else | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Helper for arithmetic instructions. Coercion, metamethod. */ | 
|  | TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, | 
|  | BCReg op) | 
|  | { | 
|  | MMS mm = bcmode_mm(op); | 
|  | TValue tempb, tempc; | 
|  | cTValue *b, *c; | 
|  | if ((b = str2num(rb, &tempb)) != NULL && | 
|  | (c = str2num(rc, &tempc)) != NULL) {  /* Try coercion first. */ | 
|  | setnumV(ra, lj_vm_foldarith(numV(b), numV(c), (int)mm-MM_add)); | 
|  | return NULL; | 
|  | } else { | 
|  | cTValue *mo = lj_meta_lookup(L, rb, mm); | 
|  | if (tvisnil(mo)) { | 
|  | mo = lj_meta_lookup(L, rc, mm); | 
|  | if (tvisnil(mo)) { | 
|  | if (str2num(rb, &tempb) == NULL) rc = rb; | 
|  | lj_err_optype(L, rc, LJ_ERR_OPARITH); | 
|  | return NULL;  /* unreachable */ | 
|  | } | 
|  | } | 
|  | return mmcall(L, lj_cont_ra, mo, rb, rc); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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 { | 
|  | 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); | 
|  | 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 LEN. __len metamethod. */ | 
|  | TValue * LJ_FASTCALL lj_meta_len(lua_State *L, cTValue *o) | 
|  | { | 
|  | cTValue *mo = lj_meta_lookup(L, o, MM_len); | 
|  | if (tvisnil(mo)) { | 
|  | if (LJ_52 && tvistab(o)) | 
|  | tabref(tabV(o)->metatable)->nomm |= (uint8_t)(1u<<MM_len); | 
|  | else | 
|  | lj_err_optype(L, o, LJ_ERR_OPLEN); | 
|  | return NULL; | 
|  | } | 
|  | return mmcall(L, lj_cont_ra, mo, o, LJ_52 ? o : niltv(L)); | 
|  | } | 
|  |  | 
|  | /* 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); | 
|  | copyTV(L, top+1, mo); | 
|  | it = ~(uint32_t)o1->gch.gct; | 
|  | setgcV(L, top+2, o1, it); | 
|  | setgcV(L, top+3, o2, it); | 
|  | return top+2;  /* Trigger metamethod call. */ | 
|  | } | 
|  | return (TValue *)(intptr_t)ne; | 
|  | } | 
|  |  | 
|  | #if LJ_HASFFI | 
|  | 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); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Helper for ordered comparisons. String compare, __lt/__le metamethods. */ | 
|  | TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op) | 
|  | { | 
|  | if (LJ_HASFFI && (tviscdata(o1) || tviscdata(o2))) { | 
|  | ASMFunction cont = (op & 1) ? lj_cont_condf : lj_cont_condt; | 
|  | MMS mm = (op & 2) ? MM_le : MM_lt; | 
|  | cTValue *mo = lj_meta_lookup(L, tviscdata(o1) ? o1 : o2, mm); | 
|  | if (LJ_UNLIKELY(tvisnil(mo))) goto err; | 
|  | return mmcall(L, cont, mo, o1, o2); | 
|  | } else if (LJ_52 || itype(o1) == itype(o2)) { | 
|  | /* Never called with two numbers. */ | 
|  | if (tvisstr(o1) && tvisstr(o2)) { | 
|  | int32_t res = lj_str_cmp(strV(o1), strV(o2)); | 
|  | return (TValue *)(intptr_t)(((op&2) ? res <= 0 : res < 0) ^ (op&1)); | 
|  | } else { | 
|  | trymt: | 
|  | while (1) { | 
|  | ASMFunction cont = (op & 1) ? lj_cont_condf : lj_cont_condt; | 
|  | MMS mm = (op & 2) ? MM_le : MM_lt; | 
|  | cTValue *mo = lj_meta_lookup(L, o1, mm); | 
|  | #if LJ_52 | 
|  | if (tvisnil(mo) && tvisnil((mo = lj_meta_lookup(L, o2, mm)))) | 
|  | #else | 
|  | cTValue *mo2 = lj_meta_lookup(L, o2, mm); | 
|  | if (tvisnil(mo) || !lj_obj_equal(mo, mo2)) | 
|  | #endif | 
|  | { | 
|  | if (op & 2) {  /* MM_le not found: retry with MM_lt. */ | 
|  | cTValue *ot = o1; o1 = o2; o2 = ot;  /* Swap operands. */ | 
|  | op ^= 3;  /* Use LT and flip condition. */ | 
|  | continue; | 
|  | } | 
|  | goto err; | 
|  | } | 
|  | return mmcall(L, cont, mo, o1, o2); | 
|  | } | 
|  | } | 
|  | } else if (tvisbool(o1) && tvisbool(o2)) { | 
|  | goto trymt; | 
|  | } else { | 
|  | err: | 
|  | lj_err_comp(L, o1, o2); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Helper for calls. __call metamethod. */ | 
|  | void lj_meta_call(lua_State *L, TValue *func, TValue *top) | 
|  | { | 
|  | cTValue *mo = lj_meta_lookup(L, func, MM_call); | 
|  | TValue *p; | 
|  | if (!tvisfunc(mo)) | 
|  | lj_err_optype_call(L, func); | 
|  | for (p = top; p > func; p--) copyTV(L, p, p-1); | 
|  | copyTV(L, func, mo); | 
|  | } | 
|  |  | 
|  | /* Helper for FORI. Coercion. */ | 
|  | void LJ_FASTCALL lj_meta_for(lua_State *L, TValue *o) | 
|  | { | 
|  | if (!lj_strscan_numberobj(o)) lj_err_msg(L, LJ_ERR_FORINIT); | 
|  | if (!lj_strscan_numberobj(o+1)) lj_err_msg(L, LJ_ERR_FORLIM); | 
|  | if (!lj_strscan_numberobj(o+2)) lj_err_msg(L, LJ_ERR_FORSTEP); | 
|  | if (LJ_DUALNUM) { | 
|  | /* Ensure all slots are integers or all slots are numbers. */ | 
|  | int32_t k[3]; | 
|  | int nint = 0; | 
|  | ptrdiff_t i; | 
|  | for (i = 0; i <= 2; i++) { | 
|  | if (tvisint(o+i)) { | 
|  | k[i] = intV(o+i); nint++; | 
|  | } else { | 
|  | k[i] = lj_num2int(numV(o+i)); nint += ((lua_Number)k[i] == numV(o+i)); | 
|  | } | 
|  | } | 
|  | if (nint == 3) {  /* Narrow to integers. */ | 
|  | setintV(o, k[0]); | 
|  | setintV(o+1, k[1]); | 
|  | setintV(o+2, k[2]); | 
|  | } else if (nint != 0) {  /* Widen to numbers. */ | 
|  | if (tvisint(o)) setnumV(o, (lua_Number)intV(o)); | 
|  | if (tvisint(o+1)) setnumV(o+1, (lua_Number)intV(o+1)); | 
|  | if (tvisint(o+2)) setnumV(o+2, (lua_Number)intV(o+2)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  |