RELEASE LuaJIT-2.0.0-beta1
This commit is contained in:
358
src/lj_meta.c
Normal file
358
src/lj_meta.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
** 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<<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(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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user