Mercurial
comparison third_party/luajit/src/lj_cdata.c @ 186:8cf4ec5e2191 hg-web
Fixed merge conflict.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:38:59 -0800 |
| parents | 94705b5986b3 |
| children |
comparison
equal
deleted
inserted
replaced
| 176:fed99fc04e12 | 186:8cf4ec5e2191 |
|---|---|
| 1 /* | |
| 2 ** C data management. | |
| 3 ** Copyright (C) 2005-2023 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 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. */ | |
| 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 |