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