/* ** Metamethod handling. ** Copyright (C) 2005-2009 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_bc.h" #include "lj_vm.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 i; for (i = 0, p = metanames; *p; i++, p = q) { GCstr *s; for (q = p+2; *q && *q != '_'; q++) ; s = lj_str_new(L, p, (size_t)(q-p)); fixstring(s); /* Never collect these names. */ /* NOBARRIER: g->mmname[] is a GC root. */ setgcref(g->mmname[i], 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 |= cast_byte(1u<metatable); else if (tvisudata(o)) mt = tabref(udataV(o)->metatable); else mt = tabref(G(L)->basemt[itypemap(o)]); if (mt) { cTValue *mo = lj_tab_getstr(mt, strref(G(L)->mmname[mm])); if (mo) return mo; } return niltv(L); } /* 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 (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 (tvistab(o)) { GCtab *t = tabV(o); TValue *tv = lj_tab_set(L, t, k); if (!tvisnil(tv) || !(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) { if (isblack(obj2gco(t))) lj_gc_barrierback(G(L), t); return tv; } } 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 (tvisstr(o) && lj_str_numconv(strVdata(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 (tvisnum(o)) { setstrV(L, o, lj_str_fromnum(L, &o->n)); return 1; } else { return 0; } } /* 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) || tvisnum(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) || tvisnum(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; } /* Helper for LEN. __len metamethod. */ TValue *lj_meta_len(lua_State *L, cTValue *o) { cTValue *mo = lj_meta_lookup(L, o, MM_len); if (tvisnil(mo)) { lj_err_optype(L, o, LJ_ERR_OPLEN); return NULL; /* unreachable */ } return mmcall(L, lj_cont_ra, mo, 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; int 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 cast(TValue *, (intptr_t)ne); } top = curr_top(L); setcont(top, ne ? lj_cont_condf : lj_cont_condt); copyTV(L, top+1, mo); it = o1->gch.gct == ~LJ_TTAB ? LJ_TTAB : LJ_TUDATA; setgcV(L, top+2, &o1->gch, it); setgcV(L, top+3, &o2->gch, it); return top+2; /* Trigger metamethod call. */ } return cast(TValue *, (intptr_t)ne); } /* Helper for ordered comparisons. String compare, __lt/__le metamethods. */ TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op) { if (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 cast(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); cTValue *mo2 = lj_meta_lookup(L, o2, mm); if (tvisnil(mo) || !lj_obj_equal(mo, mo2)) { 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_meta_for(lua_State *L, TValue *base) { if (!str2num(base, base)) lj_err_msg(L, LJ_ERR_FORINIT); if (!str2num(base+1, base+1)) lj_err_msg(L, LJ_ERR_FORLIM); if (!str2num(base+2, base+2)) lj_err_msg(L, LJ_ERR_FORSTEP); }