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. */
20GCcdata *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. */
29GCcdata *lj_cdata_newv(lua_State *L, CTypeID id, CTSize sz, CTSize align)
30{
31 global_State *g;
32 MSize extra = 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. */
53GCcdata *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. */
62void 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
87void 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. */
109CType *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
123collect_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. */
213static 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. */
226int 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. */
261void 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