diff --git a/src/lib_ffi.c b/src/lib_ffi.c index e7108c59..ed73a5fe 100644 --- a/src/lib_ffi.c +++ b/src/lib_ffi.c @@ -74,6 +74,41 @@ static void *ffi_checkptr(lua_State *L, int narg, CTypeID id) return p; } +typedef struct FFIArith { + uint8_t *p[2]; + CType *ct[2]; +} FFIArith; + +/* Check arguments for arithmetic metamethods. */ +static void ffi_checkarith(lua_State *L, FFIArith *fa) +{ + CTState *cts = ctype_cts(L); + TValue *o = L->base; + MSize i; + if (o+1 >= L->top) + lj_err_argt(L, 1, LUA_TCDATA); + for (i = 0; i < 2; i++, o++) { + if (tviscdata(o)) { + GCcdata *cd = cdataV(o); + CTypeID id = (CTypeID)cd->typeid; + CType *ct = ctype_get(cts, id); + uint8_t *p = (uint8_t *)cdataptr(cd); + if (ctype_isref(ct->info)) { + lua_assert(ct->size == CTSIZE_PTR); + p = *(uint8_t **)p; + id = ctype_cid(ct->info); + } + fa->ct[i] = ctype_raw(cts, id); + fa->p[i] = p; + } else if (tvisnum(o)) { + fa->ct[i] = ctype_get(cts, CTID_DOUBLE); + fa->p[i] = (uint8_t *)&o->n; + } else { + lj_err_optype(L, o, LJ_ERR_OPARITH); + } + } +} + /* -- C type metamethods -------------------------------------------------- */ #define LJLIB_MODULE_ffi_meta @@ -118,6 +153,81 @@ LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call) return 0; /* unreachable */ } +/* Pointer arithmetic. */ +static int ffi_arith_ptr(lua_State *L, FFIArith *fa, int sub) +{ + CTState *cts = ctype_cts(L); + CType *ctp = fa->ct[0]; + uint8_t *pp = fa->p[0]; + ptrdiff_t idx; + CTSize sz; + CTypeID id; + GCcdata *cd; + if (ctype_isptr(ctp->info) || ctype_isrefarray(ctp->info)) { + if (sub && + (ctype_isptr(fa->ct[1]->info) || ctype_isrefarray(fa->ct[1]->info))) { + /* Pointer difference. */ + intptr_t diff; + if (!lj_cconv_compatptr(cts, ctp, fa->ct[1], CCF_IGNQUAL)) + lj_err_caller(L, LJ_ERR_FFI_INVTYPE); + sz = lj_ctype_size(cts, ctype_cid(ctp->info)); /* Element size. */ + if (sz == 0 || sz == CTSIZE_INVALID) + lj_err_caller(L, LJ_ERR_FFI_INVSIZE); + if (ctype_isptr(ctp->info)) + pp = (uint8_t *)cdata_getptr(pp, ctp->size); + if (ctype_isptr(fa->ct[1]->info)) + fa->p[1] = (uint8_t *)cdata_getptr(fa->p[1], fa->ct[1]->size); + diff = ((intptr_t)pp - (intptr_t)fa->p[1]) / (int32_t)sz; + /* All valid pointer differences on x64 are in (-2^47, +2^47), + ** which fits into a double without loss of precision. + */ + setnumV(L->top-1, (lua_Number)diff); + return 1; + } + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), fa->ct[1], + (uint8_t *)&idx, fa->p[1], 0); + if (sub) idx = -idx; + } else if (!sub && + (ctype_isptr(fa->ct[1]->info) || ctype_isrefarray(fa->ct[1]->info))) { + /* Swap pointer and index. */ + ctp = fa->ct[1]; pp = fa->p[1]; + lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), fa->ct[0], + (uint8_t *)&idx, fa->p[0], 0); + } else { + return 0; + } + sz = lj_ctype_size(cts, ctype_cid(ctp->info)); /* Element size. */ + if (sz == CTSIZE_INVALID) + lj_err_caller(L, LJ_ERR_FFI_INVSIZE); + if (ctype_isptr(ctp->info)) + pp = (uint8_t *)cdata_getptr(pp, ctp->size); + pp += idx*(int32_t)sz; /* Compute pointer + index. */ + id = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(ctp->info)), + CTSIZE_PTR); + cd = lj_cdata_new(cts, id, CTSIZE_PTR); + *(uint8_t **)cdataptr(cd) = pp; + setcdataV(L, L->top-1, cd); + return 1; +} + +LJLIB_CF(ffi_meta___add) +{ + FFIArith fa; + ffi_checkarith(L, &fa); + if (!ffi_arith_ptr(L, &fa, 0)) + lj_err_caller(L, LJ_ERR_FFI_INVTYPE); + return 1; +} + +LJLIB_CF(ffi_meta___sub) +{ + FFIArith fa; + ffi_checkarith(L, &fa); + if (!ffi_arith_ptr(L, &fa, 1)) + lj_err_caller(L, LJ_ERR_FFI_INVTYPE); + return 1; +} + LJLIB_CF(ffi_meta___tostring) { GCcdata *cd = ffi_checkcdata(L, 1); diff --git a/src/lj_cconv.c b/src/lj_cconv.c index 85fd57e5..e5abf3e9 100644 --- a/src/lj_cconv.c +++ b/src/lj_cconv.c @@ -63,7 +63,7 @@ static CType *cconv_childqual(CTState *cts, CType *ct, CTInfo *qual) /* Check for compatible types when converting to a pointer. ** Note: these checks are more relaxed than what C99 mandates. */ -static int cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags) +int lj_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags) { if (!((flags & CCF_CAST) || d == s)) { CTInfo dqual = 0, squal = 0; @@ -73,7 +73,7 @@ static int cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags) if ((flags & CCF_SAME)) { if (dqual != squal) return 0; /* Different qualifiers. */ - } else { + } else if (!(flags & CCF_IGNQUAL)) { if ((dqual & squal) != squal) return 0; /* Discarded qualifiers. */ if (ctype_isvoid(d->info) || ctype_isvoid(s->info)) @@ -87,7 +87,7 @@ static int cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags) return 0; /* Different numeric types. */ } else if (ctype_ispointer(d->info)) { /* Check child types for compatibility. */ - return cconv_compatptr(cts, d, s, flags|CCF_SAME); + return lj_cconv_compatptr(cts, d, s, flags|CCF_SAME); } else if (ctype_isstruct(d->info)) { if (d != s) return 0; /* Must be exact same type for struct/union. */ @@ -339,20 +339,20 @@ void lj_cconv_ct_ct(CTState *cts, CType *d, CType *s, goto conv_I_F; case CCX(P, P): - if (!cconv_compatptr(cts, d, s, flags)) goto err_conv; + if (!lj_cconv_compatptr(cts, d, s, flags)) goto err_conv; cdata_setptr(dp, dsize, cdata_getptr(sp, ssize)); break; case CCX(P, A): case CCX(P, S): - if (!cconv_compatptr(cts, d, s, flags)) goto err_conv; + if (!lj_cconv_compatptr(cts, d, s, flags)) goto err_conv; cdata_setptr(dp, dsize, sp); break; /* Destination is an array. */ case CCX(A, A): if ((flags & CCF_CAST) || (d->info & CTF_VLA) || d->size != s->size || - d->size == CTSIZE_INVALID || !cconv_compatptr(cts, d, s, flags)) + d->size == CTSIZE_INVALID || !lj_cconv_compatptr(cts, d, s, flags)) goto err_conv; goto copyval; diff --git a/src/lj_cconv.h b/src/lj_cconv.h index 140ce8d6..dd2f2c40 100644 --- a/src/lj_cconv.h +++ b/src/lj_cconv.h @@ -46,8 +46,10 @@ static LJ_AINLINE uint32_t cconv_idx(CTInfo info) #define CCF_CAST 0x00000001u #define CCF_FROMTV 0x00000002u #define CCF_SAME 0x00000004u +#define CCF_IGNQUAL 0x00000008u +LJ_FUNC int lj_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags); LJ_FUNC void lj_cconv_ct_ct(CTState *cts, CType *d, CType *s, uint8_t *dp, uint8_t *sp, CTInfo flags); LJ_FUNC void lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid,