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

Sources/ucode/lib.c

  1 /*
  2  * Copyright (C) 2020-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 /**
 18  * # Builtin functions
 19  *
 20  * The core namespace is not an actual module but refers to the set of
 21  * builtin functions and properties available to `ucode` scripts.
 22  *
 23  * @module core
 24  */
 25 
 26 #include <stdio.h>
 27 #include <stdlib.h>
 28 #include <stdarg.h>
 29 #include <string.h>
 30 #include <signal.h>
 31 #include <ctype.h>
 32 #include <errno.h>
 33 #include <math.h>
 34 #include <time.h>
 35 #include <dlfcn.h>
 36 #include <libgen.h>
 37 #include <unistd.h>
 38 #include <arpa/inet.h>
 39 #include <sys/stat.h>
 40 #include <sys/types.h>
 41 #include <sys/wait.h>
 42 #include <fnmatch.h>
 43 #include <assert.h>
 44 
 45 
 46 #include "ucode/internal/vm.h" /* ISA: I_* opcodes */
 47 #include "ucode/internal/types.h"
 48 #include "ucode/internal/lexer.h" /* utf8enc() */
 49 #include "ucode/internal/lib.h"
 50 #include "ucode/internal/source.h"
 51 #include "ucode/internal/program.h"
 52 #include "ucode/internal/platform.h"
 53 #include "ucode/internal/json-c-compat.h"
 54 
 55 static void
 56 format_context_line(uc_stringbuf_t *buf, const char *line, size_t off, bool compact)
 57 {
 58         unsigned padlen, i;
 59         const char *p;
 60 
 61         for (p = line, padlen = 0; *p != '\n' && *p != '\0'; p++) {
 62                 if (compact && (p - line) == (ptrdiff_t)off)
 63                         ucv_stringbuf_append(buf, "\033[22m");
 64 
 65                 switch (*p) {
 66                 case '\t':
 67                         ucv_stringbuf_append(buf, "    ");
 68                         if (p < line + off)
 69                                 padlen += 4;
 70                         break;
 71 
 72                 case '\r':
 73                 case '\v':
 74                         ucv_stringbuf_append(buf, " ");
 75                         if (p < line + off)
 76                                 padlen++;
 77                         break;
 78 
 79                 default:
 80                         ucv_stringbuf_addstr(buf, p, 1);
 81                         if (p < line + off)
 82                                 padlen++;
 83                 }
 84         }
 85 
 86         if (compact) {
 87                 ucv_stringbuf_append(buf, "\033[m\n");
 88 
 89                 return;
 90         }
 91 
 92         ucv_stringbuf_append(buf, "`\n  ");
 93 
 94         if (padlen < strlen("Near here ^")) {
 95                 for (i = 0; i < padlen; i++)
 96                         ucv_stringbuf_append(buf, " ");
 97 
 98                 ucv_stringbuf_append(buf, "^-- Near here\n");
 99         }
100         else {
101                 ucv_stringbuf_append(buf, "Near here ");
102 
103                 for (i = strlen("Near here "); i < padlen; i++)
104                         ucv_stringbuf_append(buf, "-");
105 
106                 ucv_stringbuf_append(buf, "^\n");
107         }
108 }
109 
110 static char *
111 source_filename(uc_source_t *src, uint32_t line)
112 {
113         const char *name = src->filename ? basename(src->filename) : "[?]";
114         static char buf[sizeof("xxxxxxxxx.uc:0000000000")];
115         size_t len = strlen(name);
116 
117         if (len > 12)
118                 snprintf(buf, sizeof(buf), "...%s:%u", name + (len - 9), line);
119         else
120                 snprintf(buf, sizeof(buf), "%12s:%u", name, line);
121 
122         return buf;
123 }
124 
125 bool
126 uc_source_context_format(uc_stringbuf_t *buf, uc_source_t *src, size_t off, bool compact)
127 {
128         size_t len, rlen;
129         bool truncated;
130         char line[256];
131         long srcpos;
132         int eline;
133 
134         srcpos = ftell(src->fp);
135 
136         if (srcpos == -1)
137                 return false;
138 
139         fseek(src->fp, 0, SEEK_SET);
140 
141         truncated = false;
142         eline = 1;
143         rlen = 0;
144 
145         while (fgets(line, sizeof(line), src->fp)) {
146                 len = strlen(line);
147                 rlen += len;
148 
149                 if (rlen >= off) {
150                         if (compact)
151                                 ucv_stringbuf_printf(buf, "\033[2;40;97m%17s  %s",
152                                         source_filename(src, eline),
153                                         truncated ? "..." : "");
154                         else
155                                 ucv_stringbuf_printf(buf, "\n `%s",
156                                         truncated ? "..." : "");
157 
158                         format_context_line(buf, line, len - (rlen - off) + (truncated ? 3 : 0), compact);
159                         break;
160                 }
161 
162                 truncated = (len > 0 && line[len-1] != '\n');
163                 eline += !truncated;
164         }
165 
166         fseek(src->fp, srcpos, SEEK_SET);
167 
168         return true;
169 }
170 
171 bool
172 uc_error_context_format(uc_stringbuf_t *buf, uc_source_t *src, uc_value_t *stacktrace, size_t off)
173 {
174         uc_value_t *e, *fn, *file, *line, *byte, *tco;
175         const char *path;
176         size_t idx;
177 
178         for (idx = 0; idx < (stacktrace ? ucv_array_length(stacktrace) : 0); idx++) {
179                 e = ucv_array_get(stacktrace, idx);
180                 fn = ucv_object_get(e, "function", NULL);
181                 file = ucv_object_get(e, "filename", NULL);
182                 tco = ucv_object_get(e, "tco", NULL);
183 
184                 if (idx == 0) {
185                         path = (file && strcmp(ucv_string_get(file), "[stdin]"))
186                                 ? ucv_string_get(file) : NULL;
187 
188                         if (path && fn)
189                                 ucv_stringbuf_printf(buf, "In %s(), file %s, ", ucv_string_get(fn), path);
190                         else if (fn)
191                                 ucv_stringbuf_printf(buf, "In %s(), ", ucv_string_get(fn));
192                         else if (path)
193                                 ucv_stringbuf_printf(buf, "In %s, ", path);
194                         else
195                                 ucv_stringbuf_append(buf, "In ");
196 
197                         ucv_stringbuf_printf(buf, "line %" PRId64 ", byte %" PRId64 ":\n",
198                                 ucv_int64_get(ucv_object_get(e, "line", NULL)),
199                                 ucv_int64_get(ucv_object_get(e, "byte", NULL)));
200                 }
201                 else {
202                         line = ucv_object_get(e, "line", NULL);
203                         byte = ucv_object_get(e, "byte", NULL);
204 
205                         ucv_stringbuf_printf(buf, "  called from %s%s (%s",
206                                 fn ? "function " : "anonymous function",
207                                 fn ? ucv_string_get(fn) : "",
208                                 file ? ucv_string_get(file) : "");
209 
210                         if (line && byte)
211                                 ucv_stringbuf_printf(buf, ":%" PRId64 ":%" PRId64 ")\n",
212                                         ucv_int64_get(line),
213                                         ucv_int64_get(byte));
214                         else
215                                 ucv_stringbuf_append(buf, "[C])\n");
216                 }
217 
218                 /* indicate the call frames collapsed into this one by tail call
219                  * optimization, which are absent from the trace */
220                 if (tco)
221                         ucv_stringbuf_printf(buf,
222                                 "  (%" PRId64 " tail call frames omitted)\n",
223                                 ucv_int64_get(tco));
224         }
225 
226         return uc_source_context_format(buf, src, off, false);
227 }
228 
229 void
230 uc_error_message_indent(char **msg) {
231         uc_stringbuf_t *buf;
232         char *s, *p, *nl;
233         size_t len;
234 
235         if (!msg || !*msg)
236                 return;
237 
238         buf = xprintbuf_new();
239         s = *msg;
240         len = strlen(s);
241 
242         while (len > 0 && s[len-1] == '\n')
243                 s[--len] = 0;
244 
245         for (p = s, nl = strchr(p, '\n'); p != NULL;
246              p = nl ? nl + 1 : NULL, nl = p ? strchr(p, '\n') : NULL)
247         {
248                 if (!nl)
249                         ucv_stringbuf_printf(buf, "  | %s", p);
250                 else if (nl != p)
251                         ucv_stringbuf_printf(buf, "  | %.*s\n", (int)(nl - p), p);
252                 else
253                         ucv_stringbuf_append(buf, "  |\n");
254         }
255 
256         ucv_stringbuf_append(buf, "\n");
257 
258         *msg = buf->buf;
259 
260         free(buf);
261         free(s);
262 }
263 
264 static char *uc_cast_string(uc_vm_t *vm, uc_value_t **v, bool *freeable) {
265         if (ucv_type(*v) == UC_STRING) {
266                 *freeable = false;
267 
268                 return _ucv_string_get(v);
269         }
270 
271         *freeable = true;
272 
273         return ucv_to_string(vm, *v);
274 }
275 
276 static void
277 uc_vm_ctx_push(uc_vm_t *vm)
278 {
279         uc_value_t *ctx = NULL;
280 
281         if (vm->callframes.count >= 2)
282                 ctx = vm->callframes.entries[vm->callframes.count - 2].ctx;
283 
284         uc_vm_stack_push(vm, ucv_get(ctx));
285 }
286 
287 static uc_value_t *
288 uc_print_common(uc_vm_t *vm, size_t nargs, FILE *fh)
289 {
290         uc_value_t *item;
291         size_t reslen = 0;
292         size_t len = 0;
293         size_t arridx;
294         char *p;
295 
296         for (arridx = 0; arridx < nargs; arridx++) {
297                 item = uc_fn_arg(arridx);
298 
299                 if (ucv_type(item) == UC_STRING) {
300                         len = ucv_string_length(item);
301                         reslen += fwrite(ucv_string_get(item), 1, len, fh);
302                 }
303                 else if (item != NULL) {
304                         p = ucv_to_string(vm, item);
305                         len = strlen(p);
306                         reslen += fwrite(p, 1, len, fh);
307                         free(p);
308                 }
309         }
310 
311         return ucv_int64_new(reslen);
312 }
313 
314 
315 /**
316  * Print any of the given values to stdout.
317  *
318  * The `print()` function writes a string representation of each given argument
319  * to stdout and returns the amount of bytes written.
320  *
321  * String values are printed as-is, integer and double values are printed in
322  * decimal notation, boolean values are printed as `true` or `false` while
323  * arrays and objects are converted to their JSON representation before being
324  * written to the standard output. The `null` value is represented by an empty
325  * string so `print(null)` would print nothing. Resource values are printed in
326  * the form `<type address>`, e.g. `<fs.file 0x7f60f0981760>`.
327  *
328  * If resource, array or object values contain a `tostring()` function in their
329  * prototypes, then this function is invoked to obtain an alternative string
330  * representation of the value.
331  *
332  * Examples:
333  *
334  * ```javascript
335  * print(1 != 2);                       // Will print 'true'
336  * print(0xff);                         // Will print '255'
337  * print(2e3);                          // Will print '2000'
338  * print(null);                         // Will print nothing
339  * print({ hello: true, world: 123 });  // Will print '{ "hello": true, "world": 123 }'
340  * print([1,2,3]);                      // Will print '[ 1, 2, 3 ]'
341  *
342  * print(proto({ foo: "bar" },          // Will print 'MyObj'
343  *   { tostring: () => "MyObj" }));     // instead of '{ "foo": "bar" }'
344  *
345  * ```
346  *
347  * Returns the amount of bytes printed.
348  *
349  * @function module:core#print
350  *
351  * @param {...*} values
352  * Arbitrary values to print
353  *
354  * @returns {number}
355  */
356 static uc_value_t *
357 uc_print(uc_vm_t *vm, size_t nargs)
358 {
359         return uc_print_common(vm, nargs, vm->output);
360 }
361 
362 /**
363  * Determine the length of the given object, array or string.
364  *
365  * Returns the length of the given value.
366  *
367  *  - For strings, the length is the amount of bytes within the string
368  *  - For arrays, the length is the amount of array elements
369  *  - For objects, the length is defined as the amount of keys
370  *
371  * Returns `null` if the given argument is not an object, array or string.
372  *
373  * @function module:core#length
374  *
375  * @param {Object|Array|string} x - The input object, array, or string.
376  *
377  * @returns {?number} - The length of the input.
378  *
379  * @example
380  * length("test")                             // 4
381  * length([true, false, null, 123, "test"])   // 5
382  * length({foo: true, bar: 123, baz: "test"}) // 3
383  * length({})                                 // 0
384  * length(true)                               // null
385  * length(10.0)                               // null
386  */
387 static uc_value_t *
388 uc_length(uc_vm_t *vm, size_t nargs)
389 {
390         uc_value_t *arg = uc_fn_arg(0);
391 
392         switch (ucv_type(arg)) {
393         case UC_OBJECT:
394                 return ucv_int64_new(ucv_object_length(arg));
395 
396         case UC_ARRAY:
397                 return ucv_int64_new(ucv_array_length(arg));
398 
399         case UC_STRING:
400                 return ucv_int64_new(ucv_string_length(arg));
401 
402         default:
403                 return NULL;
404         }
405 }
406 
407 static int
408 uc_uniq_ucv_equal(const void *k1, const void *k2);
409 
410 static uc_value_t *
411 uc_index(uc_vm_t *vm, size_t nargs, bool right)
412 {
413         uc_value_t *stack = uc_fn_arg(0);
414         uc_value_t *needle = uc_fn_arg(1);
415         uc_value_t *offset = nargs > 2 ? uc_fn_arg(2) : NULL;
416         const char *sstr, *nstr, *p;
417         size_t arridx, slen, nlen, high;
418         ssize_t ret = -1, start = 0;
419         bool bounded = false;
420 
421         /* An optional third argument constrains where the search begins (or, for
422          * rindex(), the highest index a match may have). Negative offsets are
423          * relative to the end, mirroring substr(); out-of-range offsets are
424          * clamped rather than rejected. */
425         if (offset != NULL && ucv_type(offset) != UC_NULL) {
426                 switch (ucv_type(offset)) {
427                 case UC_INTEGER:
428                         start = (ssize_t)ucv_int64_get(offset);
429                         bounded = true;
430                         break;
431 
432                 case UC_DOUBLE:
433                         start = (ssize_t)ucv_double_get(offset);
434                         bounded = true;
435                         break;
436 
437                 default:
438                         return NULL;
439                 }
440         }
441 
442         switch (ucv_type(stack)) {
443         case UC_ARRAY:
444                 slen = ucv_array_length(stack);
445 
446                 if (bounded) {
447                         if (start < 0)
448                                 start += (ssize_t)slen;
449 
450                         if (start < 0)
451                                 start = 0;
452                         else if (start > (ssize_t)slen)
453                                 start = (ssize_t)slen;
454                 }
455 
456                 if (right) {
457                         arridx = !bounded || (size_t)start + 1 > slen ? slen : (size_t)start + 1;
458 
459                         while (arridx > 0) {
460                                 arridx--;
461 
462                                 if (uc_uniq_ucv_equal(ucv_array_get(stack, arridx), needle)) {
463                                         ret = (ssize_t)arridx;
464                                         break;
465                                 }
466                         }
467                 }
468                 else {
469                         for (arridx = bounded ? (size_t)start : 0; arridx < slen; arridx++) {
470                                 if (uc_uniq_ucv_equal(ucv_array_get(stack, arridx), needle)) {
471                                         ret = (ssize_t)arridx;
472                                         break;
473                                 }
474                         }
475                 }
476 
477                 return ucv_int64_new(ret);
478 
479         case UC_STRING:
480                 if (ucv_type(needle) != UC_STRING)
481                         return ucv_int64_new(ret);
482 
483                 sstr = ucv_string_get(stack);
484                 slen = ucv_string_length(stack);
485                 nstr = ucv_string_get(needle);
486                 nlen = ucv_string_length(needle);
487 
488                 if (bounded) {
489                         if (start < 0)
490                                 start += (ssize_t)slen;
491 
492                         if (start < 0)
493                                 start = 0;
494                         else if (start > (ssize_t)slen)
495                                 start = (ssize_t)slen;
496                 }
497 
498                 if (!right) {
499                         if (nlen <= slen && (size_t)start <= slen - nlen) {
500                                 p = (const char *)memmem(sstr + start, slen - (size_t)start, nstr, nlen);
501 
502                                 if (p)
503                                         ret = (ssize_t)(p - sstr);
504                         }
505                 }
506                 else if (nlen <= slen) {
507                         high = slen - nlen;
508 
509                         if (bounded && (size_t)start < high)
510                                 high = (size_t)start;
511 
512                         p = sstr + high;
513 
514                         for (;;) {
515                                 if (memcmp(p, nstr, nlen) == 0) {
516                                         ret = (ssize_t)(p - sstr);
517                                         break;
518                                 }
519 
520                                 if (p == sstr)
521                                         break;
522 
523                                 p--;
524                         }
525                 }
526 
527                 return ucv_int64_new(ret);
528 
529         default:
530                 return NULL;
531         }
532 }
533 
534 /**
535  * Finds the given value passed as the second argument within the array or
536  * string specified in the first argument.
537  *
538  * Returns the first matching array index or first matching string offset or
539  * `-1` if the value was not found.
540  *
541  * Returns `null` if the first argument was neither an array nor a string.
542  *
543  * @function module:core#index
544  *
545  * @param {Array|string} arr_or_str
546  * The array or string to search for the value.
547  *
548  * @param {*} needle
549  * The value to find within the array or string.
550  *
551  * @param {?number} [offset=0]
552  * Optional index to begin the search at, counted in bytes for strings and in
553  * elements for arrays. A negative offset is relative to the end and an
554  * out-of-range offset is clamped rather than treated as a failure.
555  *
556  * @returns {?number}
557  *
558  * @example
559  * index("hello world", "o")               // 4
560  * index("hello world", "o", 5)            // 7
561  * index("hello world", "l", -3)           // 9
562  * index("Hello hello hello", "ll")          // 2
563  * index([ 1, 2, 3, 1, 2, 3, 1, 2, 3 ], 2)   // 1
564  * index("foo", "bar")                       // -1
565  * index(["Red", "Blue", "Green"], "Brown")  // -1
566  * index(123, 2)                             // null
567  */
568 static uc_value_t *
569 uc_lindex(uc_vm_t *vm, size_t nargs)
570 {
571         return uc_index(vm, nargs, false);
572 }
573 
574 /**
575  * Finds the given value passed as the second argument within the array or
576  * string specified in the first argument.
577  *
578  * Returns the last matching array index or last matching string offset or
579  * `-1` if the value was not found.
580  *
581  * Returns `null` if the first argument was neither an array nor a string.
582  *
583  * @function module:core#rindex
584  *
585  * @param {Array|string} arr_or_str
586  * The array or string to search for the value.
587  *
588  * @param {*} needle
589  * The value to find within the array or string.
590  *
591  * @param {?number} [offset]
592  * Optional upper bound: only indices less than or equal to it are considered.
593  * A negative offset is relative to the end and an out-of-range offset is
594  * clamped rather than treated as a failure.
595  *
596  * @returns {?number}
597  *
598  * @example
599  * rindex("hello world", "o")              // 7
600  * rindex("hello world", "o", 5)           // 4
601  * rindex("Hello hello hello", "ll")          // 14
602  * rindex([ 1, 2, 3, 1, 2, 3, 1, 2, 3 ], 2)   //  7
603  * rindex("foo", "bar")                       // -1
604  * rindex(["Red", "Blue", "Green"], "Brown")  // -1
605  * rindex(123, 2)                             // null
606  */
607 static uc_value_t *
608 uc_rindex(uc_vm_t *vm, size_t nargs)
609 {
610         return uc_index(vm, nargs, true);
611 }
612 
613 static bool
614 assert_mutable(uc_vm_t *vm, uc_value_t *val)
615 {
616         if (ucv_is_constant(val)) {
617                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
618                                       "%s value is immutable",
619                                       ucv_typename(val));
620 
621                 return false;
622         }
623 
624         return true;
625 }
626 
627 static bool
628 assert_mutable_array(uc_vm_t *vm, uc_value_t *val)
629 {
630         if (ucv_type(val) != UC_ARRAY)
631                 return false;
632 
633         return assert_mutable(vm, val);
634 }
635 
636 /**
637  * Pushes the given argument(s) to the given array.
638  *
639  * Returns the last pushed value.
640  *
641  * @function module:core#push
642  *
643  * @param {Array} arr
644  * The array to push values to.
645  *
646  * @param {...*} [values]
647  * The values to push.
648  *
649  * @returns {*}
650  *
651  * @example
652  * let x = [ 1, 2, 3 ];
653  * push(x, 4, 5, 6);    // 6
654  * print(x, "\n");      // [ 1, 2, 3, 4, 5, 6 ]
655  */
656 static uc_value_t *
657 uc_push(uc_vm_t *vm, size_t nargs)
658 {
659         uc_value_t *arr = uc_fn_arg(0);
660         uc_value_t *item = NULL;
661         size_t arridx;
662 
663         if (!assert_mutable_array(vm, arr))
664                 return NULL;
665 
666         for (arridx = 1; arridx < nargs; arridx++) {
667                 item = uc_fn_arg(arridx);
668                 ucv_array_push(arr, ucv_get(item));
669         }
670 
671         return ucv_get(item);
672 }
673 
674 /**
675  * Pops the last item from the given array and returns it.
676  *
677  * Returns `null` if the array was empty or if a non-array argument was passed.
678  *
679  * @function module:core#pop
680  *
681  * @param {Array} arr
682  * The input array.
683  *
684  * @returns {*}
685  *
686  * @example
687  * let x = [ 1, 2, 3 ];
688  * pop(x);          // 3
689  * print(x, "\n");  // [ 1, 2 ]
690  */
691 static uc_value_t *
692 uc_pop(uc_vm_t *vm, size_t nargs)
693 {
694         uc_value_t *arr = uc_fn_arg(0);
695 
696         if (!assert_mutable_array(vm, arr))
697                 return NULL;
698 
699         return ucv_array_pop(arr);
700 }
701 
702 /**
703  * Pops the first item from the given array and returns it.
704  *
705  * Returns `null` if the array was empty or if a non-array argument was passed.
706  *
707  * @function module:core#shift
708  *
709  * @param {Array} arr
710  * The array from which to pop the first item.
711  *
712  * @returns {*}
713  *
714  * @example
715  * let x = [ 1, 2, 3 ];
716  * shift(x);        // 1
717  * print(x, "\n");  // [ 2, 3 ]
718  */
719 static uc_value_t *
720 uc_shift(uc_vm_t *vm, size_t nargs)
721 {
722         uc_value_t *arr = uc_fn_arg(0);
723 
724         if (!assert_mutable_array(vm, arr))
725                 return NULL;
726 
727         return ucv_array_shift(arr);
728 }
729 
730 /**
731  * Add the given values to the beginning of the array passed via first argument.
732  *
733  * Returns the last value added to the array.
734  *
735  * @function module:core#unshift
736  *
737  * @param {Array} arr
738  * The array to which the values will be added.
739  *
740  * @param {...*}
741  * Values to add.
742  *
743  * @returns {*}
744  *
745  * @example
746  * let x = [ 3, 4, 5 ];
747  * unshift(x, 1, 2);  // 2
748  * print(x, "\n");    // [ 1, 2, 3, 4, 5 ]
749  */
750 static uc_value_t *
751 uc_unshift(uc_vm_t *vm, size_t nargs)
752 {
753         uc_value_t *arr = uc_fn_arg(0);
754         uc_value_t *item;
755         size_t i;
756 
757         if (!assert_mutable_array(vm, arr))
758                 return NULL;
759 
760         for (i = 1; i < nargs; i++) {
761                 item = uc_fn_arg(nargs - i);
762                 ucv_array_unshift(arr, ucv_get(item));
763         }
764 
765         return (nargs > 1) ? ucv_get(uc_fn_arg(nargs - 1)) : NULL;
766 }
767 
768 /**
769  * Converts each given numeric value to a byte and return the resulting string.
770  * Invalid numeric values or values < 0 result in `\0` bytes, values larger than
771  * 255 are truncated to 255.
772  *
773  * Returns a new strings consisting of the given byte values.
774  *
775  * @function module:core#chr
776  *
777  * @param {...number} n1
778  * The numeric values.
779  *
780  * @returns {string}
781  *
782  * @example
783  * chr(65, 98, 99);  // "Abc"
784  * chr(-1, 300);     // string consisting of an `0x0` and a `0xff` byte
785  */
786 static uc_value_t *
787 uc_chr(uc_vm_t *vm, size_t nargs)
788 {
789         uc_value_t *rv = NULL;
790         size_t idx;
791         int64_t n;
792         char *str;
793 
794         if (!nargs)
795                 return ucv_string_new_length("", 0);
796 
797         str = xalloc(nargs);
798 
799         for (idx = 0; idx < nargs; idx++) {
800                 n = ucv_to_integer(uc_fn_arg(idx));
801 
802                 if (n < 0)
803                         n = 0;
804                 else if (n > 255)
805                         n = 255;
806 
807                 str[idx] = (char)n;
808         }
809 
810         rv = ucv_string_new_length(str, nargs);
811         free(str);
812 
813         return rv;
814 }
815 
816 static uc_exception_type_t
817 check_exception_object(uc_value_t *obj)
818 {
819         uc_value_t *trace, *entry, *type;
820         uc_exception_type_t i;
821         const char *typestr;
822         size_t len;
823 
824         if (ucv_type(obj) != UC_OBJECT)
825                 return EXCEPTION_NONE;
826 
827         trace = ucv_object_get(obj, "stacktrace", NULL);
828 
829         if (ucv_type(trace) != UC_ARRAY)
830                 return EXCEPTION_NONE;
831 
832         len = ucv_array_length(trace);
833 
834         if (len == 0)
835                 return EXCEPTION_NONE;
836 
837         entry = ucv_array_get(trace, 0);
838 
839         if (ucv_type(entry) != UC_OBJECT || ucv_object_get(entry, "line", NULL) == NULL)
840                 return EXCEPTION_NONE;
841 
842         type = ucv_object_get(obj, "type", NULL);
843 
844         if (ucv_type(type) != UC_STRING)
845                 return EXCEPTION_USER;
846 
847         typestr = ucv_string_get(type);
848 
849         for (i = EXCEPTION_SYNTAX; i < EXCEPTION_MAX; i++)
850                 if (exception_type_strings[i] && strcmp(typestr, exception_type_strings[i]) == 0)
851                         return i;
852 
853         return EXCEPTION_USER;
854 }
855 
856 /**
857  * Raise an exception with the given message and abort execution.
858  *
859  * When passed an exception object (an object with `message` and/or
860  * `stacktrace` properties matching the shape of a caught exception),
861  * `die()` preserves the original message and stacktrace instead of
862  * creating new ones — enabling clean re-throw semantics.
863  *
864  * @function module:core#die
865  *
866  * @param {string|Object} msg
867  * The error message, or an exception-like object with `message` and
868  * `stacktrace` properties.
869  *
870  * @throws {Error}
871  * The error with the given message.
872  *
873  * @example
874  * die(msg);
875  *
876  * @example
877  * try {
878  *     inner();
879  * } catch (e) {
880  *     die(e);  // re-throw preserving original message and stacktrace
881  * }
882  */
883 static uc_value_t *
884 uc_die(uc_vm_t *vm, size_t nargs)
885 {
886         uc_value_t *arg = uc_fn_arg(0);
887         uc_exception_type_t etype;
888         bool freeable = false;
889         char *s;
890 
891         /* If arg is an exception object with a valid stacktrace, use it
892          * directly to avoid the cost of capturing a new one. */
893         etype = check_exception_object(arg);
894 
895         if (etype != EXCEPTION_NONE)
896         {
897                 uc_value_t *msg = ucv_object_get(arg, "message", NULL);
898 
899                 s = msg ? uc_cast_string(vm, &msg, &freeable) : "Died";
900 
901                 vm->exception.type = etype;
902                 free(vm->exception.message);
903                 vm->exception.message = strdup(s);
904 
905                 ucv_put(vm->exception.stacktrace);
906                 vm->exception.stacktrace = ucv_get(ucv_object_get(arg, "stacktrace", NULL));
907         }
908         else
909         {
910                 s = arg ? uc_cast_string(vm, &arg, &freeable) : "Died";
911                 uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s);
912         }
913 
914         if (freeable)
915                 free(s);
916 
917         return NULL;
918 }
919 
920 /**
921  * Check whether the given key exists within the given object value.
922  *
923  * Returns `true` if the given key is present within the object passed as the
924  * first argument, otherwise `false`.
925  *
926  * @function module:core#exists
927  *
928  * @param {Object} obj
929  * The input object.
930  *
931  * @param {string} key
932  * The key to check for existence.
933  *
934  * @returns {boolean}
935  *
936  * @example
937  * let x = { foo: true, bar: false, qrx: null };
938  * exists(x, 'foo');  // true
939  * exists(x, 'qrx');  // true
940  * exists(x, 'baz');  // false
941  */
942 static uc_value_t *
943 uc_exists(uc_vm_t *vm, size_t nargs)
944 {
945         uc_value_t *obj = uc_fn_arg(0);
946         uc_value_t *key = uc_fn_arg(1);
947         bool found, freeable;
948         char *k;
949 
950         if (ucv_type(obj) != UC_OBJECT)
951                 return ucv_boolean_new(false);
952 
953         k = uc_cast_string(vm, &key, &freeable);
954 
955         ucv_object_get(obj, k, &found);
956 
957         if (freeable)
958                 free(k);
959 
960         return ucv_boolean_new(found);
961 }
962 
963 /**
964  * Raw property read: the non-metamethod counterpart of `obj[key]`.
965  *
966  * Reads the property the way the VM would if `obj` had no `__get__` in its
967  * prototype chain (array index / resource type-proto / object own key +
968  * prototype chain), without dispatching `__get__`. This is the escape hatch a
969  * `__get__` uses to read the underlying storage without re-dispatching itself
970  * (a `__get__` that did `return this[key]` would otherwise recurse).
971  *
972  * @example
973  * let o = proto({}, { __get__: function(k) { return rawget(this, k) || 'x'; } });
974  *
975  * @param  {object|array|resource} obj  The value to read from.
976  * @param  {string|number}         key  The property key.
977  * @return {*} The property value, or null if not present.
978  */
979 static uc_value_t *
980 uc_rawget(uc_vm_t *vm, size_t nargs)
981 {
982         uc_value_t *obj = uc_fn_arg(0);
983         uc_value_t *key = uc_fn_arg(1);
984 
985         return ucv_key_rawget(vm, obj, key);
986 }
987 
988 /**
989  * Raw property write: the non-metamethod counterpart of `obj[key] = val`.
990  *
991  * Stores the property the way the VM would if `obj` had no `__set__` in its
992  * prototype chain (own-key store for objects, index store for arrays), without
993  * dispatching `__set__`. Escape hatch for a `__set__` that needs to write the
994  * underlying storage without re-dispatching itself.
995  *
996  * @example
997  * let o = proto({}, { __set__: function(k, v) { rawset(this, k, v); } });
998  *
999  * @param  {object|array} obj  The value to write to.
1000  * @param  {string|number} key  The property key.
1001  * @param  {*}            val  The value to store.
1002  * @return {*} The stored value (null on failure, e.g. non-container).
1003  */
1004 static uc_value_t *
1005 uc_rawset(uc_vm_t *vm, size_t nargs)
1006 {
1007         uc_value_t *obj = uc_fn_arg(0);
1008         uc_value_t *key = uc_fn_arg(1);
1009         uc_value_t *val = uc_fn_arg(2);
1010 
1011         return ucv_key_rawset(vm, obj, key, val);
1012 }
1013 
1014 /**
1015  * Raw property delete: the non-metamethod counterpart of `delete obj[key]`.
1016  *
1017  * Deletes the own property the way the VM would if `obj` had no `__delete__`
1018  * in its prototype chain, without dispatching `__delete__`. Escape hatch for a
1019  * `__delete__` that needs to remove the underlying storage without
1020  * re-dispatching itself.
1021  *
1022  * @example
1023  * let o = proto({}, { __delete__: function(k) { return rawdelete(this, k); } });
1024  *
1025  * @param  {object} obj  The value to delete from.
1026  * @param  {string|number} key  The property key.
1027  * @return {boolean} true if a property was deleted, false otherwise.
1028  */
1029 static uc_value_t *
1030 uc_rawdelete(uc_vm_t *vm, size_t nargs)
1031 {
1032         uc_value_t *obj = uc_fn_arg(0);
1033         uc_value_t *key = uc_fn_arg(1);
1034 
1035         return ucv_boolean_new(ucv_key_rawdelete(vm, obj, key));
1036 }
1037 
1038 /**
1039  * Terminate the interpreter with the given exit code.
1040  *
1041  * This function does not return.
1042  *
1043  * @function module:core#exit
1044  *
1045  * @param {number} n
1046  * The exit code.
1047  *
1048  * @example
1049  * exit();
1050  * exit(5);
1051  */
1052 static uc_value_t *
1053 uc_exit(uc_vm_t *vm, size_t nargs)
1054 {
1055         int64_t n = ucv_to_integer(uc_fn_arg(0));
1056 
1057         vm->arg.s32 = (int32_t)n;
1058         uc_vm_raise_exception(vm, EXCEPTION_EXIT, "Terminated");
1059 
1060         return NULL;
1061 }
1062 
1063 /**
1064  * Query an environment variable or then entire environment.
1065  *
1066  * Returns the value of the given environment variable, or - if omitted - a
1067  * dictionary containing all environment variables.
1068  *
1069  * @function module:core#getenv
1070  *
1071  * @param {string} [name]
1072  * The name of the environment variable.
1073  *
1074  * @returns {string|Object<string, string>}
1075  */
1076 static uc_value_t *
1077 uc_getenv(uc_vm_t *vm, size_t nargs)
1078 {
1079         uc_value_t *key = uc_fn_arg(0), *rv = NULL;
1080         extern char **environ;
1081         char **env = environ;
1082         char *k, *v;
1083 
1084         if (!key) {
1085                 rv = ucv_object_new(vm);
1086 
1087                 while (*env) {
1088                         v = strchr(*env, '=');
1089 
1090                         if (v) {
1091                                 xasprintf(&k, "%.*s", (int)(v - *env), *env);
1092                                 ucv_object_add(rv, k, ucv_string_new(v + 1));
1093                                 free(k);
1094                         }
1095 
1096                         env++;
1097                 }
1098         }
1099         else if (ucv_type(key) == UC_STRING) {
1100                 k = ucv_string_get(key);
1101                 v = getenv(k);
1102 
1103                 if (v)
1104                         rv = ucv_string_new(v);
1105         }
1106 
1107         return rv;
1108 }
1109 
1110 /**
1111  * Filter the array passed as the first argument by invoking the function
1112  * specified in the second argument for each array item.
1113  *
1114  * If the invoked function returns a truthy result, the item is retained,
1115  * otherwise, it is dropped. The filter function is invoked with three
1116  * arguments:
1117  *
1118  * 1. The array value
1119  * 2. The current index
1120  * 3. The array being filtered
1121  *
1122  * (Note that the `map` function behaves similarly to `filter` with respect
1123  * to its `fn` parameters.)
1124  *
1125  * Returns a new array containing only retained items, in the same order as
1126  * the input array.
1127  *
1128  * @function module:core#filter
1129  *
1130  * @param {Array} arr
1131  * The input array.
1132  *
1133  * @param {Function} fn
1134  * The filter function.
1135  *
1136  * @returns {Array}
1137  *
1138  * @example
1139  * // filter out any empty string:
1140  * a = filter(["foo", "", "bar", "", "baz"], length)
1141  * // a = ["foo", "bar", "baz"]
1142  *
1143  * // filter out any non-number type:
1144  * a = filter(["foo", 1, true, null, 2.2], function(v) {
1145  *     return (type(v) == "int" || type(v) == "double");
1146  * });
1147  * // a = [1, 2.2]
1148  */
1149 static uc_value_t *
1150 uc_filter(uc_vm_t *vm, size_t nargs)
1151 {
1152         uc_value_t *obj = uc_fn_arg(0);
1153         uc_value_t *func = uc_fn_arg(1);
1154         uc_value_t *rv, *arr;
1155         size_t arridx, arrlen;
1156 
1157         if (ucv_type(obj) != UC_ARRAY)
1158                 return NULL;
1159 
1160         arr = ucv_array_new(vm);
1161         uc_vm_stack_push(vm, ucv_get(arr));
1162 
1163         for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) {
1164                 uc_vm_ctx_push(vm);
1165                 uc_vm_stack_push(vm, ucv_get(func));
1166                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx)));
1167                 uc_vm_stack_push(vm, ucv_int64_new(arridx));
1168                 uc_vm_stack_push(vm, ucv_get(obj));
1169 
1170                 if (uc_vm_call(vm, true, 3)) {
1171                         ucv_put(arr);
1172 
1173                         return NULL;
1174                 }
1175 
1176                 rv = uc_vm_stack_pop(vm);
1177 
1178                 if (ucv_is_truish(rv))
1179                         ucv_array_push(arr, ucv_get(ucv_array_get(obj, arridx)));
1180 
1181                 ucv_put(rv);
1182         }
1183 
1184         ucv_put(uc_vm_stack_pop(vm));
1185 
1186         return arr;
1187 }
1188 
1189 /**
1190  * Converts the given hexadecimal string into a number.
1191  *
1192  * Returns the resulting integer value or `NaN` if the input value cannot be
1193  * interpreted as hexadecimal number.
1194  *
1195  * @function module:core#hex
1196  *
1197  * @param {*} x
1198  * The hexadecimal string to be converted.
1199  *
1200  * @returns {number}
1201  */
1202 static uc_value_t *
1203 uc_hex(uc_vm_t *vm, size_t nargs)
1204 {
1205         uc_value_t *val = uc_fn_arg(0);
1206         char *e, *v;
1207         int64_t n;
1208 
1209         v = ucv_string_get(val);
1210 
1211         if (!v || !isxdigit((unsigned char)*v))
1212                 return ucv_double_new(NAN);
1213 
1214         n = strtoll(v, &e, 16);
1215 
1216         if (e == v || *e)
1217                 return ucv_double_new(NAN);
1218 
1219         return ucv_int64_new(n);
1220 }
1221 
1222 /**
1223  * Converts the given value to an integer, using an optional base.
1224  *
1225  * Returns `NaN` if the value is not convertible.
1226  *
1227  * @function module:core#int
1228  *
1229  * @param {*} x
1230  * The value to be converted to an integer.
1231  *
1232  * @param {int} [base]
1233  * The base into which the value is to be converted, the default is 10.
1234  * Note that the base parameter is ignored if the `x` value is already numeric.
1235  *
1236  * @returns {number}
1237  *
1238  * @example
1239  * int("123")         // Returns 123
1240  * int("123", 10)     // 123
1241  * int("10 or more")  // 10
1242  * int("12.3")        // 12
1243  * int("123", 7)      // 66
1244  * int("abc", 16)     // 2748
1245  * int("xyz", 36)     // 44027
1246  * int(10.10, "2")    // 10, the invalid base is ignored
1247  * int("xyz", 16)     // NaN, bad value
1248  * int("1010", "2")   // NaN, bad base
1249  */
1250 static uc_value_t *
1251 uc_int(uc_vm_t *vm, size_t nargs)
1252 {
1253         uc_value_t *val = uc_fn_arg(0);
1254         uc_value_t *base = uc_fn_arg(1);
1255         char *e, *v;
1256         int64_t n;
1257 
1258         if (ucv_type(val) == UC_STRING) {
1259                 errno = 0;
1260                 v = ucv_string_get(val);
1261                 n = strtoll(v, &e, base ? ucv_int64_get(base) : 10);
1262 
1263                 if (e == v)
1264                         return ucv_double_new(NAN);
1265         }
1266         else {
1267                 n = ucv_to_integer(val);
1268         }
1269 
1270         if (errno == EINVAL || errno == ERANGE)
1271                 return ucv_double_new(NAN);
1272 
1273         return ucv_int64_new(n);
1274 }
1275 
1276 /**
1277  * Joins the array passed as the second argument into a string, using the
1278  * separator passed in the first argument as glue.
1279  *
1280  * Returns `null` if the second argument is not an array.
1281  *
1282  * @function module:core#join
1283  *
1284  * @param {string} sep
1285  * The separator to be used in joining the array elements.
1286  *
1287  * @param {Array} arr
1288  * The array to be joined into a string.
1289  *
1290  * @returns {?string}
1291  */
1292 static uc_value_t *
1293 uc_join(uc_vm_t *vm, size_t nargs)
1294 {
1295         uc_value_t *sep = uc_fn_arg(0);
1296         uc_value_t *arr = uc_fn_arg(1);
1297         size_t arrlen, arridx;
1298         uc_stringbuf_t *buf;
1299 
1300         if (ucv_type(arr) != UC_ARRAY)
1301                 return NULL;
1302 
1303         buf = ucv_stringbuf_new();
1304 
1305         for (arrlen = ucv_array_length(arr), arridx = 0; arridx < arrlen; arridx++) {
1306                 if (arridx > 0)
1307                         ucv_to_stringbuf(vm, buf, sep, false);
1308 
1309                 ucv_to_stringbuf(vm, buf, ucv_array_get(arr, arridx), false);
1310         }
1311 
1312         return ucv_stringbuf_finish(buf);
1313 }
1314 
1315 /**
1316  * Enumerates all object key names.
1317  *
1318  * Returns an array of all key names present in the passed object.
1319  * Returns `null` if the given argument is not an object.
1320  *
1321  * @function module:core#keys
1322  *
1323  * @param {object} obj
1324  * The object from which to retrieve the key names.
1325  *
1326  * @returns {?Array}
1327  */
1328 static uc_value_t *
1329 uc_keys(uc_vm_t *vm, size_t nargs)
1330 {
1331         uc_value_t *obj = uc_fn_arg(0);
1332         uc_value_t *arr = NULL;
1333 
1334         if (ucv_type(obj) != UC_OBJECT)
1335                 return NULL;
1336 
1337         arr = ucv_array_new(vm);
1338 
1339         ucv_object_foreach(obj, key, val) {
1340                 (void)val;
1341                 ucv_array_push(arr, ucv_string_new(key));
1342         }
1343 
1344         return arr;
1345 }
1346 
1347 /**
1348  * Convert the given string to lowercase and return the resulting string.
1349  *
1350  * Returns `null` if the given argument could not be converted to a string.
1351  *
1352  * @function module:core#lc
1353  *
1354  * @param {string} s
1355  * The input string.
1356  *
1357  * @returns {?string}
1358  * The lowercase string.
1359  *
1360  * @example
1361  * lc("HeLLo WoRLd!");  // "hello world!"
1362  */
1363 static uc_value_t *
1364 uc_lc(uc_vm_t *vm, size_t nargs)
1365 {
1366         uc_stringbuf_t *buf = xprintbuf_new();
1367         uc_value_t *rv;
1368         size_t i, len;
1369         char *s;
1370 
1371         ucv_to_stringbuf(vm, buf, uc_fn_arg(0), false);
1372 
1373         s = buf->buf;
1374         len = printbuf_length(buf);
1375 
1376         for (i = 0; i < len; i++)
1377                 if (s[i] >= 'A' && s[i] <= 'Z')
1378                         s[i] |= 32;
1379 
1380         rv = ucv_string_new_length(s, len);
1381 
1382         printbuf_free(buf);
1383 
1384         return rv;
1385 }
1386 
1387 /**
1388  * Transform the array passed as the first argument by invoking the function
1389  * specified in the second argument for each array item.
1390  *
1391  * The mapping function is invoked with three arguments (see examples, below,
1392  * for some possibly counterintuitive usage):
1393  *
1394  * 1. The array value
1395  * 2. The current index
1396  * 3. The array being filtered
1397  *
1398  * (Note that the `filter` function behaves similarly to `map` with respect
1399  * to its `fn` parameters.)
1400  *
1401  * Returns a new array of the same length as the input array containing the
1402  * transformed values.
1403  *
1404  * @function module:core#map
1405  *
1406  * @param {Array} arr
1407  * The input array.
1408  *
1409  * @param {Function} fn
1410  * The mapping function.
1411  *
1412  * @returns {Array}
1413  *
1414  * @example
1415  * // turn into an array of string lengths:
1416  * a = map(["Apple", "Banana", "Bean"], length);
1417  * // a = [5, 6, 4]
1418  *
1419  * // map to type names:
1420  * a = map(["foo", 1, true, null, 2.2], type);
1421  * // a = ["string", "int", "bool", null, "double"]
1422  *
1423  * // attempt to naively use built-in 'int' to map an array:
1424  * a = map(["x", "2", "11", "7"], int)
1425  * // a = [NaN, NaN, 3, NaN]
1426  * //
1427  * // This is a direct result of 'int' being provided the second, index parameter
1428  * // for its base value in the conversion.
1429  * //
1430  * // The resulting calls to 'int' are as follows:
1431  * //  int("x",  0, [...]) - convert "x"  to base 0, 'int' ignores the third value
1432  * //  int("2",  1, [...]) - convert "2"  to base 1, digit out of range, so NaN
1433  * //  int("11", 2, [...]) - convert "11" to base 2, produced unexpected 3
1434  * //  int("7",  3, [...]) - convert "7"  to base 3, digit out of range, NaN again
1435  *
1436  * // remedy this by using an arrow function to ensure the proper base value
1437  * // (in this case, the default of 10) is passed to 'int':
1438  * a = map(["x", "2", "1", "7"], (x) => int(x))
1439  * // a = [NaN, 2, 1, 7]
1440  *
1441  * // convert base-2 values:
1442  * a = map(["22", "1010", "0001", "0101"], (x) => int(x, 2))
1443  * // a = [NaN, 10, 1, 5]
1444  */
1445 static uc_value_t *
1446 uc_map(uc_vm_t *vm, size_t nargs)
1447 {
1448         uc_value_t *obj = uc_fn_arg(0);
1449         uc_value_t *func = uc_fn_arg(1);
1450         uc_value_t *arr, *rv;
1451         size_t arridx, arrlen;
1452 
1453         if (ucv_type(obj) != UC_ARRAY)
1454                 return NULL;
1455 
1456         arr = ucv_array_new(vm);
1457         uc_vm_stack_push(vm, ucv_get(arr));
1458 
1459         for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) {
1460                 uc_vm_ctx_push(vm);
1461                 uc_vm_stack_push(vm, ucv_get(func));
1462                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx)));
1463                 uc_vm_stack_push(vm, ucv_int64_new(arridx));
1464                 uc_vm_stack_push(vm, ucv_get(obj));
1465 
1466                 if (uc_vm_call(vm, true, 3)) {
1467                         ucv_put(arr);
1468 
1469                         return NULL;
1470                 }
1471 
1472                 rv = uc_vm_stack_pop(vm);
1473 
1474                 ucv_array_push(arr, rv);
1475         }
1476 
1477         ucv_put(uc_vm_stack_pop(vm));
1478 
1479         return arr;
1480 }
1481 
1482 /**
1483  * Without further arguments, this function returns the byte value of the first
1484  * character in the given string.
1485  *
1486  * If an offset argument is supplied, the byte value of the character at this
1487  * position is returned. If an invalid index is supplied, the function will
1488  * return `null`. Negative index entries are counted towards the end of the
1489  * string, e.g. `-2` will return the value of the second last character.
1490  *
1491  * Returns the byte value of the character.
1492  * Returns `null` if the offset is invalid or if the input is not a string.
1493  *
1494  * @function module:core#ord
1495  *
1496  * @param {string} s
1497  * The input string.
1498  *
1499  * @param {number} [offset]
1500  * The offset of the character.
1501  *
1502  * @returns {?number}
1503  *
1504  * @example
1505  * ord("Abc");         // 65
1506  * ord("Abc", 0);      // 65
1507  * ord("Abc", 1);      // 98
1508  * ord("Abc", 2);      // 99
1509  * ord("Abc", 10);     // null
1510  * ord("Abc", -10);    // null
1511  * ord("Abc", "nan");  // null
1512  */
1513 static uc_value_t *
1514 uc_ord(uc_vm_t *vm, size_t nargs)
1515 {
1516         uc_value_t *obj = uc_fn_arg(0);
1517         const char *str;
1518         int64_t n = 0;
1519         size_t len;
1520 
1521         if (ucv_type(obj) != UC_STRING)
1522                 return NULL;
1523 
1524         str = ucv_string_get(obj);
1525         len = ucv_string_length(obj);
1526 
1527         if (nargs > 1) {
1528                 n = ucv_int64_get(uc_fn_arg(1));
1529 
1530                 if (errno == EINVAL)
1531                         return NULL;
1532 
1533                 if (n < 0)
1534                         n += len;
1535         }
1536 
1537         if (n < 0 || (uint64_t)n >= len)
1538                 return NULL;
1539 
1540         return ucv_int64_new((uint8_t)str[n]);
1541 }
1542 
1543 /**
1544  * Query the type of the given value.
1545  *
1546  * Returns the type of the given value as a string which might be one of
1547  * `"function"`, `"object"`, `"array"`, `"double"`, `"int"`, or `"bool"`.
1548  *
1549  * Returns `null` when no value or `null` is passed.
1550  *
1551  * @function module:core#type
1552  *
1553  * @param {*} x
1554  * The value to determine the type of.
1555  *
1556  * @returns {?string}
1557  */
1558 static uc_value_t *
1559 uc_type(uc_vm_t *vm, size_t nargs)
1560 {
1561         uc_value_t *v = uc_fn_arg(0);
1562         uc_type_t t = ucv_type(v);
1563 
1564         switch (t) {
1565         case UC_CFUNCTION:
1566         case UC_CLOSURE:
1567                 return ucv_string_new("function");
1568 
1569         case UC_INTEGER:
1570                 return ucv_string_new("int");
1571 
1572         case UC_BOOLEAN:
1573                 return ucv_string_new("bool");
1574 
1575         case UC_NULL:
1576                 return NULL;
1577 
1578         default:
1579                 return ucv_string_new(ucv_typename(v));
1580         }
1581 }
1582 
1583 /**
1584  * Reverse the order of the given input array or string.
1585  *
1586  * If an array is passed, returns the array in reverse order.
1587  * If a string is passed, returns the string with the sequence of the characters
1588  * reversed.
1589  *
1590  * Returns the reversed array or string.
1591  * Returns `null` if neither an array nor a string were passed.
1592  *
1593  * @function module:core#reverse
1594  *
1595  * @param {Array|string} arr_or_str
1596  * The input array or string.
1597  *
1598  * @returns {?(Array|string)}
1599  *
1600  * @example
1601  * reverse([1, 2, 3]);   // [ 3, 2, 1 ]
1602  * reverse("Abc");       // "cbA"
1603  */
1604 static uc_value_t *
1605 uc_reverse(uc_vm_t *vm, size_t nargs)
1606 {
1607         uc_value_t *obj = uc_fn_arg(0);
1608         uc_value_t *rv = NULL;
1609         size_t len, arridx;
1610         const char *str;
1611         char *dup, *p;
1612 
1613         if (ucv_type(obj) == UC_ARRAY) {
1614                 if (!assert_mutable_array(vm, obj))
1615                         return NULL;
1616 
1617                 rv = ucv_array_new(vm);
1618 
1619                 for (arridx = ucv_array_length(obj); arridx > 0; arridx--)
1620                         ucv_array_push(rv, ucv_get(ucv_array_get(obj, arridx - 1)));
1621         }
1622         else if (ucv_type(obj) == UC_STRING) {
1623                 len = ucv_string_length(obj);
1624                 str = ucv_string_get(obj);
1625                 p = dup = xalloc(len + 1);
1626 
1627                 while (len > 0)
1628                         *p++ = str[--len];
1629 
1630                 rv = ucv_string_new_length(dup, ucv_string_length(obj));
1631 
1632                 free(dup);
1633         }
1634 
1635         return rv;
1636 }
1637 
1638 
1639 typedef struct {
1640         uc_vm_t *vm;
1641         bool ex;
1642         uc_value_t *fn;
1643 } sort_ctx_t;
1644 
1645 static int
1646 default_cmp(uc_value_t *v1, uc_value_t *v2, uc_vm_t *vm)
1647 {
1648         char *s1, *s2;
1649         bool f1, f2;
1650         int res;
1651 
1652         /* when both operands are numeric then compare numerically */
1653         if ((ucv_type(v1) == UC_INTEGER || ucv_type(v1) == UC_DOUBLE) &&
1654             (ucv_type(v2) == UC_INTEGER || ucv_type(v2) == UC_DOUBLE)) {
1655                 ucv_compare(0, v1, v2, &res);
1656 
1657                 return res;
1658         }
1659 
1660         /* otherwise convert both operands to strings and compare lexically */
1661         s1 = uc_cast_string(vm, &v1, &f1);
1662         s2 = uc_cast_string(vm, &v2, &f2);
1663 
1664         res = strcmp(s1, s2);
1665 
1666         if (f1) free(s1);
1667         if (f2) free(s2);
1668 
1669         return res;
1670 }
1671 
1672 static int
1673 array_sort_fn(uc_value_t *v1, uc_value_t *v2, void *ud)
1674 {
1675         uc_value_t *rv, *null = ucv_int64_new(0);
1676         sort_ctx_t *ctx = ud;
1677         int res;
1678 
1679         if (!ctx->fn)
1680                 return default_cmp(v1, v2, ctx->vm);
1681 
1682         if (ctx->ex)
1683                 return 0;
1684 
1685         uc_vm_ctx_push(ctx->vm);
1686         uc_vm_stack_push(ctx->vm, ucv_get(ctx->fn));
1687         uc_vm_stack_push(ctx->vm, ucv_get(v1));
1688         uc_vm_stack_push(ctx->vm, ucv_get(v2));
1689 
1690         if (uc_vm_call(ctx->vm, true, 2)) {
1691                 ctx->ex = true;
1692 
1693                 return 0;
1694         }
1695 
1696         rv = uc_vm_stack_pop(ctx->vm);
1697 
1698         ucv_compare(0, rv, null, &res);
1699 
1700         ucv_put(null);
1701         ucv_put(rv);
1702 
1703         return res;
1704 }
1705 
1706 static int
1707 object_sort_fn(const char *k1, uc_value_t *v1, const char *k2, uc_value_t *v2,
1708                void *ud)
1709 {
1710         uc_value_t *rv, *null = ucv_int64_new(0);
1711         sort_ctx_t *ctx = ud;
1712         int res;
1713 
1714         if (!ctx->fn)
1715                 return strcmp(k1, k2);
1716 
1717         if (ctx->ex)
1718                 return 0;
1719 
1720         uc_vm_ctx_push(ctx->vm);
1721         uc_vm_stack_push(ctx->vm, ucv_get(ctx->fn));
1722         uc_vm_stack_push(ctx->vm, ucv_string_new(k1));
1723         uc_vm_stack_push(ctx->vm, ucv_string_new(k2));
1724         uc_vm_stack_push(ctx->vm, ucv_get(v1));
1725         uc_vm_stack_push(ctx->vm, ucv_get(v2));
1726 
1727         if (uc_vm_call(ctx->vm, true, 4)) {
1728                 ctx->ex = true;
1729 
1730                 return 0;
1731         }
1732 
1733         rv = uc_vm_stack_pop(ctx->vm);
1734 
1735         ucv_compare(0, rv, null, &res);
1736 
1737         ucv_put(null);
1738         ucv_put(rv);
1739 
1740         return res;
1741 }
1742 
1743 /**
1744  * Sort the given array according to the given sort function.
1745  * If no sort function is provided, a default ascending sort order is applied.
1746  *
1747  * The input array is sorted in-place, no copy is made.
1748  *
1749  * The custom sort function is repeatedly called until the entire array is
1750  * sorted. It will receive two values as arguments and should return a value
1751  * lower than, larger than or equal to zero depending on whether the first
1752  * argument is smaller, larger or equal to the second argument respectively.
1753  *
1754  * Returns the sorted input array.
1755  *
1756  * @function module:core#sort
1757  *
1758  * @param {Array} arr
1759  * The input array to be sorted.
1760  *
1761  * @param {Function} [fn]
1762  * The sort function.
1763  *
1764  * @returns {Array}
1765  *
1766  * @example
1767  * sort([8, 1, 5, 9]) // [1, 5, 8, 9]
1768  * sort(["Bean", "Orange", "Apple"], function(a, b) {
1769  *    return length(a) - length(b);
1770  * }) // ["Bean", "Apple", "Orange"]
1771  */
1772 static uc_value_t *
1773 uc_sort(uc_vm_t *vm, size_t nargs)
1774 {
1775         uc_value_t *val = uc_fn_arg(0);
1776         uc_value_t *fn = uc_fn_arg(1);
1777         sort_ctx_t ctx = {
1778                 .vm = vm,
1779                 .fn = fn,
1780                 .ex = false
1781         };
1782 
1783         if (!assert_mutable(vm, val))
1784                 return NULL;
1785 
1786         switch (ucv_type(val)) {
1787         case UC_ARRAY:
1788                 ucv_array_sort_r(val, array_sort_fn, &ctx);
1789                 break;
1790 
1791         case UC_OBJECT:
1792                 ucv_object_sort_r(val, object_sort_fn, &ctx);
1793                 break;
1794 
1795         default:
1796                 return NULL;
1797         }
1798 
1799         return ctx.ex ? NULL : ucv_get(val);
1800 }
1801 
1802 /**
1803  * Removes the elements designated by `off` and `len` from the given array,
1804  * and replaces them with the additional arguments passed, if any.
1805  *
1806  * The array grows or shrinks as necessary.
1807  *
1808  * Returns the modified input array.
1809  *
1810  * @function module:core#splice
1811  *
1812  * @param {Array} arr
1813  * The input array to be modified.
1814  *
1815  * @param {number} off
1816  * The index to start removing elements.
1817  *
1818  * @param {number} [len]
1819  * The number of elements to remove.
1820  *
1821  * @param {...*} [elements]
1822  * The elements to insert.
1823  *
1824  * @returns {*}
1825  *
1826  * @example
1827  * let x = [ 1, 2, 3, 4 ];
1828  * splice(x, 1, 2, "a", "b", "c");  // [ 1, "a", "b", "c", 4 ]
1829  * print(x, "\n");                  // [ 1, "a", "b", "c", 4 ]
1830  */
1831 static uc_value_t *
1832 uc_splice(uc_vm_t *vm, size_t nargs)
1833 {
1834         uc_value_t *arr = uc_fn_arg(0);
1835         int64_t ofs = ucv_to_integer(uc_fn_arg(1));
1836         int64_t remlen = ucv_to_integer(uc_fn_arg(2));
1837         size_t arrlen, addlen, idx;
1838 
1839         if (!assert_mutable_array(vm, arr))
1840                 return NULL;
1841 
1842         arrlen = ucv_array_length(arr);
1843         addlen = nargs;
1844 
1845         if (addlen == 1) {
1846                 ofs = 0;
1847                 addlen = 0;
1848                 remlen = arrlen;
1849         }
1850         else if (addlen == 2) {
1851                 if (ofs < 0) {
1852                         ofs = arrlen + ofs;
1853 
1854                         if (ofs < 0)
1855                                 ofs = 0;
1856                 }
1857                 else if ((uint64_t)ofs > arrlen) {
1858                         ofs = arrlen;
1859                 }
1860 
1861                 addlen = 0;
1862                 remlen = arrlen - ofs;
1863         }
1864         else {
1865                 if (ofs < 0) {
1866                         ofs = arrlen + ofs;
1867 
1868                         if (ofs < 0)
1869                                 ofs = 0;
1870                 }
1871                 else if ((uint64_t)ofs > arrlen) {
1872                         ofs = arrlen;
1873                 }
1874 
1875                 if (remlen < 0) {
1876                         remlen = arrlen - ofs + remlen;
1877 
1878                         if (remlen < 0)
1879                                 remlen = 0;
1880                 }
1881                 else if ((uint64_t)remlen > arrlen - (uint64_t)ofs) {
1882                         remlen = arrlen - ofs;
1883                 }
1884 
1885                 addlen -= 3;
1886         }
1887 
1888         if (addlen < (uint64_t)remlen) {
1889                 ucv_array_delete(arr, ofs, remlen - addlen);
1890         }
1891         else if (addlen > (uint64_t)remlen) {
1892                 for (idx = arrlen; idx > (uint64_t)ofs; idx--)
1893                         ucv_array_set(arr, idx + addlen - remlen - 1,
1894                                 ucv_get(ucv_array_get(arr, idx - 1)));
1895         }
1896 
1897         for (idx = 0; idx < addlen; idx++)
1898                 ucv_array_set(arr, ofs + idx,
1899                         ucv_get(uc_fn_arg(3 + idx)));
1900 
1901         return ucv_get(arr);
1902 }
1903 
1904 /**
1905  * Performs a shallow copy of a portion of the source array, as specified by
1906  * the start and end offsets. The original array is not modified.
1907  *
1908  * Returns a new array containing the copied elements, if any.
1909  * Returns `null` if the given source argument is not an array value.
1910  *
1911  * @function module:core#slice
1912  *
1913  * @param {Array} arr
1914  * The source array to be copied.
1915  *
1916  * @param {number} [off]
1917  * The index of the first element to copy.
1918  *
1919  * @param {number} [end]
1920  * The index of the first element to exclude from the returned array.
1921  *
1922  * @returns {Array}
1923  *
1924  * @example
1925  * slice([1, 2, 3])          // [1, 2, 3]
1926  * slice([1, 2, 3], 1)       // [2, 3]
1927  * slice([1, 2, 3], -1)      // [3]
1928  * slice([1, 2, 3], -3, -1)  // [1, 2]
1929  * slice([1, 2, 3], 10)      // []
1930  * slice([1, 2, 3], 2, 1)    // []
1931  * slice("invalid", 1, 2)    // null
1932  */
1933 static uc_value_t *
1934 uc_slice(uc_vm_t *vm, size_t nargs)
1935 {
1936         uc_value_t *arr = uc_fn_arg(0);
1937         uc_value_t *sv = uc_fn_arg(1);
1938         uc_value_t *ev = uc_fn_arg(2);
1939         uc_value_t *res = NULL;
1940         int64_t off, end;
1941         size_t len;
1942 
1943         if (ucv_type(arr) != UC_ARRAY)
1944                 return NULL;
1945 
1946         len = ucv_array_length(arr);
1947         off = sv ? ucv_to_integer(sv) : 0;
1948         end = ev ? ucv_to_integer(ev) : (int64_t)len;
1949 
1950         if (off < 0) {
1951                 off = len + off;
1952 
1953                 if (off < 0)
1954                         off = 0;
1955         }
1956         else if ((uint64_t)off > len) {
1957                 off = len;
1958         }
1959 
1960         if (end < 0) {
1961                 end = len + end;
1962 
1963                 if (end < 0)
1964                         end = 0;
1965         }
1966         else if ((uint64_t)end > len) {
1967                 end = len;
1968         }
1969 
1970         res = ucv_array_new(vm);
1971 
1972         while (off < end)
1973                 ucv_array_push(res, ucv_get(ucv_array_get(arr, off++)));
1974 
1975         return res;
1976 }
1977 
1978 /**
1979  * Split the given string using the separator passed as the second argument
1980  * and return an array containing the resulting pieces.
1981  *
1982  * If a limit argument is supplied, the resulting array contains no more than
1983  * the given amount of entries, that means the string is split at most
1984  * `limit - 1` times total.
1985  *
1986  * The separator may either be a plain string or a regular expression.
1987  *
1988  * Returns a new array containing the resulting pieces.
1989  *
1990  * @function module:core#split
1991  *
1992  * @param {string} str
1993  * The input string to be split.
1994  *
1995  * @param {string|RegExp} sep
1996  * The separator.
1997  *
1998  * @param {number} [limit]
1999  * The limit on the number of splits.
2000  *
2001  * @returns {Array}
2002  *
2003  * @example
2004  * split("foo,bar,baz", ",")     // ["foo", "bar", "baz"]
2005  * split("foobar", "")           // ["f", "o", "o", "b", "a", "r"]
2006  * split("foo,bar,baz", /[ao]/)  // ["f", "", ",b", "r,b", "z"]
2007  * split("foo=bar=baz", "=", 2)  // ["foo", "bar=baz"]
2008  */
2009 static uc_value_t *
2010 uc_split(uc_vm_t *vm, size_t nargs)
2011 {
2012         uc_value_t *str = uc_fn_arg(0);
2013         uc_value_t *sep = uc_fn_arg(1);
2014         uc_value_t *lim = uc_fn_arg(2);
2015         uc_value_t *arr = NULL;
2016         const char *p, *sepstr, *splitstr;
2017         size_t seplen, splitlen, limit;
2018         int eflags = 0, res;
2019         regmatch_t pmatch;
2020         uc_regexp_t *re;
2021 
2022         if (!sep || ucv_type(str) != UC_STRING)
2023                 return NULL;
2024 
2025         arr = ucv_array_new(vm);
2026         splitlen = ucv_string_length(str);
2027         p = splitstr = ucv_string_get(str);
2028         limit = lim ? ucv_uint64_get(lim) : SIZE_MAX;
2029 
2030         if (limit == 0)
2031                 goto out;
2032 
2033         if (ucv_type(sep) == UC_REGEXP) {
2034                 re = ucv_as_regexp(sep);
2035 
2036                 while (limit > 1) {
2037                         res = regexec(&re->regexp, splitstr, 1, &pmatch, eflags);
2038 
2039                         if (res == REG_NOMATCH)
2040                                 break;
2041 
2042                         if (pmatch.rm_so != pmatch.rm_eo) {
2043                                 ucv_array_push(arr, ucv_string_new_length(splitstr, pmatch.rm_so));
2044                                 splitstr += pmatch.rm_eo;
2045                         }
2046                         else if (*splitstr) {
2047                                 ucv_array_push(arr, ucv_string_new_length(splitstr, 1));
2048                                 splitstr++;
2049                         }
2050                         else {
2051                                 goto out;
2052                         }
2053 
2054                         eflags |= REG_NOTBOL;
2055                         limit--;
2056                 }
2057 
2058                 ucv_array_push(arr, ucv_string_new(splitstr));
2059         }
2060         else if (ucv_type(sep) == UC_STRING) {
2061                 sepstr = ucv_string_get(sep);
2062                 seplen = ucv_string_length(sep);
2063 
2064                 if (splitlen == 0) {
2065                         ucv_array_push(arr, ucv_string_new_length("", 0));
2066                 }
2067                 else if (seplen == 0) {
2068                         while (limit > 1 && splitlen > 0) {
2069                                 ucv_array_push(arr, ucv_string_new_length(p, 1));
2070 
2071                                 limit--;
2072                                 splitlen--;
2073                                 p++;
2074                         }
2075 
2076                         if (splitlen > 0)
2077                                 ucv_array_push(arr, ucv_string_new_length(p, splitlen));
2078                 }
2079                 else {
2080                         while (limit > 1 && splitlen >= seplen) {
2081                                 if (!memcmp(p, sepstr, seplen)) {
2082                                         ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr));
2083 
2084                                         p = splitstr = p + seplen;
2085                                         splitlen -= seplen;
2086                                         limit--;
2087                                         continue;
2088                                 }
2089 
2090                                 splitlen--;
2091                                 p++;
2092                         }
2093 
2094                         ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr + splitlen));
2095                 }
2096         }
2097         else {
2098                 ucv_put(arr);
2099 
2100                 return NULL;
2101         }
2102 
2103 out:
2104         return arr;
2105 }
2106 
2107 /**
2108  * Extracts a substring out of `str` and returns it. First character is at
2109  * offset zero.
2110  *
2111  *  - If `off` is negative, starts that far back from the end of the string.
2112  *  - If `len` is omitted, returns everything through the end of the string.
2113  *  - If `len` is negative, leaves that many characters off the string end.
2114  *
2115  * Returns the extracted substring.
2116  *
2117  * @function module:core#substr
2118  *
2119  * @param {string} str
2120  * The input string.
2121  *
2122  * @param {number} off
2123  * The starting offset.
2124  *
2125  * @param {number} [len]
2126  * The length of the substring.
2127  *
2128  * @returns {string}
2129  *
2130  * @example
2131  * s = "The black cat climbed the green tree";
2132  * substr(s, 4, 5);      // black
2133  * substr(s, 4, -11);    // black cat climbed the
2134  * substr(s, 14);        // climbed the green tree
2135  * substr(s, -4);        // tree
2136  * substr(s, -4, 2);     // tr
2137  */
2138 static uc_value_t *
2139 uc_substr(uc_vm_t *vm, size_t nargs)
2140 {
2141         uc_value_t *str = uc_fn_arg(0);
2142         int64_t ofs = ucv_to_integer(uc_fn_arg(1));
2143         int64_t sublen = ucv_to_integer(uc_fn_arg(2));
2144         const char *p;
2145         size_t len;
2146 
2147         if (ucv_type(str) != UC_STRING)
2148                 return NULL;
2149 
2150         p = ucv_string_get(str);
2151         len = ucv_string_length(str);
2152 
2153         switch (nargs) {
2154         case 1:
2155                 ofs = 0;
2156                 sublen = len;
2157 
2158                 break;
2159 
2160         case 2:
2161                 if (ofs < 0) {
2162                         ofs = len + ofs;
2163 
2164                         if (ofs < 0)
2165                                 ofs = 0;
2166                 }
2167                 else if ((uint64_t)ofs > len) {
2168                         ofs = len;
2169                 }
2170 
2171                 sublen = len - ofs;
2172 
2173                 break;
2174 
2175         default:
2176                 if (ofs < 0) {
2177                         ofs = len + ofs;
2178 
2179                         if (ofs < 0)
2180                                 ofs = 0;
2181                 }
2182                 else if ((uint64_t)ofs > len) {
2183                         ofs = len;
2184                 }
2185 
2186                 if (sublen < 0) {
2187                         sublen = len - ofs + sublen;
2188 
2189                         if (sublen < 0)
2190                                 sublen = 0;
2191                 }
2192                 else if ((uint64_t)sublen > len - (uint64_t)ofs) {
2193                         sublen = len - ofs;
2194                 }
2195 
2196                 break;
2197         }
2198 
2199         return ucv_string_new_length(p + ofs, sublen);
2200 }
2201 
2202 /**
2203  * Returns the current UNIX epoch.
2204  *
2205  * @function module:core#time
2206  *
2207  * @returns {number}
2208  *
2209  * @example
2210  * time();     // 1598043054
2211  */
2212 static uc_value_t *
2213 uc_time(uc_vm_t *vm, size_t nargs)
2214 {
2215         time_t t = time(NULL);
2216 
2217         return ucv_int64_new((int64_t)t);
2218 }
2219 
2220 /**
2221  * Converts the given string to uppercase and returns the resulting string.
2222  *
2223  * Returns null if the given argument could not be converted to a string.
2224  *
2225  * @function module:core#uc
2226  *
2227  * @param {*} str
2228  * The string to be converted to uppercase.
2229  *
2230  * @returns {?string}
2231  *
2232  * @example
2233  * uc("hello");   // "HELLO"
2234  * uc(123);       // null
2235  */
2236 
2237 static uc_value_t *
2238 uc_uc(uc_vm_t *vm, size_t nargs)
2239 {
2240         uc_stringbuf_t *buf = xprintbuf_new();
2241         uc_value_t *rv;
2242         size_t i, len;
2243         char *s;
2244 
2245         ucv_to_stringbuf(vm, buf, uc_fn_arg(0), false);
2246 
2247         s = buf->buf;
2248         len = printbuf_length(buf);
2249 
2250         for (i = 0; i < len; i++)
2251                 if (s[i] >= 'a' && s[i] <= 'z')
2252                         s[i] &= ~32;
2253 
2254         rv = ucv_string_new_length(s, len);
2255 
2256         printbuf_free(buf);
2257 
2258         return rv;
2259 }
2260 
2261 /**
2262  * Converts each given numeric value to an UTF-8 multibyte sequence and returns
2263  * the resulting string.
2264  *
2265  * Invalid numeric values or values outside the range `0`..`0x10FFFF` are
2266  * represented by the unicode replacement character `0xFFFD`.
2267  *
2268  * Returns a new UTF-8 encoded string consisting of unicode characters
2269  * corresponding to the given numeric codepoints.
2270  *
2271  * @function module:core#uchr
2272  *
2273  * @param {...number}
2274  * Numeric values to convert.
2275  *
2276  * @returns {string}
2277  *
2278  * @example
2279  * uchr(0x2600, 0x26C6, 0x2601);  // "☀⛆☁"
2280  * uchr(-1, 0x20ffff, "foo");     // "���"
2281  */
2282 static uc_value_t *
2283 uc_uchr(uc_vm_t *vm, size_t nargs)
2284 {
2285         uc_value_t *rv = NULL;
2286         size_t idx, ulen;
2287         char *p, *str;
2288         int64_t n;
2289         int rem;
2290 
2291         for (idx = 0, ulen = 0; idx < nargs; idx++) {
2292                 n = ucv_to_integer(uc_fn_arg(idx));
2293 
2294                 if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
2295                         ulen += 3;
2296                 else if (n <= 0x7F)
2297                         ulen++;
2298                 else if (n <= 0x7FF)
2299                         ulen += 2;
2300                 else if (n <= 0xFFFF)
2301                         ulen += 3;
2302                 else
2303                         ulen += 4;
2304         }
2305 
2306         str = xalloc(ulen);
2307 
2308         for (idx = 0, p = str, rem = ulen; idx < nargs; idx++) {
2309                 n = ucv_to_integer(uc_fn_arg(idx));
2310 
2311                 if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
2312                         n = 0xFFFD;
2313 
2314                 if (!utf8enc(&p, &rem, n))
2315                         break;
2316         }
2317 
2318         rv = ucv_string_new_length(str, ulen);
2319 
2320         free(str);
2321 
2322         return rv;
2323 }
2324 
2325 /**
2326  * Returns an array containing all values of the given object.
2327  *
2328  * Returns null if no object was passed.
2329  *
2330  * @function module:core#values
2331  *
2332  * @param {*} obj
2333  * The object from which to extract values.
2334  *
2335  * @returns {?Array}
2336  *
2337  * @example
2338  * values({ foo: true, bar: false });   // [true, false]
2339  */
2340 static uc_value_t *
2341 uc_values(uc_vm_t *vm, size_t nargs)
2342 {
2343         uc_value_t *obj = uc_fn_arg(0);
2344         uc_value_t *arr;
2345 
2346         if (ucv_type(obj) != UC_OBJECT)
2347                 return NULL;
2348 
2349         arr = ucv_array_new(vm);
2350 
2351         ucv_object_foreach(obj, key, val) {
2352                 (void)key;
2353                 ucv_array_push(arr, ucv_get(val));
2354         }
2355 
2356         return arr;
2357 }
2358 
2359 static uc_value_t *
2360 uc_trim_common(uc_vm_t *vm, size_t nargs, bool start, bool end)
2361 {
2362         uc_value_t *str = uc_fn_arg(0);
2363         uc_value_t *chr = uc_fn_arg(1);
2364         const char *p, *c;
2365         size_t len;
2366 
2367         if (ucv_type(str) != UC_STRING ||
2368                 (chr != NULL && ucv_type(chr) != UC_STRING))
2369                 return NULL;
2370 
2371         c = ucv_string_get(chr);
2372         c = c ? c : " \t\r\n";
2373 
2374         p = ucv_string_get(str);
2375         len = ucv_string_length(str);
2376 
2377         if (start) {
2378                 while (*p) {
2379                         if (!strchr(c, *p))
2380                                 break;
2381 
2382                         p++;
2383                         len--;
2384                 }
2385         }
2386 
2387         if (end) {
2388                 while (len > 0) {
2389                         if (!strchr(c, p[len - 1]))
2390                                 break;
2391 
2392                         len--;
2393                 }
2394         }
2395 
2396         return ucv_string_new_length(p, len);
2397 }
2398 
2399 /**
2400  * Trim any of the specified characters in `c` from the start and end of `str`.
2401  * If the second argument is omitted, trims the characters, ` ` (space), `\t`,
2402  * `\r`, and `\n`.
2403  *
2404  * Returns the trimmed string.
2405  *
2406  * @function module:core#trim
2407  *
2408  * @param {string} str
2409  * The string to be trimmed.
2410  *
2411  * @param {string} [c]
2412  * The characters to be trimmed from the start and end of the string.
2413  *
2414  * @returns {string}
2415  */
2416 static uc_value_t *
2417 uc_trim(uc_vm_t *vm, size_t nargs)
2418 {
2419         return uc_trim_common(vm, nargs, true, true);
2420 }
2421 
2422 /**
2423  * Trim any of the specified characters from the start of the string.
2424  * If the second argument is omitted, trims the characters ` ` (space), '\t',
2425  * '\r', and '\n'.
2426  *
2427  * Returns the left trimmed string.
2428  *
2429  * @function module:core#ltrim
2430  *
2431  * @param {string} s
2432  * The input string.
2433  *
2434  * @param {string} [c]
2435  * The characters to trim.
2436  *
2437  * @returns {string}
2438  *
2439  * @example
2440  * ltrim("  foo  \n")     // "foo  \n"
2441  * ltrim("--bar--", "-")  // "bar--"
2442  */
2443 static uc_value_t *
2444 uc_ltrim(uc_vm_t *vm, size_t nargs)
2445 {
2446         return uc_trim_common(vm, nargs, true, false);
2447 }
2448 
2449 /**
2450  * Trim any of the specified characters from the end of the string.
2451  * If the second argument is omitted, trims the characters ` ` (space), '\t',
2452  * '\r', and '\n'.
2453  *
2454 * Returns the right trimmed string.
2455  *
2456  * @function module:core#rtrim
2457  *
2458  * @param {string} str
2459  * The input string.
2460  *
2461  * @param {string} [c]
2462  * The characters to trim.
2463  *
2464  * @returns {string}
2465  *
2466  * @example
2467  * rtrim("  foo  \n")     // "  foo"
2468  * rtrim("--bar--", "-")  // "--bar"
2469  */
2470 static uc_value_t *
2471 uc_rtrim(uc_vm_t *vm, size_t nargs)
2472 {
2473         return uc_trim_common(vm, nargs, false, true);
2474 }
2475 
2476 enum {
2477         FMT_F_ALT   = (1 << 0),
2478         FMT_F_ZERO  = (1 << 1),
2479         FMT_F_LEFT  = (1 << 2),
2480         FMT_F_SPACE = (1 << 3),
2481         FMT_F_SIGN  = (1 << 4),
2482         FMT_F_WIDTH = (1 << 5),
2483         FMT_F_PREC  = (1 << 6),
2484 };
2485 
2486 enum {
2487         FMT_C_NONE = (1 << 0),
2488         FMT_C_INT  = (1 << 1),
2489         FMT_C_UINT = (1 << 2),
2490         FMT_C_DBL  = (1 << 3),
2491         FMT_C_CHR  = (1 << 4),
2492         FMT_C_STR  = (1 << 5),
2493         FMT_C_JSON = (1 << 6),
2494 };
2495 
2496 static void
2497 uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
2498 {
2499         char *s, sfmt[sizeof("%#0- +0123456789.0123456789%")];
2500         uint32_t conv, flags, width, precision;
2501         uc_value_t *fmt = uc_fn_arg(0), *arg;
2502         const char *fstr, *last, *p, *cfmt;
2503         size_t argidx = 1, argpos, sfmtlen;
2504         uint64_t u;
2505         int64_t n;
2506         double d;
2507 
2508         if (ucv_type(fmt) == UC_STRING)
2509                 fstr = ucv_string_get(fmt);
2510         else
2511                 fstr = "";
2512 
2513         for (last = p = fstr; *p; p++) {
2514                 if (*p == '%') {
2515                         ucv_stringbuf_addstr(buf, last, p - last);
2516 
2517                         last = p++;
2518 
2519                         flags = 0;
2520                         width = 0;
2521                         precision = 0;
2522 
2523                         argpos = argidx;
2524 
2525                         if (*p >= '1' && *p <= '9') {
2526                                 while (isdigit(*p))
2527                                         width = width * 10 + (*p++ - '');
2528 
2529                                 /* if a dollar sign follows, this is an argument index */
2530                                 if (*p == '$') {
2531                                         argpos = width;
2532                                         width = 0;
2533                                         p++;
2534                                 }
2535 
2536                                 /* otherwise skip to parsing precision, flags can't possibly follow */
2537                                 else {
2538                                         flags |= FMT_F_WIDTH;
2539                                         goto parse_precision;
2540                                 }
2541                         }
2542 
2543                         while (*p != '\0' && strchr("#0- +", *p)) {
2544                                 switch (*p++) {
2545                                 case '#': flags |= FMT_F_ALT;   break;
2546                                 case '': flags |= FMT_F_ZERO;  break;
2547                                 case '-': flags |= FMT_F_LEFT;  break;
2548                                 case ' ': flags |= FMT_F_SPACE; break;
2549                                 case '+': flags |= FMT_F_SIGN;  break;
2550                                 }
2551                         }
2552 
2553                         if (*p >= '1' && *p <= '9') {
2554                                 while (isdigit(*p))
2555                                         width = width * 10 + (*p++ - '');
2556 
2557                                 flags |= FMT_F_WIDTH;
2558                         }
2559 
2560 parse_precision:
2561                         if (*p == '.') {
2562                                 p++;
2563 
2564                                 if (*p == '-') {
2565                                         p++;
2566 
2567                                         while (isdigit(*p))
2568                                                 p++;
2569                                 }
2570                                 else {
2571                                         while (isdigit(*p))
2572                                                 precision = precision * 10 + (*p++ - '');
2573                                 }
2574 
2575                                 flags |= FMT_F_PREC;
2576                         }
2577 
2578                         switch (*p) {
2579                         case 'd':
2580                         case 'i':
2581                                 conv = FMT_C_INT;
2582                                 flags &= ~FMT_F_PREC;
2583                                 cfmt = PRId64;
2584                                 break;
2585 
2586                         case 'o':
2587                                 conv = FMT_C_UINT;
2588                                 flags &= ~FMT_F_PREC;
2589                                 cfmt = PRIo64;
2590                                 break;
2591 
2592                         case 'u':
2593                                 conv = FMT_C_UINT;
2594                                 flags &= ~FMT_F_PREC;
2595                                 cfmt = PRIu64;
2596                                 break;
2597 
2598                         case 'x':
2599                                 conv = FMT_C_UINT;
2600                                 flags &= ~FMT_F_PREC;
2601                                 cfmt = PRIx64;
2602                                 break;
2603 
2604                         case 'X':
2605                                 conv = FMT_C_UINT;
2606                                 flags &= ~FMT_F_PREC;
2607                                 cfmt = PRIX64;
2608                                 break;
2609 
2610                         case 'e':
2611                                 conv = FMT_C_DBL;
2612                                 cfmt = "e";
2613                                 break;
2614 
2615                         case 'E':
2616                                 conv = FMT_C_DBL;
2617                                 cfmt = "E";
2618                                 break;
2619 
2620                         case 'f':
2621                                 conv = FMT_C_DBL;
2622                                 cfmt = "f";
2623                                 break;
2624 
2625                         case 'F':
2626                                 conv = FMT_C_DBL;
2627                                 cfmt = "F";
2628                                 break;
2629 
2630                         case 'g':
2631                                 conv = FMT_C_DBL;
2632                                 cfmt = "g";
2633                                 break;
2634 
2635                         case 'G':
2636                                 conv = FMT_C_DBL;
2637                                 cfmt = "G";
2638                                 break;
2639 
2640                         case 'c':
2641                                 conv = FMT_C_CHR;
2642                                 flags &= ~FMT_F_PREC;
2643                                 cfmt = "c";
2644                                 break;
2645 
2646                         case 's':
2647                                 conv = FMT_C_STR;
2648                                 flags &= ~FMT_F_ZERO;
2649                                 cfmt = "s";
2650                                 break;
2651 
2652                         case 'J':
2653                                 conv = FMT_C_JSON;
2654 
2655                                 if (flags & FMT_F_PREC) {
2656                                         flags &= ~FMT_F_PREC;
2657                                         precision++;
2658                                 }
2659 
2660                                 cfmt = "s";
2661                                 break;
2662 
2663                         case '%':
2664                                 conv = FMT_C_NONE;
2665                                 flags = 0;
2666                                 cfmt = "%";
2667                                 break;
2668 
2669                         case '\0':
2670                                 p--;
2671                                 /* fall through */
2672 
2673                         default:
2674                                 continue;
2675                         }
2676 
2677                         sfmtlen = 0;
2678                         sfmt[sfmtlen++] = '%';
2679 
2680                         if (flags & FMT_F_ALT)   sfmt[sfmtlen++] = '#';
2681                         if (flags & FMT_F_ZERO)  sfmt[sfmtlen++] = '';
2682                         if (flags & FMT_F_LEFT)  sfmt[sfmtlen++] = '-';
2683                         if (flags & FMT_F_SPACE) sfmt[sfmtlen++] = ' ';
2684                         if (flags & FMT_F_SIGN)  sfmt[sfmtlen++] = '+';
2685 
2686                         if (flags & FMT_F_WIDTH)
2687                                 sfmtlen += snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, "%" PRIu32, width);
2688 
2689                         if (flags & FMT_F_PREC)
2690                                 sfmtlen += snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, ".%" PRIu32, precision);
2691 
2692                         snprintf(&sfmt[sfmtlen], sizeof(sfmt) - sfmtlen, "%s", cfmt);
2693 
2694                         switch (conv) {
2695                         case FMT_C_NONE:
2696                                 ucv_stringbuf_addstr(buf, cfmt, strlen(cfmt));
2697                                 break;
2698 
2699                         case FMT_C_INT:
2700                                 argidx++;
2701                                 arg = uc_fn_arg(argpos);
2702                                 n = ucv_to_integer(arg);
2703 
2704                                 if (errno == ERANGE)
2705                                         n = (int64_t)ucv_to_unsigned(arg);
2706 
2707                                 ucv_stringbuf_printf(buf, sfmt, n);
2708                                 break;
2709 
2710                         case FMT_C_UINT:
2711                                 argidx++;
2712                                 arg = uc_fn_arg(argpos);
2713                                 u = ucv_to_unsigned(arg);
2714 
2715                                 if (errno == ERANGE)
2716                                         u = (uint64_t)ucv_to_integer(arg);
2717 
2718                                 ucv_stringbuf_printf(buf, sfmt, u);
2719                                 break;
2720 
2721                         case FMT_C_DBL:
2722                                 argidx++;
2723                                 d = ucv_to_double(uc_fn_arg(argpos));
2724                                 ucv_stringbuf_printf(buf, sfmt, d);
2725                                 break;
2726 
2727                         case FMT_C_CHR:
2728                                 argidx++;
2729                                 n = ucv_to_integer(uc_fn_arg(argpos));
2730                                 ucv_stringbuf_printf(buf, sfmt, (int)n);
2731                                 break;
2732 
2733                         case FMT_C_STR:
2734                                 argidx++;
2735                                 arg = uc_fn_arg(argpos);
2736 
2737                                 switch (ucv_type(arg)) {
2738                                 case UC_STRING:
2739                                         ucv_stringbuf_printf(buf, sfmt, ucv_string_get(arg));
2740                                         break;
2741 
2742                                 case UC_NULL:
2743                                         ucv_stringbuf_append(buf, "(null)");
2744                                         break;
2745 
2746                                 default:
2747                                         s = ucv_to_string(vm, arg);
2748                                         ucv_stringbuf_printf(buf, sfmt, s ? s : "(null)");
2749                                         free(s);
2750                                 }
2751 
2752                                 break;
2753 
2754                         case FMT_C_JSON:
2755                                 argidx++;
2756                                 s = ucv_to_jsonstring_formatted(vm,
2757                                         uc_fn_arg(argpos),
2758                                         precision > 0 ? (precision > 1 ? ' ' : '\t') : '\0',
2759                                         precision > 0 ? (precision > 1 ? precision - 1 : 1) : 0);
2760 
2761                                 ucv_stringbuf_printf(buf, sfmt, s ? s : "null");
2762                                 free(s);
2763                                 break;
2764                         }
2765 
2766                         last = p + 1;
2767                 }
2768         }
2769 
2770         ucv_stringbuf_addstr(buf, last, p - last);
2771 }
2772 
2773 /**
2774  * Formats the given arguments according to the given format string.
2775  *
2776  * See `printf()` for details.
2777  *
2778  * Returns the formatted string.
2779  *
2780  * @function module:core#sprintf
2781  *
2782  * @param {string} fmt
2783  * The format string.
2784  *
2785  * @param {...*}
2786  * Arguments to be formatted.
2787  *
2788  * @returns {string}
2789  *
2790  * @example
2791  * sprintf("Hello %s", "world");    // "Hello world"
2792  * sprintf("%08x", 123);            // "0000007b"
2793  * sprintf("%c%c%c", 65, 98, 99);   // "Abc"
2794  * sprintf("%g", 10 / 3.0);         // "3.33333"
2795  * sprintf("%2$d %1$d", 12, 34);    // "34 12"
2796  * sprintf("%J", [1,2,3]);          // "[1,2,3]"
2797  */
2798 static uc_value_t *
2799 uc_sprintf(uc_vm_t *vm, size_t nargs)
2800 {
2801         uc_stringbuf_t *buf = ucv_stringbuf_new();
2802 
2803         uc_printf_common(vm, nargs, buf);
2804 
2805         return ucv_stringbuf_finish(buf);
2806 }
2807 
2808 /**
2809  * Formats the given arguments according to the given format string and outputs
2810  * the result to stdout.
2811  *
2812  * Ucode supports a restricted subset of the formats allowed by the underlying
2813  * libc's `printf()` implementation, namely it allows the `d`, `i`, `o`, `u`,
2814  * `x`, `X`, `e`, `E`, `f`, `F`, `g`, `G`, `c` and `s` conversions.
2815  *
2816  * Additionally, an ucode specific `J` format is implemented, which causes the
2817  * corresponding value to be formatted as JSON string. By prefixing the `J`
2818  * format letter with a precision specifier, the resulting JSON output will be
2819  * pretty printed. A precision of `0` will use tabs for indentation, any other
2820  * positive precision will use that many spaces for indentation while a negative
2821  * or omitted precision specifier will turn off pretty printing.
2822  *
2823  * Other format specifiers such as `n` or `z` are not accepted and returned
2824  * verbatim. Format specifiers including `*` directives are rejected as well.
2825  *
2826  * Returns the number of bytes written to the standard output.
2827  *
2828  * @function module:core#printf
2829  *
2830  * @param {string} fmt
2831  * The format string.
2832  *
2833  * @param {...*}
2834  * Arguments to be formatted.
2835  *
2836  * @returns {number}
2837  *
2838  * @example
2839  * {%
2840  *   printf("Hello %s\n", "world");  // Hello world
2841  *   printf("%08x\n", 123);          // 0000007b
2842  *   printf("%c%c%c\n", 65, 98, 99); // Abc
2843  *   printf("%g\n", 10 / 3.0);       // 3.33333
2844  *   printf("%2$d %1$d\n", 12, 34);  // 34 12
2845  *   printf("%J", [1,2,3]);          // [ 1, 2, 3 ]
2846  *
2847  *   printf("%.J", [1,2,3]);
2848  *   // [
2849  *   //         1,
2850  *   //         2,
2851  *   //         3
2852  *   // ]
2853  *
2854  *   printf("%.2J", [1,2,3]);
2855  *   // [
2856  *   //   1,
2857  *   //   2,
2858  *   //   3
2859  *   // ]
2860  * %}
2861  */
2862 static uc_value_t *
2863 uc_printf(uc_vm_t *vm, size_t nargs)
2864 {
2865         uc_stringbuf_t *buf = xprintbuf_new();
2866         size_t len;
2867 
2868         uc_printf_common(vm, nargs, buf);
2869 
2870         len = fwrite(buf->buf, 1, printbuf_length(buf), vm->output);
2871 
2872         printbuf_free(buf);
2873 
2874         return ucv_int64_new(len);
2875 }
2876 
2877 static bool
2878 uc_require_so(uc_vm_t *vm, const char *path, uc_value_t **res)
2879 {
2880         void (*init)(uc_vm_t *, uc_value_t *);
2881         uc_value_t *scope;
2882         struct stat st;
2883         void *dlh;
2884 
2885         if (stat(path, &st))
2886                 return false;
2887 
2888         dlerror();
2889         dlh = dlopen(path, RTLD_LAZY|RTLD_LOCAL);
2890 
2891         if (!dlh) {
2892                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2893                                       "Unable to dlopen file '%s': %s", path, dlerror());
2894 
2895                 return true;
2896         }
2897 
2898         *(void **)(&init) = dlsym(dlh, "uc_module_entry");
2899 
2900         if (!init) {
2901                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2902                                       "Module '%s' provides no 'uc_module_entry' function", path);
2903 
2904                 return true;
2905         }
2906 
2907         scope = ucv_object_new(vm);
2908 
2909         init(vm, scope);
2910 
2911         *res = scope;
2912 
2913         return true;
2914 }
2915 
2916 static uc_value_t *
2917 uc_loadfile(uc_vm_t *vm, size_t nargs);
2918 
2919 static uc_value_t *
2920 uc_callfunc(uc_vm_t *vm, size_t nargs);
2921 
2922 static uc_value_t *
2923 uc_require_imports(uc_vm_t *vm, uc_value_t *closure)
2924 {
2925         uc_function_t *fn = ucv_as_closure(closure)->function;
2926         uc_source_t *src = uc_program_function_source(fn);
2927         uc_value_t *ns = ucv_object_new(vm);
2928         size_t i = 0;
2929 
2930         uc_vector_foreach(&src->exports, sym) {
2931                 if (i >= fn->program->exports.count)
2932                         break;
2933 
2934                 if (ucv_type(*sym) == UC_STRING)
2935                         ucv_object_add(ns, ucv_string_get(*sym),
2936                                 ucv_get(&fn->program->exports.entries[i++]->header));
2937                 else if (ucv_type(*sym) == UC_NULL)
2938                         ucv_object_add(ns, "default",
2939                                 ucv_get(&fn->program->exports.entries[i++]->header));
2940         }
2941 
2942         ucv_set_constant(ns, true);
2943 
2944         return ns;
2945 }
2946 
2947 static bool
2948 uc_require_ucode(uc_vm_t *vm, const char *path, uc_value_t *scope, uc_value_t **res, bool raw_mode, bool module_mode)
2949 {
2950         uc_parse_config_t config = *vm->config, *prev_config = vm->config;
2951         uc_value_t *closure;
2952         struct stat st;
2953 
2954         if (stat(path, &st))
2955                 return false;
2956 
2957         config.raw_mode = raw_mode;
2958         config.compile_module = module_mode;
2959         vm->config = &config;
2960 
2961         uc_vm_stack_push(vm, ucv_string_new(path));
2962 
2963         closure = uc_loadfile(vm, 1);
2964 
2965         ucv_put(uc_vm_stack_pop(vm));
2966 
2967         if (closure) {
2968                 uc_vm_stack_push(vm, closure);
2969                 uc_vm_stack_push(vm, NULL);
2970                 uc_vm_stack_push(vm, scope);
2971 
2972                 *res = uc_callfunc(vm, 3);
2973 
2974                 if (vm->exception.type != EXCEPTION_EXIT) {
2975                         if (module_mode) {
2976                                 ucv_put(*res);
2977                                 *res = uc_require_imports(vm, closure);
2978                         }
2979 
2980                         if (vm->stack.count >= 3) {
2981                                 uc_vm_stack_pop(vm);
2982                                 uc_vm_stack_pop(vm);
2983                                 uc_vm_stack_pop(vm);
2984                         }
2985                 }
2986         }
2987 
2988         vm->config = prev_config;
2989 
2990         return true;
2991 }
2992 
2993 static bool
2994 uc_require_path(uc_vm_t *vm, const char *path_template, const char *name,
2995                 uc_value_t **res, bool module_mode)
2996 {
2997         uc_stringbuf_t *buf = xprintbuf_new();
2998         const char *p, *q, *last;
2999         uc_value_t *modtable;
3000         bool rv;
3001 
3002         modtable = ucv_property_get(uc_vm_scope_get(vm), "modules");
3003         *res = ucv_get(ucv_object_get(modtable, name, &rv));
3004 
3005         if (rv)
3006                 goto out;
3007 
3008         p = strchr(path_template, '*');
3009 
3010         if (!p)
3011                 goto out;
3012 
3013         ucv_stringbuf_addstr(buf, path_template, p - path_template);
3014 
3015         for (q = last = name;; q++) {
3016                 if (*q == '.' || *q == '\0') {
3017                         ucv_stringbuf_addstr(buf, last, q - last);
3018 
3019                         if (*q)
3020                                 ucv_stringbuf_append(buf, "/");
3021                         else
3022                                 ucv_stringbuf_addstr(buf, p + 1, strlen(p + 1));
3023 
3024                         if (*q == '\0')
3025                                 break;
3026 
3027                         last = q + 1;
3028                 }
3029                 else if (!isalnum(*q) && *q != '_') {
3030                         goto out;
3031                 }
3032         }
3033 
3034         if (!strcmp(p + 1, ".so"))
3035                 rv = uc_require_so(vm, buf->buf, res);
3036         else if (!strcmp(p + 1, ".uc"))
3037                 rv = uc_require_ucode(vm, buf->buf, NULL, res, true, module_mode);
3038 
3039         if (rv)
3040                 ucv_object_add(modtable, name, ucv_get(*res));
3041 
3042 out:
3043         printbuf_free(buf);
3044 
3045         return rv;
3046 }
3047 
3048 uc_value_t *
3049 uc_require_library(uc_vm_t *vm, uc_value_t *nameval, bool module_mode)
3050 {
3051         uc_value_t *search, *se, *res;
3052         size_t arridx, arrlen;
3053         const char *name;
3054 
3055         if (ucv_type(nameval) != UC_STRING)
3056                 return NULL;
3057 
3058         name = ucv_string_get(nameval);
3059         search = ucv_property_get(uc_vm_scope_get(vm), "REQUIRE_SEARCH_PATH");
3060 
3061         if (ucv_type(search) != UC_ARRAY) {
3062                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3063                                       "Global require search path not set");
3064 
3065                 return NULL;
3066         }
3067 
3068         for (arridx = 0, arrlen = ucv_array_length(search); arridx < arrlen; arridx++) {
3069                 se = ucv_array_get(search, arridx);
3070 
3071                 if (ucv_type(se) != UC_STRING)
3072                         continue;
3073 
3074                 if (uc_require_path(vm, ucv_string_get(se), name, &res, module_mode))
3075                         return res;
3076         }
3077 
3078         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3079                               "No module named '%s' could be found", name);
3080 
3081         return NULL;
3082 }
3083 
3084 /**
3085  * Load and evaluate ucode scripts or shared library extensions.
3086  *
3087  * The `require()` function expands each member of the global
3088  * `REQUIRE_SEARCH_PATH` array to a filesystem path by replacing the `*`
3089  * placeholder with a slash-separated version of the given dotted module name
3090  * and subsequently tries to load a file at the resulting location.
3091  *
3092  * If a file is found at one of the search path locations, it is compiled and
3093  * evaluated or loaded via the C runtime's `dlopen()` function, depending on
3094  * whether the found file is a ucode script or a compiled dynamic library.
3095  *
3096  * The resulting program function of the compiled/loaded module is then
3097  * subsequently executed with the current global environment, without a `this`
3098  * context and without arguments.
3099  *
3100  * Finally, the return value of the invoked program function is returned back
3101  * by `require()` to the caller.
3102  *
3103  * By default, modules are cached in the global `modules` dictionary and
3104  * subsequent attempts to require the same module will return the cached module
3105  * dictionary entry without re-evaluating the module.
3106  *
3107  * To force reloading a module, the corresponding entry from the global
3108  * `modules` dictionary can be deleted.
3109  *
3110  * To preload a module or to provide a "virtual" module without a corresponding
3111  * filesystem resource, an entry can be manually added to the global `modules`
3112  * dictionary.
3113  *
3114  * Summarized, the `require()` function can be roughly described by the
3115  * following code:
3116  *
3117  * ```
3118  * function require(name) {
3119  *     if (exists(modules, name))
3120  *         return modules[name];
3121  *
3122  *     for (const item in REQUIRE_SEARCH_PATH) {
3123  *         const modpath = replace(item, '*', replace(name, '.', '/'));
3124  *         const entryfunc = loadfile(modpath, { raw_mode: true });
3125  *
3126  *         if (entryfunc) {
3127  *             const modval = entryfunc();
3128  *             modules[name] = modval;
3129  *
3130  *             return modval;
3131  *         }
3132  *     }
3133  *
3134  *     die(`Module ${name} not found`);
3135  * }
3136  * ```
3137  *
3138  * Due to the fact that `require()` is a runtime operation, module source code
3139  * is only lazily evaluated/loaded upon invoking the first require invocation,
3140  * which might lead to situations where errors in module sources are only
3141  * reported much later throughout the program execution. Unless runtime loading
3142  * of modules is absolutely required, e.g. to conditionally load extensions, the
3143  * compile time
3144  * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import|`import` syntax}
3145  * should be preferred.
3146  *
3147  * Returns the module value (typically an object) on success.
3148  *
3149  * Throws an exception if the module function threw an exception.
3150  *
3151  * Throws an exception if no matching module could be found, if the module
3152  * contains syntax errors or upon other I/O related problems.
3153  *
3154  * @function module:core#require
3155  *
3156  * @param {string} name
3157  * The name of the module to require in dotted notation.
3158  *
3159  * @returns {*}
3160  *
3161  * @example
3162  * // Require the `example/acme.uc` or `example/acme.so` module
3163  * const acme = require('example.acme');
3164  *
3165  * // Requiring the same name again will yield the cached instance
3166  * const acme2 = require('example.acme');
3167  * assert(acme === acme2);
3168  *
3169  * // Deleting the module dictionary entry will force a reload
3170  * delete modules['example.acme'];
3171  * const acme3 = require('example.acme');
3172  * assert(acme !== acme3);
3173  *
3174  * // Preloading a "virtual" module
3175  * modules['example.test'] = {
3176  *   hello: function() { print("This is the example module\n"); }
3177  * };
3178  *
3179  * const test = require('example.test');
3180  * test.hello();  // will print "This is the example module"
3181  */
3182 static uc_value_t *
3183 uc_require(uc_vm_t *vm, size_t nargs)
3184 {
3185         return uc_require_library(vm, uc_fn_arg(0), false);
3186 }
3187 
3188 /**
3189  * Convert the given IP address string to an array of byte values.
3190  *
3191  * IPv4 addresses result in arrays of 4 integers while IPv6 ones in arrays
3192  * containing 16 integers. The resulting array can be turned back into IP
3193  * address strings using the inverse `arrtoip()` function.
3194  *
3195  * Returns an array containing the address byte values.
3196  * Returns `null` if the given argument is not a string or an invalid IP.
3197  *
3198  * @function module:core#iptoarr
3199  *
3200  * @param {string} address
3201  * The IP address string to convert.
3202  *
3203  * @returns {?number[]}
3204  *
3205  * @example
3206  * iptoarr("192.168.1.1")              // [ 192, 168, 1, 1 ]
3207  * iptoarr("fe80::fc54:ff:fe82:abbd")  // [ 254, 128, 0, 0, 0, 0, 0, 0, 252, 84,
3208  *                                     //   0, 255, 254, 130, 171, 189 ])
3209  * iptoarr("foo")                      // null (invalid address)
3210  * iptoarr(123)                        // null (not a string)
3211  */
3212 static uc_value_t *
3213 uc_iptoarr(uc_vm_t *vm, size_t nargs)
3214 {
3215         uc_value_t *ip = uc_fn_arg(0);
3216         uc_value_t *res;
3217         union {
3218                 uint8_t u8[4];
3219                 struct in_addr in;
3220                 struct in6_addr in6;
3221         } a;
3222         int i;
3223 
3224         if (ucv_type(ip) != UC_STRING)
3225                 return NULL;
3226 
3227         if (inet_pton(AF_INET6, ucv_string_get(ip), &a)) {
3228                 res = ucv_array_new(vm);
3229 
3230                 for (i = 0; i < 16; i++)
3231                         ucv_array_push(res, ucv_int64_new(a.in6.s6_addr[i]));
3232 
3233                 return res;
3234         }
3235         else if (inet_pton(AF_INET, ucv_string_get(ip), &a)) {
3236                 res = ucv_array_new(vm);
3237 
3238                 ucv_array_push(res, ucv_int64_new(a.u8[0]));
3239                 ucv_array_push(res, ucv_int64_new(a.u8[1]));
3240                 ucv_array_push(res, ucv_int64_new(a.u8[2]));
3241                 ucv_array_push(res, ucv_int64_new(a.u8[3]));
3242 
3243                 return res;
3244         }
3245 
3246         return NULL;
3247 }
3248 
3249 static int
3250 check_byte(uc_value_t *v)
3251 {
3252         int n;
3253 
3254         if (ucv_type(v) != UC_INTEGER)
3255                 return -1;
3256 
3257         n = ucv_int64_get(v);
3258 
3259         if (n < 0 || n > 255)
3260                 return -1;
3261 
3262         return n;
3263 }
3264 
3265 /**
3266  * Convert the given input array of byte values to an IP address string.
3267  *
3268  * Input arrays of length 4 are converted to IPv4 addresses, arrays of length 16
3269  * to IPv6 ones. All other lengths are rejected. If any array element is not an
3270  * integer or exceeds the range 0..255 (inclusive), the array is rejected.
3271  *
3272  * Returns a string containing the formatted IP address.
3273  * Returns `null` if the input array was invalid.
3274  *
3275  * @function module:core#arrtoip
3276  *
3277  * @param {number[]} arr
3278  * The byte array to convert into an IP address string.
3279  *
3280  * @returns {?string}
3281  *
3282  * @example
3283  * arrtoip([ 192, 168, 1, 1 ])   // "192.168.1.1"
3284  * arrtoip([ 254, 128, 0, 0, 0, 0, 0, 0, 252, 84, 0, 255, 254, 130, 171, 189 ])
3285  *                               // "fe80::fc54:ff:fe82:abbd"
3286  * arrtoip([ 1, 2, 3])           // null (invalid length)
3287  * arrtoip([ 1, "2", -5, 300 ])  // null (invalid values)
3288  * arrtoip("123")                // null (not an array)
3289  */
3290 static uc_value_t *
3291 uc_arrtoip(uc_vm_t *vm, size_t nargs)
3292 {
3293         uc_value_t *arr = uc_fn_arg(0);
3294         union {
3295                 uint8_t u8[4];
3296                 struct in6_addr in6;
3297         } a;
3298         char buf[INET6_ADDRSTRLEN];
3299         int i, n;
3300 
3301         if (ucv_type(arr) != UC_ARRAY)
3302                 return NULL;
3303 
3304         switch (ucv_array_length(arr)) {
3305         case 4:
3306                 for (i = 0; i < 4; i++) {
3307                         n = check_byte(ucv_array_get(arr, i));
3308 
3309                         if (n < 0)
3310                                 return NULL;
3311 
3312                         a.u8[i] = n;
3313                 }
3314 
3315                 inet_ntop(AF_INET, &a, buf, sizeof(buf));
3316 
3317                 return ucv_string_new(buf);
3318 
3319         case 16:
3320                 for (i = 0; i < 16; i++) {
3321                         n = check_byte(ucv_array_get(arr, i));
3322 
3323                         if (n < 0)
3324                                 return NULL;
3325 
3326                         a.in6.s6_addr[i] = n;
3327                 }
3328 
3329                 inet_ntop(AF_INET6, &a, buf, sizeof(buf));
3330 
3331                 return ucv_string_new(buf);
3332 
3333         default:
3334                 return NULL;
3335         }
3336 }
3337 
3338 /**
3339  * Match the given string against the regular expression pattern specified as
3340  * the second argument.
3341  *
3342  * If the passed regular expression uses the `g` flag, the return value will be
3343  * an array of arrays describing all found occurrences within the string.
3344  *
3345  * Without the `g` modifier, an array describing the first match is returned.
3346  *
3347  * Returns `null` if the pattern was not found within the given string.
3348  *
3349  * @function module:core#match
3350  *
3351  * @param {string} str
3352  * The string to be matched against the pattern.
3353  *
3354  * @param {RegExp} pattern
3355  * The regular expression pattern.
3356  *
3357  * @returns {?Array}
3358  *
3359  * @example
3360  * match("foobarbaz", /b.(.)/)   // ["bar", "r"]
3361  * match("foobarbaz", /b.(.)/g)  // [["bar", "r"], ["baz", "z"]]
3362  */
3363 static uc_value_t *
3364 uc_match(uc_vm_t *vm, size_t nargs)
3365 {
3366         uc_value_t *subject = uc_fn_arg(0);
3367         uc_value_t *pattern = uc_fn_arg(1);
3368         uc_value_t *rv = NULL, *m;
3369         regmatch_t *pmatch = NULL;
3370         int eflags = 0, res;
3371         uc_regexp_t *re;
3372         bool freeable;
3373         char *p;
3374         size_t i;
3375 
3376         if (ucv_type(pattern) != UC_REGEXP || !subject)
3377                 return NULL;
3378 
3379         re = ucv_as_regexp(pattern);
3380 
3381         pmatch = calloc(1 + re->regexp.re_nsub, sizeof(regmatch_t));
3382 
3383         if (!pmatch)
3384                 return NULL;
3385 
3386         p = uc_cast_string(vm, &subject, &freeable);
3387 
3388         while (true) {
3389                 res = regexec(&re->regexp, p, 1 + re->regexp.re_nsub, pmatch, eflags);
3390 
3391                 if (res == REG_NOMATCH)
3392                         break;
3393 
3394                 m = ucv_array_new(vm);
3395 
3396                 for (i = 0; i < 1 + re->regexp.re_nsub; i++) {
3397                         if (pmatch[i].rm_so != -1)
3398                                 ucv_array_push(m,
3399                                         ucv_string_new_length(p + pmatch[i].rm_so,
3400                                                               pmatch[i].rm_eo - pmatch[i].rm_so));
3401                         else
3402                                 ucv_array_push(m, NULL);
3403                 }
3404 
3405                 if (re->global) {
3406                         if (!rv)
3407                                 rv = ucv_array_new(vm);
3408 
3409                         ucv_array_push(rv, m);
3410 
3411                         if (pmatch[0].rm_so != pmatch[0].rm_eo)
3412                                 p += pmatch[0].rm_eo;
3413                         else if (*p)
3414                                 p++;
3415                         else
3416                                 break;
3417 
3418                         eflags |= REG_NOTBOL;
3419                 }
3420                 else {
3421                         rv = m;
3422                         break;
3423                 }
3424         }
3425 
3426         free(pmatch);
3427 
3428         if (freeable)
3429                 free(p);
3430 
3431         return rv;
3432 }
3433 
3434 static void
3435 uc_replace_cb(uc_vm_t *vm, uc_value_t *func,
3436               const char *subject, regmatch_t *pmatch, size_t plen,
3437               uc_stringbuf_t *resbuf)
3438 {
3439         uc_value_t *rv;
3440         size_t i;
3441 
3442         uc_vm_ctx_push(vm);
3443         uc_vm_stack_push(vm, ucv_get(func));
3444 
3445         for (i = 0; i < plen; i++) {
3446                 if (pmatch[i].rm_so != -1)
3447                         uc_vm_stack_push(vm,
3448                                 ucv_string_new_length(subject + pmatch[i].rm_so,
3449                                                       pmatch[i].rm_eo - pmatch[i].rm_so));
3450                 else
3451                         uc_vm_stack_push(vm, NULL);
3452         }
3453 
3454         if (uc_vm_call(vm, true, i) == EXCEPTION_NONE) {
3455                 rv = uc_vm_stack_pop(vm);
3456 
3457                 ucv_to_stringbuf(vm, resbuf, rv, false);
3458 
3459                 ucv_put(rv);
3460         }
3461 }
3462 
3463 static void
3464 uc_replace_str(uc_vm_t *vm, uc_value_t *str,
3465                const char *subject, regmatch_t *pmatch, size_t plen,
3466                uc_stringbuf_t *resbuf)
3467 {
3468         bool esc = false;
3469         char *p, *r;
3470         uint8_t i;
3471 
3472         for (p = r = ucv_to_string(vm, str); *p; p++) {
3473                 if (esc) {
3474                         switch (*p) {
3475                         case '&':
3476                                 if (pmatch[0].rm_so != -1)
3477                                         ucv_stringbuf_addstr(resbuf,
3478                                                 subject + pmatch[0].rm_so,
3479                                                 pmatch[0].rm_eo - pmatch[0].rm_so);
3480                                 break;
3481 
3482                         case '`':
3483                                 if (pmatch[0].rm_so != -1)
3484                                         ucv_stringbuf_addstr(resbuf, subject, pmatch[0].rm_so);
3485                                 break;
3486 
3487                         case '\'':
3488                                 if (pmatch[0].rm_so != -1)
3489                                         ucv_stringbuf_addstr(resbuf,
3490                                                 subject + pmatch[0].rm_eo,
3491                                                 strlen(subject + pmatch[0].rm_eo));
3492                                 break;
3493 
3494                         case '1':
3495                         case '2':
3496                         case '3':
3497                         case '4':
3498                         case '5':
3499                         case '6':
3500                         case '7':
3501                         case '8':
3502                         case '9':
3503                                 i = *p - '';
3504                                 if (i < plen && pmatch[i].rm_so != -1) {
3505                                         ucv_stringbuf_addstr(resbuf,
3506                                                 subject + pmatch[i].rm_so,
3507                                                 pmatch[i].rm_eo - pmatch[i].rm_so);
3508                                 }
3509                                 else {
3510                                         ucv_stringbuf_append(resbuf, "$");
3511                                         ucv_stringbuf_addstr(resbuf, p, 1);
3512                                 }
3513                                 break;
3514 
3515                         case '$':
3516                                 ucv_stringbuf_append(resbuf, "$");
3517                                 break;
3518 
3519                         default:
3520                                 ucv_stringbuf_append(resbuf, "$");
3521                                 ucv_stringbuf_addstr(resbuf, p, 1);
3522                         }
3523 
3524                         esc = false;
3525                 }
3526                 else if (*p == '$') {
3527                         esc = true;
3528                 }
3529                 else {
3530                         ucv_stringbuf_addstr(resbuf, p, 1);
3531                 }
3532         }
3533 
3534         free(r);
3535 }
3536 
3537 /**
3538  * Replace occurrences of the specified pattern in the string passed as the
3539  * first argument.
3540  *
3541  * - The pattern value may be either a regular expression or a plain string.
3542  * - The replace value may be a function which is invoked for each found pattern
3543  *   or any other value which is converted into a plain string and used as
3544  *   replacement.
3545  * - When an optional limit is specified, substitutions are performed only that
3546  *   many times.
3547  * - If the pattern is a regular expression and not using the `g` flag, then
3548  *   only the first occurrence in the string is replaced.
3549  * - If the `g` flag is used or if the pattern is not a regular expression, all
3550  *   occurrences are replaced.
3551  * - If the replace value is a callback function, it is invoked with the found
3552  *   substring as the first and any capture group values as subsequent
3553  *   parameters.
3554  * - If the replace value is a string, specific substrings are substituted
3555  *   before it is inserted into the result.
3556  *
3557  * Returns a new string with the pattern replaced.
3558  *
3559  * @function module:core#replace
3560  *
3561  * @param {string} str
3562  * The string in which to replace occurrences.
3563  *
3564  * @param {RegExp|string} pattern
3565  * The pattern to be replaced.
3566  *
3567  * @param {Function|string} replace
3568  * The replacement value.
3569  *
3570  * @param {number} [limit]
3571  * The optional limit of substitutions.
3572  *
3573  * @returns {string}
3574  *
3575  * @example
3576  * replace("barfoobaz", /(f)(o+)/g, "[$$|$`|$&|$'|$1|$2|$3]")  // bar[$|bar|foo|baz|f|oo|$3]baz
3577  * replace("barfoobaz", /(f)(o+)/g, uc)                        // barFOObaz
3578  * replace("barfoobaz", "a", "X")                              // bXrfoobXz
3579  * replace("barfoobaz", /(.)(.)(.)/g, function(m, c1, c2, c3) {
3580  *     return c3 + c2 + c1;
3581  * })                                                          // raboofzab
3582  * replace("aaaaa", "a", "x", 3)                               // xxxaa
3583  * replace("foo bar baz", /[ao]/g, "x", 3)                     // fxx bxr baz
3584  */
3585 static uc_value_t *
3586 uc_replace(uc_vm_t *vm, size_t nargs)
3587 {
3588         char *sb = NULL, *pt = NULL, *p, *l;
3589         uc_value_t *subject = uc_fn_arg(0);
3590         uc_value_t *pattern = uc_fn_arg(1);
3591         uc_value_t *replace = uc_fn_arg(2);
3592         uc_value_t *limitval = uc_fn_arg(3);
3593         bool sb_freeable, pt_freeable;
3594         regmatch_t *pmatch = NULL;
3595         size_t pl, nmatch, limit;
3596         uc_regexp_t *re = NULL;
3597         uc_stringbuf_t *resbuf;
3598         int eflags = 0, res;
3599 
3600         if (!pattern || !subject || !replace)
3601                 return NULL;
3602 
3603         nmatch = 1;
3604 
3605         if (ucv_type(pattern) == UC_REGEXP) {
3606                 re = ucv_as_regexp(pattern);
3607                 nmatch += re->regexp.re_nsub;
3608         }
3609 
3610         pmatch = calloc(nmatch, sizeof(regmatch_t));
3611 
3612         if (!pmatch)
3613                 return NULL;
3614 
3615         sb = uc_cast_string(vm, &subject, &sb_freeable);
3616         resbuf = ucv_stringbuf_new();
3617         limit = limitval ? ucv_uint64_get(limitval) : SIZE_MAX;
3618 
3619         if (re) {
3620                 p = sb;
3621 
3622                 while (limit > 0) {
3623                         res = regexec(&re->regexp, p, nmatch, pmatch, eflags);
3624 
3625                         if (res == REG_NOMATCH)
3626                                 break;
3627 
3628                         ucv_stringbuf_addstr(resbuf, p, pmatch[0].rm_so);
3629 
3630                         if (ucv_is_callable(replace))
3631                                 uc_replace_cb(vm, replace, p, pmatch, nmatch, resbuf);
3632                         else
3633                                 uc_replace_str(vm, replace, p, pmatch, nmatch, resbuf);
3634 
3635                         if (pmatch[0].rm_so != pmatch[0].rm_eo)
3636                                 p += pmatch[0].rm_eo;
3637                         else if (*p)
3638                                 ucv_stringbuf_addstr(resbuf, p++, 1);
3639                         else
3640                                 break;
3641 
3642                         if (re->global)
3643                                 eflags |= REG_NOTBOL;
3644                         else
3645                                 break;
3646 
3647                         limit--;
3648                 }
3649 
3650                 ucv_stringbuf_addstr(resbuf, p, strlen(p));
3651         }
3652         else {
3653                 pt = uc_cast_string(vm, &pattern, &pt_freeable);
3654                 pl = strlen(pt);
3655 
3656                 l = p = sb;
3657 
3658                 while (limit > 0) {
3659                         if (pl == 0 || !strncmp(p, pt, pl)) {
3660                                 ucv_stringbuf_addstr(resbuf, l, p - l);
3661 
3662                                 pmatch[0].rm_so = p - l;
3663                                 pmatch[0].rm_eo = pmatch[0].rm_so + pl;
3664 
3665                                 if (ucv_is_callable(replace))
3666                                         uc_replace_cb(vm, replace, l, pmatch, 1, resbuf);
3667                                 else
3668                                         uc_replace_str(vm, replace, l, pmatch, 1, resbuf);
3669 
3670                                 if (pl) {
3671                                         l = p + pl;
3672                                         p += pl - 1;
3673                                 }
3674                                 else {
3675                                         l = p;
3676                                 }
3677 
3678                                 limit--;
3679                         }
3680 
3681                         if (!*p++)
3682                                 break;
3683                 }
3684 
3685                 ucv_stringbuf_addstr(resbuf, l, strlen(l));
3686 
3687                 if (pt_freeable)
3688                         free(pt);
3689         }
3690 
3691         free(pmatch);
3692 
3693         if (sb_freeable)
3694                 free(sb);
3695 
3696         return ucv_stringbuf_finish(resbuf);
3697 }
3698 
3699 static struct json_tokener *
3700 uc_json_from_object(uc_vm_t *vm, uc_value_t *obj, json_object **jso)
3701 {
3702         bool trail = false, eof = false;
3703         enum json_tokener_error err;
3704         struct json_tokener *tok = NULL;
3705         uc_value_t *rfn, *rbuf = NULL;
3706         uc_stringbuf_t *buf;
3707 
3708         rfn = ucv_get(ucv_property_get(obj, "read"));
3709 
3710         if (!ucv_is_callable(rfn)) {
3711                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3712                                       "Input object does not implement read() method");
3713 
3714                 goto out;
3715         }
3716 
3717         tok = xjs_new_tokener();
3718 
3719         while (true) {
3720                 uc_vm_stack_push(vm, ucv_get(obj));
3721                 uc_vm_stack_push(vm, ucv_get(rfn));
3722                 uc_vm_stack_push(vm, ucv_int64_new(1024));
3723 
3724                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE)
3725                         goto fail;
3726 
3727                 rbuf = uc_vm_stack_pop(vm);
3728 
3729                 /* check EOF */
3730                 eof = (rbuf == NULL || (ucv_type(rbuf) == UC_STRING && ucv_string_length(rbuf) == 0));
3731 
3732                 /* on EOF, stop parsing unless trailing garbage was detected which handled below */
3733                 if (eof && !trail) {
3734                         /* Didn't parse a complete object yet, possibly a non-delimited atomic value
3735                            such as `null`, `true` etc. - nudge parser by sending final zero byte.
3736                            See json-c issue #681 <https://github.com/json-c/json-c/issues/681> */
3737                         if (json_tokener_get_error(tok) == json_tokener_continue)
3738                                 *jso = json_tokener_parse_ex(tok, "\0", 1);
3739 
3740                         break;
3741                 }
3742 
3743                 if (trail || *jso) {
3744                         uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3745                                               "Trailing garbage after JSON data");
3746 
3747                         goto fail;
3748                 }
3749 
3750                 if (ucv_type(rbuf) != UC_STRING) {
3751                         buf = xprintbuf_new();
3752                         ucv_to_stringbuf_formatted(vm, buf, rbuf, 0, '\0', 0);
3753 
3754                         *jso = json_tokener_parse_ex(tok, buf->buf, printbuf_length(buf));
3755 
3756                         trail = (json_tokener_get_error(tok) == json_tokener_success &&
3757                                  json_tokener_get_parse_end(tok) < (size_t)printbuf_length(buf));
3758 
3759                         printbuf_free(buf);
3760                 }
3761                 else {
3762                         *jso = json_tokener_parse_ex(tok, ucv_string_get(rbuf), ucv_string_length(rbuf));
3763 
3764                         trail = (json_tokener_get_error(tok) == json_tokener_success &&
3765                                  json_tokener_get_parse_end(tok) < ucv_string_length(rbuf));
3766                 }
3767 
3768                 ucv_put(rbuf);
3769                 rbuf = NULL;
3770 
3771                 err = json_tokener_get_error(tok);
3772 
3773                 if (err != json_tokener_success && err != json_tokener_continue)
3774                         break;
3775         }
3776 
3777         goto out;
3778 
3779 fail:
3780         json_tokener_free(tok);
3781         tok = NULL;
3782 
3783 out:
3784         ucv_put(rfn);
3785         ucv_put(rbuf);
3786 
3787         return tok;
3788 }
3789 
3790 static struct json_tokener *
3791 uc_json_from_string(uc_vm_t *vm, uc_value_t *str, json_object **jso)
3792 {
3793         struct json_tokener *tok = xjs_new_tokener();
3794         size_t i;
3795         char *p;
3796 
3797         /* NB: the len + 1 here is intentional to pass the terminating \0 byte
3798          * to the json-c parser. This is required to work-around upstream
3799          * issue #681 <https://github.com/json-c/json-c/issues/681> */
3800         *jso = json_tokener_parse_ex(tok, ucv_string_get(str), ucv_string_length(str) + 1);
3801 
3802         if (json_tokener_get_error(tok) == json_tokener_success) {
3803                 p = ucv_string_get(str);
3804 
3805                 for (i = json_tokener_get_parse_end(tok); i < ucv_string_length(str); i++) {
3806                         if (!isspace(p[i])) {
3807                                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3808                                                       "Trailing garbage after JSON data");
3809 
3810 
3811                                 json_tokener_free(tok);
3812 
3813                                 return NULL;
3814                         }
3815                 }
3816         }
3817 
3818         return tok;
3819 }
3820 
3821 /**
3822  * Parse the given string or resource as JSON and return the resulting value.
3823  *
3824  * If the input argument is a plain string, it is directly parsed as JSON.
3825  *
3826  * If an array, object or resource value is given, this function will attempt to
3827  * invoke a `read()` method on it to read chunks of input text to incrementally
3828  * parse as JSON data. Reading will stop if the object's `read()` method returns
3829  * either `null` or an empty string.
3830  *
3831  * Throws an exception on parse errors, trailing garbage, or premature EOF.
3832  *
3833  * Returns the parsed JSON data.
3834  *
3835  * @function module:core#json
3836  *
3837  * @param {string} str_or_resource
3838  * The string or resource object to be parsed as JSON.
3839  *
3840  * @returns {*}
3841  *
3842  * @example
3843  * json('{"a":true, "b":123}')   // { "a": true, "b": 123 }
3844  * json('[1,2,')                 // Throws an exception
3845  *
3846  * import { open } from 'fs';
3847  * let fd = open('example.json', 'r');
3848  * json(fd);                     // will keep invoking `fd.read()` until EOF and
3849  *                               // incrementally parse each read chunk.
3850  *
3851  * let x = proto(
3852  *     [ '{"foo":', 'true, ', '"bar":', 'false}' ],
3853  *     { read: function() { return shift(this) } }
3854  * );
3855  * json(x);                      // will keep invoking `x.read()` until array
3856  *                               // is empty incrementally parse each piece
3857  *
3858  */
3859 static uc_value_t *
3860 uc_json(uc_vm_t *vm, size_t nargs)
3861 {
3862         uc_value_t *rv = NULL, *src = uc_fn_arg(0);
3863         struct json_tokener *tok = NULL;
3864         enum json_tokener_error err;
3865         json_object *jso = NULL;
3866 
3867         switch (ucv_type(src)) {
3868         case UC_STRING:
3869                 tok = uc_json_from_string(vm, src, &jso);
3870                 break;
3871 
3872         case UC_RESOURCE:
3873         case UC_OBJECT:
3874         case UC_ARRAY:
3875                 tok = uc_json_from_object(vm, src, &jso);
3876                 break;
3877 
3878         default:
3879                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3880                                       "Passed value is neither a string nor an object");
3881         }
3882 
3883         if (!tok)
3884                 goto out;
3885 
3886         err = json_tokener_get_error(tok);
3887 
3888         if (err == json_tokener_continue) {
3889                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3890                                       "Unexpected end of string in JSON data");
3891 
3892                 goto out;
3893         }
3894         else if (err != json_tokener_success) {
3895                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX,
3896                                       "Failed to parse JSON string: %s",
3897                                       json_tokener_error_desc(err));
3898 
3899                 goto out;
3900         }
3901 
3902         rv = ucv_from_json(vm, jso);
3903 
3904 out:
3905         if (tok)
3906                 json_tokener_free(tok);
3907 
3908         json_object_put(jso);
3909 
3910         return rv;
3911 }
3912 
3913 static char *
3914 include_path(const char *curpath, const char *incpath)
3915 {
3916         const char *slash;
3917         char *dup, *res;
3918         int len;
3919 
3920         if (*incpath == '/')
3921                 return realpath(incpath, NULL);
3922 
3923         slash = curpath ? strrchr(curpath, '/') : NULL;
3924 
3925         if (slash)
3926                 len = asprintf(&res, "%.*s/%s", (int)(slash - curpath), curpath, incpath);
3927         else
3928                 len = asprintf(&res, "./%s", incpath);
3929 
3930         if (len == -1)
3931                 return NULL;
3932 
3933         dup = realpath(res, NULL);
3934 
3935         free(res);
3936 
3937         return dup;
3938 }
3939 
3940 static uc_value_t *
3941 uc_include_common(uc_vm_t *vm, size_t nargs, bool raw_mode)
3942 {
3943         uc_value_t *path = uc_fn_arg(0);
3944         uc_value_t *scope = uc_fn_arg(1);
3945         uc_value_t *rv = NULL, *sc = NULL;
3946         uc_closure_t *closure = NULL;
3947         size_t i;
3948         char *p;
3949 
3950         if (ucv_type(path) != UC_STRING) {
3951                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3952                                       "Passed filename is not a string");
3953 
3954                 return NULL;
3955         }
3956 
3957         if (scope && ucv_type(scope) != UC_OBJECT) {
3958                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
3959                                       "Passed scope value is not an object");
3960 
3961                 return NULL;
3962         }
3963 
3964         /* find calling closure */
3965         for (i = vm->callframes.count; i > 0; i--) {
3966                 closure = vm->callframes.entries[i - 1].closure;
3967 
3968                 if (closure)
3969                         break;
3970         }
3971 
3972         if (!closure)
3973                 return NULL;
3974 
3975         p = include_path(uc_program_function_source(closure->function)->runpath, ucv_string_get(path));
3976 
3977         if (!p) {
3978                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
3979                                       "Include file not found");
3980 
3981                 return NULL;
3982         }
3983 
3984         if (ucv_prototype_get(scope)) {
3985                 sc = ucv_get(scope);
3986         }
3987         else if (scope) {
3988                 sc = ucv_object_new(vm);
3989 
3990                 ucv_object_foreach(scope, key, val)
3991                         ucv_object_add(sc, key, ucv_get(val));
3992 
3993                 ucv_prototype_set(sc, ucv_get(uc_vm_scope_get(vm)));
3994         }
3995         else {
3996                 sc = ucv_get(uc_vm_scope_get(vm));
3997         }
3998 
3999         if (uc_require_ucode(vm, p, sc, &rv, raw_mode, false))
4000                 ucv_put(rv);
4001 
4002         ucv_put(sc);
4003         free(p);
4004 
4005         return NULL;
4006 }
4007 
4008 /**
4009  * Evaluate and include the file at the given path and optionally override the
4010  * execution scope with the given scope object.
4011  *
4012  * By default, the file is executed within the same scope as the calling
4013  * `include()`, but by passing an object as the second argument, it is possible
4014  * to extend the scope available to the included file.
4015  *
4016  * This is useful to supply additional properties as global variables to the
4017  * included code. To sandbox included code, that is giving it only access to
4018  * explicitly provided properties, the `proto()` function can be used to create
4019  * a scope object with an empty prototype.
4020  *
4021  * @function module:core#include
4022  *
4023  * @param {string} path
4024  * The path to the file to be included.
4025  *
4026  * @param {Object} [scope]
4027  * The optional scope object to override the execution scope.
4028  *
4029  * @example
4030  * // Load and execute "foo.uc" immediately
4031  * include("./foo.uc")
4032  *
4033  * // Execute the "supplemental.ucode" in an extended scope and make the "foo"
4034  * // and "bar" properties available as global variables
4035  * include("./supplemental.uc", {
4036  *   foo: true,
4037  *   bar: 123
4038  * })
4039  *
4040  * // Execute the "untrusted.ucode" in a sandboxed scope and make the "foo" and
4041  * // "bar" variables as well as the "print" function available to it.
4042  * // By assigning an empty prototype object to the scope, included code has no
4043  * // access to other global values anymore.
4044  * include("./untrusted.uc", proto({
4045  *   foo: true,
4046  *   bar: 123,
4047  *   print: print
4048  * }, {}))
4049  */
4050 static uc_value_t *
4051 uc_include(uc_vm_t *vm, size_t nargs)
4052 {
4053         return uc_include_common(vm, nargs, vm->config && vm->config->raw_mode);
4054 }
4055 
4056 /**
4057  * When invoked with a string value as the first argument, the function acts
4058  * like `include()` but captures the output of the included file as a string and
4059  * returns the captured contents.
4060  *
4061  * The second argument is treated as the scope.
4062  *
4063  * When invoked with a function value as the first argument, `render()` calls
4064  * the given function and passes all subsequent arguments to it.
4065  *
4066  * Any output produced by the called function is captured and returned as a
4067  * string. The return value of the called function is discarded.
4068  *
4069  * @function module:core#render
4070  *
4071  * @param {string|Function} path_or_func
4072  * The path to the file or the function to be rendered.
4073  *
4074  * @param {Object|*} [scope_or_fnarg1]
4075  * The optional scope or the first argument for the function.
4076  *
4077  * @param {*} [fnarg2]
4078  * The second argument for the function.
4079  *
4080  * @param {...*} [fnargN]
4081  * Additional arguments for the function.
4082  *
4083  * @returns {string}
4084  *
4085  * @example
4086  * // Renders template file with given scope and captures the output as a string
4087  * const output = render("./template.uc", { foo: "bar" });
4088  *
4089  * // Calls a function, captures the output, and returns it as a string
4090  * const result = render(function(name) {
4091  *     printf("Hello, %s!\n", name);
4092  * }, "Alice");
4093  */
4094 static uc_value_t *
4095 uc_render(uc_vm_t *vm, size_t nargs)
4096 {
4097         uc_string_t hdr = { .header = { .type = UC_STRING, .refcount = 1 } };
4098         uc_string_t *ustr = NULL;
4099         FILE *mem, *prev;
4100         size_t len = 0;
4101 
4102         mem = open_memstream((char **)&ustr, &len);
4103 
4104         if (!mem)
4105                 goto out;
4106 
4107         /* reserve space for uc_string_t header... */
4108         if (fwrite(&hdr, 1, sizeof(hdr), mem) != sizeof(hdr))
4109                 goto out;
4110 
4111         /* divert VM output to memory fd */
4112         prev = vm->output;
4113         vm->output = mem;
4114 
4115         /* execute function */
4116         if (ucv_is_callable(uc_fn_arg(0)))
4117                 (void) uc_vm_call(vm, false, nargs - 1);
4118 
4119         /* execute include */
4120         else
4121                 (void) uc_include_common(vm, nargs, false);
4122 
4123         /* restore previous VM output */
4124         vm->output = prev;
4125         fclose(mem);
4126 
4127         /* update uc_string_t length */
4128         ustr->length = len - sizeof(*ustr);
4129 
4130         return &ustr->header;
4131 
4132 out:
4133         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
4134                               "Unable to initialize output memory: %s",
4135                               strerror(errno));
4136 
4137         if (mem)
4138                 fclose(mem);
4139 
4140         free(ustr);
4141 
4142         return NULL;
4143 }
4144 
4145 /**
4146  * Print any of the given values to stderr. Arrays and objects are converted to
4147  * their JSON representation.
4148  *
4149  * Returns the amount of bytes printed.
4150  *
4151  * @function module:core#warn
4152  *
4153  * @param {...*} x
4154  * The values to be printed.
4155  *
4156  * @returns {number}
4157  *
4158  * @example
4159  * warn("Hello", "world");  // Print "Helloworld" to stderr
4160  * warn({ key: "value" });  // Print JSON representation of the object to stderr
4161  */
4162 static uc_value_t *
4163 uc_warn(uc_vm_t *vm, size_t nargs)
4164 {
4165         return uc_print_common(vm, nargs, stderr);
4166 }
4167 
4168 /**
4169  * Executes the given command, waits for completion, and returns the resulting
4170  * exit code.
4171  *
4172  * The command argument may be either a string, in which case it is passed to
4173  * `/bin/sh -c`, or an array, which is directly converted into an `execv()`
4174  * argument vector.
4175  *
4176  *  - If the program terminated normally, a positive integer holding the
4177  *    program's `exit()` code is returned.
4178  *  - If the program was terminated by an uncaught signal, a negative signal
4179  *    number is returned.
4180  *  - If the optional timeout argument is specified, the program is terminated
4181  *    by `SIGKILL` after that many milliseconds if it doesn't complete within
4182  *    the timeout.
4183  *
4184  * Omitting the timeout argument or passing `0` disables the command timeout.
4185  *
4186  * Returns the program exit code.
4187  *
4188  * @function module:core#system
4189  *
4190  * @param {string|Array} command
4191  * The command to be executed.
4192  *
4193  * @param {number} [timeout]
4194  * The optional timeout in milliseconds.
4195  *
4196  * @returns {number}
4197  *
4198  * @example
4199  * // Execute through `/bin/sh`
4200  * // prints "Hello world" to stdout and returns 3
4201  * system("echo 'Hello world' && exit 3");
4202  *
4203  * // Execute argument vector
4204  * // prints the UNIX timestamp to stdout and returns 0
4205  * system(["/usr/bin/date", "+%s"]);
4206  *
4207  * // Apply a timeout
4208  * // returns -9
4209  * system("sleep 3 && echo 'Success'", 1000);
4210  */
4211 static uc_value_t *
4212 uc_system(uc_vm_t *vm, size_t nargs)
4213 {
4214         uc_value_t *cmdline = uc_fn_arg(0);
4215         uc_value_t *timeout = uc_fn_arg(1);
4216         const char **arglist, *fn;
4217         sigset_t sigmask, sigomask;
4218         struct timespec ts;
4219         size_t i, len;
4220         int64_t tms;
4221         pid_t cld;
4222         int rc;
4223 
4224         if (timeout && (ucv_type(timeout) != UC_INTEGER || ucv_int64_get(timeout) < 0)) {
4225                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4226                                       "Invalid timeout specified");
4227 
4228                 return NULL;
4229         }
4230 
4231         switch (ucv_type(cmdline)) {
4232         case UC_STRING:
4233                 arglist = xalloc(sizeof(*arglist) * 4);
4234                 arglist[0] = xstrdup("/bin/sh");
4235                 arglist[1] = xstrdup("-c");
4236                 arglist[2] = ucv_to_string(vm, cmdline);
4237                 arglist[3] = NULL;
4238                 break;
4239 
4240         case UC_ARRAY:
4241                 len = ucv_array_length(cmdline);
4242 
4243                 if (len == 0) {
4244                         uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4245                                               "Passed command array is empty");
4246 
4247                         return NULL;
4248                 }
4249 
4250                 arglist = xalloc(sizeof(*arglist) * (len + 1));
4251 
4252                 for (i = 0; i < len; i++)
4253                         arglist[i] = ucv_to_string(vm, ucv_array_get(cmdline, i));
4254 
4255                 arglist[i] = NULL;
4256 
4257                 break;
4258 
4259         default:
4260                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4261                                       "Passed command is neither string nor array");
4262 
4263                 return NULL;
4264         }
4265 
4266         tms = timeout ? ucv_int64_get(timeout) : 0;
4267 
4268         if (tms > 0) {
4269                 sigemptyset(&sigmask);
4270                 sigaddset(&sigmask, SIGCHLD);
4271 
4272                 if (sigprocmask(SIG_BLOCK, &sigmask, &sigomask) < 0) {
4273                         fn = "sigprocmask";
4274                         goto fail;
4275                 }
4276         }
4277 
4278         cld = fork();
4279 
4280         switch (cld) {
4281         case -1:
4282                 fn = "fork";
4283                 goto fail;
4284 
4285         case 0:
4286                 if (tms <= 0 || sigprocmask(SIG_SETMASK, &sigomask, NULL) == 0)
4287                         execvp(arglist[0], (char * const *)arglist);
4288 
4289                 exit(-1);
4290 
4291                 break;
4292 
4293         default:
4294                 if (tms > 0) {
4295                         ts.tv_sec = tms / 1000;
4296                         ts.tv_nsec = (tms % 1000) * 1000000;
4297 
4298                         while (1) {
4299                                 if (sigtimedwait(&sigmask, NULL, &ts) < 0) {
4300                                         if (errno == EINTR)
4301                                                 continue;
4302 
4303                                         if (errno != EAGAIN) {
4304                                                 fn = "sigtimedwait";
4305                                                 goto fail;
4306                                         }
4307 
4308                                         kill(cld, SIGKILL);
4309                                 }
4310 
4311                                 break;
4312                         }
4313                 }
4314 
4315                 while (waitpid(cld, &rc, 0) < 0) {
4316                         if (errno == EINTR)
4317                                 continue;
4318 
4319                         fn = "waitpid";
4320                         goto fail;
4321                 }
4322 
4323                 if (tms > 0)
4324                         sigprocmask(SIG_SETMASK, &sigomask, NULL);
4325 
4326                 for (i = 0; arglist[i]; i++)
4327                         free((char *)arglist[i]);
4328 
4329                 free(arglist);
4330 
4331                 if (WIFEXITED(rc))
4332                         return ucv_int64_new(WEXITSTATUS(rc));
4333                 else if (WIFSIGNALED(rc))
4334                         return ucv_int64_new(-WTERMSIG(rc));
4335                 else if (WIFSTOPPED(rc))
4336                         return ucv_int64_new(-WSTOPSIG(rc));
4337 
4338                 return NULL;
4339         }
4340 
4341 fail:
4342         if (tms > 0)
4343                 sigprocmask(SIG_SETMASK, &sigomask, NULL);
4344 
4345         for (i = 0; arglist[i]; i++)
4346                 free((char *)arglist[i]);
4347 
4348         free(arglist);
4349 
4350         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
4351                               "%s(): %s", fn, strerror(errno));
4352 
4353         return NULL;
4354 }
4355 
4356 /**
4357  * Enables or disables VM opcode tracing.
4358  *
4359  * When invoked with a positive non-zero level, opcode tracing is enabled and
4360  * debug information is printed to stderr as the program is executed.
4361  *
4362  * Invoking `trace()` with zero as an argument turns off opcode tracing.
4363  *
4364  * @function module:core#trace
4365  *
4366  * @param {number} level
4367  * The level of tracing to enable.
4368  *
4369  * @example
4370  * trace(1);   // Enables opcode tracing
4371  * trace(0);   // Disables opcode tracing
4372  */
4373 static uc_value_t *
4374 uc_trace(uc_vm_t *vm, size_t nargs)
4375 {
4376         uc_value_t *level = uc_fn_arg(0);
4377         uint8_t prev_level;
4378 
4379         if (ucv_type(level) != UC_INTEGER) {
4380                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid level specified");
4381 
4382                 return NULL;
4383         }
4384 
4385         prev_level = vm->trace;
4386         vm->trace = ucv_int64_get(level);
4387 
4388         return ucv_int64_new(prev_level);
4389 }
4390 
4391 /**
4392  * Get or set the prototype of the array or object value `val`.
4393  *
4394  * When invoked without a second argument, the function returns the current
4395  * prototype of the value in `val` or `null` if there is no prototype or if the
4396  * given value is neither an object nor an array.
4397  *
4398  * When invoked with a second prototype argument, the given `proto` value is set
4399  * as the prototype on the array or object in `val`.
4400  *
4401  * Throws an exception if the given prototype value is not an object, if the
4402  * given value does not support prototypes, or if setting the prototype would
4403  * create a circular prototype chain.
4404  *
4405  * @function module:core#proto
4406  *
4407  * @param {Array|Object} val
4408  * The array or object value.
4409  *
4410  * @param {Object} [proto]
4411  * The optional prototype object.
4412  *
4413  * @returns {?Object}
4414  *
4415  * @example
4416  * const arr = [1, 2, 3];
4417  * proto(arr);                 // Returns the current prototype of the array (null by default)
4418  * proto(arr, { foo: true });  // Sets the given object as the prototype of the array
4419  */
4420 static uc_value_t *
4421 uc_proto(uc_vm_t *vm, size_t nargs)
4422 {
4423         uc_value_t *val = uc_fn_arg(0);
4424         uc_value_t *proto = NULL;
4425 
4426         if (nargs < 2)
4427                 return ucv_get(ucv_prototype_get(val));
4428 
4429         proto = uc_fn_arg(1);
4430 
4431         /* Reject a circular prototype chain up front so we can raise a specific
4432          * error without invoking ucv_prototype_set(). */
4433         if (ucv_type(proto) == UC_OBJECT) {
4434                 uc_value_t *p;
4435 
4436                 for (p = proto; p; p = ucv_prototype_get(p)) {
4437                         if (p == val) {
4438                                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4439                                         "Cannot set circular prototype");
4440 
4441                                 return NULL;
4442                         }
4443                 }
4444         }
4445 
4446         /* ucv_prototype_set() takes ownership of the passed reference, so hand it
4447          * an owned ref (uc_fn_arg() only borrows from the stack). Release the
4448          * extra ref if the set is rejected so it is not leaked. */
4449         if (!ucv_prototype_set(val, ucv_get(proto))) {
4450                 ucv_put(proto);
4451                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
4452                         "Passed value is neither a prototype, resource or object");
4453 
4454                 return NULL;
4455         }
4456 
4457         ucv_get(proto);
4458 
4459         return ucv_get(val);
4460 }
4461 
4462 /**
4463  * Pause execution for the given amount of milliseconds.
4464  *
4465  * @function module:core#sleep
4466  *
4467  * @param {number} milliseconds
4468  * The amount of milliseconds to sleep.
4469  *
4470  * @returns {boolean}
4471  *
4472  * @example
4473  * sleep(1000);                          // Sleeps for 1 second
4474  */
4475 static uc_value_t *
4476 uc_sleep(uc_vm_t *vm, size_t nargs)
4477 {
4478         uc_value_t *duration = uc_fn_arg(0);
4479         struct timeval tv;
4480         int64_t ms;
4481 
4482         ms = ucv_to_integer(duration);
4483 
4484         if (errno != 0 || ms <= 0)
4485                 return ucv_boolean_new(false);
4486 
4487         tv.tv_sec = ms / 1000;
4488         tv.tv_usec = (ms % 1000) * 1000;
4489 
4490         select(0, NULL, NULL, NULL, &tv);
4491 
4492         return ucv_boolean_new(true);
4493 }
4494 
4495 /**
4496  * Raise an exception with the given message parameter when the value in `cond`
4497  * is not truthy.
4498  *
4499  * When `message` is omitted, the default value is `Assertion failed`.
4500  *
4501  * @function module:core#assert
4502  *
4503  * @param {*} cond
4504  * The value to check for truthiness.
4505  *
4506  * @param {string} [message]
4507  * The message to include in the exception.
4508  *
4509  * @throws {Error} When the condition is falsy.
4510  *
4511  * @example
4512  * assert(true, "This is true");  // No exception is raised
4513  * assert(false);                 // Exception is raised with the default message "Assertion failed"
4514  */
4515 static uc_value_t *
4516 uc_assert(uc_vm_t *vm, size_t nargs)
4517 {
4518         uc_value_t *cond = uc_fn_arg(0);
4519         uc_value_t *msg = uc_fn_arg(1);
4520         bool freeable = false;
4521         char *s;
4522 
4523         if (!ucv_is_truish(cond)) {
4524                 s = msg ? uc_cast_string(vm, &msg, &freeable) : "Assertion failed";
4525 
4526                 uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s);
4527 
4528                 if (freeable)
4529                         free(s);
4530 
4531                 return NULL;
4532         }
4533 
4534         return ucv_get(cond);
4535 }
4536 
4537 /**
4538  * Construct a regular expression instance from the given `source` pattern
4539  * string and any flags optionally specified by the `flags` argument.
4540  *
4541  * Supported flags:
4542  *  - `i`: Case-insensitive matching
4543  *  - `s`: DotAll - makes `.` match newline characters (default: `.` does not match newlines)
4544  *  - `g`: Global matching (for match() function)
4545  *
4546  *  - Throws a type error exception if `flags` is not a string or if the string
4547  *    in `flags` contains unrecognized regular expression flag characters.
4548  *  - Throws a syntax error when the pattern in `source` cannot be compiled into
4549  *    a valid regular expression.
4550  *
4551  * Returns the compiled regular expression value.
4552  *
4553  * @function module:core#regexp
4554  *
4555  * @param {string} source
4556  * The pattern string.
4557  *
4558  * @param {string} [flags]
4559  * The optional regular expression flags (i=ignore case, s=dotAll, g=global).
4560  *
4561  * @returns {RegExp}
4562  *
4563  * @example
4564  * regexp('foo.*bar', 'is');   // equivalent to /foo.*bar/is
4565  * regexp('foo.*bar', 'x');    // throws a "Type error: Unrecognized flag character 'x'" exception
4566  * regexp('foo.*(');           // throws a "Syntax error: Unmatched ( or \( exception"
4567  *
4568  * @example
4569  * // Without 's' flag, . does not match newlines
4570  * match("hello\nworld", /hello.world/);    // null
4571  *
4572  * @example
4573  * // With 's' flag, . matches newlines (dotAll behavior)
4574  * match("hello\nworld", /hello.world/s);   // matches
4575  */
4576 static uc_value_t *
4577 uc_regexp(uc_vm_t *vm, size_t nargs)
4578 {
4579         bool icase = false, newline = false, global = false, freeable;
4580         uc_value_t *source = uc_fn_arg(0);
4581         uc_value_t *flags = uc_fn_arg(1);
4582         uc_value_t *regex = NULL;
4583         char *p, *err = NULL;
4584 
4585         if (flags) {
4586                 if (ucv_type(flags) != UC_STRING) {
4587                         uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Given flags argument is not a string");
4588 
4589                         return NULL;
4590                 }
4591 
4592                 for (p = ucv_string_get(flags); *p; p++) {
4593                         switch (*p) {
4594                         case 'i':
4595                                 icase = true;
4596                                 break;
4597 
4598                         case 's':
4599                                 newline = true;
4600                                 break;
4601 
4602                         case 'g':
4603                                 global = true;
4604                                 break;
4605 
4606                         default:
4607                                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Unrecognized flag character '%c'", *p);
4608 
4609                                 return NULL;
4610                         }
4611                 }
4612         }
4613 
4614         p = uc_cast_string(vm, &source, &freeable);
4615         regex = ucv_regexp_new(p, icase, newline, global, &err);
4616 
4617         if (freeable)
4618                 free(p);
4619 
4620         if (err) {
4621                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "%s", err);
4622                 ucv_put(regex);
4623                 free(err);
4624 
4625                 return NULL;
4626         }
4627 
4628         return regex;
4629 }
4630 
4631 /**
4632  * Match the given subject against the supplied wildcard (file glob) pattern.
4633  *
4634  *  - If a truthy value is supplied as the third argument, case-insensitive
4635  *    matching is performed.
4636  *  - If a non-string value is supplied as the subject, it is converted into a
4637  *    string before being matched.
4638  *
4639  * Returns `true` when the value matched the given pattern, otherwise `false`.
4640  *
4641  * @function module:core#wildcard
4642  *
4643  * @param {*} subject
4644  * The subject to match against the wildcard pattern.
4645  *
4646  * @param {string} pattern
4647  * The wildcard pattern.
4648  *
4649  * @param {boolean} [nocase]
4650  * Whether to perform case-insensitive matching.
4651  *
4652  * @returns {boolean}
4653  *
4654  * @example
4655  * wildcard("file.txt", "*.txt");        // Returns true
4656  * wildcard("file.txt", "*.TXT", true);  // Returns true (case-insensitive match)
4657  * wildcard("file.txt", "*.jpg");        // Returns false
4658  */
4659 static uc_value_t *
4660 uc_wildcard(uc_vm_t *vm, size_t nargs)
4661 {
4662         uc_value_t *subject = uc_fn_arg(0);
4663         uc_value_t *pattern = uc_fn_arg(1);
4664         uc_value_t *icase = uc_fn_arg(2);
4665         int flags = 0, rv;
4666         bool freeable;
4667         char *s;
4668 
4669         if (!subject || ucv_type(pattern) != UC_STRING)
4670                 return NULL;
4671 
4672         if (ucv_is_truish(icase))
4673                 flags |= FNM_CASEFOLD;
4674 
4675         s = uc_cast_string(vm, &subject, &freeable);
4676         rv = fnmatch(ucv_string_get(pattern), s, flags);
4677 
4678         if (freeable)
4679                 free(s);
4680 
4681         return ucv_boolean_new(rv == 0);
4682 }
4683 
4684 /**
4685  * Determine the path of the source file currently being executed by ucode.
4686  *
4687  * @function module:core#sourcepath
4688  *
4689  * @param {number} [depth=0]
4690  * The depth to walk up the call stack.
4691  *
4692  * @param {boolean} [dironly]
4693  * Whether to return only the directory portion of the source file path.
4694  *
4695  * @returns {?string}
4696  *
4697  * @example
4698  * sourcepath();         // Returns the path of the currently executed file
4699  * sourcepath(1);        // Returns the path of the parent source file
4700  * sourcepath(2, true);  // Returns the directory portion of the grandparent source file path
4701  */
4702 static uc_value_t *
4703 uc_sourcepath(uc_vm_t *vm, size_t nargs)
4704 {
4705         uc_value_t *calldepth = uc_fn_arg(0);
4706         uc_value_t *dironly = uc_fn_arg(1);
4707         uc_value_t *rv = NULL;
4708         uc_callframe_t *frame;
4709         char *path = NULL;
4710         int64_t depth;
4711         size_t i;
4712 
4713         depth = ucv_to_integer(calldepth);
4714 
4715         if (errno)
4716                 depth = 0;
4717 
4718         for (i = vm->callframes.count; i > 0; i--) {
4719                 frame = &vm->callframes.entries[i - 1];
4720 
4721                 if (!frame->closure)
4722                         continue;
4723 
4724                 if (depth > 0) {
4725                         depth--;
4726                         continue;
4727                 }
4728 
4729                 path = realpath(uc_program_function_source(frame->closure->function)->runpath, NULL);
4730                 break;
4731         }
4732 
4733         if (path) {
4734                 if (ucv_is_truish(dironly))
4735                         rv = ucv_string_new(dirname(path));
4736                 else
4737                         rv = ucv_string_new(path);
4738 
4739                 free(path);
4740         }
4741 
4742         return rv;
4743 }
4744 
4745 static uc_value_t *
4746 uc_min_max(uc_vm_t *vm, size_t nargs, int cmp)
4747 {
4748         uc_value_t *rv = NULL, *val;
4749         bool set = false;
4750         size_t i;
4751 
4752         for (i = 0; i < nargs; i++) {
4753                 val = uc_fn_arg(i);
4754 
4755                 if (!set || ucv_compare(cmp, val, rv, NULL)) {
4756                         set = true;
4757                         rv = val;
4758                 }
4759         }
4760 
4761         return ucv_get(rv);
4762 }
4763 
4764 /**
4765  * Return the smallest value among all parameters passed to the function.
4766  *
4767  * @function module:core#min
4768  *
4769  * @param {...*} [val]
4770  * The values to compare.
4771  *
4772  * @returns {*}
4773  *
4774  * @example
4775  * min(5, 2.1, 3, "abc", 0.3);            // Returns 0.3
4776  * min(1, "abc");                         // Returns 1
4777  * min("1", "abc");                       // Returns "1"
4778  * min("def", "abc", "ghi");              // Returns "abc"
4779  * min(true, false);                      // Returns false
4780  */
4781 static uc_value_t *
4782 uc_min(uc_vm_t *vm, size_t nargs)
4783 {
4784         return uc_min_max(vm, nargs, I_LT);
4785 }
4786 
4787 /**
4788  * Return the largest value among all parameters passed to the function.
4789  *
4790  * @function module:core#max
4791  *
4792  * @param {...*} [val]
4793  * The values to compare.
4794  *
4795  * @returns {*}
4796  *
4797  * @example
4798  * max(5, 2.1, 3, "abc", 0.3);            // Returns 5
4799  * max(1, "abc");                         // Returns 1 (!)
4800  * max("1", "abc");                       // Returns "abc"
4801  * max("def", "abc", "ghi");              // Returns "ghi"
4802  * max(true, false);                      // Returns true
4803  */
4804 static uc_value_t *
4805 uc_max(uc_vm_t *vm, size_t nargs)
4806 {
4807         return uc_min_max(vm, nargs, I_GT);
4808 }
4809 
4810 
4811 /* -------------------------------------------------------------------------
4812  * The following base64 encoding and decoding routines are taken from
4813  * https://git.openwrt.org/?p=project/libubox.git;a=blob;f=base64.c
4814  * and modified for use in ucode.
4815  *
4816  * Original copyright and license statements below.
4817  */
4818 
4819 /*
4820  * base64 - libubox base64 functions
4821  *
4822  * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
4823  *
4824  * Permission to use, copy, modify, and/or distribute this software for any
4825  * purpose with or without fee is hereby granted, provided that the above
4826  * copyright notice and this permission notice appear in all copies.
4827  *
4828  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
4829  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
4830  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
4831  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4832  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4833  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
4834  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4835  */
4836 
4837 /*      $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */
4838 
4839 /*
4840  * Copyright (c) 1996 by Internet Software Consortium.
4841  *
4842  * Permission to use, copy, modify, and distribute this software for any
4843  * purpose with or without fee is hereby granted, provided that the above
4844  * copyright notice and this permission notice appear in all copies.
4845  *
4846  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
4847  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
4848  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
4849  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
4850  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
4851  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
4852  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
4853  * SOFTWARE.
4854  */
4855 
4856 /*
4857  * Portions Copyright (c) 1995 by International Business Machines, Inc.
4858  *
4859  * International Business Machines, Inc. (hereinafter called IBM) grants
4860  * permission under its copyrights to use, copy, modify, and distribute this
4861  * Software with or without fee, provided that the above copyright notice and
4862  * all paragraphs of this notice appear in all copies, and that the name of IBM
4863  * not be used in connection with the marketing of any product incorporating
4864  * the Software or modifications thereof, without specific, written prior
4865  * permission.
4866  *
4867  * To the extent it has a right to do so, IBM grants an immunity from suit
4868  * under its patents, if any, for the use, sale or manufacture of products to
4869  * the extent that such products are used for performing Domain Name System
4870  * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
4871  * granted for any product per se or for any other function of any product.
4872  *
4873  * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
4874  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
4875  * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
4876  * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
4877  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
4878  * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
4879  */
4880 
4881 /* skips all whitespace anywhere.
4882    converts characters, four at a time, starting at (or after)
4883    src from base - 64 numbers into three 8 bit bytes in the target area.
4884    it returns the number of data bytes stored at the target, or -1 on error.
4885  */
4886 
4887 /**
4888  * Decodes the given base64 encoded string and returns the decoded result.
4889  *
4890  *  - If non-whitespace, non-base64 characters are encountered, if invalid
4891  *    padding or trailing garbage is found, the function returns `null`.
4892  *  - If a non-string argument is given, the function returns `null`.
4893  *
4894  * @function module:core#b64dec
4895  *
4896  * @param {string} str
4897  * The base64 encoded string to decode.
4898  *
4899  * @returns {?string}
4900  *
4901  * @example
4902  * b64dec("VGhpcyBpcyBhIHRlc3Q=");         // Returns "This is a test"
4903  * b64dec(123);                           // Returns null
4904  * b64dec("XXX");                         // Returns null
4905  */
4906 static uc_value_t *
4907 uc_b64dec(uc_vm_t *vm, size_t nargs)
4908 {
4909         enum { BYTE1, BYTE2, BYTE3, BYTE4 } state;
4910         uc_value_t *str = uc_fn_arg(0);
4911         uc_stringbuf_t *buf;
4912         const char *src;
4913         unsigned int ch;
4914         uint8_t val;
4915         size_t off;
4916 
4917         if (ucv_type(str) != UC_STRING)
4918                 return NULL;
4919 
4920         buf = ucv_stringbuf_new();
4921         src = ucv_string_get(str);
4922         off = printbuf_length(buf);
4923 
4924         state = BYTE1;
4925 
4926         /* memset the last expected output char to pre-grow the output buffer */
4927         printbuf_memset(buf, off + (ucv_string_length(str) / 4) * 3, 0, 1);
4928 
4929         while ((ch = (unsigned char)*src++) != '\0') {
4930                 if (isspace(ch))        /* Skip whitespace anywhere. */
4931                         continue;
4932 
4933                 if (ch == '=')
4934                         break;
4935 
4936                 if (ch >= 'A' && ch <= 'Z')
4937                         val = ch - 'A';
4938                 else if (ch >= 'a' && ch <= 'z')
4939                         val = ch - 'a' + 26;
4940                 else if (ch >= '' && ch <= '9')
4941                         val = ch - '' + 52;
4942                 else if (ch == '+')
4943                         val = 62;
4944                 else if (ch == '/')
4945                         val = 63;
4946                 else
4947                         goto err;
4948 
4949                 switch (state) {
4950                 case BYTE1:
4951                         buf->buf[off] = val << 2;
4952                         state = BYTE2;
4953                         break;
4954 
4955                 case BYTE2:
4956                         buf->buf[off++] |= val >> 4;
4957                         buf->buf[off] = (val & 0x0f) << 4;
4958                         state = BYTE3;
4959                         break;
4960 
4961                 case BYTE3:
4962                         buf->buf[off++] |= val >> 2;
4963                         buf->buf[off] = (val & 0x03) << 6;
4964                         state = BYTE4;
4965                         break;
4966 
4967                 case BYTE4:
4968                         buf->buf[off++] |= val;
4969                         state = BYTE1;
4970                         break;
4971                 }
4972         }
4973 
4974         /*
4975          * We are done decoding Base-64 chars.  Let's see if we ended
4976          * on a byte boundary, and/or with erroneous trailing characters.
4977          */
4978 
4979         if (ch == '=') {                        /* We got a pad char. */
4980                 ch = (unsigned char)*src++;     /* Skip it, get next. */
4981                 switch (state) {
4982                 case BYTE1:             /* Invalid = in first position */
4983                 case BYTE2:             /* Invalid = in second position */
4984                         goto err;
4985 
4986                 case BYTE3:             /* Valid, means one byte of info */
4987                         /* Skip any number of spaces. */
4988                         for (; ch != '\0'; ch = (unsigned char)*src++)
4989                                 if (!isspace(ch))
4990                                         break;
4991                         /* Make sure there is another trailing = sign. */
4992                         if (ch != '=')
4993                                 goto err;
4994                         ch = (unsigned char)*src++;             /* Skip the = */
4995                         /* Fall through to "single trailing =" case. */
4996                         /* FALLTHROUGH */
4997 
4998                 case BYTE4:             /* Valid, means two bytes of info */
4999                         /*
5000                          * We know this char is an =.  Is there anything but
5001                          * whitespace after it?
5002                          */
5003                         for (; ch != '\0'; ch = (unsigned char)*src++)
5004                                 if (!isspace(ch))
5005                                         goto err;
5006 
5007                         /*
5008                          * Now make sure for cases BYTE3 and BYTE4 that the "extra"
5009                          * bits that slopped past the last full byte were
5010                          * zeros.  If we don't check them, they become a
5011                          * subliminal channel.
5012                          */
5013                         if (buf->buf[off] != 0)
5014                                 goto err;
5015                 }
5016         } else {
5017                 /*
5018                  * We ended by seeing the end of the string.  Make sure we
5019                  * have no partial bytes lying around.
5020                  */
5021                 if (state != BYTE1)
5022                         goto err;
5023         }
5024 
5025         /* Truncate buffer length to actual output length */
5026         buf->bpos = off;
5027 
5028         return ucv_stringbuf_finish(buf);
5029 
5030 err:
5031         printbuf_free(buf);
5032 
5033         return NULL;
5034 }
5035 
5036 static const char Base64[] =
5037         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
5038 
5039 /**
5040  * Encodes the given string into base64 and returns the resulting string.
5041  *
5042  *  - If a non-string argument is given, the function returns `null`.
5043  *
5044  * @function module:core#b64enc
5045  *
5046  * @param {string} str
5047  * The string to encode.
5048  *
5049  * @returns {?string}
5050  *
5051  * @example
5052  * b64enc("This is a test");  // Returns "VGhpcyBpcyBhIHRlc3Q="
5053  * b64enc(123);               // Returns null
5054  */
5055 static uc_value_t *
5056 uc_b64enc(uc_vm_t *vm, size_t nargs)
5057 {
5058         uc_value_t *str = uc_fn_arg(0);
5059         unsigned char input[3] = {0};
5060         uc_stringbuf_t *buf;
5061         const char *src;
5062         char output[4];
5063         size_t len, i;
5064 
5065         if (ucv_type(str) != UC_STRING)
5066                 return NULL;
5067 
5068         buf = ucv_stringbuf_new();
5069         src = ucv_string_get(str);
5070         len = ucv_string_length(str);
5071 
5072         while (2 < len) {
5073                 input[0] = (unsigned char)*src++;
5074                 input[1] = (unsigned char)*src++;
5075                 input[2] = (unsigned char)*src++;
5076                 len -= 3;
5077 
5078                 output[0] = Base64[input[0] >> 2];
5079                 output[1] = Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)];
5080                 output[2] = Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)];
5081                 output[3] = Base64[input[2] & 0x3f];
5082 
5083                 ucv_stringbuf_addstr(buf, output, sizeof(output));
5084         }
5085 
5086         /* Now we worry about padding. */
5087         if (0 != len) {
5088                 /* Get what's left. */
5089                 input[0] = input[1] = input[2] = '\0';
5090                 for (i = 0; i < len; i++)
5091                         input[i] = *src++;
5092 
5093                 output[0] = Base64[input[0] >> 2];
5094                 output[1] = Base64[((input[0] & 0x03) << 4) + (input[1] >> 4)];
5095                 output[2] = (len == 1) ? '=' : Base64[((input[1] & 0x0f) << 2) + (input[2] >> 6)];
5096                 output[3] = '=';
5097 
5098                 ucv_stringbuf_addstr(buf, output, sizeof(output));
5099         }
5100 
5101         return ucv_stringbuf_finish(buf);
5102 }
5103 
5104 /* End of base64 code.
5105  * -------------------------------------------------------------------------
5106  */
5107 
5108 static unsigned long
5109 uc_uniq_ucv_hash(const void *k)
5110 {
5111         union { double d; int64_t i; uint64_t u; } conv;
5112         uc_value_t *uv = (uc_value_t *)k;
5113         unsigned int h;
5114         uint8_t *u8;
5115         size_t len;
5116 
5117         h = ucv_type(uv);
5118 
5119         switch (h) {
5120         case UC_STRING:
5121                 u8 = (uint8_t *)ucv_string_get(uv);
5122                 len = ucv_string_length(uv);
5123                 break;
5124 
5125         case UC_INTEGER:
5126                 conv.i = ucv_int64_get(uv);
5127 
5128                 if (errno == ERANGE) {
5129                         h *= 2;
5130                         conv.u = ucv_uint64_get(uv);
5131                 }
5132 
5133                 u8 = (uint8_t *)&conv.u;
5134                 len = sizeof(conv.u);
5135                 break;
5136 
5137         case UC_DOUBLE:
5138                 conv.d = ucv_double_get(uv);
5139 
5140                 u8 = (uint8_t *)&conv.u;
5141                 len = sizeof(conv.u);
5142                 break;
5143 
5144         default:
5145                 u8 = (uint8_t *)&uv;
5146                 len = sizeof(uv);
5147                 break;
5148         }
5149 
5150         while (len > 0) {
5151                 h = h * 129 + (*u8++) + LH_PRIME;
5152                 len--;
5153         }
5154 
5155         return h;
5156 }
5157 
5158 static int
5159 uc_uniq_ucv_equal(const void *k1, const void *k2)
5160 {
5161         uc_value_t *uv1 = (uc_value_t *)k1;
5162         uc_value_t *uv2 = (uc_value_t *)k2;
5163 
5164         if (!ucv_is_scalar(uv1) && !ucv_is_scalar(uv2))
5165                 return (uv1 == uv2);
5166 
5167         /* for the sake of array item uniqueness, treat two NaNs as equal */
5168         if (ucv_type(uv1) == UC_DOUBLE && ucv_type(uv2) == UC_DOUBLE &&
5169             isnan(ucv_double_get(uv1)) && isnan(ucv_double_get(uv2)))
5170             return true;
5171 
5172         return ucv_is_equal(uv1, uv2);
5173 }
5174 
5175 /**
5176  * Returns a new array containing all unique values of the given input array.
5177  *
5178  *  - The order is preserved, and subsequent duplicate values are skipped.
5179  *  - If a non-array argument is given, the function returns `null`.
5180  *
5181  * @function module:core#uniq
5182  *
5183  * @param {Array} array
5184  * The input array.
5185  *
5186  * @returns {?Array}
5187  *
5188  * @example
5189  * uniq([1, true, "foo", 2, true, "bar", "foo"]);       // Returns [1, true, "foo", 2, "bar"]
5190  * uniq("test");                                        // Returns null
5191  */
5192 static uc_value_t *
5193 uc_uniq(uc_vm_t *vm, size_t nargs)
5194 {
5195         uc_value_t *list = uc_fn_arg(0);
5196         uc_value_t *uniq = NULL;
5197         struct lh_table *seen;
5198         unsigned long hash;
5199         uc_value_t *item;
5200         size_t i, len;
5201 
5202         if (ucv_type(list) != UC_ARRAY)
5203                 return NULL;
5204 
5205         seen = lh_table_new(16, NULL, uc_uniq_ucv_hash, uc_uniq_ucv_equal);
5206         uniq = ucv_array_new(vm);
5207 
5208         assert(seen && uniq);
5209 
5210         for (i = 0, len = ucv_array_length(list); i < len; i++) {
5211                 item = ucv_array_get(list, i);
5212                 hash = lh_get_hash(seen, item);
5213 
5214                 if (!lh_table_lookup_entry_w_hash(seen, item, hash)) {
5215                         lh_table_insert_w_hash(seen, item, NULL, hash, 0);
5216                         ucv_array_push(uniq, ucv_get(item));
5217                 }
5218         }
5219 
5220         lh_table_free(seen);
5221 
5222         return uniq;
5223 }
5224 
5225 /**
5226  * A time spec is a plain object describing a point in time, it is returned by
5227  * the {@link module:core#gmtime|gmtime()} and
5228  * {@link module:core#localtime|localtime()} functions and expected as parameter
5229  * by the complementary {@link module:core#timegm|timegm()} and
5230  * {@link module:core#timelocal|timelocal()} functions.
5231  *
5232  * When returned by `gmtime()` or `localtime()`, all members of the object will
5233  * be initialized, when passed as argument to `timegm()` or `timelocal()`, most
5234  * member values are optional.
5235  *
5236  * @typedef {Object} module:core.TimeSpec
5237  * @property {number} sec - Seconds (0..60)
5238  * @property {number} min - Minutes (0..59)
5239  * @property {number} hour - Hours (0..23)
5240  * @property {number} mday - Day of month (1..31)
5241  * @property {number} mon - Month (1..12)
5242  * @property {number} year - Year (>= 1900)
5243  * @property {number} wday - Day of week (1..7, Sunday = 7)
5244  * @property {number} yday - Day of year (1-366, Jan 1st = 1)
5245  * @property {number} isdst - Daylight saving time in effect (yes = 1)
5246  */
5247 static uc_value_t *
5248 uc_gettime_common(uc_vm_t *vm, size_t nargs, bool local)
5249 {
5250         uc_value_t *ts = uc_fn_arg(0), *res;
5251         time_t t = ts ? (time_t)ucv_to_integer(ts) : time(NULL);
5252         struct tm *tm = (local ? localtime : gmtime)(&t);
5253 
5254         if (!tm)
5255                 return NULL;
5256 
5257         res = ucv_object_new(vm);
5258 
5259         ucv_object_add(res, "sec", ucv_int64_new(tm->tm_sec));
5260         ucv_object_add(res, "min", ucv_int64_new(tm->tm_min));
5261         ucv_object_add(res, "hour", ucv_int64_new(tm->tm_hour));
5262         ucv_object_add(res, "mday", ucv_int64_new(tm->tm_mday));
5263         ucv_object_add(res, "mon", ucv_int64_new(tm->tm_mon + 1));
5264         ucv_object_add(res, "year", ucv_int64_new(tm->tm_year + 1900));
5265         ucv_object_add(res, "wday", ucv_int64_new(tm->tm_wday ? tm->tm_wday : 7));
5266         ucv_object_add(res, "yday", ucv_int64_new(tm->tm_yday + 1));
5267         ucv_object_add(res, "isdst", ucv_int64_new(tm->tm_isdst));
5268 
5269         return res;
5270 }
5271 
5272 /**
5273  * Return the given epoch timestamp (or now, if omitted) as a dictionary
5274  * containing broken-down date and time information according to the local
5275  * system timezone.
5276  *
5277  * See {@link module:core.TimeSpec|TimeSpec} for a description of the fields.
5278  *
5279  * Note that in contrast to the underlying `localtime(3)` C library function,
5280  * the values for `mon`, `wday`, and `yday` are 1-based, and the `year` is
5281  * 1900-based.
5282  *
5283  * @function module:core#localtime
5284  *
5285  * @param {number} [epoch]
5286  * The epoch timestamp.
5287  *
5288  * @returns {module:core.TimeSpec}
5289  *
5290  * @example
5291  * localtime(1647953502);
5292  * // Returns:
5293  * // {
5294  * //     sec: 42,
5295  * //     min: 51,
5296  * //     hour: 13,
5297  * //     mday: 22,
5298  * //     mon: 3,
5299  * //     year: 2022,
5300  * //     wday: 2,
5301  * //     yday: 81,
5302  * //     isdst: 0
5303  * // }
5304  */
5305 static uc_value_t *
5306 uc_localtime(uc_vm_t *vm, size_t nargs)
5307 {
5308         return uc_gettime_common(vm, nargs, true);
5309 }
5310 
5311 /**
5312  * Like `localtime()` but interpreting the given epoch value as UTC time.
5313  *
5314  * See {@link module:core#localtime|localtime()} for details on the return value.
5315  *
5316  * @function module:core#gmtime
5317  *
5318  * @param {number} [epoch]
5319  * The epoch timestamp.
5320  *
5321  * @returns {module:core.TimeSpec}
5322  *
5323  * @example
5324  * gmtime(1647953502);
5325  * // Returns:
5326  * // {
5327  * //     sec: 42,
5328  * //     min: 51,
5329  * //     hour: 13,
5330  * //     mday: 22,
5331  * //     mon: 3,
5332  * //     year: 2022,
5333  * //     wday: 2,
5334  * //     yday: 81,
5335  * //     isdst: 0
5336  * // }
5337  */
5338 static uc_value_t *
5339 uc_gmtime(uc_vm_t *vm, size_t nargs)
5340 {
5341         return uc_gettime_common(vm, nargs, false);
5342 }
5343 
5344 static uc_value_t *
5345 uc_mktime_common(uc_vm_t *vm, size_t nargs, bool local)
5346 {
5347 #define FIELD(name, required) \
5348         { #name, required, offsetof(struct tm, tm_##name) }
5349 
5350         const struct {
5351                 const char *name;
5352                 bool required;
5353                 size_t off;
5354         } fields[] = {
5355                 FIELD(sec, false),
5356                 FIELD(min, false),
5357                 FIELD(hour, false),
5358                 FIELD(mday, true),
5359                 FIELD(mon, true),
5360                 FIELD(year, true),
5361                 FIELD(isdst, false)
5362         };
5363 
5364         uc_value_t *to = uc_fn_arg(0), *v;
5365         struct tm tm = { 0 };
5366         bool exists;
5367         time_t t;
5368         size_t i;
5369 
5370         if (ucv_type(to) != UC_OBJECT)
5371                 return NULL;
5372 
5373         for (i = 0; i < ARRAY_SIZE(fields); i++) {
5374                 v = ucv_object_get(to, fields[i].name, &exists);
5375 
5376                 if (!exists && fields[i].required)
5377                         return NULL;
5378 
5379                 *(int *)((char *)&tm + fields[i].off) = (int)ucv_to_integer(v);
5380         }
5381 
5382         if (tm.tm_mon > 0)
5383                 tm.tm_mon--;
5384 
5385         if (tm.tm_year >= 1900)
5386                 tm.tm_year -= 1900;
5387 
5388         t = (local ? mktime : timegm)(&tm);
5389 
5390         return (t != (time_t)-1) ? ucv_int64_new((int64_t)t) : NULL;
5391 }
5392 
5393 /**
5394  * Performs the inverse operation of {@link module:core#localtime|localtime()}
5395  * by taking a broken-down date and time dictionary and transforming it into an
5396  * epoch value according to the local system timezone.
5397  *
5398  * The `wday` and `yday` fields of the given date time specification are
5399  * ignored. Field values outside of their valid range are internally normalized,
5400  * e.g. October 40th is interpreted as November 9th.
5401  *
5402  * Returns the resulting epoch value or null if the input date time dictionary
5403  * was invalid or if the date time specification cannot be represented as epoch
5404  * value.
5405  *
5406  * @function module:core#timelocal
5407  *
5408  * @param {module:core.TimeSpec} datetimespec
5409  * The broken-down date and time dictionary.
5410  *
5411  * @returns {?number}
5412  *
5413  * @example
5414  * timelocal({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 });
5415  * // Returns 1647953502
5416  */
5417 static uc_value_t *
5418 uc_timelocal(uc_vm_t *vm, size_t nargs)
5419 {
5420         return uc_mktime_common(vm, nargs, true);
5421 }
5422 
5423 /**
5424  * Like `timelocal()` but interpreting the given date time specification as UTC
5425  * time.
5426  *
5427  * See {@link module:core#timelocal|timelocal()} for details.
5428  *
5429  * @function module:core#timegm
5430  *
5431  * @param {module:core.TimeSpec} datetimespec
5432  * The broken-down date and time dictionary.
5433  *
5434  * @returns {?number}
5435  *
5436  * @example
5437  * timegm({ "sec": 42, "min": 51, "hour": 13, "mday": 22, "mon": 3, "year": 2022, "isdst": 0 });
5438  * // Returns 1647953502
5439  */
5440 static uc_value_t *
5441 uc_timegm(uc_vm_t *vm, size_t nargs)
5442 {
5443         return uc_mktime_common(vm, nargs, false);
5444 }
5445 
5446 /**
5447  * Reads the current second and microsecond value of the system clock.
5448  *
5449  * By default, the realtime clock is queried which might skew forwards or
5450  * backwards due to NTP changes, system sleep modes etc. If a truthy value is
5451  * passed as argument, the monotonic system clock is queried instead, which will
5452  * return the monotonically increasing time since some arbitrary point in the
5453  * past (usually the system boot time).
5454  *
5455  * Returns a two element array containing the full seconds as the first element
5456  * and the nanosecond fraction as the second element.
5457  *
5458  * Returns `null` if a monotonic clock value is requested and the system does
5459  * not implement this clock type.
5460  *
5461  * @function module:core#clock
5462  *
5463  * @param {boolean} [monotonic]
5464  * Whether to query the monotonic system clock.
5465  *
5466  * @returns {?number[]}
5467  *
5468  * @example
5469  * clock();        // [ 1647954926, 798269464 ]
5470  * clock(true);    // [ 474751, 527959975 ]
5471  */
5472 static uc_value_t *
5473 uc_clock(uc_vm_t *vm, size_t nargs)
5474 {
5475         clockid_t id = ucv_is_truish(uc_fn_arg(0)) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
5476         struct timespec ts;
5477         uc_value_t *res;
5478 
5479         if (clock_gettime(id, &ts) == -1)
5480                 return NULL;
5481 
5482         res = ucv_array_new(vm);
5483 
5484         ucv_array_set(res, 0, ucv_int64_new((int64_t)ts.tv_sec));
5485         ucv_array_set(res, 1, ucv_int64_new((int64_t)ts.tv_nsec));
5486 
5487         return res;
5488 }
5489 
5490 /**
5491  * Encodes the given byte string into a hexadecimal digit string, converting
5492  * the input value to a string if needed.
5493  *
5494  * @function module:core#hexenc
5495  *
5496  * @param {string} val
5497  * The byte string to encode.
5498  *
5499  * @returns {string}
5500  *
5501  * @example
5502  * hexenc("Hello world!\n");   // "48656c6c6f20776f726c64210a"
5503  */
5504 static uc_value_t *
5505 uc_hexenc(uc_vm_t *vm, size_t nargs)
5506 {
5507         const char *hex = "0123456789abcdef";
5508         uc_value_t *input = uc_fn_arg(0);
5509         uc_stringbuf_t *buf;
5510         size_t off, len;
5511         uint8_t byte;
5512 
5513         if (!input)
5514                 return NULL;
5515 
5516         buf = ucv_stringbuf_new();
5517         off = printbuf_length(buf);
5518 
5519         ucv_to_stringbuf(vm, buf, input, false);
5520 
5521         len = printbuf_length(buf) - off;
5522 
5523         /* memset the last expected output char to grow the output buffer */
5524         printbuf_memset(buf, off + len * 2, 0, 1);
5525 
5526         /* translate string into hex back to front to reuse the same buffer */
5527         while (len > 0) {
5528                 byte = buf->buf[--len + off];
5529                 buf->buf[off + len * 2 + 0] = hex[byte / 16];
5530                 buf->buf[off + len * 2 + 1] = hex[byte % 16];
5531         }
5532 
5533         /* do not include sentinel `\0` in string length */
5534         buf->bpos--;
5535 
5536         return ucv_stringbuf_finish(buf);
5537 }
5538 
5539 static inline uint8_t
5540 hexval(unsigned char c, bool lo)
5541 {
5542         return ((c > '9') ? (c - 'a') + 10 : c - '') << (lo ? 0 : 4);
5543 }
5544 
5545 /**
5546  * Decodes the given hexadecimal digit string into a byte string, optionally
5547  * skipping specified characters.
5548  *
5549  * If the characters to skip are not specified, a default of `" \t\n"` is used.
5550  *
5551  * Returns null if the input string contains invalid characters or an uneven
5552  * amount of hex digits.
5553  *
5554  * Returns the decoded byte string on success.
5555  *
5556  * @function module:core#hexdec
5557  *
5558  * @param {string} hexstring
5559  * The hexadecimal digit string to decode.
5560  *
5561  * @param {string} [skipchars]
5562  * The characters to skip during decoding.
5563  *
5564  * @returns {?string}
5565  *
5566  * @example
5567  * hexdec("48656c6c6f20776f726c64210a");  // "Hello world!\n"
5568  * hexdec("44:55:66:77:33:44", ":");      // "DUfw3D"
5569  */
5570 static uc_value_t *
5571 uc_hexdec(uc_vm_t *vm, size_t nargs)
5572 {
5573         uc_value_t *input = uc_fn_arg(0);
5574         uc_value_t *skip = uc_fn_arg(1);
5575         size_t len, off, n, i;
5576         uc_stringbuf_t *buf;
5577         unsigned char *p;
5578         const char *s;
5579 
5580         if (ucv_type(input) != UC_STRING)
5581                 return NULL;
5582 
5583         if (skip && ucv_type(skip) != UC_STRING)
5584                 return NULL;
5585 
5586         p = (unsigned char *)ucv_string_get(input);
5587         len = ucv_string_length(input);
5588 
5589         s = skip ? (const char *)ucv_string_get(skip) : " \t\n";
5590 
5591         for (i = 0, n = 0; i < len; i++) {
5592                 if (isxdigit(p[i]))
5593                         n++;
5594                 else if (!s || !strchr(s, p[i]))
5595                         return NULL;
5596         }
5597 
5598         if (n & 1)
5599                 return NULL;
5600 
5601         buf = ucv_stringbuf_new();
5602         off = printbuf_length(buf);
5603 
5604         /* preallocate the output buffer */
5605         printbuf_memset(buf, off, 0, n / 2 + 1);
5606 
5607         for (i = 0, n = 0; i < len; i++) {
5608                 if (!isxdigit(p[i]))
5609                         continue;
5610 
5611                 buf->buf[off + (n >> 1)] |= hexval(p[i] | 32, n & 1);
5612                 n++;
5613         }
5614 
5615         /* do not include sentinel `\0` in string length */
5616         buf->bpos--;
5617 
5618         return ucv_stringbuf_finish(buf);
5619 }
5620 
5621 /**
5622  * Interacts with the mark and sweep garbage collector of the running ucode
5623  * virtual machine.
5624  *
5625  * Depending on the given `operation` string argument, the meaning of `argument`
5626  * and the function return value differs.
5627  *
5628  * The following operations are defined:
5629  *
5630  * - `collect` - Perform a complete garbage collection cycle, returns `true`.
5631  * - `start` - (Re-)start periodic garbage collection, `argument` is an optional
5632  *             integer in the range `1..65535` specifying the interval.
5633  *             Defaults to `1000` if omitted. Returns `true` if the periodic GC
5634  *             was previously stopped and is now started or if the interval
5635  *             changed. Returns `false` otherwise.
5636  * - `stop` - Stop periodic garbage collection. Returns `true` if the periodic
5637  *            GC was previously started and is now stopped, `false` otherwise.
5638  * - `count` - Count the amount of active complex object references in the VM
5639  *             context, returns the counted amount.
5640  *
5641  * If the `operation` argument is omitted, the default is `collect`.
5642  *
5643  * @function module:core#gc
5644  *
5645  * @param {string} [operation]
5646  * The operation to perform.
5647  *
5648  * @param {*} [argument]
5649  * The argument for the operation.
5650  *
5651  * @returns {?(boolean|number)}
5652  *
5653  * @example
5654  * gc();         // true
5655  * gc("start");  // true
5656  * gc("count");  // 42
5657  */
5658 static uc_value_t *
5659 uc_gc(uc_vm_t *vm, size_t nargs)
5660 {
5661         uc_value_t *operation = uc_fn_arg(0);
5662         uc_value_t *argument = uc_fn_arg(1);
5663         const char *op = NULL;
5664         uc_weakref_t *ref;
5665         int64_t n;
5666 
5667         if (operation != NULL && ucv_type(operation) != UC_STRING)
5668                 return NULL;
5669 
5670         op = ucv_string_get(operation);
5671 
5672         if (!op || !strcmp(op, "collect")) {
5673                 ucv_gc(vm);
5674 
5675                 return ucv_boolean_new(true);
5676         }
5677         else if (!strcmp(op, "start")) {
5678                 n = argument ? ucv_int64_get(argument) : 0;
5679 
5680                 if (errno || n < 0 || n > 0xFFFF)
5681                         return NULL;
5682 
5683                 if (n == 0)
5684                         n = UC_GC_DEFAULT_INTERVAL;
5685 
5686                 return ucv_boolean_new(uc_vm_gc_start(vm, n));
5687         }
5688         else if (!strcmp(op, "stop")) {
5689                 return ucv_boolean_new(uc_vm_gc_stop(vm));
5690         }
5691         else if (!strcmp(op, "count")) {
5692                 for (n = 0, ref = vm->values.next; ref != &vm->values; ref = ref->next)
5693                         n++;
5694 
5695                 return ucv_uint64_new(n);
5696         }
5697 
5698         return NULL;
5699 }
5700 
5701 /**
5702  * A parse configuration is a plain object describing options to use when
5703  * compiling ucode at runtime. It is expected as parameter by the
5704  * {@link module:core#loadfile|loadfile()} and
5705  * {@link module:core#loadstring|loadstring()} functions.
5706  *
5707  * All members of the parse configuration object are optional and will default
5708  * to the state of the running ucode file if omitted.
5709  *
5710  * @typedef {Object} module:core.ParseConfig
5711  *
5712  * @property {boolean} lstrip_blocks
5713  * Whether to strip whitespace preceding template directives.
5714  * See {@link tutorial-02-syntax.html#whitespace-handling|Whitespace handling}.
5715  *
5716  * @property {boolean} trim_blocks
5717  * Whether to trim trailing newlines following template directives.
5718  * See {@link tutorial-02-syntax.html#whitespace-handling|Whitespace handling}.
5719  *
5720  * @property {boolean} strict_declarations
5721  * Whether to compile the code in strict mode (`true`) or not (`false`).
5722  *
5723  * @property {boolean} raw_mode
5724  * Whether to compile the code in plain script mode (`true`) or not (`false`).
5725  *
5726  * @property {string[]} module_search_path
5727  * Override the module search path for compile time imports while compiling the
5728  * ucode source.
5729  *
5730  * @property {string[]} force_dynlink_list
5731  * List of module names assumed to be dynamic library extensions, allows
5732  * compiling ucode source with import statements referring to `*.so` extensions
5733  * not present at compile time.
5734  */
5735 static void
5736 uc_compile_parse_config(uc_parse_config_t *config, uc_value_t *spec)
5737 {
5738         uc_value_t *v, *p;
5739         size_t i, j;
5740         bool found;
5741 
5742         struct {
5743                 const char *key;
5744                 bool *flag;
5745                 uc_search_path_t *path;
5746         } fields[] = {
5747                 { "lstrip_blocks",       &config->lstrip_blocks,       NULL },
5748                 { "trim_blocks",         &config->trim_blocks,         NULL },
5749                 { "strict_declarations", &config->strict_declarations, NULL },
5750                 { "raw_mode",            &config->raw_mode,            NULL },
5751                 { "module_search_path",  NULL, &config->module_search_path  },
5752                 { "force_dynlink_list",  NULL, &config->force_dynlink_list  }
5753         };
5754 
5755         for (i = 0; i < ARRAY_SIZE(fields); i++) {
5756                 v = ucv_object_get(spec, fields[i].key, &found);
5757 
5758                 if (!found)
5759                         continue;
5760 
5761                 if (fields[i].flag) {
5762                         *fields[i].flag = ucv_is_truish(v);
5763                 }
5764                 else if (fields[i].path) {
5765                         fields[i].path->count = 0;
5766                         fields[i].path->entries = NULL;
5767 
5768                         for (j = 0; j < ucv_array_length(v); j++) {
5769                                 p = ucv_array_get(v, j);
5770 
5771                                 if (ucv_type(p) != UC_STRING)
5772                                         continue;
5773 
5774                                 uc_vector_push(fields[i].path, ucv_string_get(p));
5775                         }
5776                 }
5777         }
5778 }
5779 
5780 static uc_value_t *
5781 uc_load_common(uc_vm_t *vm, size_t nargs, uc_source_t *source)
5782 {
5783         uc_parse_config_t conf = *vm->config;
5784         uc_program_t *program;
5785         uc_value_t *closure;
5786         char *err = NULL;
5787 
5788         uc_compile_parse_config(&conf, uc_fn_arg(1));
5789 
5790         program = uc_compile(&conf, source, &err);
5791         closure = program ? uc_program_main(vm, program) : NULL;
5792 
5793         uc_program_put(program);
5794 
5795         if (!vm->config || conf.module_search_path.entries != vm->config->module_search_path.entries)
5796                 uc_vector_clear(&conf.module_search_path);
5797 
5798         if (!vm->config || conf.force_dynlink_list.entries != vm->config->force_dynlink_list.entries)
5799                 uc_vector_clear(&conf.force_dynlink_list);
5800 
5801         if (!closure) {
5802                 uc_error_message_indent(&err);
5803 
5804                 if (source->buffer)
5805                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5806                                 "Unable to compile source string:\n\n%s", err);
5807                 else
5808                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5809                                 "Unable to compile source file '%s':\n\n%s", source->filename, err);
5810         }
5811 
5812         uc_source_put(source);
5813         free(err);
5814 
5815         return closure;
5816 }
5817 
5818 /**
5819  * Compiles the given code string into a ucode program and returns the resulting
5820  * program entry function.
5821  *
5822  * The optional `options` dictionary overrides parse and compile options.
5823  *
5824  *  - If a non-string `code` argument is given, it is implicitly converted to a
5825  *    string value first.
5826  *  - If `options` is omitted or a non-object value, the compile options of the
5827  *    running ucode program are reused.
5828  *
5829  * See {@link module:core.ParseConfig|ParseConfig} for known keys within the
5830  * `options` object. Unrecognized keys are ignored, unspecified options default
5831  * to those of the running program.
5832  *
5833  * Returns the compiled program entry function.
5834  *
5835  * Throws an exception on compilation errors.
5836  *
5837  * @function module:core#loadstring
5838  *
5839  * @param {string} code
5840  * The code string to compile.
5841  *
5842  * @param {module:core.ParseConfig} [options]
5843  * The options for compilation.
5844  *
5845  * @returns {Function}
5846  *
5847  * @example
5848  * let fn1 = loadstring("Hello, {{ name }}", { raw_mode: false });
5849  *
5850  * global.name = "Alice";
5851  * fn1(); // prints `Hello, Alice`
5852  *
5853  *
5854  * let fn2 = loadstring("return 1 + 2;", { raw_mode: true });
5855  * fn2(); // 3
5856  */
5857 static uc_value_t *
5858 uc_loadstring(uc_vm_t *vm, size_t nargs)
5859 {
5860         uc_value_t *code = uc_fn_arg(0);
5861         uc_source_t *source;
5862         size_t len;
5863         char *s;
5864 
5865         if (ucv_type(code) == UC_STRING) {
5866                 len = ucv_string_length(code);
5867                 s = xalloc(len);
5868                 memcpy(s, ucv_string_get(code), len);
5869         }
5870         else {
5871                 s = ucv_to_string(vm, code);
5872                 len = strlen(s);
5873         }
5874 
5875         source = uc_source_new_buffer("[loadstring argument]", s, len);
5876 
5877         if (!source) {
5878                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5879                         "Unable to allocate source buffer: %s",
5880                         strerror(errno));
5881 
5882                 return NULL;
5883         }
5884 
5885         return uc_load_common(vm, nargs, source);
5886 }
5887 
5888 /**
5889  * Compiles the given file into a ucode program and returns the resulting
5890  * program entry function.
5891  *
5892  * See {@link module:core#loadstring|`loadstring()`} for details.
5893  *
5894  * Returns the compiled program entry function.
5895  *
5896  * Throws an exception on compilation or file I/O errors.
5897  *
5898  * @function module:core#loadfile
5899  *
5900  * @param {string} path
5901  * The path of the file to compile.
5902  *
5903  * @param {module:core.ParseConfig} [options]
5904  * The options for compilation.
5905  *
5906  * @returns {Function}
5907  *
5908  * @example
5909  * loadfile("./templates/example.uc");  // function main() { ... }
5910  */
5911 static uc_value_t *
5912 uc_loadfile(uc_vm_t *vm, size_t nargs)
5913 {
5914         uc_value_t *path = uc_fn_arg(0);
5915         uc_source_t *source = NULL;
5916         char *resolved;
5917 
5918         if (ucv_type(path) != UC_STRING)
5919                 return NULL;
5920 
5921         resolved = realpath(ucv_string_get(path), NULL);
5922 
5923         if (resolved && lh_table_lookup_ex(vm->sources, (const void *)resolved, (void **)&source)) {
5924                 source = uc_source_get(source);
5925         }
5926 
5927         if (!source) {
5928                 source = uc_source_new_file(ucv_string_get(path));
5929 
5930                 if (!source) {
5931                         free(resolved);
5932                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
5933                                 "Unable to open source file %s: %s",
5934                                 ucv_string_get(path), strerror(errno));
5935 
5936                         return NULL;
5937                 }
5938 
5939                 if (resolved)
5940                         lh_table_insert(vm->sources, xstrdup(resolved), uc_source_get(source));
5941         }
5942 
5943         free(resolved);
5944 
5945         return uc_load_common(vm, nargs, source);
5946 }
5947 
5948 /**
5949  * Calls the given function value with a modified environment.
5950  *
5951  * The given `ctx` argument is used as `this` context for the invoked function
5952  * and the given `scope` value as global environment. Any further arguments are
5953  * passed to the invoked function as-is.
5954  *
5955  * When `ctx` is omitted or `null`, the function will get invoked with `this`
5956  * being `null`.
5957  *
5958  * When `scope` is omitted or `null`, the function will get executed with the
5959  * current global environment of the running program. When `scope` is set to a
5960  * dictionary, the dictionary is used as global function environment.
5961  *
5962  * When the `scope` dictionary has no prototype, the current global environment
5963  * will be set as prototype, means the scope will inherit from it.
5964  *
5965  * When a scope prototype is set, it is kept. This allows passing an isolated
5966  * (sandboxed) function scope without access to the global environment.
5967  *
5968  * Any further argument is forwarded as-is to the invoked function as function
5969  * call argument.
5970  *
5971  * Returns `null` if the given function value `fn` is not callable.
5972  *
5973  * Returns the return value of the invoked function in all other cases.
5974  *
5975  * Forwards exceptions thrown by the invoked function.
5976  *
5977  * @function module:core#call
5978  *
5979  * @param {Function} fn
5980  * Function value to call.
5981  *
5982  * @param {*} [ctx=null]
5983  * `this` context for the invoked function.
5984  *
5985  * @param {Object} [scope=null]
5986  * Global environment for the invoked function.
5987  *
5988  * @param {...*} [arg]
5989  * Additional arguments to pass to the invoked function.
5990  *
5991  * @returns {*}
5992  *
5993  * @example
5994  * // Override this context
5995  * call(function() { printf("%J\n", this) });            // null
5996  * call(function() { printf("%J\n", this) }, null);      // null
5997  * call(function() { printf("%J\n", this) }, { x: 1 });  // { "x": 1 }
5998  * call(function() { printf("%J\n", this) }, { x: 2 });  // { "x": 2 }
5999  *
6000  * // Run with default scope
6001  * global.a = 1;
6002  * call(function() { printf("%J\n", a) });                  // 1
6003  *
6004  * // Override scope, inherit from current global scope (implicit)
6005  * call(function() { printf("%J\n", a) }, null, { a: 2 });  // 2
6006  *
6007  * // Override scope, inherit from current global scope (explicit)
6008  * call(function() { printf("%J\n", a) }, null,
6009  *         proto({ a: 2 }, global));                        // 2
6010  *
6011  * // Override scope, don't inherit (pass `printf()` but not `a`)
6012  * call(function() { printf("%J\n", a) }, null,
6013  *         proto({}, { printf }));                          // null
6014  *
6015  * // Forward arguments
6016  * x = call((x, y, z) => x * y * z, null, null, 2, 3, 4);   // x = 24
6017  */
6018 static uc_value_t *
6019 uc_callfunc(uc_vm_t *vm, size_t nargs)
6020 {
6021         size_t argoff = vm->stack.count - nargs, i;
6022         uc_value_t *fn_scope, *prev_scope = NULL, *res;
6023         uc_value_t *fn = uc_fn_arg(0);
6024         uc_value_t *this = uc_fn_arg(1);
6025         uc_value_t *scope = uc_fn_arg(2);
6026 
6027         if (!ucv_is_callable(fn))
6028                 return NULL;
6029 
6030         if (scope && ucv_type(scope) != UC_OBJECT)
6031                 return NULL;
6032 
6033         if (ucv_prototype_get(scope)) {
6034                 fn_scope = ucv_get(scope);
6035         }
6036         else if (scope) {
6037                 fn_scope = ucv_object_new(vm);
6038 
6039                 ucv_object_foreach(scope, k, v)
6040                         ucv_object_add(fn_scope, k, ucv_get(v));
6041 
6042                 ucv_prototype_set(fn_scope, ucv_get(uc_vm_scope_get(vm)));
6043         }
6044         else {
6045                 fn_scope = NULL;
6046         }
6047 
6048         if (fn_scope) {
6049                 prev_scope = ucv_get(uc_vm_scope_get(vm));
6050                 uc_vm_stack_push(vm, ucv_get(prev_scope));
6051                 uc_vm_scope_set(vm, fn_scope);
6052         }
6053 
6054         uc_vm_stack_push(vm, ucv_get(this));
6055         uc_vm_stack_push(vm, ucv_get(fn));
6056 
6057         for (i = 3; i < nargs; i++)
6058                 uc_vm_stack_push(vm, ucv_get(vm->stack.entries[3 + argoff++]));
6059 
6060         if (uc_vm_call(vm, true, i - 3) == EXCEPTION_NONE)
6061                 res = uc_vm_stack_pop(vm);
6062         else
6063                 res = NULL;
6064 
6065         if (fn_scope) {
6066                 uc_vm_scope_set(vm, prev_scope);
6067                 ucv_put(uc_vm_stack_pop(vm));
6068         }
6069 
6070         return res;
6071 }
6072 
6073 /**
6074  * Set or query process signal handler function.
6075  *
6076  * When invoked with two arguments, a signal specification and a signal handler
6077  * value, this function configures a new process signal handler.
6078  *
6079  * When invoked with one argument, a signal specification, this function returns
6080  * the currently configured handler for the given signal.
6081  *
6082  * The signal specification might either be an integer signal number or a string
6083  * value containing a signal name (with or without "SIG" prefix). Signal names
6084  * are treated case-insensitively.
6085  *
6086  * The signal handler might be either a callable function value or one of the
6087  * two special string values `"ignore"` and `"default"`. Passing `"ignore"` will
6088  * mask the given process signal while `"default"` will restore the operating
6089  * systems default behaviour for the given signal.
6090  *
6091  * In case a callable handler function is provided, it is invoked at the
6092  * earliest  opportunity after receiving the corresponding signal from the
6093  * operating system. The invoked function will receive a single argument, the
6094  * number of the signal it is invoked for.
6095  *
6096  * Note that within the ucode VM, process signals are not immediately delivered,
6097  * instead the VM keeps track of received signals and delivers them to the ucode
6098  * script environment at the next opportunity, usually before executing the next
6099  * byte code instruction. This means that if a signal is received while
6100  * performing a computationally expensive operation in C mode, such as a complex
6101  * regexp match, the corresponding ucode signal handler will only be invoked
6102  * after that operation concluded and control flow returns to the VM.
6103  *
6104  * Returns the signal handler function or one of the special values `"ignore"`
6105  * or `"default"` corresponding to the given signal specification.
6106  *
6107  * Returns `null` if an invalid signal spec or signal handler was provided.
6108  *
6109  * Returns `null` if changing the signal action failed, e.g. due to insufficient
6110  * permission, or when attempting to ignore a non-ignorable signal.
6111  *
6112  * @function module:core#signal
6113  *
6114  * @param {number|string} signal
6115  * The signal to query/set handler for.
6116  *
6117  * @param {Function|string} [handler]
6118  * The signal handler to install for the given signal.
6119  *
6120  * @returns {Function|string}
6121  *
6122  * @example
6123  * // Ignore signals
6124  * signal('INT', 'ignore');      // "ignore"
6125  * signal('SIGINT', 'ignore');   // "ignore" (equivalent to 'INT')
6126  * signal('sigterm', 'ignore');  // "ignore" (signal names are case insensitive)
6127  * signal(9, 'ignore');          // null (SIGKILL cannot be ignored)
6128  *
6129  * // Restore signal default behavior
6130  * signal('INT', 'default');     // "default"
6131  * signal('foobar', 'default');  // null (unknown signal name)
6132  * signal(-313, 'default');      // null (invalid signal number)
6133  *
6134  * // Set custom handler function
6135  * function intexit(signo) {
6136  *   printf("I received signal number %d\n", signo);
6137  *   exit(1);
6138  * }
6139  *
6140  * signal('SIGINT', intexit);    // returns intexit
6141  * signal('SIGINT') == intexit;  // true
6142  */
6143 static uc_value_t *
6144 uc_signal(uc_vm_t *vm, size_t nargs)
6145 {
6146         uc_value_t *signame = uc_fn_arg(0);
6147         uc_value_t *sighandler = uc_fn_arg(1);
6148         struct sigaction sa = { 0 };
6149         char *sigstr;
6150         int sig;
6151 
6152         if (ucv_type(signame) == UC_INTEGER) {
6153                 sig = (int)ucv_int64_get(signame);
6154 
6155                 if (errno || sig < 0 || sig >= UC_SYSTEM_SIGNAL_COUNT)
6156                         return NULL;
6157 
6158                 if (!uc_system_signal_names[sig])
6159                         return NULL;
6160         }
6161         else if (ucv_type(signame) == UC_STRING) {
6162                 sigstr = ucv_string_get(signame);
6163 
6164                 if (!strncasecmp(sigstr, "SIG", 3))
6165                         sigstr += 3;
6166 
6167                 for (sig = 0; sig < UC_SYSTEM_SIGNAL_COUNT; sig++)
6168                         if (uc_system_signal_names[sig] &&
6169                             !strcasecmp(uc_system_signal_names[sig], sigstr))
6170                                 break;
6171 
6172                 if (sig == UC_SYSTEM_SIGNAL_COUNT)
6173                         return NULL;
6174         }
6175         else {
6176                 return NULL;
6177         }
6178 
6179         /* Query current signal handler state */
6180         if (nargs < 2) {
6181                 if (sigaction(sig, NULL, &sa) != 0)
6182                         return NULL;
6183 
6184                 if (sa.sa_handler == SIG_IGN)
6185                         return ucv_string_new("ignore");
6186 
6187                 if (sa.sa_handler == SIG_DFL)
6188                         return ucv_string_new("default");
6189 
6190                 return ucv_get(ucv_array_get(vm->signal.handler, sig));
6191         }
6192 
6193         /* Install new signal handler */
6194         if (ucv_type(sighandler) == UC_STRING) {
6195                 sigstr = ucv_string_get(sighandler);
6196 
6197                 sa.sa_flags = SA_ONSTACK | SA_RESTART;
6198                 sigemptyset(&sa.sa_mask);
6199 
6200                 if (!strcmp(sigstr, "ignore"))
6201                         sa.sa_handler = SIG_IGN;
6202                 else if (!strcmp(sigstr, "default"))
6203                         sa.sa_handler = SIG_DFL;
6204                 else
6205                         return NULL;
6206 
6207                 if (sigaction(sig, &sa, NULL) != 0)
6208                         return NULL;
6209 
6210                 ucv_array_set(vm->signal.handler, sig, NULL);
6211         }
6212         else if (ucv_is_callable(sighandler)) {
6213                 if (sigaction(sig, &vm->signal.sa, NULL) != 0)
6214                         return NULL;
6215 
6216                 ucv_array_set(vm->signal.handler, sig, ucv_get(sighandler));
6217         }
6218         else {
6219                 return NULL;
6220         }
6221 
6222         return ucv_get(sighandler);
6223 }
6224 
6225 
6226 const uc_function_list_t uc_stdlib_functions[] = {
6227         { "chr",                uc_chr },
6228         { "die",                uc_die },
6229         { "exists",             uc_exists },
6230         { "rawget",             uc_rawget },
6231         { "rawset",             uc_rawset },
6232         { "rawdelete",          uc_rawdelete },
6233         { "exit",               uc_exit },
6234         { "filter",             uc_filter },
6235         { "getenv",             uc_getenv },
6236         { "hex",                uc_hex },
6237         { "index",              uc_lindex },
6238         { "int",                uc_int },
6239         { "join",               uc_join },
6240         { "keys",               uc_keys },
6241         { "lc",                 uc_lc },
6242         { "length",             uc_length },
6243         { "ltrim",              uc_ltrim },
6244         { "map",                uc_map },
6245         { "ord",                uc_ord },
6246         { "pop",                uc_pop },
6247         { "print",              uc_print },
6248         { "push",               uc_push },
6249         { "reverse",    uc_reverse },
6250         { "rindex",             uc_rindex },
6251         { "rtrim",              uc_rtrim },
6252         { "shift",              uc_shift },
6253         { "sort",               uc_sort },
6254         { "splice",             uc_splice },
6255         { "slice",              uc_slice },
6256         { "split",              uc_split },
6257         { "substr",             uc_substr },
6258         { "time",               uc_time },
6259         { "trim",               uc_trim },
6260         { "type",               uc_type },
6261         { "uchr",               uc_uchr },
6262         { "uc",                 uc_uc },
6263         { "unshift",    uc_unshift },
6264         { "values",             uc_values },
6265         { "sprintf",    uc_sprintf },
6266         { "printf",             uc_printf },
6267         { "require",    uc_require },
6268         { "iptoarr",    uc_iptoarr },
6269         { "arrtoip",    uc_arrtoip },
6270         { "match",              uc_match },
6271         { "replace",    uc_replace },
6272         { "json",               uc_json },
6273         { "include",    uc_include },
6274         { "warn",               uc_warn },
6275         { "system",             uc_system },
6276         { "trace",              uc_trace },
6277         { "proto",              uc_proto },
6278         { "sleep",              uc_sleep },
6279         { "assert",             uc_assert },
6280         { "render",             uc_render },
6281         { "regexp",             uc_regexp },
6282         { "wildcard",   uc_wildcard },
6283         { "sourcepath", uc_sourcepath },
6284         { "min",                uc_min },
6285         { "max",                uc_max },
6286         { "b64dec",             uc_b64dec },
6287         { "b64enc",             uc_b64enc },
6288         { "uniq",               uc_uniq },
6289         { "localtime",  uc_localtime },
6290         { "gmtime",             uc_gmtime },
6291         { "timelocal",  uc_timelocal },
6292         { "timegm",             uc_timegm },
6293         { "clock",              uc_clock },
6294         { "hexdec",             uc_hexdec },
6295         { "hexenc",             uc_hexenc },
6296         { "gc",                 uc_gc },
6297         { "loadstring", uc_loadstring },
6298         { "loadfile",   uc_loadfile },
6299         { "call",               uc_callfunc },
6300         { "signal",             uc_signal },
6301 };
6302 
6303 
6304 void
6305 uc_stdlib_load(uc_value_t *scope)
6306 {
6307         uc_function_list_register(scope, uc_stdlib_functions);
6308 }
6309 
6310 uc_cfn_ptr_t
6311 uc_stdlib_function(const char *name)
6312 {
6313         size_t i;
6314 
6315         for (i = 0; i < ARRAY_SIZE(uc_stdlib_functions); i++)
6316                 if (!strcmp(uc_stdlib_functions[i].name, name))
6317                         return uc_stdlib_functions[i].func;
6318 
6319         return NULL;
6320 }
6321 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt