diff options
Diffstat (limited to 'src/lj_meta.c')
-rw-r--r-- | src/lj_meta.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/src/lj_meta.c b/src/lj_meta.c new file mode 100644 index 00000000..dff01f85 --- /dev/null +++ b/src/lj_meta.c | |||
@@ -0,0 +1,358 @@ | |||
1 | /* | ||
2 | ** Metamethod handling. | ||
3 | ** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h | ||
4 | ** | ||
5 | ** Portions taken verbatim or adapted from the Lua interpreter. | ||
6 | ** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h | ||
7 | */ | ||
8 | |||
9 | #define lj_meta_c | ||
10 | #define LUA_CORE | ||
11 | |||
12 | #include "lj_obj.h" | ||
13 | #include "lj_gc.h" | ||
14 | #include "lj_err.h" | ||
15 | #include "lj_str.h" | ||
16 | #include "lj_tab.h" | ||
17 | #include "lj_meta.h" | ||
18 | #include "lj_bc.h" | ||
19 | #include "lj_vm.h" | ||
20 | |||
21 | /* -- Metamethod handling ------------------------------------------------- */ | ||
22 | |||
23 | /* String interning of metamethod names for fast indexing. */ | ||
24 | void lj_meta_init(lua_State *L) | ||
25 | { | ||
26 | #define MMNAME(name) "__" #name | ||
27 | const char *metanames = MMDEF(MMNAME); | ||
28 | #undef MMNAME | ||
29 | global_State *g = G(L); | ||
30 | const char *p, *q; | ||
31 | uint32_t i; | ||
32 | for (i = 0, p = metanames; *p; i++, p = q) { | ||
33 | GCstr *s; | ||
34 | for (q = p+2; *q && *q != '_'; q++) ; | ||
35 | s = lj_str_new(L, p, (size_t)(q-p)); | ||
36 | fixstring(s); /* Never collect these names. */ | ||
37 | /* NOBARRIER: g->mmname[] is a GC root. */ | ||
38 | setgcref(g->mmname[i], obj2gco(s)); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | /* Negative caching of a few fast metamethods. See the lj_meta_fast() macro. */ | ||
43 | cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name) | ||
44 | { | ||
45 | cTValue *mo = lj_tab_getstr(mt, name); | ||
46 | lua_assert(mm <= MM_FAST); | ||
47 | if (!mo || tvisnil(mo)) { /* No metamethod? */ | ||
48 | mt->nomm |= cast_byte(1u<<mm); /* Set negative cache flag. */ | ||
49 | return NULL; | ||
50 | } | ||
51 | return mo; | ||
52 | } | ||
53 | |||
54 | /* Lookup metamethod for object. */ | ||
55 | cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm) | ||
56 | { | ||
57 | GCtab *mt; | ||
58 | if (tvistab(o)) | ||
59 | mt = tabref(tabV(o)->metatable); | ||
60 | else if (tvisudata(o)) | ||
61 | mt = tabref(udataV(o)->metatable); | ||
62 | else | ||
63 | mt = tabref(G(L)->basemt[itypemap(o)]); | ||
64 | if (mt) { | ||
65 | cTValue *mo = lj_tab_getstr(mt, strref(G(L)->mmname[mm])); | ||
66 | if (mo) | ||
67 | return mo; | ||
68 | } | ||
69 | return niltv(L); | ||
70 | } | ||
71 | |||
72 | /* Setup call to metamethod to be run by Assembler VM. */ | ||
73 | static TValue *mmcall(lua_State *L, ASMFunction cont, cTValue *mo, | ||
74 | cTValue *a, cTValue *b) | ||
75 | { | ||
76 | /* | ||
77 | ** |-- framesize -> top top+1 top+2 top+3 | ||
78 | ** before: [func slots ...] | ||
79 | ** mm setup: [func slots ...] [cont|?] [mo|tmtype] [a] [b] | ||
80 | ** in asm: [func slots ...] [cont|PC] [mo|delta] [a] [b] | ||
81 | ** ^-- func base ^-- mm base | ||
82 | ** after mm: [func slots ...] [result] | ||
83 | ** ^-- copy to base[PC_RA] --/ for lj_cont_ra | ||
84 | ** istruecond + branch for lj_cont_cond* | ||
85 | ** ignore for lj_cont_nop | ||
86 | ** next PC: [func slots ...] | ||
87 | */ | ||
88 | TValue *top = L->top; | ||
89 | if (curr_funcisL(L)) top = curr_topL(L); | ||
90 | setcont(top, cont); /* Assembler VM stores PC in upper word. */ | ||
91 | copyTV(L, top+1, mo); /* Store metamethod and two arguments. */ | ||
92 | copyTV(L, top+2, a); | ||
93 | copyTV(L, top+3, b); | ||
94 | return top+2; /* Return new base. */ | ||
95 | } | ||
96 | |||
97 | /* -- C helpers for some instructions, called from assembler VM ----------- */ | ||
98 | |||
99 | /* Helper for TGET*. __index chain and metamethod. */ | ||
100 | cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k) | ||
101 | { | ||
102 | int loop; | ||
103 | for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) { | ||
104 | cTValue *mo; | ||
105 | if (tvistab(o)) { | ||
106 | GCtab *t = tabV(o); | ||
107 | cTValue *tv = lj_tab_get(L, t, k); | ||
108 | if (!tvisnil(tv) || | ||
109 | !(mo = lj_meta_fast(L, tabref(t->metatable), MM_index))) | ||
110 | return tv; | ||
111 | } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_index))) { | ||
112 | lj_err_optype(L, o, LJ_ERR_OPINDEX); | ||
113 | return NULL; /* unreachable */ | ||
114 | } | ||
115 | if (tvisfunc(mo)) { | ||
116 | L->top = mmcall(L, lj_cont_ra, mo, o, k); | ||
117 | return NULL; /* Trigger metamethod call. */ | ||
118 | } | ||
119 | o = mo; | ||
120 | } | ||
121 | lj_err_msg(L, LJ_ERR_GETLOOP); | ||
122 | return NULL; /* unreachable */ | ||
123 | } | ||
124 | |||
125 | /* Helper for TSET*. __newindex chain and metamethod. */ | ||
126 | TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k) | ||
127 | { | ||
128 | TValue tmp; | ||
129 | int loop; | ||
130 | for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) { | ||
131 | cTValue *mo; | ||
132 | if (tvistab(o)) { | ||
133 | GCtab *t = tabV(o); | ||
134 | TValue *tv = lj_tab_set(L, t, k); | ||
135 | if (!tvisnil(tv) || | ||
136 | !(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) { | ||
137 | if (isblack(obj2gco(t))) lj_gc_barrierback(G(L), t); | ||
138 | return tv; | ||
139 | } | ||
140 | } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_newindex))) { | ||
141 | lj_err_optype(L, o, LJ_ERR_OPINDEX); | ||
142 | return NULL; /* unreachable */ | ||
143 | } | ||
144 | if (tvisfunc(mo)) { | ||
145 | L->top = mmcall(L, lj_cont_nop, mo, o, k); | ||
146 | /* L->top+2 = v filled in by caller. */ | ||
147 | return NULL; /* Trigger metamethod call. */ | ||
148 | } | ||
149 | copyTV(L, &tmp, mo); | ||
150 | o = &tmp; | ||
151 | } | ||
152 | lj_err_msg(L, LJ_ERR_SETLOOP); | ||
153 | return NULL; /* unreachable */ | ||
154 | } | ||
155 | |||
156 | static cTValue *str2num(cTValue *o, TValue *n) | ||
157 | { | ||
158 | if (tvisnum(o)) | ||
159 | return o; | ||
160 | else if (tvisstr(o) && lj_str_numconv(strVdata(o), n)) | ||
161 | return n; | ||
162 | else | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | /* Helper for arithmetic instructions. Coercion, metamethod. */ | ||
167 | TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, | ||
168 | BCReg op) | ||
169 | { | ||
170 | MMS mm = bcmode_mm(op); | ||
171 | TValue tempb, tempc; | ||
172 | cTValue *b, *c; | ||
173 | if ((b = str2num(rb, &tempb)) != NULL && | ||
174 | (c = str2num(rc, &tempc)) != NULL) { /* Try coercion first. */ | ||
175 | setnumV(ra, lj_vm_foldarith(numV(b), numV(c), (int)mm-MM_add)); | ||
176 | return NULL; | ||
177 | } else { | ||
178 | cTValue *mo = lj_meta_lookup(L, rb, mm); | ||
179 | if (tvisnil(mo)) { | ||
180 | mo = lj_meta_lookup(L, rc, mm); | ||
181 | if (tvisnil(mo)) { | ||
182 | if (str2num(rb, &tempb) == NULL) rc = rb; | ||
183 | lj_err_optype(L, rc, LJ_ERR_OPARITH); | ||
184 | return NULL; /* unreachable */ | ||
185 | } | ||
186 | } | ||
187 | return mmcall(L, lj_cont_ra, mo, rb, rc); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /* In-place coercion of a number to a string. */ | ||
192 | static LJ_AINLINE int tostring(lua_State *L, TValue *o) | ||
193 | { | ||
194 | if (tvisstr(o)) { | ||
195 | return 1; | ||
196 | } else if (tvisnum(o)) { | ||
197 | setstrV(L, o, lj_str_fromnum(L, &o->n)); | ||
198 | return 1; | ||
199 | } else { | ||
200 | return 0; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | /* Helper for CAT. Coercion, iterative concat, __concat metamethod. */ | ||
205 | TValue *lj_meta_cat(lua_State *L, TValue *top, int left) | ||
206 | { | ||
207 | do { | ||
208 | int n = 1; | ||
209 | if (!(tvisstr(top-1) || tvisnum(top-1)) || !tostring(L, top)) { | ||
210 | cTValue *mo = lj_meta_lookup(L, top-1, MM_concat); | ||
211 | if (tvisnil(mo)) { | ||
212 | mo = lj_meta_lookup(L, top, MM_concat); | ||
213 | if (tvisnil(mo)) { | ||
214 | if (tvisstr(top-1) || tvisnum(top-1)) top++; | ||
215 | lj_err_optype(L, top-1, LJ_ERR_OPCAT); | ||
216 | return NULL; /* unreachable */ | ||
217 | } | ||
218 | } | ||
219 | /* One of the top two elements is not a string, call __cat metamethod: | ||
220 | ** | ||
221 | ** before: [...][CAT stack .........................] | ||
222 | ** top-1 top top+1 top+2 | ||
223 | ** pick two: [...][CAT stack ...] [o1] [o2] | ||
224 | ** setup mm: [...][CAT stack ...] [cont|?] [mo|tmtype] [o1] [o2] | ||
225 | ** in asm: [...][CAT stack ...] [cont|PC] [mo|delta] [o1] [o2] | ||
226 | ** ^-- func base ^-- mm base | ||
227 | ** after mm: [...][CAT stack ...] <--push-- [result] | ||
228 | ** next step: [...][CAT stack .............] | ||
229 | */ | ||
230 | copyTV(L, top+2, top) /* Careful with the order of stack copies! */ | ||
231 | copyTV(L, top+1, top-1) | ||
232 | copyTV(L, top, mo) | ||
233 | setcont(top-1, lj_cont_cat); | ||
234 | return top+1; /* Trigger metamethod call. */ | ||
235 | } else if (strV(top)->len == 0) { /* Shortcut. */ | ||
236 | (void)tostring(L, top-1); | ||
237 | } else { | ||
238 | /* Pick as many strings as possible from the top and concatenate them: | ||
239 | ** | ||
240 | ** before: [...][CAT stack ...........................] | ||
241 | ** pick str: [...][CAT stack ...] [...... strings ......] | ||
242 | ** concat: [...][CAT stack ...] [result] | ||
243 | ** next step: [...][CAT stack ............] | ||
244 | */ | ||
245 | MSize tlen = strV(top)->len; | ||
246 | char *buffer; | ||
247 | int i; | ||
248 | for (n = 1; n <= left && tostring(L, top-n); n++) { | ||
249 | MSize len = strV(top-n)->len; | ||
250 | if (len >= LJ_MAX_STR - tlen) | ||
251 | lj_err_msg(L, LJ_ERR_STROV); | ||
252 | tlen += len; | ||
253 | } | ||
254 | buffer = lj_str_needbuf(L, &G(L)->tmpbuf, tlen); | ||
255 | n--; | ||
256 | tlen = 0; | ||
257 | for (i = n; i >= 0; i--) { | ||
258 | MSize len = strV(top-i)->len; | ||
259 | memcpy(buffer + tlen, strVdata(top-i), len); | ||
260 | tlen += len; | ||
261 | } | ||
262 | setstrV(L, top-n, lj_str_new(L, buffer, tlen)); | ||
263 | } | ||
264 | left -= n; | ||
265 | top -= n; | ||
266 | } while (left >= 1); | ||
267 | lj_gc_check_fixtop(L); | ||
268 | return NULL; | ||
269 | } | ||
270 | |||
271 | /* Helper for LEN. __len metamethod. */ | ||
272 | TValue *lj_meta_len(lua_State *L, cTValue *o) | ||
273 | { | ||
274 | cTValue *mo = lj_meta_lookup(L, o, MM_len); | ||
275 | if (tvisnil(mo)) { | ||
276 | lj_err_optype(L, o, LJ_ERR_OPLEN); | ||
277 | return NULL; /* unreachable */ | ||
278 | } | ||
279 | return mmcall(L, lj_cont_ra, mo, o, niltv(L)); | ||
280 | } | ||
281 | |||
282 | /* Helper for equality comparisons. __eq metamethod. */ | ||
283 | TValue *lj_meta_equal(lua_State *L, GCobj *o1, GCobj *o2, int ne) | ||
284 | { | ||
285 | /* Field metatable must be at same offset for GCtab and GCudata! */ | ||
286 | cTValue *mo = lj_meta_fast(L, tabref(o1->gch.metatable), MM_eq); | ||
287 | if (mo) { | ||
288 | TValue *top; | ||
289 | int it; | ||
290 | if (tabref(o1->gch.metatable) != tabref(o2->gch.metatable)) { | ||
291 | cTValue *mo2 = lj_meta_fast(L, tabref(o2->gch.metatable), MM_eq); | ||
292 | if (mo2 == NULL || !lj_obj_equal(mo, mo2)) | ||
293 | return cast(TValue *, (intptr_t)ne); | ||
294 | } | ||
295 | top = curr_top(L); | ||
296 | setcont(top, ne ? lj_cont_condf : lj_cont_condt); | ||
297 | copyTV(L, top+1, mo); | ||
298 | it = o1->gch.gct == ~LJ_TTAB ? LJ_TTAB : LJ_TUDATA; | ||
299 | setgcV(L, top+2, &o1->gch, it); | ||
300 | setgcV(L, top+3, &o2->gch, it); | ||
301 | return top+2; /* Trigger metamethod call. */ | ||
302 | } | ||
303 | return cast(TValue *, (intptr_t)ne); | ||
304 | } | ||
305 | |||
306 | /* Helper for ordered comparisons. String compare, __lt/__le metamethods. */ | ||
307 | TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op) | ||
308 | { | ||
309 | if (itype(o1) == itype(o2)) { /* Never called with two numbers. */ | ||
310 | if (tvisstr(o1) && tvisstr(o2)) { | ||
311 | int32_t res = lj_str_cmp(strV(o1), strV(o2)); | ||
312 | return cast(TValue *, (intptr_t)(((op&2) ? res <= 0 : res < 0) ^ (op&1))); | ||
313 | } else { | ||
314 | trymt: | ||
315 | while (1) { | ||
316 | ASMFunction cont = (op & 1) ? lj_cont_condf : lj_cont_condt; | ||
317 | MMS mm = (op & 2) ? MM_le : MM_lt; | ||
318 | cTValue *mo = lj_meta_lookup(L, o1, mm); | ||
319 | cTValue *mo2 = lj_meta_lookup(L, o2, mm); | ||
320 | if (tvisnil(mo) || !lj_obj_equal(mo, mo2)) { | ||
321 | if (op & 2) { /* MM_le not found: retry with MM_lt. */ | ||
322 | cTValue *ot = o1; o1 = o2; o2 = ot; /* Swap operands. */ | ||
323 | op ^= 3; /* Use LT and flip condition. */ | ||
324 | continue; | ||
325 | } | ||
326 | goto err; | ||
327 | } | ||
328 | return mmcall(L, cont, mo, o1, o2); | ||
329 | } | ||
330 | } | ||
331 | } else if (tvisbool(o1) && tvisbool(o2)) { | ||
332 | goto trymt; | ||
333 | } else { | ||
334 | err: | ||
335 | lj_err_comp(L, o1, o2); | ||
336 | return NULL; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | /* Helper for calls. __call metamethod. */ | ||
341 | void lj_meta_call(lua_State *L, TValue *func, TValue *top) | ||
342 | { | ||
343 | cTValue *mo = lj_meta_lookup(L, func, MM_call); | ||
344 | TValue *p; | ||
345 | if (!tvisfunc(mo)) | ||
346 | lj_err_optype_call(L, func); | ||
347 | for (p = top; p > func; p--) copyTV(L, p, p-1); | ||
348 | copyTV(L, func, mo); | ||
349 | } | ||
350 | |||
351 | /* Helper for FORI. Coercion. */ | ||
352 | void lj_meta_for(lua_State *L, TValue *base) | ||
353 | { | ||
354 | if (!str2num(base, base)) lj_err_msg(L, LJ_ERR_FORINIT); | ||
355 | if (!str2num(base+1, base+1)) lj_err_msg(L, LJ_ERR_FORLIM); | ||
356 | if (!str2num(base+2, base+2)) lj_err_msg(L, LJ_ERR_FORSTEP); | ||
357 | } | ||
358 | |||