1 | /* |
2 | ** C data management. |
3 | ** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h |
4 | */ |
5 | |
6 | #include "lj_obj.h" |
7 | |
8 | #if LJ_HASFFI |
9 | |
10 | #include "lj_gc.h" |
11 | #include "lj_err.h" |
12 | #include "lj_tab.h" |
13 | #include "lj_ctype.h" |
14 | #include "lj_cconv.h" |
15 | #include "lj_cdata.h" |
16 | |
17 | /* -- C data allocation --------------------------------------------------- */ |
18 | |
19 | /* Allocate a new C data object holding a reference to another object. */ |
20 | GCcdata *lj_cdata_newref(CTState *cts, const void *p, CTypeID id) |
21 | { |
22 | CTypeID refid = lj_ctype_intern(cts, CTINFO_REF(id), CTSIZE_PTR); |
23 | GCcdata *cd = lj_cdata_new(cts, refid, CTSIZE_PTR); |
24 | *(const void **)cdataptr(cd) = p; |
25 | return cd; |
26 | } |
27 | |
28 | /* Allocate variable-sized or specially aligned C data object. */ |
29 | GCcdata *lj_cdata_newv(lua_State *L, CTypeID id, CTSize sz, CTSize align) |
30 | { |
31 | global_State *g; |
32 | MSize = sizeof(GCcdataVar) + sizeof(GCcdata) + |
33 | (align > CT_MEMALIGN ? (1u<<align) - (1u<<CT_MEMALIGN) : 0); |
34 | char *p = lj_mem_newt(L, extra + sz, char); |
35 | uintptr_t adata = (uintptr_t)p + sizeof(GCcdataVar) + sizeof(GCcdata); |
36 | uintptr_t almask = (1u << align) - 1u; |
37 | GCcdata *cd = (GCcdata *)(((adata + almask) & ~almask) - sizeof(GCcdata)); |
38 | lj_assertL((char *)cd - p < 65536, "excessive cdata alignment" ); |
39 | cdatav(cd)->offset = (uint16_t)((char *)cd - p); |
40 | cdatav(cd)->extra = extra; |
41 | cdatav(cd)->len = sz; |
42 | g = G(L); |
43 | setgcrefr(cd->nextgc, g->gc.root); |
44 | setgcref(g->gc.root, obj2gco(cd)); |
45 | newwhite(g, obj2gco(cd)); |
46 | cd->marked |= 0x80; |
47 | cd->gct = ~LJ_TCDATA; |
48 | cd->ctypeid = id; |
49 | return cd; |
50 | } |
51 | |
52 | /* Allocate arbitrary C data object. */ |
53 | GCcdata *lj_cdata_newx(CTState *cts, CTypeID id, CTSize sz, CTInfo info) |
54 | { |
55 | if (!(info & CTF_VLA) && ctype_align(info) <= CT_MEMALIGN) |
56 | return lj_cdata_new(cts, id, sz); |
57 | else |
58 | return lj_cdata_newv(cts->L, id, sz, ctype_align(info)); |
59 | } |
60 | |
61 | /* Free a C data object. */ |
62 | void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) |
63 | { |
64 | if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) { |
65 | GCobj *root; |
66 | makewhite(g, obj2gco(cd)); |
67 | markfinalized(obj2gco(cd)); |
68 | if ((root = gcref(g->gc.mmudata)) != NULL) { |
69 | setgcrefr(cd->nextgc, root->gch.nextgc); |
70 | setgcref(root->gch.nextgc, obj2gco(cd)); |
71 | setgcref(g->gc.mmudata, obj2gco(cd)); |
72 | } else { |
73 | setgcref(cd->nextgc, obj2gco(cd)); |
74 | setgcref(g->gc.mmudata, obj2gco(cd)); |
75 | } |
76 | } else if (LJ_LIKELY(!cdataisv(cd))) { |
77 | CType *ct = ctype_raw(ctype_ctsG(g), cd->ctypeid); |
78 | CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR; |
79 | lj_assertG(ctype_hassize(ct->info) || ctype_isfunc(ct->info) || |
80 | ctype_isextern(ct->info), "free of ctype without a size" ); |
81 | lj_mem_free(g, cd, sizeof(GCcdata) + sz); |
82 | } else { |
83 | lj_mem_free(g, memcdatav(cd), sizecdatav(cd)); |
84 | } |
85 | } |
86 | |
87 | void lj_cdata_setfin(lua_State *L, GCcdata *cd, GCobj *obj, uint32_t it) |
88 | { |
89 | GCtab *t = ctype_ctsG(G(L))->finalizer; |
90 | if (gcref(t->metatable)) { |
91 | /* Add cdata to finalizer table, if still enabled. */ |
92 | TValue *tv, tmp; |
93 | setcdataV(L, &tmp, cd); |
94 | lj_gc_anybarriert(L, t); |
95 | tv = lj_tab_set(L, t, &tmp); |
96 | if (it == LJ_TNIL) { |
97 | setnilV(tv); |
98 | cd->marked &= ~LJ_GC_CDATA_FIN; |
99 | } else { |
100 | setgcV(L, tv, obj, it); |
101 | cd->marked |= LJ_GC_CDATA_FIN; |
102 | } |
103 | } |
104 | } |
105 | |
106 | /* -- C data indexing ----------------------------------------------------- */ |
107 | |
108 | /* Index C data by a TValue. Return CType and pointer. */ |
109 | CType *lj_cdata_index(CTState *cts, GCcdata *cd, cTValue *key, uint8_t **pp, |
110 | CTInfo *qual) |
111 | { |
112 | uint8_t *p = (uint8_t *)cdataptr(cd); |
113 | CType *ct = ctype_get(cts, cd->ctypeid); |
114 | ptrdiff_t idx; |
115 | |
116 | /* Resolve reference for cdata object. */ |
117 | if (ctype_isref(ct->info)) { |
118 | lj_assertCTS(ct->size == CTSIZE_PTR, "ref is not pointer-sized" ); |
119 | p = *(uint8_t **)p; |
120 | ct = ctype_child(cts, ct); |
121 | } |
122 | |
123 | collect_attrib: |
124 | /* Skip attributes and collect qualifiers. */ |
125 | while (ctype_isattrib(ct->info)) { |
126 | if (ctype_attrib(ct->info) == CTA_QUAL) *qual |= ct->size; |
127 | ct = ctype_child(cts, ct); |
128 | } |
129 | /* Interning rejects refs to refs. */ |
130 | lj_assertCTS(!ctype_isref(ct->info), "bad ref of ref" ); |
131 | |
132 | if (tvisint(key)) { |
133 | idx = (ptrdiff_t)intV(key); |
134 | goto integer_key; |
135 | } else if (tvisnum(key)) { /* Numeric key. */ |
136 | #ifdef _MSC_VER |
137 | /* Workaround for MSVC bug. */ |
138 | volatile |
139 | #endif |
140 | lua_Number n = numV(key); |
141 | idx = LJ_64 ? (ptrdiff_t)n : (ptrdiff_t)lj_num2int(n); |
142 | integer_key: |
143 | if (ctype_ispointer(ct->info)) { |
144 | CTSize sz = lj_ctype_size(cts, ctype_cid(ct->info)); /* Element size. */ |
145 | if (sz == CTSIZE_INVALID) |
146 | lj_err_caller(cts->L, LJ_ERR_FFI_INVSIZE); |
147 | if (ctype_isptr(ct->info)) { |
148 | p = (uint8_t *)cdata_getptr(p, ct->size); |
149 | } else if ((ct->info & (CTF_VECTOR|CTF_COMPLEX))) { |
150 | if ((ct->info & CTF_COMPLEX)) idx &= 1; |
151 | *qual |= CTF_CONST; /* Valarray elements are constant. */ |
152 | } |
153 | *pp = p + idx*(int32_t)sz; |
154 | return ct; |
155 | } |
156 | } else if (tviscdata(key)) { /* Integer cdata key. */ |
157 | GCcdata *cdk = cdataV(key); |
158 | CType *ctk = ctype_raw(cts, cdk->ctypeid); |
159 | if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk); |
160 | if (ctype_isinteger(ctk->info)) { |
161 | lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ctk, |
162 | (uint8_t *)&idx, cdataptr(cdk), 0); |
163 | goto integer_key; |
164 | } |
165 | } else if (tvisstr(key)) { /* String key. */ |
166 | GCstr *name = strV(key); |
167 | if (ctype_isstruct(ct->info)) { |
168 | CTSize ofs; |
169 | CType *fct = lj_ctype_getfieldq(cts, ct, name, &ofs, qual); |
170 | if (fct) { |
171 | *pp = p + ofs; |
172 | return fct; |
173 | } |
174 | } else if (ctype_iscomplex(ct->info)) { |
175 | if (name->len == 2) { |
176 | *qual |= CTF_CONST; /* Complex fields are constant. */ |
177 | if (strdata(name)[0] == 'r' && strdata(name)[1] == 'e') { |
178 | *pp = p; |
179 | return ct; |
180 | } else if (strdata(name)[0] == 'i' && strdata(name)[1] == 'm') { |
181 | *pp = p + (ct->size >> 1); |
182 | return ct; |
183 | } |
184 | } |
185 | } else if (cd->ctypeid == CTID_CTYPEID) { |
186 | /* Allow indexing a (pointer to) struct constructor to get constants. */ |
187 | CType *sct = ctype_raw(cts, *(CTypeID *)p); |
188 | if (ctype_isptr(sct->info)) |
189 | sct = ctype_rawchild(cts, sct); |
190 | if (ctype_isstruct(sct->info)) { |
191 | CTSize ofs; |
192 | CType *fct = lj_ctype_getfield(cts, sct, name, &ofs); |
193 | if (fct && ctype_isconstval(fct->info)) |
194 | return fct; |
195 | } |
196 | ct = sct; /* Allow resolving metamethods for constructors, too. */ |
197 | } |
198 | } |
199 | if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */ |
200 | if (ctype_isstruct(ctype_rawchild(cts, ct)->info)) { |
201 | p = (uint8_t *)cdata_getptr(p, ct->size); |
202 | ct = ctype_child(cts, ct); |
203 | goto collect_attrib; |
204 | } |
205 | } |
206 | *qual |= 1; /* Lookup failed. */ |
207 | return ct; /* But return the resolved raw type. */ |
208 | } |
209 | |
210 | /* -- C data getters ------------------------------------------------------ */ |
211 | |
212 | /* Get constant value and convert to TValue. */ |
213 | static void cdata_getconst(CTState *cts, TValue *o, CType *ct) |
214 | { |
215 | CType *ctt = ctype_child(cts, ct); |
216 | lj_assertCTS(ctype_isinteger(ctt->info) && ctt->size <= 4, |
217 | "only 32 bit const supported" ); /* NYI */ |
218 | /* Constants are already zero-extended/sign-extended to 32 bits. */ |
219 | if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0) |
220 | setnumV(o, (lua_Number)(uint32_t)ct->size); |
221 | else |
222 | setintV(o, (int32_t)ct->size); |
223 | } |
224 | |
225 | /* Get C data value and convert to TValue. */ |
226 | int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp) |
227 | { |
228 | CTypeID sid; |
229 | |
230 | if (ctype_isconstval(s->info)) { |
231 | cdata_getconst(cts, o, s); |
232 | return 0; /* No GC step needed. */ |
233 | } else if (ctype_isbitfield(s->info)) { |
234 | return lj_cconv_tv_bf(cts, s, o, sp); |
235 | } |
236 | |
237 | /* Get child type of pointer/array/field. */ |
238 | lj_assertCTS(ctype_ispointer(s->info) || ctype_isfield(s->info), |
239 | "pointer or field expected" ); |
240 | sid = ctype_cid(s->info); |
241 | s = ctype_get(cts, sid); |
242 | |
243 | /* Resolve reference for field. */ |
244 | if (ctype_isref(s->info)) { |
245 | lj_assertCTS(s->size == CTSIZE_PTR, "ref is not pointer-sized" ); |
246 | sp = *(uint8_t **)sp; |
247 | sid = ctype_cid(s->info); |
248 | s = ctype_get(cts, sid); |
249 | } |
250 | |
251 | /* Skip attributes. */ |
252 | while (ctype_isattrib(s->info)) |
253 | s = ctype_child(cts, s); |
254 | |
255 | return lj_cconv_tv_ct(cts, s, sid, o, sp); |
256 | } |
257 | |
258 | /* -- C data setters ------------------------------------------------------ */ |
259 | |
260 | /* Convert TValue and set C data value. */ |
261 | void lj_cdata_set(CTState *cts, CType *d, uint8_t *dp, TValue *o, CTInfo qual) |
262 | { |
263 | if (ctype_isconstval(d->info)) { |
264 | goto err_const; |
265 | } else if (ctype_isbitfield(d->info)) { |
266 | if (((d->info|qual) & CTF_CONST)) goto err_const; |
267 | lj_cconv_bf_tv(cts, d, dp, o); |
268 | return; |
269 | } |
270 | |
271 | /* Get child type of pointer/array/field. */ |
272 | lj_assertCTS(ctype_ispointer(d->info) || ctype_isfield(d->info), |
273 | "pointer or field expected" ); |
274 | d = ctype_child(cts, d); |
275 | |
276 | /* Resolve reference for field. */ |
277 | if (ctype_isref(d->info)) { |
278 | lj_assertCTS(d->size == CTSIZE_PTR, "ref is not pointer-sized" ); |
279 | dp = *(uint8_t **)dp; |
280 | d = ctype_child(cts, d); |
281 | } |
282 | |
283 | /* Skip attributes and collect qualifiers. */ |
284 | for (;;) { |
285 | if (ctype_isattrib(d->info)) { |
286 | if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size; |
287 | } else { |
288 | break; |
289 | } |
290 | d = ctype_child(cts, d); |
291 | } |
292 | |
293 | lj_assertCTS(ctype_hassize(d->info), "store to ctype without size" ); |
294 | lj_assertCTS(!ctype_isvoid(d->info), "store to void type" ); |
295 | |
296 | if (((d->info|qual) & CTF_CONST)) { |
297 | err_const: |
298 | lj_err_caller(cts->L, LJ_ERR_FFI_WRCONST); |
299 | } |
300 | |
301 | lj_cconv_ct_tv(cts, d, dp, o, 0); |
302 | } |
303 | |
304 | #endif |
305 | |