• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/ffi/uc_cconv.c

  1 /*
  2 ** C type conversions.
  3 ** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice below.
  4 **
  5 ** This file contains derived work from LuaJIT's FFI conversion machinery (lj_cconv.c).
  6 **
  7 ** Modifications:
  8 ** - Adapted VM interactions to use ucode's API (uc_vm_t, uc_value_t, etc.)
  9 ** - Replaced LuaJIT's parallel call infrastructure with libffi
 10 ** - Adapted value marshaling between ucode and C types
 11 ** - Removed JIT-specific code and dependencies
 12 **
 13 ** See NOTICE and ATTRIBUTION.md for complete attribution details.
 14 */
 15 
 16 
 17 #include "uc_ctype.h"
 18 #include "uc_cdata.h"
 19 #include "uc_cconv.h"
 20 
 21 #include <ffi.h>
 22 
 23 /* -- Conversion errors --------------------------------------------------- */
 24 
 25 /* Bad conversion. */
 26 static bool
 27 cconv_err_conv(CTState *cts, CType *d, CType *s, CTInfo flags)
 28 {
 29         uc_value_t *dst_repr = uc_ctype_repr(cts->vm, ctype_typeid(cts, d), NULL);
 30         uc_value_t *src_repr = NULL;
 31         const char *dst = ucv_string_get(dst_repr);
 32         const char *src;
 33 
 34         if ((flags & CCF_FROMTV))
 35         {
 36                 src = ctype_isnum(s->info)
 37                         ? "number"
 38                         : ctype_isarray(s->info)
 39                                 ? "string"
 40                                 : "null";
 41         }
 42         else
 43         {
 44                 src_repr = uc_ctype_repr(cts->vm, ctype_typeid(cts, s), NULL);
 45                 src = ucv_string_get(src_repr);
 46         }
 47 
 48         if (CCF_GETARG(flags)) {
 49                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
 50                                       "cannot convert argument #%d from '%s' to '%s'",
 51                                       CCF_GETARG(flags), src, dst);
 52         }
 53         // lj_err_argv(cts->L, CCF_GETARG(flags), LJ_ERR_FFI_BADCONV, src, dst);
 54         else {
 55                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
 56                                       "cannot convert from '%s' to '%s'",
 57                                       src, dst);
 58         }
 59 
 60         // lj_err_callerv(cts->L, LJ_ERR_FFI_BADCONV, src, dst);
 61         ucv_put(dst_repr);
 62         ucv_put(src_repr);
 63 
 64         return false;
 65 }
 66 
 67 /* Bad conversion from TValue. */
 68 static bool
 69 cconv_err_convtv(CTState *cts, CType *d, uc_value_t *uv, CTInfo flags)
 70 {
 71         uc_value_t *dst_repr = uc_ctype_repr(cts->vm, ctype_typeid(cts, d), NULL);
 72         const char *dst = ucv_string_get(dst_repr);
 73         const char *src = ucv_typename(uv);
 74 
 75         if (CCF_GETARG(flags)) {
 76                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
 77                                       "cannot convert argument #%d from '%s' to '%s'",
 78                                       CCF_GETARG(flags), src, dst);
 79         }
 80         // lj_err_argv(cts->L, CCF_GETARG(flags), LJ_ERR_FFI_BADCONV, src, dst);
 81         else {
 82                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
 83                                       "cannot convert from '%s' to '%s'",
 84                                       src, dst);
 85         }
 86 
 87         // lj_err_callerv(cts->L, LJ_ERR_FFI_BADCONV, src, dst);
 88         ucv_put(dst_repr);
 89 
 90         return false;
 91 }
 92 
 93 /* Initializer overflow. */
 94 static bool
 95 cconv_err_initov(CTState *cts, CType *d)
 96 {
 97         uc_value_t *repr = uc_ctype_repr(cts->vm, ctype_typeid(cts, d), NULL);
 98 
 99         uc_vm_raise_exception(cts->vm, EXCEPTION_REFERENCE,
100                               "too many initializers for '%s'",
101                               ucv_string_get(repr));
102 
103         ucv_put(repr);
104 
105         return false;
106 }
107 
108 /* -- C type compatibility checks ----------------------------------------- */
109 
110 /* Get raw type and qualifiers for a child type. Resolves enums, too. */
111 static CType *cconv_childqual(CTState *cts, CType *ct, CTInfo *qual)
112 {
113         ct = ctype_child(cts, ct);
114         for (;;)
115         {
116                 if (ctype_isattrib(ct->info))
117                 {
118                         if (ctype_attrib(ct->info) == CTA_QUAL)
119                                 *qual |= ct->size;
120                 }
121                 else if (!ctype_isenum(ct->info))
122                 {
123                         break;
124                 }
125                 ct = ctype_child(cts, ct);
126         }
127         *qual |= (ct->info & CTF_QUAL);
128         return ct;
129 }
130 
131 /* Check for compatible types when converting to a pointer.
132 ** Note: these checks are more relaxed than what C99 mandates.
133 */
134 int uc_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags)
135 {
136         if (!((flags & CCF_CAST) || d == s))
137         {
138                 CTInfo dqual = 0, squal = 0;
139                 d = cconv_childqual(cts, d, &dqual);
140                 if (!ctype_isstruct(s->info))
141                         s = cconv_childqual(cts, s, &squal);
142                 if ((flags & CCF_SAME))
143                 {
144                         if (dqual != squal)
145                                 return 0; /* Different qualifiers. */
146                 }
147                 else if (!(flags & CCF_IGNQUAL))
148                 {
149                         if ((dqual & squal) != squal)
150                                 return 0; /* Discarded qualifiers. */
151                         if (ctype_isvoid(d->info) || ctype_isvoid(s->info))
152                                 return 1; /* Converting to/from void * is always ok. */
153                 }
154                 if (ctype_type(d->info) != ctype_type(s->info) ||
155                         d->size != s->size)
156                         return 0; /* Different type or different size. */
157                 if (ctype_isnum(d->info))
158                 {
159                         if (((d->info ^ s->info) & (CTF_BOOL | CTF_FP)))
160                                 return 0; /* Different numeric types. */
161                 }
162                 else if (ctype_ispointer(d->info))
163                 {
164                         /* Check child types for compatibility. */
165                         return uc_cconv_compatptr(cts, d, s, flags | CCF_SAME);
166                 }
167                 else if (ctype_isstruct(d->info))
168                 {
169                         if (d != s)
170                                 return 0; /* Must be exact same type for struct/union. */
171                 }
172                 else if (ctype_isfunc(d->info))
173                 {
174                         /* NYI: structural equality of functions. */
175                 }
176         }
177         return 1; /* Types are compatible. */
178 }
179 
180 /* -- C type to C type conversion ----------------------------------------- */
181 
182 /* Convert C type to C type. Caveat: expects to get the raw CType!
183 **
184 ** Note: This is only used by the interpreter and not optimized at all.
185 ** The JIT compiler will do a much better job specializing for each case.
186 */
187 bool uc_cconv_ct_ct(CTState *cts, CType *d, CType *s,
188                                         uint8_t *dp, uint8_t *sp, CTInfo flags)
189 {
190         CTSize dsize = d->size, ssize = s->size;
191         CTInfo dinfo = d->info, sinfo = s->info;
192         void *tmpptr;
193 
194         uc_assertCTS(!ctype_isenum(dinfo) && !ctype_isenum(sinfo),
195                                  "unresolved enum");
196         uc_assertCTS(!ctype_isattrib(dinfo) && !ctype_isattrib(sinfo),
197                                  "unstripped attribute");
198 
199         if (ctype_type(dinfo) > CT_MAYCONVERT || ctype_type(sinfo) > CT_MAYCONVERT)
200                 goto err_conv;
201 
202         /* Some basic sanity checks. */
203         uc_assertCTS(!ctype_isnum(dinfo) || dsize > 0, "bad size for number type");
204         uc_assertCTS(!ctype_isnum(sinfo) || ssize > 0, "bad size for number type");
205         uc_assertCTS(!ctype_isbool(dinfo) || dsize == 1 || dsize == 4,
206                                  "bad size for bool type");
207         uc_assertCTS(!ctype_isbool(sinfo) || ssize == 1 || ssize == 4,
208                                  "bad size for bool type");
209         uc_assertCTS(!ctype_isinteger(dinfo) || (1u << uc_fls(dsize)) == dsize,
210                                  "bad size for integer type");
211         uc_assertCTS(!ctype_isinteger(sinfo) || (1u << uc_fls(ssize)) == ssize,
212                                  "bad size for integer type");
213 
214         switch (cconv_idx2(dinfo, sinfo))
215         {
216         /* Destination is a bool. */
217         case CCX(B, B):
218                 /* Source operand is already normalized. */
219                 if (dsize == 1)
220                         *dp = *sp;
221                 else
222                         *(int *)dp = *sp;
223                 break;
224         case CCX(B, I):
225         {
226                 size_t i;
227                 uint8_t b = 0;
228                 for (i = 0; i < ssize; i++)
229                         b |= sp[i];
230                 b = (b != 0);
231                 if (dsize == 1)
232                         *dp = b;
233                 else
234                         *(int *)dp = b;
235                 break;
236         }
237         case CCX(B, F):
238         {
239                 uint8_t b;
240                 if (ssize == sizeof(double))
241                         b = (*(double *)sp != 0);
242                 else if (ssize == sizeof(float))
243                         b = (*(float *)sp != 0);
244                 else
245                         goto err_conv; /* NYI: long double. */
246                 if (dsize == 1)
247                         *dp = b;
248                 else
249                         *(int *)dp = b;
250                 break;
251         }
252 
253         /* Destination is an integer. */
254         case CCX(I, B):
255         case CCX(I, I):
256         conv_I_I:
257                 if (dsize > ssize)
258                 { /* Zero-extend or sign-extend LSB. */
259 #if __BYTE_ORDER == __LITTLE_ENDIAN
260                         uint8_t fill = (!(sinfo & CTF_UNSIGNED) && (sp[ssize - 1] & 0x80)) ? 0xff : 0;
261                         memcpy(dp, sp, ssize);
262                         memset(dp + ssize, fill, dsize - ssize);
263 #else
264                         uint8_t fill = (!(sinfo & CTF_UNSIGNED) && (sp[0] & 0x80)) ? 0xff : 0;
265                         memset(dp, fill, dsize - ssize);
266                         memcpy(dp + (dsize - ssize), sp, ssize);
267 #endif
268                 }
269                 else
270                 { /* Copy LSB. */
271 #if __BYTE_ORDER == __LITTLE_ENDIAN
272                         memcpy(dp, sp, dsize);
273 #else
274                         memcpy(dp, sp + (ssize - dsize), dsize);
275 #endif
276                 }
277                 break;
278         case CCX(I, F):
279         {
280                 double n; /* Always convert via double. */
281         conv_I_F:
282                 /* Convert source to double. */
283                 if (ssize == sizeof(double))
284                         n = *(double *)sp;
285                 else if (ssize == sizeof(float))
286                         n = (double)*(float *)sp;
287                 else
288                         goto err_conv; /* NYI: long double. */
289                 /* Then convert double to integer. */
290                 /* The conversion must exactly match the semantics of JIT-compiled code! */
291                 if (dsize < 4 || (dsize == 4 && !(dinfo & CTF_UNSIGNED)))
292                 {
293                         int32_t i = (int32_t)n;
294                         if (dsize == 4)
295                                 *(int32_t *)dp = i;
296                         else if (dsize == 2)
297                                 *(int16_t *)dp = (int16_t)i;
298                         else
299                                 *(int8_t *)dp = (int8_t)i;
300                 }
301                 else if (dsize == 4)
302                 {
303                         *(uint32_t *)dp = (uint32_t)n;
304                 }
305                 else if (dsize == 8)
306                 {
307                         if (!(dinfo & CTF_UNSIGNED))
308                                 *(int64_t *)dp = (int64_t)n;
309                         else
310                                 *(uint64_t *)dp = uc_num2u64(n);
311                 }
312                 else
313                 {
314                         goto err_conv; /* NYI: conversion to >64 bit integers. */
315                 }
316                 break;
317         }
318         case CCX(I, C):
319                 s = ctype_child(cts, s);
320                 ssize = s->size;
321                 goto conv_I_F; /* Just convert re. */
322         case CCX(I, P):
323                 if (!(flags & CCF_CAST))
324                         goto err_conv;
325                 sinfo = CTINFO(CT_NUM, CTF_UNSIGNED);
326                 goto conv_I_I;
327         case CCX(I, A):
328                 if (!(flags & CCF_CAST))
329                         goto err_conv;
330                 sinfo = CTINFO(CT_NUM, CTF_UNSIGNED);
331                 ssize = CTSIZE_PTR;
332                 tmpptr = sp;
333                 sp = (uint8_t *)&tmpptr;
334                 goto conv_I_I;
335 
336         /* Destination is a floating-point number. */
337         case CCX(F, B):
338         case CCX(F, I):
339         {
340                 double n; /* Always convert via double. */
341         conv_F_I:
342                 /* First convert source to double. */
343                 /* The conversion must exactly match the semantics of JIT-compiled code! */
344                 if (ssize < 4 || (ssize == 4 && !(sinfo & CTF_UNSIGNED)))
345                 {
346                         int32_t i;
347                         if (ssize == 4)
348                         {
349                                 i = *(int32_t *)sp;
350                         }
351                         else if (!(sinfo & CTF_UNSIGNED))
352                         {
353                                 if (ssize == 2)
354                                         i = *(int16_t *)sp;
355                                 else
356                                         i = *(int8_t *)sp;
357                         }
358                         else
359                         {
360                                 if (ssize == 2)
361                                         i = *(uint16_t *)sp;
362                                 else
363                                         i = *(uint8_t *)sp;
364                         }
365                         n = (double)i;
366                 }
367                 else if (ssize == 4)
368                 {
369                         n = (double)*(uint32_t *)sp;
370                 }
371                 else if (ssize == 8)
372                 {
373                         if (!(sinfo & CTF_UNSIGNED))
374                                 n = (double)*(int64_t *)sp;
375                         else
376                                 n = (double)*(uint64_t *)sp;
377                 }
378                 else
379                 {
380                         goto err_conv; /* NYI: conversion from >64 bit integers. */
381                 }
382                 /* Convert double to destination. */
383                 if (dsize == sizeof(double))
384                         *(double *)dp = n;
385                 else if (dsize == sizeof(float))
386                         *(float *)dp = (float)n;
387                 else
388                         goto err_conv; /* NYI: long double. */
389                 break;
390         }
391         case CCX(F, F):
392         {
393                 double n; /* Always convert via double. */
394         conv_F_F:
395                 if (ssize == dsize)
396                         goto copyval;
397                 /* Convert source to double. */
398                 if (ssize == sizeof(double))
399                         n = *(double *)sp;
400                 else if (ssize == sizeof(float))
401                         n = (double)*(float *)sp;
402                 else
403                         goto err_conv; /* NYI: long double. */
404                 /* Convert double to destination. */
405                 if (dsize == sizeof(double))
406                         *(double *)dp = n;
407                 else if (dsize == sizeof(float))
408                         *(float *)dp = (float)n;
409                 else
410                         goto err_conv; /* NYI: long double. */
411                 break;
412         }
413         case CCX(F, C):
414                 s = ctype_child(cts, s);
415                 ssize = s->size;
416                 goto conv_F_F; /* Ignore im, and convert from re. */
417 
418         /* Destination is a complex number. */
419         case CCX(C, I):
420                 d = ctype_child(cts, d);
421                 dsize = d->size;
422                 memset(dp + dsize, 0, dsize); /* Clear im. */
423                 goto conv_F_I;                            /* Convert to re. */
424         case CCX(C, F):
425                 d = ctype_child(cts, d);
426                 dsize = d->size;
427                 memset(dp + dsize, 0, dsize); /* Clear im. */
428                 goto conv_F_F;                            /* Convert to re. */
429 
430         case CCX(C, C):
431                 if (dsize != ssize)
432                 { /* Different types: convert re/im separately. */
433                         CType *dc = ctype_child(cts, d);
434                         CType *sc = ctype_child(cts, s);
435                         return uc_cconv_ct_ct(cts, dc, sc, dp, sp, flags) &&
436                                uc_cconv_ct_ct(cts, dc, sc, dp + dc->size, sp + sc->size, flags);
437                 }
438                 goto copyval; /* Otherwise this is easy. */
439 
440         /* Destination is a vector. */
441         case CCX(V, I):
442         case CCX(V, F):
443         case CCX(V, C):
444         {
445                 CType *dc = ctype_child(cts, d);
446                 CTSize esize;
447                 /* First convert the scalar to the first element. */
448                 if (!uc_cconv_ct_ct(cts, dc, s, dp, sp, flags))
449                         return false;
450                 /* Then replicate it to the other elements (splat). */
451                 for (sp = dp, esize = dc->size; dsize > esize; dsize -= esize)
452                 {
453                         dp += esize;
454                         memcpy(dp, sp, esize);
455                 }
456                 break;
457         }
458 
459         case CCX(V, V):
460                 /* Copy same-sized vectors, even for different lengths/element-types. */
461                 if (dsize != ssize)
462                         goto err_conv;
463                 goto copyval;
464 
465         /* Destination is a pointer. */
466         case CCX(P, I):
467                 if (!(flags & CCF_CAST))
468                         goto err_conv;
469                 goto conv_I_I;
470 
471         case CCX(P, F):
472                 if (!(flags & CCF_CAST) || !(flags & CCF_FROMTV))
473                         goto err_conv;
474                 /* The signed conversion is cheaper. x64 really has 47 bit pointers. */
475                 dinfo = CTINFO(CT_NUM, (UC_64 && dsize == 8) ? 0 : CTF_UNSIGNED);
476                 goto conv_I_F;
477 
478         case CCX(P, P):
479                 if (!uc_cconv_compatptr(cts, d, s, flags))
480                         goto err_conv;
481                 cdata_setptr(dp, dsize, cdata_getptr(sp, ssize));
482                 break;
483 
484         case CCX(P, A):
485         case CCX(P, S):
486                 if (!uc_cconv_compatptr(cts, d, s, flags)) {
487                         goto err_conv;
488                 }
489                 cdata_setptr(dp, dsize, sp);
490                 break;
491 
492         /* Destination is an array. */
493         case CCX(A, A):
494                 if ((flags & CCF_CAST) || (d->info & CTF_VLA) || dsize != ssize ||
495                         d->size == CTSIZE_INVALID || !uc_cconv_compatptr(cts, d, s, flags))
496                         goto err_conv;
497                 goto copyval;
498 
499         /* Destination is a struct/union. */
500         case CCX(S, S):
501                 if ((flags & CCF_CAST) || (d->info & CTF_VLA) || d != s)
502                         goto err_conv; /* Must be exact same type. */
503         copyval:                           /* Copy value. */
504                 uc_assertCTS(dsize == ssize, "value copy with different sizes");
505                 memcpy(dp, sp, dsize);
506                 break;
507 
508         default:
509         err_conv:
510                 return cconv_err_conv(cts, d, s, flags);
511         }
512 
513         return true;
514 }
515 
516 /* -- C type to TValue conversion ----------------------------------------- */
517 
518 /* Convert C type to TValue. Caveat: expects to get the raw CType! */
519 int uc_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid,
520                                    uc_value_t **uv, uint8_t *sp)
521 {
522         CTInfo sinfo = s->info;
523         if (ctype_isnum(sinfo))
524         {
525                 ucv_put(*uv);
526 
527                 if (!ctype_isbool(sinfo))
528                 {
529                         if (ctype_isinteger(sinfo)) {
530                                 if (sinfo & CTF_UNSIGNED) {
531                                         uint64_t u;
532                                         uc_cconv_ct_ct(cts, ctype_get(cts, CTID_UINT64), s,
533                                                        (uint8_t *)&u, sp, 0);
534                                         *uv = ucv_uint64_new(u);
535                                 }
536                                 else {
537                                         int64_t n;
538                                         uc_cconv_ct_ct(cts, ctype_get(cts, CTID_INT64), s,
539                                                        (uint8_t *)&n, sp, 0);
540                                         *uv = ucv_int64_new(n);
541                                 }
542                         }
543                         else {
544                                 double d;
545                                 uc_cconv_ct_ct(cts, ctype_get(cts, CTID_DOUBLE), s,
546                                                (uint8_t *)&d, sp, 0);
547                                 *uv = ucv_double_new(d);
548                         }
549                 }
550                 else
551                 {
552                         uint32_t b = s->size == 1 ? (*sp != 0) : (*(int *)sp != 0);
553                         *uv = ucv_boolean_new(b);
554                         //setboolV(o, b);
555                         //setboolV(&cts->g->tmptv2, b); /* Remember for trace recorder. */
556                 }
557                 return 0;
558         }
559         else if (ctype_isrefarray(sinfo) || ctype_isstruct(sinfo))
560         {
561                 /* Create reference. */
562                 ucv_put(*uv);
563                 *uv = uc_cdata_newref(cts->vm, sp, sid);
564 
565                 //setcdataV(cts->L, o, lj_cdata_newref(cts, sp, sid));
566                 return 1; /* Need GC step. */
567         }
568         else
569         {
570                 CTSize sz = s->size;
571                 uc_assertCTS(sz != CTSIZE_INVALID, "value copy with invalid size");
572 
573                 /* Attributes are stripped, qualifiers are kept (but mostly ignored). */
574                 uc_value_t *res = uc_cdata_new(cts->vm, ctype_typeid(cts, s), sz);
575                 memcpy(uc_cdata_dataptr(res), sp, sz);
576 
577                 ucv_put(*uv);
578                 *uv = res;
579 
580                 return 1; /* Need GC step. */
581         }
582 }
583 
584 /* Convert bitfield to TValue. */
585 int uc_cconv_tv_bf(CTState *cts, CType *s, uc_value_t **uv, uint8_t *sp)
586 {
587         CTInfo info = s->info;
588         CTSize pos, bsz;
589         uint32_t val;
590         uc_assertCTS(ctype_isbitfield(info), "bitfield expected");
591         /* NYI: packed bitfields may cause misaligned reads. */
592         switch (ctype_bitcsz(info))
593         {
594         case 4:
595                 val = *(uint32_t *)sp;
596                 break;
597         case 2:
598                 val = *(uint16_t *)sp;
599                 break;
600         case 1:
601                 val = *(uint8_t *)sp;
602                 break;
603         default:
604                 uc_assertCTS(0, "bad bitfield container size %d", ctype_bitcsz(info));
605                 val = 0;
606                 break;
607         }
608         /* Check if a packed bitfield crosses a container boundary. */
609         pos = ctype_bitpos(info);
610         bsz = ctype_bitbsz(info);
611         uc_assertCTS(pos < 8 * ctype_bitcsz(info), "bad bitfield position");
612         uc_assertCTS(bsz > 0 && bsz <= 8 * ctype_bitcsz(info), "bad bitfield size");
613         if (pos + bsz > 8 * ctype_bitcsz(info)) {
614                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
615                         "packed bit fields are not implemented yet");
616                 return 0;
617         }
618         if (!(info & CTF_BOOL))
619         {
620                 CTSize shift = 32 - bsz;
621                 if (!(info & CTF_UNSIGNED))
622                 {
623                         ucv_put(*uv);
624                         *uv = ucv_int64_new((int32_t)(val << (shift - pos)) >> shift);
625                         //setintV(o, (int32_t)(val << (shift - pos)) >> shift);
626                 }
627                 else
628                 {
629                         val = (val << (shift - pos)) >> shift;
630                         ucv_put(*uv);
631                         *uv = ucv_uint64_new(val);
632                         //if (!LJ_DUALNUM || (int32_t)val < 0)
633                         //      setnumV(o, (lua_Number)(uint32_t)val);
634                         //else
635                         //      setintV(o, (int32_t)val);
636                 }
637         }
638         else
639         {
640                 uint32_t b = (val >> pos) & 1;
641                 uc_assertCTS(bsz == 1, "bad bool bitfield size");
642                 ucv_put(*uv);
643                 *uv = ucv_boolean_new(b);
644                 //setboolV(o, b);
645                 //setboolV(&cts->g->tmptv2, b); /* Remember for trace recorder. */
646         }
647         return 0; /* No GC step needed. */
648 }
649 
650 /* -- TValue to C type conversion ----------------------------------------- */
651 
652 /* Convert ucode array to C array. */
653 static bool cconv_array_tab(CTState *cts, CType *d,
654                                                         uint8_t *dp, uc_value_t *arr, CTInfo flags,
655                                                         uc_value_t **refs)
656 {
657         size_t arrlen = ucv_array_length(arr);
658         size_t i;
659         CType *dc = ctype_rawchild(cts, d); /* Array element type. */
660         CTSize size = d->size, esize = dc->size, ofs = 0;
661         for (i = 0; i < arrlen; i++)
662         {
663                 if (ofs >= size)
664                         cconv_err_initov(cts, d);
665                 uc_cconv_ct_tv(cts, dc, dp + ofs, ucv_array_get(arr, i), flags, refs);
666                 ofs += esize;
667         }
668         if (size != CTSIZE_INVALID)
669         { /* Only fill up arrays with known size. */
670                 if (ofs == esize)
671                 { /* Replicate a single element. */
672                         for (; ofs < size; ofs += esize)
673                                 memcpy(dp + ofs, dp, esize);
674                 }
675                 else
676                 { /* Otherwise fill the remainder with zero. */
677                         memset(dp + ofs, 0, size - ofs);
678                 }
679         }
680 
681         return true;
682 }
683 
684 /* Convert ucode array to sub-struct/union. */
685 static bool cconv_substruct_tab(CTState *cts, CType *d, uint8_t *dp,
686                                                                 uc_value_t *tab, int32_t *ip, CTInfo flags,
687                                                                 uc_value_t **refs)
688 {
689         CTypeID id = d->sib;
690 
691         while (id)
692         {
693                 CType *df = ctype_get(cts, id);
694                 id = df->sib;
695 
696                 if (ctype_isfield(df->info) || ctype_isbitfield(df->info))
697                 {
698                         uc_value_t *uv;
699 
700                         if (!df->uv_name)
701                                 continue; /* Ignore unnamed fields. */
702 
703                         if (ucv_type(tab) == UC_ARRAY)
704                         {
705                                 int32_t i = *ip;
706 
707                                 uv = ucv_array_get(tab, i);
708 
709                                 if (!uv)
710                                         break; /* Stop at first nil. */
711 
712                                 *ip = i + 1;
713                         }
714                         else if (ucv_type(tab) == UC_OBJECT)
715                         {
716                                 uv = ucv_object_get(tab, ucv_string_get(df->uv_name), NULL);
717 
718                                 if (!uv)
719                                         continue;
720                         }
721                         else {
722                                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
723                                         "array or object initializer value expected, got %s",
724                                         ucv_typename(tab));
725 
726                                 return false;
727                         }
728 
729                         if (ctype_isfield(df->info))
730                                 uc_cconv_ct_tv(cts, ctype_rawchild(cts, df), dp + df->size,
731                                                uv, flags, refs);
732                         else
733                                 uc_cconv_bf_tv(cts, df, dp + df->size, uv, refs);
734 
735                         if ((d->info & CTF_UNION))
736                                 break;
737                 }
738                 else if (ctype_isxattrib(df->info, CTA_SUBTYPE))
739                 {
740                         if (!cconv_substruct_tab(cts, ctype_rawchild(cts, df),
741                                                  dp + df->size, tab, ip, flags, refs))
742                                 return false;
743                 } /* Ignore all other entries in the chain. */
744         }
745 
746         return true;
747 }
748 
749 /* Convert table to struct/union. */
750 static bool cconv_struct_tab(CTState *cts, CType *d,
751                              uint8_t *dp, uc_value_t *tab, CTInfo flags,
752                                                          uc_value_t **refs)
753 {
754         int32_t i = 0;
755 
756         memset(dp, 0, d->size); /* Much simpler to clear the struct first. */
757 
758         return cconv_substruct_tab(cts, d, dp, tab, &i, flags, refs);
759 }
760 
761 /* Convert TValue to C type. Caveat: expects to get the raw CType! */
762 bool uc_cconv_ct_tv(CTState *cts, CType *d,
763                                         uint8_t *dp, uc_value_t *uv, CTInfo flags,
764                                         uc_value_t **refs)
765 {
766         uintptr_t pv = (uintptr_t)uv;
767         CTypeID sid = CTID_P_VOID;
768         CType *s;
769         void *tmpptr;
770         uint8_t tmpbool, *sp = (uint8_t *)&tmpptr;
771         GCcdata *cd;
772         uc_type_t utt = pv & 3;
773         uc_type_t ut = (!utt && uv) ? uv->type : utt;
774 
775         /* optimized case: fast tagged integer read */
776         if (UC_LIKELY(utt == UC_INTEGER)) {
777                 /* unsigned */
778                 if (((pv >> 2) & 1) == 0) {
779                         tmpptr = (void *)(pv >> 3);
780                         sid = (sizeof(tmpptr) == sizeof(uint64_t)) ? CTID_UINT64 : CTID_UINT32;
781                 }
782                 else {
783                         tmpptr = (void *)(uintptr_t)-(pv >> 3);
784                         sid = (sizeof(tmpptr) == sizeof(int64_t)) ? CTID_INT64 : CTID_INT32;
785                 }
786 
787                 flags |= CCF_FROMTV;
788         }
789         /* optimized case: fast bool read */
790         else if (UC_LIKELY(utt == UC_BOOLEAN))
791         {
792                 tmpbool = (pv >> 2) & 1;
793                 sp = &tmpbool;
794                 sid = CTID_BOOL;
795         }
796         else if (UC_LIKELY(ut == UC_INTEGER))
797         {
798                 uc_integer_t *ui = (uc_integer_t *)uv;
799 
800                 /* unsigned */
801                 if (ui->header.ext_flag) {
802                         sp = (uint8_t *)&ui->i.u64;
803                         sid = CTID_UINT64;
804                 }
805                 else {
806                         sp = (uint8_t *)&ui->i.s64;
807                         sid = CTID_INT64;
808                 }
809 
810                 flags |= CCF_FROMTV;
811         }
812         else if (UC_LIKELY(ut == UC_DOUBLE))
813         {
814                 uc_double_t *ud = (uc_double_t *)uv;
815 
816                 sp = (uint8_t *)&ud->dbl;
817                 sid = CTID_DOUBLE;
818                 flags |= CCF_FROMTV;
819         }
820         else if (ut == UC_RESOURCE && (cd = ucv_resource_data(uv, "ffi.ctype")) != NULL)
821         {
822                 sp = cdataptr(cd);
823                 sid = cd->ctypeid;
824                 s = ctype_get(cts, sid);
825                 if (ctype_isref(s->info))
826                 { /* Resolve reference for value. */
827                         uc_assertCTS(s->size == CTSIZE_PTR, "ref is not pointer-sized");
828                         sp = *(void **)sp;
829                         sid = ctype_cid(s->info);
830                 }
831                 s = ctype_raw(cts, sid);
832                 if (ctype_isfunc(s->info))
833                 {
834                         CTypeID did = ctype_typeid(cts, d);
835                         sid = uc_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR | sid), CTSIZE_PTR);
836                         d = ctype_get(cts, did); /* cts->tab may have been reallocated. */
837                 }
838                 else
839                 {
840                         if (ctype_isenum(s->info))
841                                 s = ctype_child(cts, s);
842                         goto doconv;
843                 }
844         }
845         else if (ut == UC_STRING)
846         {
847                 if (ctype_isenum(d->info))
848                 { /* Match string against enum constant. */
849                         CTSize ofs;
850                         CType *cct = uc_ctype_getfield(cts, d, uv, &ofs);
851                         if (!cct || !ctype_isconstval(cct->info))
852                                 goto err_conv;
853                         uc_assertCTS(d->size == 4, "only 32 bit enum supported"); /* NYI */
854                         sp = (uint8_t *)&cct->size;
855                         sid = ctype_cid(cct->info);
856                 }
857                 else if (ctype_isrefarray(d->info))
858                 { /* Copy string to array. */
859                         CType *dc = ctype_rawchild(cts, d);
860                         CTSize sz = ucv_string_length(uv) + 1;
861                         if (!ctype_isinteger(dc->info) || dc->size != 1)
862                                 goto err_conv;
863                         if (d->size != 0 && d->size < sz)
864                                 sz = d->size;
865                         return memcpy(dp, ucv_string_get(uv), sz);
866                 }
867                 else
868                 { /* Otherwise pass it as a const char[]. */
869                         uc_value_t *us = uc_cconv_addref(refs, uv);
870                         sp = (uint8_t *)ucv_string_get(us);
871                         sid = CTID_A_CCHAR;
872                         flags |= CCF_FROMTV;
873                 }
874         }
875         else if (ut == UC_ARRAY)
876         {
877                 if (ctype_isarray(d->info))
878                 {
879                         return cconv_array_tab(cts, d, dp, uv, flags, refs);
880                 }
881                 else if (ctype_isstruct(d->info))
882                 {
883                         return cconv_struct_tab(cts, d, dp, uv, flags, refs);
884                 }
885                 else
886                 {
887                         goto err_conv;
888                 }
889         }
890         else if (ut == UC_OBJECT)
891         {
892                 if (ctype_isstruct(d->info))
893                 {
894                         return cconv_struct_tab(cts, d, dp, uv, flags, refs);
895                 }
896                 else
897                 {
898                         goto err_conv;
899                 }
900         }
901         else if (ut == UC_NULL)
902         {
903                 tmpptr = (void *)0;
904                 flags |= CCF_FROMTV;
905         }
906         else if (ut == UC_RESOURCE)
907         {
908                 tmpptr = ucv_resource_data(uv, NULL);
909         }
910         else
911         {
912         err_conv:
913                 return cconv_err_convtv(cts, d, uv, flags);
914         }
915         s = ctype_get(cts, sid);
916 doconv:
917         if (ctype_isenum(d->info))
918                 d = ctype_child(cts, d);
919         return uc_cconv_ct_ct(cts, d, s, dp, sp, flags);
920 }
921 
922 /* Convert TValue to bitfield. */
923 bool uc_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, uc_value_t *uv,
924                     uc_value_t **refs)
925 {
926         CTInfo info = d->info;
927         CTSize pos, bsz;
928         uint32_t val = 0, mask;
929         uc_assertCTS(ctype_isbitfield(info), "bitfield expected");
930         if ((info & CTF_BOOL))
931         {
932                 uint8_t tmpbool = 0;
933                 uc_assertCTS(ctype_bitbsz(info) == 1, "bad bool bitfield size");
934                 if (!uc_cconv_ct_tv(cts, ctype_get(cts, CTID_BOOL), &tmpbool, uv, 0, refs))
935                         return false;
936                 val = tmpbool;
937         }
938         else
939         {
940                 CTypeID did = (info & CTF_UNSIGNED) ? CTID_UINT32 : CTID_INT32;
941                 if (!uc_cconv_ct_tv(cts, ctype_get(cts, did), (uint8_t *)&val, uv, 0, refs))
942                         return false;
943         }
944         pos = ctype_bitpos(info);
945         bsz = ctype_bitbsz(info);
946         uc_assertCTS(pos < 8 * ctype_bitcsz(info), "bad bitfield position");
947         uc_assertCTS(bsz > 0 && bsz <= 8 * ctype_bitcsz(info), "bad bitfield size");
948         /* Check if a packed bitfield crosses a container boundary. */
949         if (pos + bsz > 8 * ctype_bitcsz(info)) {
950                 //lj_err_caller(cts->L, LJ_ERR_FFI_NYIPACKBIT);
951                 uc_vm_raise_exception(cts->vm, EXCEPTION_TYPE,
952                                       "packed bit fields are not implemented yet");
953 
954                 return false;
955         }
956         mask = ((1u << bsz) - 1u) << pos;
957         val = (val << pos) & mask;
958         /* NYI: packed bitfields may cause misaligned reads/writes. */
959         switch (ctype_bitcsz(info))
960         {
961         case 4:
962                 *(uint32_t *)dp = (*(uint32_t *)dp & ~mask) | (uint32_t)val;
963                 break;
964         case 2:
965                 *(uint16_t *)dp = (*(uint16_t *)dp & ~mask) | (uint16_t)val;
966                 break;
967         case 1:
968                 *(uint8_t *)dp = (*(uint8_t *)dp & ~mask) | (uint8_t)val;
969                 break;
970         default:
971                 uc_assertCTS(0, "bad bitfield container size %d", ctype_bitcsz(info));
972                 break;
973         }
974 
975         return true;
976 }
977 
978 /* -- Initialize C type with TValues -------------------------------------- */
979 
980 /* Initialize an array with TValues. */
981 static bool cconv_array_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp,
982                                                          uc_value_t **uv, size_t len, uc_value_t **refs)
983 {
984         CType *dc = ctype_rawchild(cts, d); /* Array element type. */
985         CTSize ofs, esize = dc->size;
986         size_t i;
987         if (len * esize > sz)
988                 return cconv_err_initov(cts, d);
989         for (i = 0, ofs = 0; i < len; i++, ofs += esize)
990                 if (!uc_cconv_ct_tv(cts, dc, dp + ofs, uv[i], 0, refs))
991                         return false;
992         if (ofs == esize)
993         { /* Replicate a single element. */
994                 for (; ofs < sz; ofs += esize)
995                         memcpy(dp + ofs, dp, esize);
996         }
997         else
998         { /* Otherwise fill the remainder with zero. */
999                 memset(dp + ofs, 0, sz - ofs);
1000         }
1001 
1002         return true;
1003 }
1004 
1005 /* Initialize a sub-struct/union with TValues. */
1006 static bool cconv_substruct_init(CTState *cts, CType *d, uint8_t *dp,
1007                                                                  uc_value_t **uv, size_t len, size_t *ip,
1008                                                                  uc_value_t **refs)
1009 {
1010         CTypeID id = d->sib;
1011         while (id)
1012         {
1013                 CType *df = ctype_get(cts, id);
1014                 id = df->sib;
1015                 if (ctype_isfield(df->info) || ctype_isbitfield(df->info))
1016                 {
1017                         size_t i = *ip;
1018                         if (!df->uv_name)
1019                                 continue; /* Ignore unnamed fields. */
1020                         if (i >= len)
1021                                 break;
1022                         *ip = i + 1;
1023 
1024                         if (ctype_isfield(df->info)) {
1025                                 if (!uc_cconv_ct_tv(cts, ctype_rawchild(cts, df),
1026                                                     dp + df->size, uv[i], 0, refs))
1027                                         return false;
1028                         }
1029                         else {
1030                                 if (!uc_cconv_bf_tv(cts, df, dp + df->size, uv[i], refs))
1031                                         return false;
1032                         }
1033 
1034                         if ((d->info & CTF_UNION))
1035                                 break;
1036                 }
1037                 else if (ctype_isxattrib(df->info, CTA_SUBTYPE))
1038                 {
1039                         if (!cconv_substruct_init(cts, ctype_rawchild(cts, df),
1040                                                   dp + df->size, uv, len, ip, refs))
1041                                 return false;
1042 
1043                         if ((d->info & CTF_UNION))
1044                                 break;
1045                 } /* Ignore all other entries in the chain. */
1046         }
1047 
1048         return true;
1049 }
1050 
1051 /* Initialize a struct/union with TValues. */
1052 static bool cconv_struct_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp,
1053                                                           uc_value_t **uv, size_t len, uc_value_t **refs)
1054 {
1055         size_t i = 0;
1056 
1057         memset(dp, 0, sz); /* Much simpler to clear the struct first. */
1058 
1059         if (!cconv_substruct_init(cts, d, dp, uv, len, &i, refs))
1060                 return false;
1061 
1062         if (i < len)
1063                 return cconv_err_initov(cts, d);
1064 
1065         return true;
1066 }
1067 
1068 /* Initialize a struct/union with a ucode object. */
1069 static bool cconv_struct_object_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp,
1070                                      uc_value_t *obj, uc_value_t **refs)
1071 {
1072         CTypeID id = d->sib;
1073 
1074         memset(dp, 0, sz);
1075 
1076         while (id)
1077         {
1078                 CType *df = ctype_get(cts, id);
1079                 id = df->sib;
1080 
1081                 if (ctype_isfield(df->info) || ctype_isbitfield(df->info))
1082                 {
1083                         if (!df->uv_name)
1084                                 continue;
1085 
1086                         uc_value_t *uv = ucv_object_get(obj, ucv_string_get(df->uv_name), NULL);
1087 
1088                         if (uv)
1089                         {
1090                                 if (ctype_isfield(df->info)) {
1091                                         if (!uc_cconv_ct_tv(cts, ctype_rawchild(cts, df),
1092                                                             dp + df->size, uv, 0, refs))
1093                                                 return false;
1094                                 }
1095                                 else {
1096                                         if (!uc_cconv_bf_tv(cts, df, dp + df->size, uv, refs))
1097                                                 return false;
1098                                 }
1099                         }
1100 
1101                         if ((d->info & CTF_UNION))
1102                                 break;
1103                 }
1104                 else if (ctype_isxattrib(df->info, CTA_SUBTYPE))
1105                 {
1106                         CType *child = ctype_rawchild(cts, df);
1107                         if (!cconv_struct_object_init(cts, child, child->size,
1108                                                       dp + df->size, obj, refs))
1109                                 return false;
1110 
1111                         if ((d->info & CTF_UNION))
1112                                 break;
1113                 }
1114         }
1115 
1116         return true;
1117 }
1118 
1119 /* Check whether to use a multi-value initializer.
1120 ** This is true if an aggregate is to be initialized with a value.
1121 ** Valarrays are treated as values here so ct_tv handles (V|C, I|F).
1122 */
1123 int uc_cconv_multi_init(CTState *cts, CType *d, uc_value_t *uv)
1124 {
1125         uc_type_t ut = ucv_type(uv);
1126         GCcdata *cd = (ut == UC_RESOURCE) ? ucv_resource_data(uv, "ffi.ctype") : NULL;
1127 
1128         if (!(ctype_isrefarray(d->info) || ctype_isstruct(d->info)))
1129                 return 0; /* Destination is not an aggregate. */
1130         if (ut == UC_ARRAY || ut == UC_OBJECT || (ut == UC_STRING && !ctype_isstruct(d->info)))
1131                 return 0; /* Initializer is not a value. */
1132         if (cd && uc_ctype_rawref(cts, cd->ctypeid) == d)
1133                 return 0; /* Source and destination are identical aggregates. */
1134         return 1;         /* Otherwise the initializer is a value. */
1135 }
1136 
1137 /* Initialize C type with TValues. Caveat: expects to get the raw CType! */
1138 bool uc_cconv_ct_init(CTState *cts, CType *d, CTSize sz,
1139                                           uint8_t *dp, uc_value_t **uv, size_t len, uc_value_t **refs)
1140 {
1141         if (len == 0)
1142                 return memset(dp, 0, sz);
1143         else if (len == 1 && !uc_cconv_multi_init(cts, d, *uv))
1144                 return uc_cconv_ct_tv(cts, d, dp, *uv, 0, refs);
1145         else if (ctype_isarray(d->info)) /* Also handles valarray init with len>1. */
1146                 return cconv_array_init(cts, d, sz, dp, uv, len, refs);
1147         else if (ctype_isstruct(d->info)) {
1148                 if (len == 1 && ucv_type(*uv) == UC_OBJECT)
1149                         return cconv_struct_object_init(cts, d, sz, dp, *uv, refs);
1150                 return cconv_struct_init(cts, d, sz, dp, uv, len, refs);
1151         }
1152         else
1153                 return cconv_err_initov(cts, d);
1154 }
1155 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt