| /* |
| ** C data management. |
| ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h |
| */ |
| |
| #include "lj_obj.h" |
| |
| #if LJ_HASFFI |
| |
| #include "lj_gc.h" |
| #include "lj_err.h" |
| #include "lj_str.h" |
| #include "lj_tab.h" |
| #include "lj_ctype.h" |
| #include "lj_cconv.h" |
| #include "lj_cdata.h" |
| |
| /* -- C data allocation --------------------------------------------------- */ |
| |
| /* Allocate a new C data object holding a reference to another object. */ |
| GCcdata *lj_cdata_newref(CTState *cts, const void *p, CTypeID id) |
| { |
| CTypeID refid = lj_ctype_intern(cts, CTINFO_REF(id), CTSIZE_PTR); |
| GCcdata *cd = lj_cdata_new(cts, refid, CTSIZE_PTR); |
| *(const void **)cdataptr(cd) = p; |
| return cd; |
| } |
| |
| /* Allocate variable-sized or specially aligned C data object. */ |
| GCcdata *lj_cdata_newv(CTState *cts, CTypeID id, CTSize sz, CTSize align) |
| { |
| global_State *g; |
| MSize extra = sizeof(GCcdataVar) + sizeof(GCcdata) + |
| (align > CT_MEMALIGN ? (1u<<align) - (1u<<CT_MEMALIGN) : 0); |
| char *p = lj_mem_newt(cts->L, extra + sz, char); |
| uintptr_t adata = (uintptr_t)p + sizeof(GCcdataVar) + sizeof(GCcdata); |
| uintptr_t almask = (1u << align) - 1u; |
| GCcdata *cd = (GCcdata *)(((adata + almask) & ~almask) - sizeof(GCcdata)); |
| lua_assert((char *)cd - p < 65536); |
| cdatav(cd)->offset = (uint16_t)((char *)cd - p); |
| cdatav(cd)->extra = extra; |
| cdatav(cd)->len = sz; |
| g = cts->g; |
| setgcrefr(cd->nextgc, g->gc.root); |
| setgcref(g->gc.root, obj2gco(cd)); |
| newwhite(g, obj2gco(cd)); |
| cd->marked |= 0x80; |
| cd->gct = ~LJ_TCDATA; |
| cd->ctypeid = id; |
| return cd; |
| } |
| |
| /* Free a C data object. */ |
| void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) |
| { |
| if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) { |
| GCobj *root; |
| makewhite(g, obj2gco(cd)); |
| markfinalized(obj2gco(cd)); |
| if ((root = gcref(g->gc.mmudata)) != NULL) { |
| setgcrefr(cd->nextgc, root->gch.nextgc); |
| setgcref(root->gch.nextgc, obj2gco(cd)); |
| setgcref(g->gc.mmudata, obj2gco(cd)); |
| } else { |
| setgcref(cd->nextgc, obj2gco(cd)); |
| setgcref(g->gc.mmudata, obj2gco(cd)); |
| } |
| } else if (LJ_LIKELY(!cdataisv(cd))) { |
| CType *ct = ctype_raw(ctype_ctsG(g), cd->ctypeid); |
| CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR; |
| lua_assert(ctype_hassize(ct->info) || ctype_isfunc(ct->info) || |
| ctype_isextern(ct->info)); |
| lj_mem_free(g, cd, sizeof(GCcdata) + sz); |
| } else { |
| lj_mem_free(g, memcdatav(cd), sizecdatav(cd)); |
| } |
| } |
| |
| TValue * LJ_FASTCALL lj_cdata_setfin(lua_State *L, GCcdata *cd) |
| { |
| global_State *g = G(L); |
| GCtab *t = ctype_ctsG(g)->finalizer; |
| if (gcref(t->metatable)) { |
| /* Add cdata to finalizer table, if still enabled. */ |
| TValue *tv, tmp; |
| setcdataV(L, &tmp, cd); |
| lj_gc_anybarriert(L, t); |
| tv = lj_tab_set(L, t, &tmp); |
| cd->marked |= LJ_GC_CDATA_FIN; |
| return tv; |
| } else { |
| /* Otherwise return dummy TValue. */ |
| return &g->tmptv; |
| } |
| } |
| |
| /* -- C data indexing ----------------------------------------------------- */ |
| |
| /* Index C data by a TValue. Return CType and pointer. */ |
| CType *lj_cdata_index(CTState *cts, GCcdata *cd, cTValue *key, uint8_t **pp, |
| CTInfo *qual) |
| { |
| uint8_t *p = (uint8_t *)cdataptr(cd); |
| CType *ct = ctype_get(cts, cd->ctypeid); |
| ptrdiff_t idx; |
| |
| /* Resolve reference for cdata object. */ |
| if (ctype_isref(ct->info)) { |
| lua_assert(ct->size == CTSIZE_PTR); |
| p = *(uint8_t **)p; |
| ct = ctype_child(cts, ct); |
| } |
| |
| collect_attrib: |
| /* Skip attributes and collect qualifiers. */ |
| while (ctype_isattrib(ct->info)) { |
| if (ctype_attrib(ct->info) == CTA_QUAL) *qual |= ct->size; |
| ct = ctype_child(cts, ct); |
| } |
| lua_assert(!ctype_isref(ct->info)); /* Interning rejects refs to refs. */ |
| |
| if (tvisint(key)) { |
| idx = (ptrdiff_t)intV(key); |
| goto integer_key; |
| } else if (tvisnum(key)) { /* Numeric key. */ |
| idx = LJ_64 ? (ptrdiff_t)numV(key) : (ptrdiff_t)lj_num2int(numV(key)); |
| integer_key: |
| if (ctype_ispointer(ct->info)) { |
| CTSize sz = lj_ctype_size(cts, ctype_cid(ct->info)); /* Element size. */ |
| if (sz == CTSIZE_INVALID) |
| lj_err_caller(cts->L, LJ_ERR_FFI_INVSIZE); |
| if (ctype_isptr(ct->info)) { |
| p = (uint8_t *)cdata_getptr(p, ct->size); |
| } else if ((ct->info & (CTF_VECTOR|CTF_COMPLEX))) { |
| if ((ct->info & CTF_COMPLEX)) idx &= 1; |
| *qual |= CTF_CONST; /* Valarray elements are constant. */ |
| } |
| *pp = p + idx*(int32_t)sz; |
| return ct; |
| } |
| } else if (tviscdata(key)) { /* Integer cdata key. */ |
| GCcdata *cdk = cdataV(key); |
| CType *ctk = ctype_raw(cts, cdk->ctypeid); |
| if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk); |
| if (ctype_isinteger(ctk->info)) { |
| lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ctk, |
| (uint8_t *)&idx, cdataptr(cdk), 0); |
| goto integer_key; |
| } |
| } else if (tvisstr(key)) { /* String key. */ |
| GCstr *name = strV(key); |
| if (ctype_isstruct(ct->info)) { |
| CTSize ofs; |
| CType *fct = lj_ctype_getfieldq(cts, ct, name, &ofs, qual); |
| if (fct) { |
| *pp = p + ofs; |
| return fct; |
| } |
| } else if (ctype_iscomplex(ct->info)) { |
| if (name->len == 2) { |
| *qual |= CTF_CONST; /* Complex fields are constant. */ |
| if (strdata(name)[0] == 'r' && strdata(name)[1] == 'e') { |
| *pp = p; |
| return ct; |
| } else if (strdata(name)[0] == 'i' && strdata(name)[1] == 'm') { |
| *pp = p + (ct->size >> 1); |
| return ct; |
| } |
| } |
| } else if (cd->ctypeid == CTID_CTYPEID) { |
| /* Allow indexing a (pointer to) struct constructor to get constants. */ |
| CType *sct = ctype_raw(cts, *(CTypeID *)p); |
| if (ctype_isptr(sct->info)) |
| sct = ctype_rawchild(cts, sct); |
| if (ctype_isstruct(sct->info)) { |
| CTSize ofs; |
| CType *fct = lj_ctype_getfield(cts, sct, name, &ofs); |
| if (fct && ctype_isconstval(fct->info)) |
| return fct; |
| } |
| ct = sct; /* Allow resolving metamethods for constructors, too. */ |
| } |
| } |
| if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */ |
| if (ctype_isstruct(ctype_rawchild(cts, ct)->info)) { |
| p = (uint8_t *)cdata_getptr(p, ct->size); |
| ct = ctype_child(cts, ct); |
| goto collect_attrib; |
| } |
| } |
| *qual |= 1; /* Lookup failed. */ |
| return ct; /* But return the resolved raw type. */ |
| } |
| |
| /* -- C data getters ------------------------------------------------------ */ |
| |
| /* Get constant value and convert to TValue. */ |
| static void cdata_getconst(CTState *cts, TValue *o, CType *ct) |
| { |
| CType *ctt = ctype_child(cts, ct); |
| lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4); |
| /* Constants are already zero-extended/sign-extended to 32 bits. */ |
| if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0) |
| setnumV(o, (lua_Number)(uint32_t)ct->size); |
| else |
| setintV(o, (int32_t)ct->size); |
| } |
| |
| /* Get C data value and convert to TValue. */ |
| int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp) |
| { |
| CTypeID sid; |
| |
| if (ctype_isconstval(s->info)) { |
| cdata_getconst(cts, o, s); |
| return 0; /* No GC step needed. */ |
| } else if (ctype_isbitfield(s->info)) { |
| return lj_cconv_tv_bf(cts, s, o, sp); |
| } |
| |
| /* Get child type of pointer/array/field. */ |
| lua_assert(ctype_ispointer(s->info) || ctype_isfield(s->info)); |
| sid = ctype_cid(s->info); |
| s = ctype_get(cts, sid); |
| |
| /* Resolve reference for field. */ |
| if (ctype_isref(s->info)) { |
| lua_assert(s->size == CTSIZE_PTR); |
| sp = *(uint8_t **)sp; |
| sid = ctype_cid(s->info); |
| s = ctype_get(cts, sid); |
| } |
| |
| /* Skip attributes. */ |
| while (ctype_isattrib(s->info)) |
| s = ctype_child(cts, s); |
| |
| return lj_cconv_tv_ct(cts, s, sid, o, sp); |
| } |
| |
| /* -- C data setters ------------------------------------------------------ */ |
| |
| /* Convert TValue and set C data value. */ |
| void lj_cdata_set(CTState *cts, CType *d, uint8_t *dp, TValue *o, CTInfo qual) |
| { |
| if (ctype_isconstval(d->info)) { |
| goto err_const; |
| } else if (ctype_isbitfield(d->info)) { |
| if (((d->info|qual) & CTF_CONST)) goto err_const; |
| lj_cconv_bf_tv(cts, d, dp, o); |
| return; |
| } |
| |
| /* Get child type of pointer/array/field. */ |
| lua_assert(ctype_ispointer(d->info) || ctype_isfield(d->info)); |
| d = ctype_child(cts, d); |
| |
| /* Resolve reference for field. */ |
| if (ctype_isref(d->info)) { |
| lua_assert(d->size == CTSIZE_PTR); |
| dp = *(uint8_t **)dp; |
| d = ctype_child(cts, d); |
| } |
| |
| /* Skip attributes and collect qualifiers. */ |
| for (;;) { |
| if (ctype_isattrib(d->info)) { |
| if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size; |
| } else { |
| break; |
| } |
| d = ctype_child(cts, d); |
| } |
| |
| lua_assert(ctype_hassize(d->info) && !ctype_isvoid(d->info)); |
| |
| if (((d->info|qual) & CTF_CONST)) { |
| err_const: |
| lj_err_caller(cts->L, LJ_ERR_FFI_WRCONST); |
| } |
| |
| lj_cconv_ct_tv(cts, d, dp, o, 0); |
| } |
| |
| #endif |