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

Sources/ucode/types.c

  1 /*
  2  * Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <stdlib.h>
 18 #include <assert.h>
 19 #include <errno.h>
 20 #include <math.h>
 21 #include <float.h>
 22 
 23 
 24 #include "ucode/internal/types.h"
 25 #include "ucode/internal/vm.h" /* ISA: I_* opcodes */
 26 #include "ucode/internal/program.h"
 27 #include "ucode/internal/chunk.h"
 28 #include "ucode/internal/json-c-compat.h"
 29 
 30 static char *uc_default_search_path[] = { LIB_SEARCH_PATH };
 31 
 32 uc_parse_config_t uc_default_parse_config = {
 33         .module_search_path = {
 34                 .count = ARRAY_SIZE(uc_default_search_path),
 35                 .entries = uc_default_search_path
 36         }
 37 };
 38 
 39 uc_type_t
 40 ucv_type(uc_value_t *uv)
 41 {
 42         uc_type_t type = ((uintptr_t)uv & 3);
 43 
 44         if (type == UC_NULL && uv != NULL)
 45                 type = uv->type;
 46 
 47         return type;
 48 }
 49 
 50 const char *
 51 ucv_typename(uc_value_t *uv)
 52 {
 53         switch (ucv_type(uv)) {
 54         case UC_NULL:      return "null";
 55         case UC_INTEGER:   return "integer";
 56         case UC_BOOLEAN:   return "boolean";
 57         case UC_STRING:    return "string";
 58         case UC_DOUBLE:    return "double";
 59         case UC_ARRAY:     return "array";
 60         case UC_OBJECT:    return "object";
 61         case UC_REGEXP:    return "regexp";
 62         case UC_CFUNCTION: return "cfunction";
 63         case UC_CLOSURE:   return "closure";
 64         case UC_UPVALUE:   return "upvalue";
 65         case UC_RESOURCE:  return "resource";
 66         case UC_PROGRAM:   return "program";
 67         case UC_SOURCE:    return "source";
 68         }
 69 
 70         return "unknown";
 71 }
 72 
 73 void
 74 ucv_unref(uc_weakref_t *ref)
 75 {
 76         ref->prev->next = ref->next;
 77         ref->next->prev = ref->prev;
 78 }
 79 
 80 static void
 81 ucv_ref_tail(uc_weakref_t *head, uc_weakref_t *item)
 82 {
 83         item->next = head;
 84         item->prev = head->prev;
 85         head->prev->next = item;
 86         head->prev = item;
 87 }
 88 
 89 void
 90 ucv_ref(uc_weakref_t *head, uc_weakref_t *item)
 91 {
 92         item->next = head->next;
 93         item->prev = head;
 94         head->next->prev = item;
 95         head->next = item;
 96 }
 97 
 98 static uc_value_t **
 99 ucv_resource_values(uc_resource_ext_t *res)
100 {
101         void *data;
102 
103         if (!res->uvcount && !res->hasproto)
104                 return NULL;
105 
106         data = res + 1;
107         data += res->datasize * 8;
108 
109         return data;
110 }
111 
112 #if 0
113 static uc_weakref_t *
114 ucv_get_weakref(uc_value_t *uv)
115 {
116         switch (ucv_type(uv)) {
117         case UC_ARRAY:
118                 return &ucv_as_array(uv)->ref;
119 
120         case UC_OBJECT:
121                 return &ucv_as_object(uv)->ref;
122 
123         case UC_CLOSURE:
124                 return &ucv_as_closure(uv)->ref;
125 
126         default:
127                 return NULL;
128         }
129 }
130 #endif
131 
132 static void
133 ucv_put_value(uc_value_t *uv, bool retain)
134 {
135         if (uv == NULL || (uintptr_t)uv & 3)
136                 return;
137 
138         assert(uv->type == UC_NULL || uv->refcount > 0);
139 
140         if (uv->refcount > 0)
141                 uv->refcount--;
142 
143         if (uv->refcount == 0)
144                 ucv_free(uv, retain);
145 }
146 
147 static void
148 ucv_gc_mark(uc_value_t *uv);
149 
150 static void
151 ucv_gc_mark(uc_value_t *uv)
152 {
153         uc_function_t *function;
154         uc_closure_t *closure;
155         uc_upvalref_t *upval;
156         uc_object_t *object;
157         uc_array_t *array;
158         uc_resource_type_t *restype;
159         uc_program_t *program;
160         uc_value_t **values;
161         struct lh_entry *entry;
162         size_t i;
163 
164         if (ucv_is_marked(uv))
165                 return;
166 
167         switch (ucv_type(uv)) {
168         case UC_ARRAY:
169                 array = ucv_as_array(uv);
170 
171                 if (array->ref.next)
172                         ucv_set_mark(uv);
173 
174                 ucv_gc_mark(array->proto);
175 
176                 for (i = 0; i < array->count; i++)
177                         ucv_gc_mark(array->entries[i]);
178 
179                 break;
180 
181         case UC_OBJECT:
182                 object = ucv_as_object(uv);
183 
184                 if (object->ref.next)
185                         ucv_set_mark(uv);
186 
187                 ucv_gc_mark(object->proto);
188 
189                 lh_foreach(object->table, entry)
190                         ucv_gc_mark((uc_value_t *)lh_entry_v(entry));
191 
192                 break;
193 
194         case UC_CLOSURE:
195                 closure = ucv_as_closure(uv);
196                 function = closure->function;
197 
198                 if (closure->ref.next)
199                         ucv_set_mark(uv);
200 
201                 for (i = 0; i < function->nupvals; i++)
202                         ucv_gc_mark(&closure->upvals[i]->header);
203 
204                 ucv_gc_mark(&function->program->header);
205 
206                 break;
207 
208         case UC_UPVALUE:
209                 upval = ucv_as_upvalue(uv);
210                 ucv_gc_mark(upval->value);
211                 break;
212 
213         case UC_RESOURCE:
214                 if (uv->ext_flag) {
215                         uc_resource_ext_t *res = (uc_resource_ext_t *)uv;
216 
217                         if (res->ref.next)
218                                 ucv_set_mark(uv);
219 
220                         values = ucv_resource_values(res);
221 
222                         if (!values)
223                                 break;
224 
225                         for (i = 0; i < (size_t)res->uvcount + res->hasproto; i++)
226                                 ucv_gc_mark(values[i]);
227                 }
228 
229                 restype = ucv_resource_type(uv);
230                 if (restype)
231                         ucv_gc_mark(restype->proto);
232 
233                 break;
234 
235         case UC_PROGRAM:
236                 program = ucv_as_program(uv);
237 
238                 for (i = 0; i < program->sources.count; i++)
239                         ucv_gc_mark(&program->sources.entries[i]->header);
240 
241                 for (i = 0; i < program->exports.count; i++)
242                         ucv_gc_mark(&program->exports.entries[i]->header);
243 
244                 break;
245 
246         default:
247                 break;
248         }
249 }
250 
251 void
252 ucv_free(uc_value_t *uv, bool retain)
253 {
254         uc_function_t *function;
255         uc_closure_t *closure;
256         uc_program_t *program;
257         uc_upvalref_t *upval;
258         uc_source_t *source;
259         uc_regexp_t *regexp;
260         uc_object_t *object;
261         uc_array_t *array;
262         uc_weakref_t *ref;
263         size_t i;
264 
265         if (uv == NULL || (uintptr_t)uv & 3)
266                 return;
267 
268         if (uv->mark)
269                 return;
270 
271         uv->mark = true;
272 
273         ref = NULL;
274 
275         switch (uv->type) {
276         case UC_ARRAY:
277                 array = ucv_as_array(uv);
278                 ref = &array->ref;
279                 ucv_put_value(array->proto, retain);
280 
281                 for (i = 0; i < array->count; i++)
282                         ucv_put_value(array->entries[i], retain);
283 
284                 uc_vector_clear(array);
285                 break;
286 
287         case UC_OBJECT:
288                 object = ucv_as_object(uv);
289                 ref = &object->ref;
290                 ucv_put_value(object->proto, retain);
291                 lh_table_free(object->table);
292                 break;
293 
294         case UC_REGEXP:
295                 regexp = ucv_as_regexp(uv);
296                 regfree(&regexp->regexp);
297                 break;
298 
299         case UC_CLOSURE:
300                 closure = ucv_as_closure(uv);
301                 function = closure->function;
302                 ref = &closure->ref;
303 
304                 for (i = 0; i < function->nupvals; i++)
305                         if (closure->upvals[i])
306                                 ucv_put_value(&closure->upvals[i]->header, retain);
307 
308                 ucv_put_value(&function->program->header, retain);
309                 break;
310 
311         case UC_RESOURCE:
312                 if (uv->ext_flag) {
313                         uc_resource_ext_t *res = (uc_resource_ext_t *)uv;
314                         void *data = res + 1;
315                         uc_value_t **values;
316 
317                         values = ucv_resource_values(res);
318 
319                         if (values) {
320                                 for (i = 0; i < (size_t)res->uvcount + res->hasproto; i++)
321                                         ucv_put_value(values[i], retain);
322                         }
323 
324                         if (res->type && res->type->free)
325                                 res->type->free(data);
326 
327                         ref = &res->ref;
328                 }
329                 else {
330                         uc_resource_t *res = ucv_as_resource(uv);
331 
332                         if (res->type && res->type->free)
333                                 res->type->free(res->data);
334                 }
335                 break;
336 
337         case UC_UPVALUE:
338                 upval = ucv_as_upvalue(uv);
339                 ucv_put_value(upval->value, retain);
340                 break;
341 
342         case UC_PROGRAM:
343                 program = ucv_as_program(uv);
344 
345                 uc_program_function_foreach_safe(program, func)
346                         uc_program_function_free(func);
347 
348                 uc_vallist_free(&program->constants);
349 
350                 for (i = 0; i < program->sources.count; i++)
351                         ucv_put_value(&program->sources.entries[i]->header, retain);
352 
353                 for (i = 0; i < program->exports.count; i++)
354                         ucv_put_value(&program->exports.entries[i]->header, retain);
355 
356                 uc_vector_clear(&program->sources);
357                 uc_vector_clear(&program->exports);
358                 break;
359 
360         case UC_SOURCE:
361                 source = ucv_as_source(uv);
362 
363                 if (source->runpath != source->filename)
364                         free(source->runpath);
365 
366                 for (i = 0; i < source->exports.count; i++)
367                         ucv_put(source->exports.entries[i]);
368 
369                 uc_vector_clear(&source->lineinfo);
370                 uc_vector_clear(&source->exports);
371                 fclose(source->fp);
372                 free(source->buffer);
373                 break;
374         }
375 
376         if (!ref || !retain || !ref->prev || !ref->next) {
377                 if (ref && ref->prev && ref->next)
378                         ucv_unref(ref);
379 
380                 free(uv);
381         }
382         else {
383                 uv->type = UC_NULL;
384         }
385 }
386 
387 void
388 ucv_put(uc_value_t *uv)
389 {
390         ucv_put_value(uv, false);
391 }
392 
393 uc_value_t *
394 ucv_get(uc_value_t *uv)
395 {
396         if (uv == NULL || (uintptr_t)uv & 3)
397                 return uv;
398 
399         assert(uv->refcount < 0x03ffffff);
400 
401         uv->refcount++;
402 
403         return uv;
404 }
405 
406 uc_value_t *
407 ucv_boolean_new(bool val)
408 {
409         uintptr_t pv = UC_BOOLEAN | (val << 2);
410 
411         return (uc_value_t *)pv;
412 }
413 
414 bool
415 ucv_boolean_get(uc_value_t *uv)
416 {
417         uintptr_t pv = (uintptr_t)uv;
418 
419         if ((pv & 3) == UC_BOOLEAN)
420                 return (pv >> 2) & 1;
421 
422         return false;
423 }
424 
425 
426 uc_value_t *
427 ucv_string_new(const char *str)
428 {
429         return ucv_string_new_length(str, strlen(str));
430 }
431 
432 uc_value_t *
433 ucv_string_alloc(char **str, size_t length)
434 {
435         uc_string_t *ustr;
436 
437         ustr = xalloc(sizeof(*ustr) + length + 1);
438         ustr->header.type = UC_STRING;
439         ustr->header.refcount = 1;
440         ustr->length = length;
441         *str = (char *)ustr->str;
442 
443         return &ustr->header;
444 }
445 
446 uc_value_t *
447 ucv_string_new_length(const char *str, size_t length)
448 {
449         uc_value_t *ustr;
450         char *buf;
451 
452         if ((length + 1) < sizeof(void *)) {
453                 uintptr_t pv = UC_STRING | (length << 2);
454                 size_t i;
455                 char *s;
456 
457 #if __BYTE_ORDER == __LITTLE_ENDIAN
458                 s = (char *)&pv + 1;
459 #else
460                 s = (char *)&pv;
461 #endif
462 
463                 for (i = 0; i < length; i++)
464                         s[i] = str[i];
465 
466                 return (uc_value_t *)pv;
467         }
468 
469         ustr = ucv_string_alloc(&buf, length);
470         memcpy(buf, str, length);
471 
472         return ustr;
473 }
474 
475 uc_stringbuf_t *
476 ucv_stringbuf_new(void)
477 {
478         uc_stringbuf_t *sb = xprintbuf_new();
479         uc_string_t ustr = {
480                 .header = {
481                         .type = UC_STRING,
482                         .refcount = 1
483                 }
484         };
485 
486         printbuf_memappend_fast(sb, (char *)&ustr, (int)sizeof(ustr));
487 
488         return sb;
489 }
490 
491 uc_value_t *
492 ucv_stringbuf_finish(uc_stringbuf_t *sb)
493 {
494         uc_string_t *ustr = (uc_string_t *)sb->buf;
495 
496         ustr->length = printbuf_length(sb) - offsetof(uc_string_t, str);
497 
498         free(sb);
499 
500         return &ustr->header;
501 }
502 
503 char *
504 _ucv_string_get(uc_value_t **uv)
505 {
506         uc_string_t *str;
507 
508         switch ((uintptr_t)*uv & 3) {
509         case UC_STRING:
510 #if __BYTE_ORDER == __LITTLE_ENDIAN
511                 return (char *)uv + 1;
512 #else
513                 return (char *)uv;
514 #endif
515 
516         case UC_NULL:
517                 if (*uv != NULL && (*uv)->type == UC_STRING) {
518                         str = (uc_string_t *)*uv;
519 
520                         return str->str;
521                 }
522         }
523 
524         return NULL;
525 }
526 
527 size_t
528 ucv_string_length(uc_value_t *uv)
529 {
530         uc_string_t *str = ucv_as_string(uv);
531         uintptr_t pv = (uintptr_t)uv;
532 
533         if ((pv & 3) == UC_STRING)
534                 return (pv & 0xff) >> 2;
535         else if (uv != NULL && uv->type == UC_STRING)
536                 return str->length;
537 
538         return 0;
539 }
540 
541 
542 uc_value_t *
543 ucv_int64_new(int64_t n)
544 {
545         uint64_t uval = (n < 0) ? ((n > INT64_MIN) ? (~n + 1) : INT64_MAX) : n;
546         uint64_t max = (1ULL << ((sizeof(void *) * 8) - 3)) - 1;
547         uc_integer_t *integer;
548         uintptr_t pv;
549 
550         if (uval <= max) {
551                 pv = UC_INTEGER | ((n < 0) << 2) | (uval << 3);
552 
553                 return (uc_value_t *)pv;
554         }
555 
556         integer = xalloc(sizeof(*integer));
557         integer->header.type = UC_INTEGER;
558         integer->header.refcount = 1;
559         integer->header.ext_flag = 0;
560         integer->i.s64 = n;
561 
562         return &integer->header;
563 }
564 
565 uc_value_t *
566 ucv_uint64_new(uint64_t n)
567 {
568         uint64_t max = (1ULL << ((sizeof(void *) * 8) - 3)) - 1;
569         uc_integer_t *integer;
570         uintptr_t pv;
571 
572         if (n <= max) {
573                 pv = UC_INTEGER | (n << 3);
574 
575                 return (uc_value_t *)pv;
576         }
577 
578         integer = xalloc(sizeof(*integer));
579         integer->header.type = UC_INTEGER;
580         integer->header.refcount = 1;
581         integer->header.ext_flag = 1;
582         integer->i.u64 = n;
583 
584         return &integer->header;
585 }
586 
587 uint64_t
588 ucv_uint64_get(uc_value_t *uv)
589 {
590         uintptr_t pv = (uintptr_t)uv;
591         uc_integer_t *integer;
592         double d;
593 
594         errno = 0;
595 
596         if ((pv & 3) == UC_INTEGER) {
597                 if (((pv >> 2) & 1) == 0)
598                         return (uint64_t)(pv >> 3);
599 
600                 errno = ERANGE;
601 
602                 return 0;
603         }
604 
605         switch (ucv_type(uv)) {
606         case UC_INTEGER:
607                 integer = ucv_as_integer(uv);
608 
609                 if (integer->header.ext_flag)
610                         return integer->i.u64;
611 
612                 if (integer->i.s64 >= 0)
613                         return (uint64_t)integer->i.s64;
614 
615                 errno = ERANGE;
616 
617                 return 0;
618 
619         case UC_DOUBLE:
620                 d = ucv_double_get(uv);
621 
622                 if (d < 0.0) {
623                         errno = ERANGE;
624 
625                         return 0;
626                 }
627 
628                 if (d >= ldexp(1.0, 64)) {
629                         errno = ERANGE;
630 
631                         return UINT64_MAX;
632                 }
633 
634                 return (uint64_t)d;
635 
636         default:
637                 errno = EINVAL;
638 
639                 return 0;
640         }
641 }
642 
643 int64_t
644 ucv_int64_get(uc_value_t *uv)
645 {
646         uintptr_t pv = (uintptr_t)uv;
647         uc_integer_t *integer;
648         double d;
649 
650         errno = 0;
651 
652         if ((pv & 3) == UC_INTEGER) {
653                 if (((pv >> 2) & 1) == 0)
654                         return (int64_t)(pv >> 3);
655 
656                 return -(int64_t)(pv >> 3);
657         }
658 
659         switch (ucv_type(uv)) {
660         case UC_INTEGER:
661                 integer = ucv_as_integer(uv);
662 
663                 if (integer->header.ext_flag && integer->i.u64 <= (uint64_t)INT64_MAX)
664                         return (int64_t)integer->i.u64;
665 
666                 if (!integer->header.ext_flag)
667                         return integer->i.s64;
668 
669                 errno = ERANGE;
670 
671                 return INT64_MAX;
672 
673         case UC_DOUBLE:
674                 d = ucv_double_get(uv);
675 
676                 if (d < ldexp(-1.0, 63)) {
677                         errno = ERANGE;
678 
679                         return INT64_MIN;
680                 }
681 
682                 if (d >= ldexp(1.0, 63)) {
683                         errno = ERANGE;
684 
685                         return INT64_MAX;
686                 }
687 
688                 return (int64_t)d;
689 
690         default:
691                 errno = EINVAL;
692 
693                 return 0;
694         }
695 }
696 
697 
698 uc_value_t *
699 ucv_double_new(double d)
700 {
701         uc_double_t *dbl;
702 
703         dbl = xalloc(sizeof(*dbl));
704         dbl->header.type = UC_DOUBLE;
705         dbl->header.refcount = 1;
706         dbl->dbl = d;
707 
708         return &dbl->header;
709 }
710 
711 double
712 ucv_double_get(uc_value_t *uv)
713 {
714         uint64_t max_int = (2ULL << (DBL_MANT_DIG - 1));
715         uc_double_t *dbl;
716         uint64_t u;
717         int64_t n;
718 
719         errno = 0;
720 
721         switch (ucv_type(uv)) {
722         case UC_DOUBLE:
723                 dbl = ucv_as_double(uv);
724 
725                 return dbl->dbl;
726 
727         case UC_INTEGER:
728                 n = ucv_int64_get(uv);
729 
730                 if (errno == ERANGE) {
731                         u = ucv_uint64_get(uv);
732 
733                         /* signal precision loss for integral values >2^53 */
734                         if (u > max_int)
735                                 errno = ERANGE;
736 
737                         return (double)u;
738                 }
739 
740                 /* signal precision loss for integral values <-2^53 or >2^53 */
741                 if (n < -(int64_t)max_int || n > (int64_t)max_int)
742                         errno = ERANGE;
743 
744                 return (double)n;
745 
746         default:
747                 errno = EINVAL;
748 
749                 return NAN;
750         }
751 }
752 
753 
754 uc_value_t *
755 ucv_array_new(uc_vm_t *vm)
756 {
757         return ucv_array_new_length(vm, 0);
758 }
759 
760 uc_value_t *
761 ucv_array_new_length(uc_vm_t *vm, size_t length)
762 {
763         uc_array_t *array;
764 
765         array = xalloc(sizeof(*array));
766         array->header.type = UC_ARRAY;
767         array->header.refcount = 1;
768 
769         /* preallocate memory */
770         if (length)
771                 uc_vector_extend(array, length);
772 
773         if (vm) {
774                 ucv_ref(&vm->values, &array->ref);
775                 vm->alloc_refs++;
776         }
777 
778         return &array->header;
779 }
780 
781 uc_value_t *
782 ucv_array_pop(uc_value_t *uv)
783 {
784         uc_array_t *array = ucv_as_array(uv);
785         uc_value_t *item;
786 
787         if (ucv_type(uv) != UC_ARRAY || array->count == 0)
788                 return NULL;
789 
790         item = ucv_get(array->entries[array->count - 1]);
791 
792         ucv_array_delete(uv, array->count - 1, 1);
793 
794         return item;
795 }
796 
797 uc_value_t *
798 ucv_array_push(uc_value_t *uv, uc_value_t *item)
799 {
800         uc_array_t *array = ucv_as_array(uv);
801 
802         if (ucv_type(uv) != UC_ARRAY || uv->ext_flag)
803                 return NULL;
804 
805         ucv_array_set(uv, array->count, item);
806 
807         return item;
808 }
809 
810 uc_value_t *
811 ucv_array_shift(uc_value_t *uv)
812 {
813         uc_array_t *array = ucv_as_array(uv);
814         uc_value_t *item;
815 
816         if (ucv_type(uv) != UC_ARRAY || array->count == 0)
817                 return NULL;
818 
819         item = ucv_get(array->entries[0]);
820 
821         ucv_array_delete(uv, 0, 1);
822 
823         return item;
824 }
825 
826 uc_value_t *
827 ucv_array_unshift(uc_value_t *uv, uc_value_t *item)
828 {
829         uc_array_t *array = ucv_as_array(uv);
830         size_t i;
831 
832         if (ucv_type(uv) != UC_ARRAY)
833                 return NULL;
834 
835         uc_vector_extend(array, 1);
836 
837         for (i = ++array->count; i > 1; i--)
838                 array->entries[i - 1] = array->entries[i - 2];
839 
840         array->entries[0] = item;
841 
842         return item;
843 }
844 
845 typedef struct {
846         int (*cmp)(uc_value_t *, uc_value_t *, void *);
847         void *ud;
848 } array_sort_ctx_t;
849 
850 static uc_vector_sort_cb(ucv_array_sort_r_cb, uc_value_t *, array_sort_ctx_t *, {
851         return ctx->cmp(v1, v2, ctx->ud);
852 });
853 
854 void
855 ucv_array_sort_r(uc_value_t *uv,
856                  int (*cmp)(uc_value_t *, uc_value_t *, void *), void *ud)
857 {
858         array_sort_ctx_t ctx = { .cmp = cmp, .ud = ud };
859         uc_array_t *array = ucv_as_array(uv);
860 
861         if (ucv_type(uv) != UC_ARRAY || array->count <= 1)
862                 return;
863 
864         uc_vector_sort(array, ucv_array_sort_r_cb, &ctx);
865 }
866 
867 void
868 ucv_array_sort(uc_value_t *uv, int (*cmp)(const void *, const void *))
869 {
870         uc_array_t *array = ucv_as_array(uv);
871 
872         if (ucv_type(uv) != UC_ARRAY || array->count <= 1)
873                 return;
874 
875         qsort(array->entries, array->count, sizeof(array->entries[0]), cmp);
876 }
877 
878 bool
879 ucv_array_delete(uc_value_t *uv, size_t offset, size_t count)
880 {
881         uc_array_t *array = ucv_as_array(uv);
882         size_t i;
883 
884         if (ucv_type(uv) != UC_ARRAY || array->count == 0)
885                 return false;
886 
887         if (offset >= array->count)
888                 return false;
889 
890         if ((offset + count) < offset)
891                 return false;
892 
893         if ((offset + count) > array->count)
894                 count = array->count - offset;
895 
896         for (i = 0; i < count; i++)
897                 ucv_put(array->entries[offset + i]);
898 
899         memmove(&array->entries[offset],
900                 &array->entries[offset + count],
901                 (array->count - (offset + count)) * sizeof(array->entries[0]));
902 
903         uc_vector_reduce(array, count);
904         array->count -= count;
905 
906         return true;
907 }
908 
909 bool
910 ucv_array_set(uc_value_t *uv, size_t index, uc_value_t *item)
911 {
912         uc_array_t *array = ucv_as_array(uv);
913 
914         if (ucv_type(uv) != UC_ARRAY)
915                 return false;
916 
917         if (index >= array->count) {
918                 uc_vector_extend(array, index + 1 - array->count);
919                 array->count = index + 1;
920         }
921         else {
922                 ucv_put(array->entries[index]);
923         }
924 
925         array->entries[index] = item;
926 
927         return true;
928 }
929 
930 uc_value_t *
931 ucv_array_get(uc_value_t *uv, size_t index)
932 {
933         uc_array_t *array = ucv_as_array(uv);
934 
935         if (ucv_type(uv) != UC_ARRAY)
936                 return NULL;
937 
938         if (index >= array->count)
939                 return NULL;
940 
941         return array->entries[index];
942 }
943 size_t
944 ucv_array_length(uc_value_t *uv)
945 {
946         uc_array_t *array = ucv_as_array(uv);
947 
948         if (ucv_type(uv) != UC_ARRAY)
949                 return 0;
950 
951         return array->count;
952 }
953 
954 
955 static void
956 ucv_free_object_entry(struct lh_entry *entry)
957 {
958         uc_list_foreach(item, &uc_thread_context_get()->object_iterators) {
959                 uc_object_iterator_t *iter = (uc_object_iterator_t *)item;
960 
961                 if (iter->u.pos == entry)
962                         iter->u.pos = entry->next;
963         }
964 
965         free(lh_entry_k(entry));
966         ucv_put(lh_entry_v(entry));
967 }
968 
969 uc_value_t *
970 ucv_object_new(uc_vm_t *vm)
971 {
972         struct lh_table *table;
973         uc_object_t *object;
974 
975         table = lh_kchar_table_new(16, ucv_free_object_entry);
976 
977         if (!table) {
978                 fprintf(stderr, "Out of memory\n");
979                 abort();
980         }
981 
982         object = xalloc(sizeof(*object));
983         object->header.type = UC_OBJECT;
984         object->header.refcount = 1;
985         object->table = table;
986 
987         if (vm) {
988                 ucv_ref(&vm->values, &object->ref);
989                 vm->alloc_refs++;
990         }
991 
992         return &object->header;
993 }
994 
995 bool
996 ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val)
997 {
998         uc_object_t *object = ucv_as_object(uv);
999         struct lh_entry *existing_entry;
1000         uc_value_t *existing_value;
1001         unsigned long hash;
1002         void *k;
1003 
1004         if (ucv_type(uv) != UC_OBJECT || uv->ext_flag)
1005                 return false;
1006 
1007         hash = lh_get_hash(object->table, (const void *)key);
1008         existing_entry = lh_table_lookup_entry_w_hash(object->table, (const void *)key, hash);
1009 
1010         if (existing_entry == NULL) {
1011                 bool rehash = (object->table->count >= object->table->size * LH_LOAD_FACTOR);
1012 
1013                 /* insert will rehash table, backup affected iterator states */
1014                 if (rehash) {
1015                         uc_list_foreach(item, &uc_thread_context_get()->object_iterators) {
1016                                 uc_object_iterator_t *iter = (uc_object_iterator_t *)item;
1017 
1018                                 if (iter->table != object->table)
1019                                         continue;
1020 
1021                                 if (iter->u.pos == NULL)
1022                                         continue;
1023 
1024                                 iter->u.kh.k = iter->u.pos->k;
1025                                 iter->u.kh.hash = lh_get_hash(iter->table, iter->u.kh.k);
1026                         }
1027                 }
1028 
1029                 k = xstrdup(key);
1030 
1031                 if (lh_table_insert_w_hash(object->table, k, val, hash, 0) != 0) {
1032                         free(k);
1033 
1034                         return false;
1035                 }
1036 
1037                 /* restore affected iterator state pointer after rehash */
1038                 if (rehash) {
1039                         uc_list_foreach(item, &uc_thread_context_get()->object_iterators) {
1040                                 uc_object_iterator_t *iter = (uc_object_iterator_t *)item;
1041 
1042                                 if (iter->table != object->table)
1043                                         continue;
1044 
1045                                 if (iter->u.kh.k == NULL)
1046                                         continue;
1047 
1048                                 iter->u.pos = lh_table_lookup_entry_w_hash(iter->table,
1049                                                                            iter->u.kh.k,
1050                                                                            iter->u.kh.hash);
1051                         }
1052                 }
1053 
1054                 return true;
1055         }
1056 
1057         existing_value = (uc_value_t *)lh_entry_v(existing_entry);
1058 
1059         if (existing_value)
1060                 ucv_put(existing_value);
1061 
1062         existing_entry->v = val;
1063 
1064         return true;
1065 }
1066 
1067 
1068 typedef struct {
1069         int (*cmp)(const void *, const void *);
1070         int (*cmpr)(const char *, uc_value_t *, const char *, uc_value_t *, void *);
1071         void *ud;
1072 } object_sort_ctx_t;
1073 
1074 static uc_vector_sort_cb(ucv_object_sort_cb, const void *, object_sort_ctx_t *, {
1075         (void)v1;
1076         (void)v2;
1077 
1078         return ctx->cmp(k1, k2);
1079 });
1080 
1081 static uc_vector_sort_cb(ucv_object_sort_r_cb, const struct lh_entry *, object_sort_ctx_t *, {
1082         return ctx->cmpr(
1083                 v1 ? lh_entry_k(v1) : NULL, v1 ? lh_entry_v(v1) : NULL,
1084                 v2 ? lh_entry_k(v2) : NULL, v2 ? lh_entry_v(v2) : NULL,
1085                 ctx->ud);
1086 });
1087 
1088 static void
1089 ucv_object_sort_common(uc_value_t *uv, object_sort_ctx_t *ctx)
1090 {
1091         uc_object_t *object = ucv_as_object(uv);
1092         struct lh_table *t;
1093         struct lh_entry *e;
1094         size_t i;
1095 
1096         struct {
1097                 struct lh_entry **entries;
1098                 size_t count;
1099         } keys = { 0 };
1100 
1101         if (ucv_type(uv) != UC_OBJECT || lh_table_length(object->table) <= 1)
1102                 return;
1103 
1104         for (t = object->table, e = t->head; e; e = e->next)
1105                 uc_vector_push(&keys, e);
1106 
1107         if (!keys.entries)
1108                 return;
1109 
1110         uc_vector_sort(&keys,
1111                 ctx->cmpr ? ucv_object_sort_r_cb : ucv_object_sort_cb, ctx);
1112 
1113         for (i = 0; i < keys.count; i++) {
1114                 e = keys.entries[i];
1115 
1116                 if (i == 0) {
1117                         t->head = t->tail = e;
1118                         e->next = e->prev = NULL;
1119                 }
1120                 else {
1121                         t->tail->next = e;
1122                         e->prev = t->tail;
1123                         e->next = NULL;
1124                         t->tail = e;
1125                 }
1126         }
1127 
1128         uc_vector_clear(&keys);
1129 }
1130 
1131 void
1132 ucv_object_sort_r(uc_value_t *uv,
1133                   int (*cmp)(const char *, uc_value_t *,
1134                              const char *, uc_value_t *, void *),
1135                   void *ud)
1136 {
1137         object_sort_ctx_t ctx = { .cmp = NULL, .cmpr = cmp, .ud = ud };
1138 
1139         ucv_object_sort_common(uv, &ctx);
1140 }
1141 
1142 void
1143 ucv_object_sort(uc_value_t *uv, int (*cmp)(const void *, const void *))
1144 {
1145         object_sort_ctx_t ctx = { .cmp = cmp, .cmpr = NULL, .ud = NULL };
1146 
1147         ucv_object_sort_common(uv, &ctx);
1148 }
1149 
1150 bool
1151 ucv_object_delete(uc_value_t *uv, const char *key)
1152 {
1153         uc_object_t *object = ucv_as_object(uv);
1154 
1155         if (ucv_type(uv) != UC_OBJECT || uv->ext_flag)
1156                 return false;
1157 
1158         return (lh_table_delete(object->table, key) == 0);
1159 }
1160 
1161 uc_value_t *
1162 ucv_object_get(uc_value_t *uv, const char *key, bool *found)
1163 {
1164         uc_object_t *object = ucv_as_object(uv);
1165         uc_value_t *val = NULL;
1166         bool rv;
1167 
1168         if (found != NULL)
1169                 *found = false;
1170 
1171         if (ucv_type(uv) != UC_OBJECT)
1172                 return NULL;
1173 
1174         rv = lh_table_lookup_ex(object->table, (const void *)key, (void **)&val);
1175 
1176         if (found != NULL)
1177                 *found = rv;
1178 
1179         return val;
1180 }
1181 
1182 size_t
1183 ucv_object_length(uc_value_t *uv)
1184 {
1185         uc_object_t *object = ucv_as_object(uv);
1186 
1187         if (ucv_type(uv) != UC_OBJECT)
1188                 return 0;
1189 
1190         return lh_table_length(object->table);
1191 }
1192 
1193 
1194 uc_value_t *
1195 ucv_cfunction_new(const char *name, uc_cfn_ptr_t fptr)
1196 {
1197         uc_cfunction_t *cfn;
1198         size_t namelen = 0;
1199 
1200         if (name)
1201                 namelen = strlen(name);
1202 
1203         cfn = xalloc(sizeof(*cfn) + namelen + 1);
1204         cfn->header.type = UC_CFUNCTION;
1205         cfn->header.refcount = 1;
1206 
1207         if (name)
1208                 strcpy(cfn->name, name);
1209 
1210         cfn->cfn = fptr;
1211 
1212         return &cfn->header;
1213 }
1214 
1215 
1216 uc_value_t *
1217 ucv_closure_new(uc_vm_t *vm, uc_function_t *function, bool arrow_fn)
1218 {
1219         uc_closure_t *closure;
1220 
1221         closure = xalloc(sizeof(*closure) + (sizeof(uc_upvalref_t *) * function->nupvals));
1222         closure->header.type = UC_CLOSURE;
1223         closure->header.refcount = 1;
1224         closure->function = function;
1225         closure->is_arrow = arrow_fn;
1226         closure->upvals = function->nupvals ? (uc_upvalref_t **)((uintptr_t)closure + ALIGN(sizeof(*closure))) : NULL;
1227 
1228         if (vm) {
1229                 ucv_ref(&vm->values, &closure->ref);
1230                 vm->alloc_refs++;
1231         }
1232 
1233         uc_program_get(function->program);
1234 
1235         return &closure->header;
1236 }
1237 
1238 
1239 uc_resource_type_t *
1240 ucv_resource_type_add(uc_vm_t *vm, const char *name, uc_value_t *proto, void (*freefn)(void *))
1241 {
1242         uc_resource_type_t *type;
1243 
1244         type = ucv_resource_type_lookup(vm, name);
1245 
1246         if (type) {
1247                 ucv_put(proto);
1248 
1249                 return type;
1250         }
1251 
1252         type = xalloc(sizeof(*type));
1253         type->name = name;
1254         type->proto = proto;
1255         type->free = freefn;
1256 
1257         uc_vector_push(&vm->restypes, type);
1258 
1259         return type;
1260 }
1261 
1262 uc_resource_type_t *
1263 ucv_resource_type_lookup(uc_vm_t *vm, const char *name)
1264 {
1265         size_t i;
1266 
1267         for (i = 0; i < vm->restypes.count; i++)
1268                 if (!strcmp(vm->restypes.entries[i]->name, name))
1269                         return vm->restypes.entries[i];
1270 
1271         return NULL;
1272 }
1273 
1274 
1275 uc_value_t *
1276 ucv_resource_new(uc_resource_type_t *type, void *data)
1277 {
1278         uc_resource_t *res;
1279 
1280         res = xalloc(sizeof(*res));
1281         res->header.type = UC_RESOURCE;
1282         res->header.refcount = 1;
1283         res->type = type;
1284         res->data = data;
1285 
1286         return &res->header;
1287 }
1288 
1289 uc_value_t *
1290 ucv_resource_new_ex(uc_vm_t *vm, uc_resource_type_t *type, void **data, size_t uvcount, size_t datasize)
1291 {
1292         uc_resource_ext_t *res;
1293         size_t size;
1294 
1295         assert(uvcount < (1 << 8));
1296         assert(datasize < (8 << 20));
1297 
1298         size = sizeof(*res);
1299         size += datasize = (datasize + 7) & ~7;
1300         size += uvcount * sizeof(uc_value_t *);
1301 
1302         res = xalloc(size);
1303         res->header.type = UC_RESOURCE;
1304         res->header.refcount = 1;
1305         res->header.ext_flag = 1;
1306         res->type = type;
1307         res->uvcount = uvcount;
1308         res->datasize = datasize / 8;
1309         res->hasproto = 0;
1310         if (data)
1311                 *data = res + 1;
1312 
1313         if (vm && uvcount) {
1314                 ucv_ref_tail(&vm->values, &res->ref);
1315                 vm->alloc_refs++;
1316         }
1317 
1318         return &res->header;
1319 }
1320 
1321 uc_value_t *
1322 ucv_resource_new_with_proto(uc_vm_t *vm, uc_resource_type_t *type, void **data, size_t uvcount, size_t datasize, uc_value_t *proto)
1323 {
1324         uc_resource_ext_t *res;
1325         size_t size;
1326         uc_value_t **values;
1327 
1328         assert(uvcount < (1 << 8));
1329         assert(datasize < (8 << 20));
1330 
1331         /* If no proto provided, fall back to type->proto (no instance proto slot) */
1332         if (!proto)
1333                 return ucv_resource_new_ex(vm, type, data, uvcount, datasize);
1334 
1335         size = sizeof(*res);
1336         size += datasize = (datasize + 7) & ~7;
1337         size += (uvcount + 1) * sizeof(uc_value_t *);
1338 
1339         res = xalloc(size);
1340         res->header.type = UC_RESOURCE;
1341         res->header.refcount = 1;
1342         res->header.ext_flag = 1;
1343         res->type = type;
1344         res->uvcount = uvcount;
1345         res->datasize = datasize / 8;
1346         res->hasproto = 1;
1347 
1348         if (data)
1349                 *data = res + 1;
1350 
1351         if (vm && uvcount) {
1352                 ucv_ref_tail(&vm->values, &res->ref);
1353                 vm->alloc_refs++;
1354         }
1355 
1356         /* Chain the given proto to type->proto so prototype lookup works correctly */
1357         if (type)
1358                 ucv_prototype_set(proto, ucv_get(type->proto));
1359 
1360         values = (uc_value_t **)((char *)(res + 1) + datasize);
1361         values[0] = proto;
1362 
1363         return &res->header;
1364 }
1365 
1366 static bool
1367 ucv_resource_check(uc_value_t *uv, const char *name)
1368 {
1369         uc_resource_type_t *restype;
1370 
1371         if (ucv_type(uv) != UC_RESOURCE)
1372                 return false;
1373 
1374         if (!name)
1375                 return true;
1376 
1377         restype = ucv_resource_type(uv);
1378 
1379         return restype && !strcmp(restype->name, name);
1380 }
1381 
1382 void *
1383 ucv_resource_data(uc_value_t *uv, const char *name)
1384 {
1385         if (!ucv_resource_check(uv, name))
1386                 return NULL;
1387 
1388         if (uv->ext_flag) {
1389                 uc_resource_ext_t *res = (uc_resource_ext_t *)uv;
1390                 void *data = res + 1;
1391 
1392                 return data;
1393         }
1394         else {
1395                 uc_resource_t *res = ucv_as_resource(uv);
1396 
1397                 return res->data;
1398         }
1399 }
1400 
1401 void **
1402 ucv_resource_dataptr(uc_value_t *uv, const char *name)
1403 {
1404         uc_resource_t *res = ucv_as_resource(uv);
1405 
1406         if (!ucv_resource_check(uv, name) || res->header.ext_flag)
1407                 return NULL;
1408 
1409         return &res->data;
1410 }
1411 
1412 uc_value_t *
1413 ucv_resource_value_get(uc_value_t *uv, size_t idx)
1414 {
1415         uc_resource_ext_t *res = (uc_resource_ext_t *)uv;
1416         uc_value_t **uvdata = ucv_resource_values(res);
1417 
1418         if (!uvdata)
1419                 return NULL;
1420 
1421         if (idx == UCV_RESOURCE_PROTO_IDX) {
1422                 if (!res->hasproto)
1423                         return NULL;
1424 
1425                 return uvdata[0];
1426         }
1427 
1428         if (idx >= res->uvcount)
1429                 return NULL;
1430 
1431         return uvdata[idx + res->hasproto];
1432 }
1433 
1434 bool
1435 ucv_resource_value_set(uc_value_t *uv, size_t idx, uc_value_t *val)
1436 {
1437         uc_resource_ext_t *res = (uc_resource_ext_t *)uv;
1438         uc_value_t **uvdata = ucv_resource_values(res);
1439 
1440         if (!uvdata)
1441                 return false;
1442 
1443         if (idx == UCV_RESOURCE_PROTO_IDX) {
1444                 if (!res->hasproto)
1445                         return false;
1446 
1447                 ucv_put(uvdata[0]);
1448                 uvdata[0] = val;
1449 
1450                 return true;
1451         }
1452 
1453         if (idx >= res->uvcount)
1454                 return false;
1455 
1456         ucv_put(uvdata[idx + res->hasproto]);
1457         uvdata[idx + res->hasproto] = val;
1458 
1459         return true;
1460 }
1461 
1462 uc_value_t *
1463 ucv_regexp_new(const char *pattern, bool icase, bool newline, bool global, char **error)
1464 {
1465         int cflags = REG_EXTENDED, res;
1466         uc_regexp_t *re;
1467         size_t len;
1468 
1469         re = xalloc(sizeof(*re) + strlen(pattern) + 1);
1470         re->header.type = UC_REGEXP;
1471         re->header.refcount = 1;
1472         re->icase = icase;
1473         re->global = global;
1474         re->newline = newline;
1475         strcpy(re->source, pattern);
1476 
1477         if (icase)
1478                 cflags |= REG_ICASE;
1479 
1480         if (!newline)
1481                 cflags |= REG_NEWLINE;
1482 
1483         res = regcomp(&re->regexp, pattern, cflags);
1484 
1485         if (res != 0) {
1486                 if (error) {
1487                         len = regerror(res, &re->regexp, NULL, 0);
1488                         *error = xalloc(len);
1489 
1490                         regerror(res, &re->regexp, *error, len);
1491                 }
1492 
1493                 free(re);
1494 
1495                 return NULL;
1496         }
1497 
1498         return &re->header;
1499 }
1500 
1501 
1502 uc_value_t *
1503 ucv_upvalref_new(size_t slot)
1504 {
1505         uc_upvalref_t *up;
1506 
1507         up = xalloc(sizeof(*up));
1508         up->header.type = UC_UPVALUE;
1509         up->header.refcount = 1;
1510         up->slot = slot;
1511 
1512         return &up->header;
1513 }
1514 
1515 
1516 uc_value_t *
1517 ucv_prototype_get(uc_value_t *uv)
1518 {
1519         uc_resource_type_t *restype;
1520         uc_object_t *object;
1521         uc_array_t *array;
1522 
1523         switch (ucv_type(uv)) {
1524         case UC_ARRAY:
1525                 array = ucv_as_array(uv);
1526 
1527                 return array->proto;
1528 
1529         case UC_OBJECT:
1530                 object = ucv_as_object(uv);
1531 
1532                 return object->proto;
1533 
1534         case UC_RESOURCE:
1535                 if (ucv_resource_hasproto(uv))
1536                         return ucv_resource_proto_get(uv);
1537 
1538                 restype = ucv_resource_type(uv);
1539 
1540                 return restype ? restype->proto : NULL;
1541 
1542         default:
1543                 return NULL;
1544         }
1545 }
1546 
1547 bool
1548 ucv_prototype_set(uc_value_t *uv, uc_value_t *proto)
1549 {
1550         uc_object_t *object;
1551         uc_array_t *array;
1552 
1553         if (ucv_type(proto) != UC_OBJECT)
1554                 return false;
1555 
1556         switch (ucv_type(uv)) {
1557         case UC_ARRAY:
1558                 array = ucv_as_array(uv);
1559                 ucv_put(array->proto);
1560                 array->proto = proto;
1561 
1562                 return true;
1563 
1564         case UC_OBJECT:
1565                 object = ucv_as_object(uv);
1566                 ucv_put(object->proto);
1567                 object->proto = proto;
1568 
1569                 return true;
1570 
1571         case UC_RESOURCE:
1572                 if (uv->ext_flag) {
1573                         uc_resource_ext_t *res = (uc_resource_ext_t *)uv;
1574                         uc_value_t **values;
1575 
1576                         if (!res->hasproto)
1577                                 return false;
1578 
1579                         values = ucv_resource_values(res);
1580 
1581                         if (!values)
1582                                 return false;
1583 
1584                         values[0] = proto;
1585 
1586                         return true;
1587                 }
1588 
1589                 return false;
1590 
1591         default:
1592                 return false;
1593         }
1594 }
1595 
1596 uc_value_t *
1597 ucv_metamethod_lookup(uc_value_t *v, const char *name)
1598 {
1599         uc_value_t *m;
1600 
1601         /* The walk starts at the prototype, not at the instance: an own property
1602          * with a dunder name is plain data, not a metamethod. Honoring own keys
1603          * would let ordinary property writes (mixin loops, external data, a
1604          * function stored under a dunder name) silently change how the value
1605          * behaves; metamethods are an explicit, prototype-level mechanism. */
1606         for (v = ucv_prototype_get(v); v; v = ucv_prototype_get(v)) {
1607                 m = ucv_object_get(v, name, NULL);
1608 
1609                 if (ucv_type(m) != UC_NULL)
1610                         return m;
1611         }
1612 
1613         return NULL;
1614 }
1615 
1616 uc_value_t *
1617 ucv_property_get(uc_value_t *uv, const char *key)
1618 {
1619         uc_value_t *val;
1620         bool found;
1621 
1622         for (; uv; uv = ucv_prototype_get(uv)) {
1623                 val = ucv_object_get(uv, key, &found);
1624 
1625                 if (found)
1626                         return val;
1627         }
1628 
1629         return NULL;
1630 }
1631 
1632 
1633 uc_value_t *
1634 ucv_from_json(uc_vm_t *vm, json_object *jso)
1635 {
1636         //uc_array_t *arr;
1637         uc_value_t *uv, *item;
1638         int64_t n;
1639         size_t i;
1640 
1641         switch (json_object_get_type(jso)) {
1642         case json_type_null:
1643                 return NULL;
1644 
1645         case json_type_boolean:
1646                 return ucv_boolean_new(json_object_get_boolean(jso));
1647 
1648         case json_type_double:
1649                 return ucv_double_new(json_object_get_double(jso));
1650 
1651         case json_type_int:
1652                 n = json_object_get_int64(jso);
1653 
1654                 if (n == INT64_MAX)
1655                         return ucv_uint64_new(json_object_get_uint64(jso));
1656 
1657                 return ucv_int64_new(n);
1658 
1659         case json_type_object:
1660                 uv = ucv_object_new(vm);
1661 
1662                 json_object_object_foreach(jso, key, val) {
1663                         item = ucv_from_json(vm, val);
1664 
1665                         if (!ucv_object_add(uv, key, item))
1666                                 ucv_put(item);
1667 
1668 #ifdef __clang_analyzer__
1669                         /* Clang static analyzer does not understand that the object retains
1670                          * our item so pretend to free it here to suppress the false positive
1671                          * memory leak warning. */
1672                         ucv_put(item);
1673 #endif
1674                 }
1675 
1676                 return uv;
1677 
1678         case json_type_array:
1679                 /* XXX
1680                 arr = (uc_array_t *)ucv_array_new_length(vm, json_object_array_length(jso));
1681 
1682                 for (i = 0; i < arr->count; i++)
1683                         arr->entries[i] = ucv_from_json(vm, json_object_array_get_idx(jso, i));
1684 
1685                 return &arr->header;
1686                 */
1687                 uv = ucv_array_new(vm);
1688 
1689                 for (i = 0; i < json_object_array_length(jso); i++) {
1690                         item = ucv_from_json(vm, json_object_array_get_idx(jso, i));
1691 
1692                         if (!ucv_array_push(uv, item))
1693                                 ucv_put(item);
1694 
1695 #ifdef __clang_analyzer__
1696                         /* Clang static analyzer does not understand that the array retains
1697                          * our item so pretend to free it here to suppress the false positive
1698                          * memory leak warning. */
1699                         ucv_put(item);
1700 #endif
1701                 }
1702 
1703                 return uv;
1704 
1705         case json_type_string:
1706                 return ucv_string_new_length(json_object_get_string(jso), json_object_get_string_len(jso));
1707         }
1708 
1709         return NULL;
1710 }
1711 
1712 json_object *
1713 ucv_to_json(uc_value_t *uv)
1714 {
1715         uc_regexp_t *regexp;
1716         uc_array_t *array;
1717         json_object *jso;
1718         size_t i;
1719         char *s;
1720 
1721         switch (ucv_type(uv)) {
1722         case UC_BOOLEAN:
1723                 return json_object_new_boolean(ucv_boolean_get(uv));
1724 
1725         case UC_INTEGER:
1726                 if (ucv_is_u64(uv))
1727                         return json_object_new_uint64(ucv_uint64_get(uv));
1728 
1729                 return json_object_new_int64(ucv_int64_get(uv));
1730 
1731         case UC_DOUBLE:
1732                 return json_object_new_double(ucv_double_get(uv));
1733 
1734         case UC_STRING:
1735                 return json_object_new_string_len(ucv_string_get(uv), ucv_string_length(uv));
1736 
1737         case UC_ARRAY:
1738                 array = ucv_as_array(uv);
1739                 jso = json_object_new_array_ext(array->count);
1740 
1741                 for (i = 0; i < array->count; i++)
1742                         json_object_array_put_idx(jso, i, ucv_to_json(array->entries[i]));
1743 
1744                 return jso;
1745 
1746         case UC_OBJECT:
1747                 jso = json_object_new_object();
1748 
1749                 ucv_object_foreach(uv, key, val)
1750                         json_object_object_add(jso, key, ucv_to_json(val));
1751 
1752                 return jso;
1753 
1754         case UC_REGEXP:
1755                 regexp = ucv_as_regexp(uv);
1756                 i = asprintf(&s, "/%s/%s%s%s",
1757                         regexp->source,
1758                         regexp->global ? "g" : "",
1759                         regexp->icase ? "i" : "",
1760                         regexp->newline ? "s" : "");
1761 
1762                 if (i <= 0)
1763                         return NULL;
1764 
1765                 jso = json_object_new_string_len(s, i);
1766 
1767                 free(s);
1768 
1769                 return jso;
1770 
1771         case UC_CLOSURE:
1772         case UC_CFUNCTION:
1773         case UC_RESOURCE:
1774         case UC_UPVALUE:
1775         case UC_PROGRAM:
1776         case UC_SOURCE:
1777         case UC_NULL:
1778                 return NULL;
1779         }
1780 
1781         return NULL;
1782 }
1783 
1784 static void
1785 ucv_to_string_json_encoded(uc_stringbuf_t *pb, const char *s, size_t len, bool regexp)
1786 {
1787         size_t i;
1788 
1789         if (!regexp)
1790                 ucv_stringbuf_append(pb, "\"");
1791 
1792         for (i = 0; s != NULL && i < len; i++, s++) {
1793                 switch (*s) {
1794                 case '"':
1795                         ucv_stringbuf_append(pb, "\\\"");
1796                         break;
1797 
1798                 case '\\':
1799                         ucv_stringbuf_append(pb, "\\\\");
1800                         break;
1801 
1802                 case '\b':
1803                         ucv_stringbuf_append(pb, "\\b");
1804                         break;
1805 
1806                 case '\f':
1807                         ucv_stringbuf_append(pb, "\\f");
1808                         break;
1809 
1810                 case '\n':
1811                         ucv_stringbuf_append(pb, "\\n");
1812                         break;
1813 
1814                 case '\r':
1815                         ucv_stringbuf_append(pb, "\\r");
1816                         break;
1817 
1818                 case '\t':
1819                         ucv_stringbuf_append(pb, "\\t");
1820                         break;
1821 
1822                 case '/':
1823                         ucv_stringbuf_append(pb, "/");
1824                         break;
1825 
1826                 default:
1827                         if ((unsigned char)*s < 0x20)
1828                                 ucv_stringbuf_printf(pb, "\\u%04x", (unsigned char)*s);
1829                         else
1830                                 ucv_stringbuf_addstr(pb, s, 1);
1831 
1832                         break;
1833                 }
1834         }
1835 
1836         if (!regexp)
1837                 ucv_stringbuf_append(pb, "\"");
1838 }
1839 
1840 static bool
1841 ucv_call_tostring(uc_vm_t *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json)
1842 {
1843         uc_value_t *tostr;
1844         uc_value_t *str;
1845         size_t l;
1846         char *s;
1847 
1848         /* Generalized from the old direct-prototype `tostring` lookup: the full
1849          * chain is walked, `__tostring__` wins over the legacy `tostring` alias, a
1850          * strict superset of the old behavior */
1851         tostr = ucv_metamethod_lookup(uv, "__tostring__");
1852         if (tostr == NULL)
1853                 tostr = ucv_metamethod_lookup(uv, "tostring");
1854 
1855         if (!ucv_is_callable(tostr))
1856                 return false;
1857 
1858         uc_vm_stack_push(vm, ucv_get(uv));
1859         uc_vm_stack_push(vm, ucv_get(tostr));
1860 
1861         if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
1862                 return false;
1863 
1864         str = uc_vm_stack_pop(vm);
1865 
1866         if (ucv_type(str) == UC_STRING) {
1867                 s = ucv_string_get(str);
1868                 l = ucv_string_length(str);
1869 
1870                 if (json)
1871                         ucv_to_string_json_encoded(pb, s, l, false);
1872                 else if (s)
1873                         ucv_stringbuf_addstr(pb, s, l);
1874         }
1875         else if (json) {
1876                 ucv_stringbuf_append(pb, "\"\"");
1877         }
1878 
1879         ucv_put(str);
1880 
1881         return true;
1882 }
1883 
1884 void
1885 _ucv_stringbuf_append(uc_stringbuf_t *pb, const char *str, size_t len)
1886 {
1887         printbuf_memappend_fast(pb, str, (int)len);
1888 }
1889 
1890 static void
1891 ucv_to_stringbuf_add_padding(uc_stringbuf_t *pb, char pad_char, size_t pad_size)
1892 {
1893         if (pad_char != '\0' && pad_char != '\1') {
1894                 ucv_stringbuf_append(pb, "\n");
1895                 printbuf_memset(pb, -1, pad_char, pad_size);
1896         }
1897         else {
1898                 ucv_stringbuf_append(pb, " ");
1899         }
1900 }
1901 
1902 static void
1903 ucv_to_stringbuf_add_double(uc_stringbuf_t *pb, double val, bool json)
1904 {
1905         int len = ucv_stringbuf_printf(pb, "%.14g", val);
1906 
1907         if (!json)
1908                 return;
1909 
1910         for (char *p = pb->buf + pb->bpos - len; len > 0; len--, p++)
1911                 if (*p == '.' || *p == 'e')
1912                         return;
1913 
1914         ucv_stringbuf_append(pb, ".0");
1915 }
1916 
1917 void
1918 ucv_to_stringbuf_formatted(uc_vm_t *vm, uc_stringbuf_t *pb, uc_value_t *uv, size_t depth, char pad_char, size_t pad_size)
1919 {
1920         bool json = (pad_char != '\0');
1921         uc_resource_type_t *restype;
1922         uc_cfunction_t *cfunction;
1923         uc_function_t *function;
1924         uc_closure_t *closure;
1925         uc_regexp_t *regexp;
1926         uc_value_t *argname;
1927         uc_upvalref_t *ref;
1928         uc_array_t *array;
1929         size_t i, l;
1930         double d;
1931         char *s;
1932 
1933         if (ucv_is_marked(uv)) {
1934                 ucv_stringbuf_append(pb, "null");
1935 
1936                 return;
1937         }
1938 
1939         if (vm != NULL && ucv_call_tostring(vm, pb, uv, json))
1940                 return;
1941 
1942         ucv_set_mark(uv);
1943 
1944         switch (ucv_type(uv)) {
1945         case UC_NULL:
1946                 ucv_stringbuf_append(pb, "null");
1947                 break;
1948 
1949         case UC_BOOLEAN:
1950                 if (ucv_boolean_get(uv))
1951                         ucv_stringbuf_append(pb, "true");
1952                 else
1953                         ucv_stringbuf_append(pb, "false");
1954                 break;
1955 
1956         case UC_INTEGER:
1957                 if (ucv_is_u64(uv))
1958                         ucv_stringbuf_printf(pb, "%" PRIu64, ucv_uint64_get(uv));
1959                 else
1960                         ucv_stringbuf_printf(pb, "%" PRId64, ucv_int64_get(uv));
1961                 break;
1962 
1963         case UC_DOUBLE:
1964                 d = ucv_double_get(uv);
1965 
1966                 if (json && isnan(d))
1967                         ucv_stringbuf_append(pb, "\"NaN\"");
1968                 else if (json && d == INFINITY)
1969                         ucv_stringbuf_append(pb, "1e309");
1970                 else if (json && d == -INFINITY)
1971                         ucv_stringbuf_append(pb, "-1e309");
1972                 else if (isnan(d))
1973                         ucv_stringbuf_append(pb, "NaN");
1974                 else if (d == INFINITY)
1975                         ucv_stringbuf_append(pb, "Infinity");
1976                 else if (d == -INFINITY)
1977                         ucv_stringbuf_append(pb, "-Infinity");
1978                 else
1979                         ucv_to_stringbuf_add_double(pb, d, json);
1980 
1981                 break;
1982 
1983         case UC_STRING:
1984                 s = ucv_string_get(uv);
1985                 l = ucv_string_length(uv);
1986 
1987                 if (s) {
1988                         if (json)
1989                                 ucv_to_string_json_encoded(pb, s, l, false);
1990                         else
1991                                 ucv_stringbuf_addstr(pb, s, l);
1992                 }
1993 
1994                 break;
1995 
1996         case UC_ARRAY:
1997                 array = ucv_as_array(uv);
1998 
1999                 ucv_stringbuf_append(pb, "[");
2000 
2001                 for (i = 0; i < array->count; i++) {
2002                         if (i)
2003                                 ucv_stringbuf_append(pb, ",");
2004 
2005                         ucv_to_stringbuf_add_padding(pb, pad_char, (depth + 1) * pad_size);
2006                         ucv_to_stringbuf_formatted(vm, pb, array->entries[i], depth + 1, pad_char ? pad_char : '\1', pad_size);
2007                 }
2008 
2009                 ucv_to_stringbuf_add_padding(pb, pad_char, depth * pad_size);
2010                 ucv_stringbuf_append(pb, "]");
2011                 break;
2012 
2013         case UC_OBJECT:
2014                 ucv_stringbuf_append(pb, "{");
2015 
2016                 i = 0;
2017                 ucv_object_foreach(uv, key, val) {
2018                         if (i++)
2019                                 ucv_stringbuf_append(pb, ",");
2020 
2021                         ucv_to_stringbuf_add_padding(pb, pad_char, (depth + 1) * pad_size);
2022                         ucv_to_string_json_encoded(pb, key, strlen(key), false);
2023                         ucv_stringbuf_append(pb, ": ");
2024                         ucv_to_stringbuf_formatted(vm, pb, val, depth + 1, pad_char ? pad_char : '\1', pad_size);
2025                 }
2026 
2027                 ucv_to_stringbuf_add_padding(pb, pad_char, depth * pad_size);
2028                 ucv_stringbuf_append(pb, "}");
2029                 break;
2030 
2031         case UC_REGEXP:
2032                 regexp = ucv_as_regexp(uv);
2033 
2034                 if (json)
2035                         ucv_stringbuf_append(pb, "\"");
2036 
2037                 ucv_stringbuf_append(pb, "/");
2038                 ucv_to_string_json_encoded(pb, regexp->source, strlen(regexp->source), true);
2039                 ucv_stringbuf_append(pb, "/");
2040 
2041                 if (regexp->global)
2042                         ucv_stringbuf_append(pb, "g");
2043 
2044                 if (regexp->icase)
2045                         ucv_stringbuf_append(pb, "i");
2046 
2047                 if (regexp->newline)
2048                         ucv_stringbuf_append(pb, "s");
2049 
2050                 if (json)
2051                         ucv_stringbuf_append(pb, "\"");
2052 
2053                 break;
2054 
2055         case UC_CLOSURE:
2056                 closure = ucv_as_closure(uv);
2057                 function = closure->function;
2058 
2059                 if (json)
2060                         ucv_stringbuf_append(pb, "\"");
2061 
2062                 if (!closure->is_arrow) {
2063                         ucv_stringbuf_append(pb, "function");
2064 
2065                         if (function->name[0]) {
2066                                 ucv_stringbuf_append(pb, " ");
2067                                 ucv_stringbuf_addstr(pb, function->name, strlen(function->name));
2068                         }
2069                 }
2070 
2071                 ucv_stringbuf_append(pb, "(");
2072 
2073                 for (i = 1; i <= function->nargs; i++) {
2074                         argname = uc_chunk_debug_get_variable(&function->chunk, i - 1, i, false);
2075 
2076                         if (i > 1)
2077                                 ucv_stringbuf_append(pb, ", ");
2078 
2079                         if (i == function->nargs && function->vararg)
2080                                 ucv_stringbuf_append(pb, "...");
2081 
2082                         if (argname) {
2083                                 s = ucv_string_get(argname);
2084                                 l = ucv_string_length(argname);
2085 
2086                                 if (s)
2087                                         ucv_stringbuf_addstr(pb, s, l);
2088 
2089                                 ucv_put(argname);
2090 
2091                                 continue;
2092                         }
2093 
2094                         ucv_stringbuf_printf(pb, "[arg%zu]", i);
2095                 }
2096 
2097                 ucv_stringbuf_printf(pb, ")%s { ... }%s",
2098                         closure->is_arrow ? " =>" : "",
2099                         json ? "\"" : "");
2100 
2101                 break;
2102 
2103         case UC_CFUNCTION:
2104                 cfunction = ucv_as_cfunction(uv);
2105 
2106                 ucv_stringbuf_printf(pb, "%sfunction%s%s(...) { [native code] }%s",
2107                         json ? "\"" : "",
2108                         cfunction->name[0] ? " " : "",
2109                         cfunction->name[0] ? cfunction->name : "",
2110                         json ? "\"" : "");
2111 
2112                 break;
2113 
2114         case UC_RESOURCE:
2115                 restype = ucv_resource_type(uv);
2116 
2117                 ucv_stringbuf_printf(pb, "%s<%s %p>%s",
2118                         json ? "\"" : "",
2119                         restype ? restype->name : "resource",
2120                         ucv_resource_data(uv, NULL),
2121                         json ? "\"" : "");
2122 
2123                 break;
2124 
2125         case UC_UPVALUE:
2126                 ref = ucv_as_upvalue(uv);
2127 
2128                 if (ref->closed)
2129                         ucv_to_stringbuf_formatted(vm, pb, ref->value, depth, pad_char, pad_size);
2130                 else if (vm != NULL && ref->slot < vm->stack.count)
2131                         ucv_to_stringbuf_formatted(vm, pb, vm->stack.entries[ref->slot], depth, pad_char, pad_size);
2132                 else
2133                         ucv_stringbuf_printf(pb, "%s<upvalref %p>%s",
2134                                 json ? "\"" : "",
2135                                 uv,
2136                                 json ? "\"" : "");
2137 
2138                 break;
2139 
2140         case UC_PROGRAM:
2141                 ucv_stringbuf_printf(pb, "%s<program %p>%s",
2142                         json ? "\"" : "",
2143                         uv,
2144                         json ? "\"" : "");
2145 
2146                 break;
2147 
2148         case UC_SOURCE:
2149                 ucv_stringbuf_printf(pb, "%s<source %p>%s",
2150                         json ? "\"" : "",
2151                         uv,
2152                         json ? "\"" : "");
2153         }
2154 
2155         ucv_clear_mark(uv);
2156 }
2157 
2158 static char *
2159 ucv_to_string_any(uc_vm_t *vm, uc_value_t *uv, char pad_char, size_t pad_size)
2160 {
2161         uc_stringbuf_t *pb = xprintbuf_new();
2162         char *rv;
2163 
2164         ucv_to_stringbuf_formatted(vm, pb, uv, 0, pad_char, pad_size);
2165 
2166         rv = pb->buf;
2167 
2168         free(pb);
2169 
2170         return rv;
2171 }
2172 
2173 char *
2174 ucv_to_string(uc_vm_t *vm, uc_value_t *uv)
2175 {
2176         return ucv_to_string_any(vm, uv, '\0', 0);
2177 }
2178 
2179 char *
2180 ucv_to_jsonstring_formatted(uc_vm_t *vm, uc_value_t *uv, char pad_char, size_t pad_size)
2181 {
2182         return ucv_to_string_any(vm, uv, pad_char ? pad_char : '\1', pad_size);
2183 }
2184 
2185 
2186 bool
2187 ucv_is_equal(uc_value_t *uv1, uc_value_t *uv2)
2188 {
2189         uc_type_t t1 = ucv_type(uv1);
2190         uc_type_t t2 = ucv_type(uv2);
2191         const char *s1, *s2;
2192         uint64_t u1, u2;
2193         int64_t n1, n2;
2194         bool b1, b2;
2195 
2196         if (t1 != t2)
2197                 return false;
2198 
2199         if (t1 != UC_DOUBLE && uv1 == uv2)
2200                 return true;
2201 
2202         switch (t1) {
2203         case UC_NULL:
2204                 return true;
2205 
2206         case UC_BOOLEAN:
2207                 return ucv_boolean_get(uv1) == ucv_boolean_get(uv2);
2208 
2209         case UC_DOUBLE:
2210                 return ucv_double_get(uv1) == ucv_double_get(uv2);
2211 
2212         case UC_INTEGER:
2213                 n1 = ucv_int64_get(uv1);
2214                 b1 = (errno == 0);
2215 
2216                 n2 = ucv_int64_get(uv2);
2217                 b2 = (errno == 0);
2218 
2219                 if (b1 && b2)
2220                         return (n1 == n2);
2221 
2222                 u1 = ucv_uint64_get(uv1);
2223                 b1 = (errno == 0);
2224 
2225                 u2 = ucv_uint64_get(uv2);
2226                 b2 = (errno == 0);
2227 
2228                 if (b1 && b2)
2229                         return (u1 == u2);
2230 
2231                 return false;
2232 
2233         case UC_STRING:
2234                 s1 = ucv_string_get(uv1);
2235                 s2 = ucv_string_get(uv2);
2236                 u1 = ucv_string_length(uv1);
2237                 u2 = ucv_string_length(uv2);
2238 
2239                 if (s1 == NULL || s2 == NULL || u1 != u2)
2240                         return false;
2241 
2242                 return (memcmp(s1, s2, u1) == 0);
2243 
2244         case UC_ARRAY:
2245                 u1 = ucv_array_length(uv1);
2246                 u2 = ucv_array_length(uv2);
2247 
2248                 if (u1 != u2)
2249                         return false;
2250 
2251                 for (u1 = 0; u1 < u2; u1++)
2252                         if (!ucv_is_equal(ucv_array_get(uv1, u1), ucv_array_get(uv2, u1)))
2253                                 return false;
2254 
2255                 return true;
2256 
2257         case UC_OBJECT:
2258                 u1 = ucv_object_length(uv1);
2259                 u2 = ucv_object_length(uv2);
2260 
2261                 if (u1 != u2)
2262                         return false;
2263 
2264                 ucv_object_foreach(uv1, key, val) {
2265                         if (!ucv_is_equal(val, ucv_object_get(uv2, key, NULL)))
2266                                 return false;
2267                 }
2268 
2269                 ucv_object_foreach(uv2, key2, val2) {
2270                         (void)val2;
2271                         ucv_object_get(uv1, key2, &b1);
2272 
2273                         if (!b1)
2274                                 return false;
2275                 }
2276 
2277                 return true;
2278 
2279         default:
2280                 return false;
2281         }
2282 }
2283 
2284 bool
2285 ucv_is_truish(uc_value_t *val)
2286 {
2287         double d;
2288 
2289         switch (ucv_type(val)) {
2290         case UC_INTEGER:
2291                 if (ucv_is_u64(val))
2292                         return (ucv_uint64_get(val) != 0);
2293 
2294                 return (ucv_int64_get(val) != 0);
2295 
2296         case UC_DOUBLE:
2297                 d = ucv_double_get(val);
2298 
2299                 return (d != 0 && !isnan(d));
2300 
2301         case UC_BOOLEAN:
2302                 return ucv_boolean_get(val);
2303 
2304         case UC_STRING:
2305                 return (ucv_string_length(val) > 0);
2306 
2307         case UC_NULL:
2308                 return false;
2309 
2310         default:
2311                 return true;
2312         }
2313 }
2314 
2315 uc_value_t *
2316 ucv_to_number(uc_value_t *v)
2317 {
2318         switch (ucv_type(v)) {
2319         case UC_NULL:
2320                 return ucv_uint64_new(0);
2321 
2322         case UC_BOOLEAN:
2323                 return ucv_uint64_new(ucv_boolean_get(v));
2324 
2325         case UC_INTEGER:
2326                 return ucv_get(v);
2327 
2328         case UC_DOUBLE:
2329                 if (isnan(ucv_double_get(v)))
2330                         return NULL;
2331 
2332                 return ucv_get(v);
2333 
2334         case UC_STRING:
2335                 return uc_number_parse(ucv_string_get(v), NULL);
2336 
2337         default:
2338                 return NULL;
2339         }
2340 }
2341 
2342 bool
2343 ucv_compare(int how, uc_value_t *v1, uc_value_t *v2, int *deltap)
2344 {
2345         uc_type_t t1 = ucv_type(v1);
2346         uc_type_t t2 = ucv_type(v2);
2347         uc_value_t *nv1, *nv2;
2348         uint64_t u1, u2;
2349         int64_t n1, n2;
2350         double d1, d2;
2351         int delta;
2352 
2353         /* at least one operand is null and we compare for equality or inequality ... */
2354         if ((!v1 || !v2) && (how == I_EQ || how == I_NE)) {
2355                 delta = (v1 != v2);
2356         }
2357 
2358         /* ... otherwise if both operands are strings, compare bytewise ... */
2359         else if (t1 == UC_STRING && t2 == UC_STRING) {
2360                 u1 = ucv_string_length(v1);
2361                 u2 = ucv_string_length(v2);
2362 
2363                 delta = memcmp(ucv_string_get(v1), ucv_string_get(v2),
2364                         (u1 < u2) ? u1 : u2);
2365 
2366                 if (delta == 0 && u1 < u2)
2367                         delta = -1;
2368                 else if (delta == 0 && u1 > u2)
2369                         delta = 1;
2370         }
2371 
2372         /* handle non-string cases... */
2373         else {
2374                 /* ... both operands are of the same, non-scalar type... */
2375                 if (t1 == t2 && !ucv_is_scalar(v1)) {
2376                         /* ... compare memory addrs */
2377                         if ((uintptr_t)v1 == (uintptr_t)v2)
2378                                 delta = 0;
2379                         else if ((uintptr_t)v1 < (uintptr_t)v2)
2380                                 delta = -1;
2381                         else
2382                                 delta = 1;
2383                 }
2384 
2385                 /* ... operands are of different type or at least one is scalar... */
2386                 else {
2387                         nv1 = ucv_to_number(v1);
2388                         nv2 = ucv_to_number(v2);
2389 
2390                         /* ... at least one of them is NaN (not convertible)... */
2391                         if (!nv1 || !nv2) {
2392                                 ucv_put(nv1);
2393                                 ucv_put(nv2);
2394 
2395                                 if (deltap)
2396                                         *deltap = 2;
2397 
2398                                 /* ... all comparison results except `!=` involving NaN are false */
2399                                 return (how == I_NE);
2400                         }
2401 
2402                         /* ... either of them is a double, compare both as double */
2403                         if (ucv_type(nv1) == UC_DOUBLE || ucv_type(nv2) == UC_DOUBLE) {
2404                                 d1 = ucv_double_get(nv1);
2405                                 d2 = ucv_double_get(nv2);
2406 
2407                                 if (d1 == d2)
2408                                         delta = 0;
2409                                 else if (d1 < d2)
2410                                         delta = -1;
2411                                 else
2412                                         delta = 1;
2413                         }
2414 
2415                         /* ... both are integers... */
2416                         else {
2417                                 n1 = ucv_int64_get(nv1);
2418 
2419                                 /* ... left operand is large positive... */
2420                                 if (errno == ERANGE) {
2421                                         ucv_int64_get(nv2);
2422 
2423                                         /* ... right operand is large positive too... */
2424                                         if (errno == ERANGE) {
2425                                                 /* ... compare both as unsigned */
2426                                                 u1 = ucv_uint64_get(nv1);
2427                                                 u2 = ucv_uint64_get(nv2);
2428 
2429                                                 if (u1 == u2)
2430                                                         delta = 0;
2431                                                 else if (u1 < u2)
2432                                                         delta = -1;
2433                                                 else
2434                                                         delta = 1;
2435                                         }
2436 
2437                                         /* ... right operand is within int64_t range... */
2438                                         else {
2439                                                 /* ... left > right by definition */
2440                                                 delta = 1;
2441                                         }
2442                                 }
2443 
2444                                 /* ... left operand is within int64_t range... */
2445                                 else {
2446                                         n2 = ucv_int64_get(nv2);
2447 
2448                                         /* ... right operand is large positive... */
2449                                         if (errno == ERANGE) {
2450                                                 /* ... left < right by definition */
2451                                                 delta = -1;
2452                                         }
2453 
2454                                         /* ... right operand is within int64_t range... */
2455                                         else {
2456                                                 /* ... compare both as signed */
2457                                                 if (n1 == n2)
2458                                                         delta = 0;
2459                                                 else if (n1 < n2)
2460                                                         delta = -1;
2461                                                 else
2462                                                         delta = 1;
2463                                         }
2464                                 }
2465                         }
2466 
2467                         ucv_put(nv1);
2468                         ucv_put(nv2);
2469                 }
2470         }
2471 
2472         if (deltap)
2473                 *deltap = delta;
2474 
2475         switch (how) {
2476         case I_LT:
2477                 return (delta < 0);
2478 
2479         case I_LE:
2480                 return (delta <= 0);
2481 
2482         case I_GT:
2483                 return (delta > 0);
2484 
2485         case I_GE:
2486                 return (delta >= 0);
2487 
2488         case I_EQ:
2489                 return (delta == 0);
2490 
2491         case I_NE:
2492                 return (delta != 0);
2493 
2494         default:
2495                 return false;
2496         }
2497 }
2498 
2499 
2500 static char *
2501 ucv_key_to_string(uc_vm_t *vm, uc_value_t *val)
2502 {
2503         if (ucv_type(val) != UC_STRING)
2504                 return ucv_to_string(vm, val);
2505 
2506         return NULL;
2507 }
2508 
2509 static int64_t
2510 ucv_key_to_index(uc_value_t *val)
2511 {
2512         const char *k;
2513         int64_t idx;
2514         double d;
2515         char *e;
2516 
2517         /* only consider doubles with integer values as array keys */
2518         if (ucv_type(val) == UC_DOUBLE) {
2519                 d = ucv_double_get(val);
2520 
2521                 if (trunc(d) != d)
2522                         return INT64_MIN;
2523 
2524                 return (int64_t)d;
2525         }
2526         else if (ucv_type(val) == UC_INTEGER) {
2527                 return ucv_int64_get(val);
2528         }
2529         else if (ucv_type(val) == UC_STRING) {
2530                 errno = 0;
2531                 k = ucv_string_get(val);
2532                 idx = strtoll(k, &e, 0);
2533 
2534                 if (errno != 0 || e == k || *e != 0)
2535                         return INT64_MIN;
2536 
2537                 return idx;
2538         }
2539 
2540         return INT64_MIN;
2541 }
2542 
2543 /*
2544  * Raw key lookup: array index, resource type-proto, object prototype chain.
2545  * Sets *found and returns the located value (or NULL). Does NOT resolve
2546  * upvalue references and does NOT dispatch __get__ — those are layered on by
2547  * the aware ucv_key_get().
2548  */
2549 static uc_value_t *
2550 ucv_key_get_raw(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key, bool *found)
2551 {
2552         uc_value_t *o, *v = NULL, *inst_proto;
2553         int64_t idx;
2554         char *k;
2555 
2556         *found = false;
2557 
2558         if (ucv_type(scope) == UC_ARRAY) {
2559                 idx = ucv_key_to_index(key);
2560 
2561                 if (idx < 0 && idx > INT64_MIN && (uint64_t)llabs(idx) <= ucv_array_length(scope))
2562                         idx += ucv_array_length(scope);
2563 
2564                 if (idx >= 0 && (uint64_t)idx < ucv_array_length(scope)) {
2565                         v = ucv_array_get(scope, idx);
2566                         *found = true;
2567                 }
2568         }
2569 
2570         if (!*found) {
2571                 k = ucv_key_to_string(vm, key);
2572 
2573                 /* Check resource type prototype first */
2574                 if (ucv_type(scope) == UC_RESOURCE) {
2575                         uc_value_t *uv = scope;
2576 
2577                         if (uv->ext_flag) {
2578                                 uc_resource_ext_t *ext = (uc_resource_ext_t *)scope;
2579 
2580                                 /* Check instance-specific proto first (set via
2581                                  * ucv_resource_new_prototyped); returns NULL for resources
2582                                  * without an instance proto. */
2583                                 if ((inst_proto = ucv_resource_proto_get(scope)) != NULL) {
2584                                         v = ucv_object_get(inst_proto, k ? k : ucv_string_get(key), found);
2585                                 }
2586 
2587                                 /* Then fall back to type prototype */
2588                                 if (!*found && ext->type && ext->type->proto) {
2589                                         v = ucv_object_get(ext->type->proto, k ? k : ucv_string_get(key), found);
2590                                 }
2591                         } else {
2592                                 uc_resource_t *res = ucv_as_resource(scope);
2593                                 if (res->type && res->type->proto) {
2594                                         v = ucv_object_get(res->type->proto, k ? k : ucv_string_get(key), found);
2595                                 }
2596                         }
2597                 }
2598 
2599                 /* Then check object prototype chain */
2600                 if (!*found) {
2601                         for (o = scope; o; o = ucv_prototype_get(o)) {
2602                                 if (ucv_type(o) != UC_OBJECT)
2603                                         continue;
2604 
2605                                 v = ucv_object_get(o, k ? k : ucv_string_get(key), found);
2606 
2607                                 if (*found)
2608                                         break;
2609                         }
2610                 }
2611 
2612                 free(k);
2613         }
2614 
2615         return v;
2616 }
2617 
2618 /* Resolve an upvalue reference (if any) to its current value. */
2619 static uc_value_t *
2620 ucv_key_resolve_upval(uc_vm_t *vm, uc_value_t *v)
2621 {
2622         uc_upvalref_t *ref;
2623 
2624 #ifdef __clang_analyzer__
2625         /* Clang static analyzer does not understand that ucv_type(NULL) can't
2626          * possibly yield UC_UPVALUE. Nudge it. */
2627         if (v != NULL && ucv_type(v) == UC_UPVALUE)
2628 #else
2629         if (ucv_type(v) == UC_UPVALUE)
2630 #endif
2631         {
2632                 ref = ucv_as_upvalue(v);
2633 
2634                 if (ref->closed)
2635                         return ucv_get(ref->value);
2636                 else if (vm)
2637                         return ucv_get(vm->stack.entries[ref->slot]);
2638                 else
2639                         return NULL;
2640         }
2641 
2642         return ucv_get(v);
2643 }
2644 
2645 /*
2646  * Property accessors implementing `foo.bar`, `foo.bar = x` and
2647  * `delete foo.bar` for any kind of value. Own keys respectively numeric array
2648  * indices take precedence, metamethods - looked up in the prototype chain of
2649  * the accessed value, see ucv_metamethod_lookup() - serve as fallback:
2650  *
2651  *   __get__(key)      invoked when a raw lookup yields nothing; the result
2652  *                     of the read is its return value
2653  *   __set__(key, val) invoked when writing a key an object does not have yet,
2654  *                     like Lua's __newindex; its return value is discarded
2655  *   __delete__(key)   invoked when deleting a key an object does not have
2656  *                     yet; a truthy result means "deleted"
2657  *
2658  * A metamethod which is an object (or array) rather than a function is not
2659  * invoked: the operation is re-dispatched on that value with the same key,
2660  * which gives the delegated value a chance to store or delete the key itself
2661  * or provide its own metamethod. Cycles are not a problem, each hop is a
2662  * fresh property access bound by the VM's recursion limit.
2663  *
2664  * Arrays keep their dense storage: keys which are valid indices never dispatch
2665  * a metamethod at all. Passing vm == NULL performs the raw operation only.
2666  */
2667 
2668 /* Invoke `meta` as method on `scope`, passing `key` and optionally `val` and
2669  * storing the result in `rvp`. Returns false if the metamethod raised, in
2670  * which case the exception is left pending for the interpreter loop to unwind.
2671  *
2672  * The operands are pushed on the VM stack to form the method call argument
2673  * list, but also to keep them reachable: a garbage collection may run at any
2674  * instruction of the invoked metamethod and its mark pass walks the VM stack,
2675  * not C locals. */
2676 static bool
2677 ucv_meta_call(uc_vm_t *vm, uc_value_t *scope, uc_value_t *meta,
2678               uc_value_t *key, uc_value_t *val, uc_value_t **rvp)
2679 {
2680         size_t base = vm->stack.count, nargs = 1;
2681 
2682         if (rvp)
2683                 *rvp = NULL;
2684 
2685         uc_vm_stack_push(vm, ucv_get(scope));
2686         uc_vm_stack_push(vm, ucv_get(meta));
2687         uc_vm_stack_push(vm, ucv_get(key));
2688 
2689         if (val != NULL) {
2690                 uc_vm_stack_push(vm, ucv_get(val));
2691                 nargs = 2;
2692         }
2693 
2694         if (uc_vm_call(vm, true, nargs) != EXCEPTION_NONE) {
2695                 /* An exception raised within the metamethod unwinds its frame - and
2696                  * with it the operands pushed above - while one raised before that
2697                  * frame got established, out of recursion for instance, leaves them
2698                  * behind; either way, drop back to the slots pushed here. */
2699                 while (vm->stack.count > base)
2700                         ucv_put(uc_vm_stack_pop(vm));
2701 
2702                 return false;
2703         }
2704 
2705         *rvp = uc_vm_stack_pop(vm);
2706 
2707         return true;
2708 }
2709 
2710 /* Invoke the metamethod held in `meta` on `scope`, passing `key` and
2711  * optionally `val`, without storing its return value anywhere. Returns
2712  * false if the call raised, in which case the exception is left pending for
2713  * the interpreter loop to unwind. */
2714 static bool
2715 ucv_meta_invoke(uc_vm_t *vm, uc_value_t *scope, uc_value_t *meta,
2716                 uc_value_t *key, uc_value_t *val)
2717 {
2718         uc_value_t *rv;
2719 
2720         if (!ucv_meta_call(vm, scope, meta, key, val, &rv))
2721                 return false;
2722 
2723         ucv_put(rv);
2724 
2725         return true;
2726 }
2727 
2728 /* ucv_object_get(), ucv_object_add() and ucv_object_delete() taking a script
2729  * level key value instead of a C string. Like ucv_object_add(), the value to
2730  * store is consumed, also where the store fails - which it does for a frozen
2731  * object or a value which is not an object at all. */
2732 static bool
2733 ucv_object_has_key(uc_vm_t *vm, uc_value_t *obj, uc_value_t *key)
2734 {
2735         char *s = ucv_key_to_string(vm, key);
2736         bool found = false;
2737 
2738         ucv_object_get(obj, s ? s : ucv_string_get(key), &found);
2739         free(s);
2740 
2741         return found;
2742 }
2743 
2744 static bool
2745 ucv_object_put_key(uc_vm_t *vm, uc_value_t *obj, uc_value_t *key, uc_value_t *val)
2746 {
2747         char *s = ucv_key_to_string(vm, key);
2748         bool rv;
2749 
2750         rv = ucv_object_add(obj, s ? s : ucv_string_get(key), val);
2751         free(s);
2752 
2753         if (!rv)
2754                 ucv_put(val);
2755 
2756         return rv;
2757 }
2758 
2759 static bool
2760 ucv_object_del_key(uc_vm_t *vm, uc_value_t *obj, uc_value_t *key)
2761 {
2762         char *s = ucv_key_to_string(vm, key);
2763         bool rv;
2764 
2765         rv = ucv_object_delete(obj, s ? s : ucv_string_get(key));
2766         free(s);
2767 
2768         return rv;
2769 }
2770 
2771 /* Store `val` at the array index denoted by `key`, extending the array as
2772  * needed. Returns false for keys which are no indices and for negative ones
2773  * which denote no element; like ucv_array_set(), the value to store is
2774  * consumed either way. */
2775 static bool
2776 ucv_array_set_key(uc_value_t *arr, uc_value_t *key, uc_value_t *val)
2777 {
2778         int64_t idx = ucv_key_to_index(key);
2779 
2780         if (idx != INT64_MIN) {
2781                 if (idx < 0 && (uint64_t)llabs(idx) <= ucv_array_length(arr))
2782                         idx += ucv_array_length(arr);
2783 
2784                 if (idx >= 0 && ucv_array_set(arr, idx, val))
2785                         return true;
2786         }
2787 
2788         ucv_put(val);
2789 
2790         return false;
2791 }
2792 
2793 uc_value_t *
2794 ucv_key_get(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key)
2795 {
2796         uc_value_t *meta, *val;
2797         bool found;
2798 
2799         if (!scope || !key)
2800                 return NULL;
2801 
2802         val = ucv_key_get_raw(vm, scope, key, &found);
2803 
2804         if (found)
2805                 return ucv_key_resolve_upval(vm, val);
2806 
2807         /* a valid array index which is out of bounds reads as null, it does not
2808          * dispatch __get__ */
2809         if (vm == NULL || (ucv_type(scope) == UC_ARRAY && ucv_key_to_index(key) != INT64_MIN))
2810                 return NULL;
2811 
2812         meta = ucv_metamethod_lookup(scope, "__get__");
2813 
2814         /* a __get__ which is itself an object is a delegation target: look the key
2815          * up inside it instead, where it may exist, be virtual, or miss just as
2816          * on the original scope */
2817         if (ucv_type(meta) == UC_OBJECT || ucv_type(meta) == UC_ARRAY)
2818                 return ucv_key_get(vm, meta, key);
2819 
2820         if (!ucv_is_callable(meta))
2821                 return NULL;
2822 
2823         if (!ucv_meta_call(vm, scope, meta, key, NULL, &val))
2824                 return NULL;
2825 
2826         return val;
2827 }
2828 
2829 uc_value_t *
2830 ucv_key_set(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key, uc_value_t *val)
2831 {
2832         uc_value_t *meta = NULL;
2833         int64_t idx;
2834 
2835         /* only a null key is invalid, a null value stores null */
2836         if (!scope || !key)
2837                 return NULL;
2838 
2839         switch (ucv_type(scope)) {
2840         case UC_ARRAY:
2841                 idx = ucv_key_to_index(key);
2842 
2843                 if (idx != INT64_MIN)
2844                         return ucv_array_set_key(scope, key, ucv_get(val)) ? ucv_get(val) : NULL;
2845 
2846                 /* a non-index key on an array has no own-key storage to fall back to:
2847                  * without a __set__ metamethod the store is guaranteed to be dropped.
2848                  * In strict mode that is a type error, consistent with setting a
2849                  * property on any other non-object type; in non-strict mode it
2850                  * remains a silent no-op. */
2851                 if (vm != NULL && uc_vm_is_strict(vm)) {
2852                         meta = ucv_metamethod_lookup(scope, "__set__");
2853 
2854                         if (!meta) {
2855                                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
2856                                                       "attempt to set property on %s value",
2857                                                       ucv_typename(scope));
2858 
2859                                 return NULL;
2860                         }
2861                 }
2862 
2863                 break;
2864 
2865         case UC_OBJECT:
2866                 if (ucv_object_has_key(vm, scope, key))
2867                         return ucv_object_put_key(vm, scope, key, ucv_get(val)) ? ucv_get(val) : NULL;
2868 
2869                 break;
2870 
2871         case UC_RESOURCE:
2872                 break;
2873 
2874         default:
2875                 return NULL;
2876         }
2877 
2878         /* meta may already be set from the strict array check above */
2879         if (vm != NULL) {
2880                 if (meta == NULL)
2881                         meta = ucv_metamethod_lookup(scope, "__set__");
2882 
2883                 /* a __set__ which is itself an object is a delegation target: store the
2884                  * key inside it instead, mirroring an object __get__ */
2885                 if (ucv_type(meta) == UC_OBJECT || ucv_type(meta) == UC_ARRAY)
2886                         return ucv_key_set(vm, meta, key, val);
2887 
2888                 if (ucv_is_callable(meta)) {
2889                         if (!ucv_meta_invoke(vm, scope, meta, key, val))
2890                                 return NULL;
2891 
2892                         /* an assignment evaluates to the assigned value, not to whatever
2893                          * the metamethod returned */
2894                         return ucv_get(val);
2895                 }
2896         }
2897 
2898         /* without a __set__ in effect, the value ends up as own key */
2899         return ucv_object_put_key(vm, scope, key, ucv_get(val)) ? ucv_get(val) : NULL;
2900 }
2901 
2902 bool
2903 ucv_key_delete(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key)
2904 {
2905         uc_value_t *meta, *rv;
2906         bool deleted;
2907 
2908         if (!scope || !key)
2909                 return false;
2910 
2911         switch (ucv_type(scope)) {
2912         case UC_OBJECT:
2913                 if (ucv_object_has_key(vm, scope, key))
2914                         return ucv_object_del_key(vm, scope, key);
2915 
2916                 break;
2917 
2918         case UC_RESOURCE:
2919                 break;
2920 
2921         /* arrays consist of positional elements, they are not deletable */
2922         default:
2923                 return false;
2924         }
2925 
2926         if (vm != NULL) {
2927                 meta = ucv_metamethod_lookup(scope, "__delete__");
2928 
2929                 /* a __delete__ which is itself an object is a delegation target:
2930                  * delete the key inside it, mirroring an object __get__ */
2931                 if (ucv_type(meta) == UC_OBJECT || ucv_type(meta) == UC_ARRAY)
2932                         return ucv_key_delete(vm, meta, key);
2933 
2934                 if (ucv_is_callable(meta)) {
2935                         /* by convention, a truthy return value means "deleted" */
2936                         deleted = ucv_meta_call(vm, scope, meta, key, NULL, &rv) && ucv_is_truish(rv);
2937                         ucv_put(rv);
2938 
2939                         return deleted;
2940                 }
2941         }
2942 
2943         return ucv_object_del_key(vm, scope, key);
2944 }
2945 
2946 /*
2947  * Raw counterparts of the accessors above: what `foo.bar`, `foo.bar = x` and
2948  * `delete foo.bar` do in the absence of metamethods. These are the escape
2949  * hatch for a metamethod which needs to reach the storage below itself without
2950  * dispatching again, and are exposed to scripts as rawget(), rawset() and
2951  * rawdelete().
2952  */
2953 
2954 uc_value_t *
2955 ucv_key_rawget(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key)
2956 {
2957         uc_value_t *val;
2958         bool found;
2959 
2960         if (!scope || !key)
2961                 return NULL;
2962 
2963         val = ucv_key_get_raw(vm, scope, key, &found);
2964 
2965         return (found ? ucv_key_resolve_upval(vm, val) : NULL);
2966 }
2967 
2968 uc_value_t *
2969 ucv_key_rawset(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key, uc_value_t *val)
2970 {
2971         if (!scope || !key)
2972                 return NULL;
2973 
2974         switch (ucv_type(scope)) {
2975         case UC_ARRAY:
2976                 return ucv_array_set_key(scope, key, ucv_get(val)) ? ucv_get(val) : NULL;
2977 
2978         case UC_OBJECT:
2979                 return ucv_object_put_key(vm, scope, key, ucv_get(val)) ? ucv_get(val) : NULL;
2980 
2981         default:
2982                 return NULL;
2983         }
2984 }
2985 
2986 bool
2987 ucv_key_rawdelete(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key)
2988 {
2989         if (!scope || !key)
2990                 return false;
2991 
2992         return ucv_object_del_key(vm, scope, key);
2993 }
2994 
2995 static void
2996 ucv_gc_common(uc_vm_t *vm, bool final)
2997 {
2998         uc_weakref_t *ref, *tmp;
2999         uc_value_t *val;
3000         size_t i;
3001 
3002         vm->alloc_refs = 0;
3003 
3004         /* back out early if value list is uninitialized */
3005         if (!vm->values.prev || !vm->values.next)
3006                 return;
3007 
3008         if (!final) {
3009                 /* mark reachable objects */
3010                 ucv_gc_mark(vm->globals);
3011                 ucv_gc_mark(vm->registry);
3012                 ucv_gc_mark(vm->signal.handler);
3013                 ucv_gc_mark(vm->exception.stacktrace);
3014 
3015                 for (i = 0; i < vm->callframes.count; i++) {
3016                         ucv_gc_mark(vm->callframes.entries[i].ctx);
3017 
3018                         if (vm->callframes.entries[i].closure)
3019                                 ucv_gc_mark(&vm->callframes.entries[i].closure->header);
3020 
3021                         if (vm->callframes.entries[i].cfunction)
3022                                 ucv_gc_mark(&vm->callframes.entries[i].cfunction->header);
3023                 }
3024 
3025                 for (i = 0; i < vm->stack.count; i++)
3026                         ucv_gc_mark(vm->stack.entries[i]);
3027 
3028                 for (i = 0; i < vm->restypes.count; i++)
3029                         ucv_gc_mark(vm->restypes.entries[i]->proto);
3030 
3031                 /* mark no-gc resources with embedded values */
3032                 for (ref = vm->values.prev; ref != &vm->values; ref = ref->prev) {
3033                         val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref));
3034                         if (ucv_type(val) != UC_RESOURCE)
3035                                 break;
3036 
3037                         if (ucv_resource_is_persistent(val))
3038                                 ucv_gc_mark(val);
3039                 }
3040         }
3041 
3042         /* unref unreachable objects */
3043         for (ref = vm->values.next; ref != &vm->values; ref = ref->next) {
3044                 val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref));
3045 
3046                 if (ucv_is_marked(val))
3047                         ucv_clear_mark(val);
3048                 else
3049                         ucv_free(val, true);
3050         }
3051 
3052         /* free destroyed objects */
3053         for (ref = vm->values.next, tmp = ref->next; ref != &vm->values; ref = tmp, tmp = tmp->next) {
3054                 val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref));
3055 
3056                 if (val->type == UC_NULL) {
3057                         ucv_unref(ref);
3058                         free(val);
3059                 }
3060         }
3061 }
3062 
3063 void
3064 ucv_gc(uc_vm_t *vm)
3065 {
3066         ucv_gc_common(vm, false);
3067 }
3068 
3069 void
3070 ucv_freeall(uc_vm_t *vm)
3071 {
3072         ucv_gc_common(vm, true);
3073 }
3074 
3075 void
3076 uc_search_path_init(uc_search_path_t *search_path)
3077 {
3078         size_t i;
3079 
3080         for (i = 0; i < ARRAY_SIZE(uc_default_search_path); i++)
3081                 uc_vector_push(search_path, xstrdup(uc_default_search_path[i]));
3082 }
3083 
3084 
3085 static __thread uc_thread_context_t *tls_ctx;
3086 
3087 uc_thread_context_t *
3088 uc_thread_context_get(void)
3089 {
3090         if (tls_ctx == NULL) {
3091                 tls_ctx = xalloc(sizeof(*tls_ctx));
3092                 tls_ctx->object_iterators.prev = &tls_ctx->object_iterators;
3093                 tls_ctx->object_iterators.next = &tls_ctx->object_iterators;
3094         }
3095 
3096         return tls_ctx;
3097 }
3098 
3099 void
3100 uc_thread_context_free(void)
3101 {
3102         free(tls_ctx);
3103         tls_ctx = NULL;
3104 }
3105 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt