1 /* 2 * Copyright (C) 2023 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 * # Debugger Module 19 * 20 * This module provides runtime debug functionality for ucode scripts. 21 * 22 * Functions can be individually imported and directly accessed using the 23 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 24 * syntax: 25 * 26 * ``` 27 * import { memdump, traceback } from 'debug'; 28 * 29 * let stacktrace = traceback(1); 30 * 31 * memdump("/tmp/dump.txt"); 32 * ``` 33 * 34 * Alternatively, the module namespace can be imported 35 * using a wildcard import statement: 36 * 37 * ``` 38 * import * as debug from 'debug'; 39 * 40 * let stacktrace = debug.traceback(1); 41 * 42 * debug.memdump("/tmp/dump.txt"); 43 * ``` 44 * 45 * Additionally, the debug module namespace may also be imported by invoking the 46 * `ucode` interpreter with the `-ldebug` switch. 47 * 48 * Upon loading, the `debug` module will register a `SIGUSR2` signal handler 49 * which, upon receipt of the signal, will write a memory dump of the currently 50 * running program to `/tmp/ucode.$timestamp.$pid.memdump`. This default 51 * behavior can be inhibited by setting the `UCODE_DEBUG_MEMDUMP_ENABLED` 52 * environment variable to `0` when starting the process. The memory dump signal 53 * and output directory can be overridden with the `UCODE_DEBUG_MEMDUMP_SIGNAL` 54 * and `UCODE_DEBUG_MEMDUMP_PATH` environment variables respectively. 55 * 56 * @module debug 57 */ 58 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <assert.h> 62 #include <unistd.h> 63 #include <errno.h> 64 65 #ifdef HAVE_ULOOP 66 #include <libubox/uloop.h> 67 #endif 68 69 #include <json-c/printbuf.h> 70 #include <json-c/linkhash.h> 71 72 #include "ucode/module.h" 73 #include "ucode/platform.h" 74 75 76 static char *memdump_signal = "USR2"; 77 static char *memdump_directory = "/tmp"; 78 79 struct memdump_walk_ctx { 80 FILE *out; 81 uc_closure_t *current_closure; 82 struct lh_table *seen; 83 }; 84 85 static uc_callframe_t * 86 debuginfo_stackslot_to_callframe(uc_vm_t *vm, size_t slot) 87 { 88 size_t stackframe, i; 89 90 for (i = vm->callframes.count; i > 0; i--) { 91 stackframe = vm->callframes.entries[i - 1].stackframe; 92 93 if (vm->callframes.entries[i - 1].mcall) 94 stackframe--; 95 96 if (stackframe <= slot) 97 return &vm->callframes.entries[i - 1]; 98 } 99 100 return NULL; 101 } 102 103 static void 104 uc_debug_discover_ucv(uc_value_t *uv, struct lh_table *seen); 105 106 static void 107 uc_debug_discover_ucv(uc_value_t *uv, struct lh_table *seen) 108 { 109 uc_function_t *function; 110 uc_closure_t *closure; 111 uc_upvalref_t *upval; 112 uc_object_t *object; 113 uc_array_t *array; 114 uc_resource_t *resource; 115 uc_program_t *program; 116 struct lh_entry *entry; 117 unsigned long hash; 118 size_t i; 119 120 hash = lh_get_hash(seen, uv); 121 122 if (ucv_is_scalar(uv)) 123 return; 124 125 if (lh_table_lookup_entry_w_hash(seen, uv, hash)) 126 return; 127 128 lh_table_insert_w_hash(seen, uv, NULL, hash, 0); 129 130 switch (ucv_type(uv)) { 131 case UC_ARRAY: 132 array = (uc_array_t *)uv; 133 134 uc_debug_discover_ucv(array->proto, seen); 135 136 for (i = 0; i < array->count; i++) 137 uc_debug_discover_ucv(array->entries[i], seen); 138 139 break; 140 141 case UC_OBJECT: 142 object = (uc_object_t *)uv; 143 144 uc_debug_discover_ucv(object->proto, seen); 145 146 lh_foreach(object->table, entry) 147 uc_debug_discover_ucv((uc_value_t *)lh_entry_v(entry), seen); 148 149 break; 150 151 case UC_CLOSURE: 152 closure = (uc_closure_t *)uv; 153 function = closure->function; 154 155 for (i = 0; i < function->nupvals; i++) 156 uc_debug_discover_ucv(&closure->upvals[i]->header, seen); 157 158 uc_debug_discover_ucv(&function->program->header, seen); 159 160 break; 161 162 case UC_UPVALUE: 163 upval = (uc_upvalref_t *)uv; 164 uc_debug_discover_ucv(upval->value, seen); 165 break; 166 167 case UC_RESOURCE: 168 resource = (uc_resource_t *)uv; 169 170 if (resource->type) 171 uc_debug_discover_ucv(resource->type->proto, seen); 172 173 break; 174 175 case UC_PROGRAM: 176 program = (uc_program_t *)uv; 177 178 for (i = 0; i < program->sources.count; i++) 179 uc_debug_discover_ucv(&program->sources.entries[i]->header, seen); 180 181 for (i = 0; i < program->exports.count; i++) 182 uc_debug_discover_ucv(&program->exports.entries[i]->header, seen); 183 184 break; 185 186 default: 187 break; 188 } 189 } 190 191 static void 192 print_value(FILE *out, size_t pad, struct lh_table *seen, 193 uc_vm_t *vm, uc_value_t *uv); 194 195 static void 196 print_value(FILE *out, size_t pad, struct lh_table *seen, 197 uc_vm_t *vm, uc_value_t *uv) 198 { 199 uc_resource_t *resource; 200 uc_closure_t *closure; 201 uc_object_t *object; 202 uc_array_t *array; 203 size_t i, j; 204 char *s; 205 206 fprintf(out, "%s", ucv_typename(uv)); 207 208 if (!uv) { 209 fprintf(out, "\n"); 210 211 return; 212 } 213 214 if (!ucv_is_scalar(uv)) 215 fprintf(out, "; %" PRIu32 " refs", uv->refcount); 216 217 if (!lh_table_lookup_entry(seen, uv)) 218 fprintf(out, "; unreachable"); 219 220 if (ucv_is_constant(uv)) 221 fprintf(out, "; constant"); 222 223 fprintf(out, "\n"); 224 225 for (j = 0; j < pad + 1; j++) 226 fprintf(out, " "); 227 228 s = ucv_to_string(NULL, uv); 229 fprintf(out, "#value = %s\n", s); 230 free(s); 231 232 if (ucv_type(uv) == UC_CLOSURE) { 233 closure = (uc_closure_t *)uv; 234 235 for (i = 0; i < closure->function->nupvals; i++) { 236 for (j = 0; j < pad + 1; j++) 237 fprintf(out, " "); 238 239 fprintf(out, "#upvalue[%zu] ", i); 240 241 if (closure->upvals[i]->closed) { 242 fprintf(out, "closed; "); 243 print_value(out, pad + 1, seen, vm, closure->upvals[i]->value); 244 } 245 else { 246 fprintf(out, "open; stack slot %zu\n", 247 closure->upvals[i]->slot); 248 } 249 } 250 } 251 else if (ucv_type(uv) == UC_OBJECT) { 252 object = (uc_object_t *)uv; 253 254 if (object->proto) { 255 for (j = 0; j < pad + 1; j++) 256 fprintf(out, " "); 257 258 fprintf(out, "#prototype = "); 259 print_value(out, pad + 1, seen, vm, object->proto); 260 } 261 } 262 else if (ucv_type(uv) == UC_ARRAY) { 263 array = (uc_array_t *)uv; 264 265 if (array->proto) { 266 for (j = 0; j < pad + 1; j++) 267 fprintf(out, " "); 268 269 fprintf(out, "#prototype = "); 270 print_value(out, pad + 1, seen, vm, array->proto); 271 } 272 } 273 else if (ucv_type(uv) == UC_RESOURCE) { 274 resource = (uc_resource_t *)uv; 275 276 if (resource->type) { 277 for (j = 0; j < pad + 1; j++) 278 fprintf(out, " "); 279 280 fprintf(out, "#type %s\n", resource->type->name); 281 282 if (resource->type->proto) { 283 for (j = 0; j < pad + 2; j++) 284 fprintf(out, " "); 285 286 fprintf(out, "#prototype = "); 287 print_value(out, pad + 2, seen, vm, resource->type->proto); 288 } 289 } 290 } 291 } 292 293 static size_t 294 insnoff_to_srcpos(uc_function_t *function, size_t *insnoff) 295 { 296 size_t byteoff, lineno; 297 uc_source_t *source; 298 299 source = uc_program_function_source(function); 300 byteoff = uc_program_function_srcpos(function, *insnoff); 301 lineno = uc_source_get_line(source, &byteoff); 302 303 *insnoff = byteoff; 304 305 return lineno; 306 } 307 308 static void 309 print_declaration_srcpos(FILE *out, uc_callframe_t *frame, size_t off, size_t slot, bool upval) 310 { 311 uc_function_t *function = frame->closure->function; 312 uc_variables_t *variables = &function->chunk.debuginfo.variables; 313 size_t i, line; 314 315 assert(slot <= ((size_t)-1 / 2)); 316 317 if (upval) 318 slot += (size_t)-1 / 2; 319 320 for (i = 0; i < variables->count; i++) { 321 if (variables->entries[i].slot != slot || 322 variables->entries[i].from > off || 323 variables->entries[i].to < off) 324 continue; 325 326 off = variables->entries[i].from; 327 line = insnoff_to_srcpos(function, &off); 328 329 fprintf(out, "%s:%zu:%zu", 330 uc_program_function_source(function)->filename, line, off); 331 332 return; 333 } 334 335 fprintf(out, "[unknown source position]"); 336 } 337 338 static void 339 print_function_srcpos(FILE *out, uc_closure_t *closure) 340 { 341 size_t line, off; 342 343 if (!closure) 344 return; 345 346 off = 0; 347 line = insnoff_to_srcpos(closure->function, &off); 348 349 fprintf(out, " @ %s:%zu:%zu", 350 uc_program_function_source(closure->function)->filename, line, off); 351 } 352 353 static void 354 print_ip_srcpos(FILE *out, uc_callframe_t *frame) 355 { 356 uc_function_t *function; 357 size_t line, off; 358 359 if (!frame->closure) 360 return; 361 362 function = frame->closure->function; 363 off = frame->ip - function->chunk.entries; 364 line = insnoff_to_srcpos(function, &off); 365 366 fprintf(out, " @ %s:%zu:%zu", 367 uc_program_function_source(function)->filename, line, off); 368 } 369 370 static void 371 print_memdump(uc_vm_t *vm, FILE *out) 372 { 373 struct memdump_walk_ctx ctx = { 0 }; 374 uc_callframe_t *frame; 375 uc_chunk_t *chunk; 376 uc_weakref_t *ref; 377 uc_value_t *uv; 378 size_t i; 379 char *s; 380 381 ctx.out = out; 382 ctx.seen = lh_kptr_table_new(16, NULL); 383 384 if (!ctx.seen) { 385 fprintf(stderr, "Unable to allocate lookup table: %m\n"); 386 387 return; 388 } 389 390 fprintf(ctx.out, "STACK\n"); 391 392 for (i = 0; i < vm->stack.count; i++) { 393 fprintf(ctx.out, "[%zu]", i); 394 395 frame = debuginfo_stackslot_to_callframe(vm, i); 396 397 if (frame) { 398 chunk = frame->closure ? &frame->closure->function->chunk : NULL; 399 uv = chunk ? uc_chunk_debug_get_variable( 400 chunk, 401 frame->ip - chunk->entries + 1, 402 i - frame->stackframe, 403 false) : NULL; 404 405 if (uv) { 406 fprintf(ctx.out, " %s @ ", 407 ucv_string_get(uv)); 408 409 print_declaration_srcpos(ctx.out, frame, 410 frame->ip - chunk->entries + 1, 411 i - frame->stackframe, false); 412 413 ucv_put(uv); 414 } 415 else if (frame->mcall && i == frame->stackframe - 1) { 416 fprintf(ctx.out, " (this)"); 417 418 if (frame->closure) 419 print_function_srcpos(ctx.out, frame->closure); 420 else 421 fprintf(ctx.out, " @ [C function \"%s\"]", 422 frame->cfunction->name); 423 } 424 else if (i == frame->stackframe) { 425 fprintf(ctx.out, " (callee)"); 426 427 if (frame->closure) 428 print_function_srcpos(ctx.out, frame->closure); 429 else 430 fprintf(ctx.out, " @ [C function \"%s\"]", 431 frame->cfunction->name); 432 } 433 else if (i > frame->stackframe) { 434 fprintf(ctx.out, " (argument #%zu)", 435 i - frame->stackframe); 436 437 if (frame->closure) 438 print_function_srcpos(ctx.out, frame->closure); 439 else 440 fprintf(ctx.out, " @ [C function \"%s\"]", 441 frame->cfunction->name); 442 } 443 } 444 445 fprintf(ctx.out, "\n"); 446 447 uc_debug_discover_ucv(vm->stack.entries[i], ctx.seen); 448 449 s = ucv_to_string(NULL, vm->stack.entries[i]); 450 fprintf(ctx.out, " #value = %s\n", s); 451 free(s); 452 } 453 454 fprintf(ctx.out, "---\n\n"); 455 456 fprintf(ctx.out, "CALLFRAMES\n"); 457 458 for (i = 0; i < vm->callframes.count; i++) { 459 fprintf(ctx.out, "[%zu]", i); 460 print_ip_srcpos(ctx.out, &vm->callframes.entries[i]); 461 fprintf(ctx.out, "\n"); 462 463 uc_debug_discover_ucv(vm->callframes.entries[i].ctx, 464 ctx.seen); 465 466 uc_debug_discover_ucv(&vm->callframes.entries[i].closure->header, 467 ctx.seen); 468 469 uc_debug_discover_ucv(&vm->callframes.entries[i].cfunction->header, 470 ctx.seen); 471 472 s = ucv_to_string(NULL, vm->callframes.entries[i].ctx); 473 fprintf(ctx.out, " #context = %s\n", s); 474 free(s); 475 476 if (vm->callframes.entries[i].closure) { 477 s = ucv_to_string(NULL, 478 &vm->callframes.entries[i].closure->header); 479 fprintf(ctx.out, " #closure = %s\n", s); 480 free(s); 481 } 482 483 if (vm->callframes.entries[i].cfunction) { 484 s = ucv_to_string(NULL, 485 &vm->callframes.entries[i].cfunction->header); 486 487 fprintf(ctx.out, " #cfunction = %s\n", s); 488 free(s); 489 } 490 } 491 492 fprintf(ctx.out, "---\n\n"); 493 494 fprintf(ctx.out, "GLOBALS\n"); 495 uc_debug_discover_ucv(vm->globals, ctx.seen); 496 i = 0; 497 ucv_object_foreach(vm->globals, gk, gv) { 498 s = ucv_to_string(NULL, gv); 499 fprintf(ctx.out, "[%zu] %s\n", i++, gk); 500 fprintf(ctx.out, " #value = %s\n", s); 501 free(s); 502 } 503 fprintf(ctx.out, "---\n\n"); 504 505 fprintf(ctx.out, "REGISTRY\n"); 506 uc_debug_discover_ucv(vm->registry, ctx.seen); 507 i = 0; 508 ucv_object_foreach(vm->registry, rk, rv) { 509 s = ucv_to_string(NULL, rv); 510 fprintf(ctx.out, "[%zu] %s\n", i++, rk); 511 fprintf(ctx.out, " #value = %s\n", s); 512 free(s); 513 } 514 fprintf(ctx.out, "---\n\n"); 515 516 fprintf(ctx.out, "EXCEPTION\n"); 517 uc_debug_discover_ucv(vm->exception.stacktrace, ctx.seen); 518 s = ucv_to_string(NULL, vm->exception.stacktrace); 519 fprintf(ctx.out, "%s\n", s); 520 free(s); 521 fprintf(ctx.out, "---\n\n"); 522 523 fprintf(ctx.out, "RESOURCE TYPES\n"); 524 525 for (i = 0; i < vm->restypes.count; i++) { 526 fprintf(ctx.out, "[%zu] %s\n", i, 527 vm->restypes.entries[i]->name); 528 529 uc_debug_discover_ucv(vm->restypes.entries[i]->proto, ctx.seen); 530 531 s = ucv_to_string(NULL, vm->restypes.entries[i]->proto); 532 fprintf(ctx.out, " #prototype = %s\n", s); 533 free(s); 534 } 535 536 fprintf(ctx.out, "---\n\n"); 537 538 fprintf(ctx.out, "OBJECT POOL\n"); 539 540 for (ref = vm->values.next, i = 0; 541 ref != &vm->values; 542 ref = ref->next, i++) { 543 544 uv = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref)); 545 546 fprintf(ctx.out, "[%zu] ", i); 547 print_value(ctx.out, 0, ctx.seen, vm, uv); 548 } 549 550 lh_table_free(ctx.seen); 551 } 552 553 static uc_value_t * 554 debug_handle_memdump(uc_vm_t *vm, size_t nargs) 555 { 556 char *path; 557 FILE *out; 558 559 xasprintf(&path, "%s/ucode.%llu.%llu.memdump", 560 memdump_directory, 561 (long long unsigned int)time(NULL), 562 (long long unsigned int)getpid()); 563 564 out = fopen(path, "w"); 565 566 if (!out) { 567 fprintf(stderr, "Unable to open memdump file '%s': %m\n", path); 568 569 return NULL; 570 } 571 572 print_memdump(vm, out); 573 574 fclose(out); 575 free(path); 576 577 return NULL; 578 } 579 580 #ifdef HAVE_ULOOP 581 /* The uloop signal handling activation has been intentionally copied from 582 the uloop module here to ensure that uloop signal dispatching also works 583 when just loading the debug module without the uloop one. */ 584 static struct { 585 struct uloop_fd ufd; 586 uc_vm_t *vm; 587 } signal_handle; 588 589 static void 590 uc_uloop_signal_cb(struct uloop_fd *ufd, unsigned int events) 591 { 592 if (uc_vm_signal_dispatch(signal_handle.vm) != EXCEPTION_NONE) 593 uloop_end(); 594 } 595 596 static void 597 debug_setup_uloop(uc_vm_t *vm) 598 { 599 int signal_fd = uc_vm_signal_notifyfd(vm); 600 601 if (signal_fd != -1 && uloop_init() == 0) { 602 signal_handle.vm = vm; 603 signal_handle.ufd.cb = uc_uloop_signal_cb; 604 signal_handle.ufd.fd = signal_fd; 605 606 uloop_fd_add(&signal_handle.ufd, ULOOP_READ); 607 } 608 } 609 #else 610 static void debug_setup_uloop(uc_vm_t *vm) {} 611 #endif 612 613 static void 614 debug_setup_memdump(uc_vm_t *vm) 615 { 616 uc_cfn_ptr_t ucsignal = uc_stdlib_function("signal"); 617 uc_value_t *memdump = ucv_cfunction_new("memdump", debug_handle_memdump); 618 char *ev; 619 620 ev = getenv("UCODE_DEBUG_MEMDUMP_PATH"); 621 memdump_directory = ev ? ev : memdump_directory; 622 623 ev = getenv("UCODE_DEBUG_MEMDUMP_SIGNAL"); 624 memdump_signal = ev ? ev : memdump_signal; 625 626 debug_setup_uloop(vm); 627 628 uc_vm_stack_push(vm, ucv_string_new(memdump_signal)); 629 uc_vm_stack_push(vm, memdump); 630 631 if (ucsignal(vm, 2) != memdump) 632 fprintf(stderr, "Unable to install debug signal handler\n"); 633 634 ucv_put(uc_vm_stack_pop(vm)); 635 ucv_put(uc_vm_stack_pop(vm)); 636 } 637 638 static void 639 debug_setup(uc_vm_t *vm) 640 { 641 char *ev; 642 643 ev = getenv("UCODE_DEBUG_MEMDUMP_ENABLED"); 644 645 if (!ev || !strcmp(ev, "1") || !strcmp(ev, "yes") || !strcmp(ev, "true")) 646 debug_setup_memdump(vm); 647 } 648 649 650 /** 651 * Write a memory dump report to the given file. 652 * 653 * This function generates a human readable memory dump of ucode values 654 * currently managed by the running VM which is useful to track down logical 655 * memory leaks in scripts. 656 * 657 * The file parameter can be either a string value containing a file path, in 658 * which case this function tries to create and write the report file at the 659 * given location, or an already open file handle this function should write to. 660 * 661 * Returns `true` if the report has been written. 662 * 663 * Returns `null` if the file could not be opened or if the handle was invalid. 664 * 665 * @function module:debug#memdump 666 * 667 * @param {string|module:fs.file|module:fs.proc} file 668 * The file path or open file handle to write report to. 669 * 670 * @return {?boolean} 671 */ 672 static uc_value_t * 673 uc_memdump(uc_vm_t *vm, size_t nargs) 674 { 675 uc_value_t *file = uc_fn_arg(0); 676 FILE *fp = NULL; 677 678 if (ucv_type(file) == UC_RESOURCE) { 679 fp = ucv_resource_data(file, "fs.file"); 680 681 if (!fp) 682 fp = ucv_resource_data(file, "fs.proc"); 683 } 684 else if (ucv_type(file) == UC_STRING) { 685 fp = fopen(ucv_string_get(file), "w"); 686 } 687 688 if (!fp) 689 return NULL; 690 691 print_memdump(vm, fp); 692 693 return ucv_boolean_new(true); 694 } 695 696 /** 697 * Capture call stack trace. 698 * 699 * This function captures the current call stack and returns it. The optional 700 * level parameter controls how many calls up the trace should start. It 701 * defaults to `1`, that is the function calling this `traceback()` function. 702 * 703 * Returns an array of stack trace entries describing the function invocations 704 * up to the point where `traceback()` is called. 705 * 706 * @function module:debug#traceback 707 * 708 * @param {number} [level=1] 709 * The number of callframes up the call trace should start, `0` is this function 710 * itself, `1` the function calling it and so on. 711 * 712 * @return {module:debug.StackTraceEntry[]} 713 */ 714 715 /** 716 * @typedef {Object} module:debug.StackTraceEntry 717 * 718 * @property {function} callee 719 * The function that was called. 720 * 721 * @property {*} this 722 * The `this` context the function was called with. 723 * 724 * @property {boolean} mcall 725 * Indicates whether the function was invoked as a method. 726 * 727 * @property {boolean} [strict] 728 * Indicates whether the VM was running in strict mode when the function was 729 * called (only applicable to non-C, pure ucode calls). 730 * 731 * @property {string} [filename] 732 * The name of the source file that called the function (only applicable to 733 * non-C, pure ucode calls). 734 * 735 * @property {number} [line] 736 * The source line of the function call (only applicable to non-C, pure ucode 737 * calls). 738 * 739 * @property {number} [byte] 740 * The source line offset of the function call (only applicable to non-C, pure 741 * ucode calls). 742 * 743 * @property {string} [context] 744 * The surrounding source code context formatted as human-readable string, 745 * useful for generating debug messages (only applicable to non-C, pure ucode 746 * calls). 747 */ 748 749 static uc_value_t * 750 uc_traceback(uc_vm_t *vm, size_t nargs) 751 { 752 uc_value_t *stacktrace, *entry, *level = uc_fn_arg(0); 753 uc_function_t *function; 754 uc_stringbuf_t *context; 755 uc_callframe_t *frame; 756 uc_source_t *source; 757 size_t off, srcpos; 758 size_t i, lv; 759 760 lv = level ? ucv_uint64_get(level) : 1; 761 762 if (level && errno) 763 return NULL; 764 765 stacktrace = ucv_array_new(vm); 766 767 for (i = (lv < vm->callframes.count) ? vm->callframes.count - lv : 0; 768 i > 0; 769 i--) { 770 771 frame = &vm->callframes.entries[i - 1]; 772 entry = ucv_object_new(vm); 773 774 if (frame->closure) { 775 function = frame->closure->function; 776 source = uc_program_function_source(function); 777 778 off = (frame->ip - function->chunk.entries) - 1; 779 srcpos = uc_program_function_srcpos(function, off); 780 781 context = ucv_stringbuf_new(); 782 783 uc_source_context_format(context, 784 uc_program_function_source(function), 785 srcpos, false); 786 787 ucv_object_add(entry, "callee", ucv_get(&frame->closure->header)); 788 ucv_object_add(entry, "this", ucv_get(frame->ctx)); 789 ucv_object_add(entry, "mcall", ucv_boolean_new(frame->mcall)); 790 ucv_object_add(entry, "strict", ucv_boolean_new(frame->strict)); 791 ucv_object_add(entry, "filename", ucv_string_new(source->filename)); 792 ucv_object_add(entry, "line", ucv_int64_new(uc_source_get_line(source, &srcpos))); 793 ucv_object_add(entry, "byte", ucv_int64_new(srcpos)); 794 ucv_object_add(entry, "context", ucv_stringbuf_finish(context)); 795 } 796 else if (frame->cfunction) { 797 ucv_object_add(entry, "callee", ucv_get(&frame->cfunction->header)); 798 ucv_object_add(entry, "this", ucv_get(frame->ctx)); 799 ucv_object_add(entry, "mcall", ucv_boolean_new(frame->mcall)); 800 } 801 802 ucv_array_push(stacktrace, entry); 803 } 804 805 return stacktrace; 806 } 807 808 /** 809 * Obtain information about the current source position. 810 * 811 * The `sourcepos()` function determines the source code position of the 812 * current instruction invoking this function. 813 * 814 * Returns a dictionary containing the filename, line number and line byte 815 * offset of the call site. 816 * 817 * Returns `null` if this function was invoked from C code. 818 * 819 * @function module:debug#sourcepos 820 * 821 * @return {?module:debug.SourcePosition} 822 */ 823 824 /** 825 * @typedef {Object} module:debug.SourcePosition 826 * 827 * @property {string} filename 828 * The name of the source file that called this function. 829 * 830 * @property {number} line 831 * The source line of the function call. 832 * 833 * @property {number} byte 834 * The source line offset of the function call. 835 */ 836 837 static uc_value_t * 838 uc_sourcepos(uc_vm_t *vm, size_t nargs) 839 { 840 uc_function_t *function; 841 uc_callframe_t *frame; 842 uc_source_t *source; 843 uc_value_t *rv; 844 size_t byte; 845 846 if (vm->callframes.count < 2) 847 return NULL; 848 849 frame = &vm->callframes.entries[vm->callframes.count - 2]; 850 851 if (!frame->closure) 852 return NULL; 853 854 function = frame->closure->function; 855 source = uc_program_function_source(function); 856 byte = uc_program_function_srcpos(function, 857 (frame->ip - function->chunk.entries) - 1); 858 859 rv = ucv_object_new(vm); 860 861 ucv_object_add(rv, "filename", ucv_string_new(source->filename)); 862 ucv_object_add(rv, "line", ucv_int64_new(uc_source_get_line(source, &byte))); 863 ucv_object_add(rv, "byte", ucv_int64_new(byte)); 864 865 return rv; 866 } 867 868 static uc_value_t * 869 uc_getinfo_fnargs(uc_vm_t *vm, uc_function_t *function) 870 { 871 uc_value_t *rv = NULL, *name; 872 size_t i; 873 874 for (i = 0; i < function->nargs; i++) { 875 name = uc_chunk_debug_get_variable(&function->chunk, i, i + 1, false); 876 877 if (!name) 878 continue; 879 880 if (!rv) 881 rv = ucv_array_new_length(vm, function->nargs); 882 883 ucv_array_push(rv, name); 884 } 885 886 return rv; 887 } 888 889 /** 890 * @typedef {Object} module:debug.UpvalRef 891 * 892 * @property {string} name 893 * The name of the captured variable. 894 * 895 * @property {boolean} closed 896 * Indicates whether the captured variable (upvalue) is closed or not. A closed 897 * upvalue means that the function value outlived the declaration scope of the 898 * captured variable. 899 * 900 * @property {*} value 901 * The current value of the captured variable. 902 * 903 * @property {number} [slot] 904 * The stack slot of the captured variable. Only applicable to open (non-closed) 905 * captured variables. 906 */ 907 static uc_value_t * 908 uc_getinfo_upvals(uc_vm_t *vm, uc_closure_t *closure) 909 { 910 uc_function_t *function = closure->function; 911 uc_upvalref_t **upvals = closure->upvals; 912 uc_value_t *rv = NULL, *up, *name; 913 size_t i; 914 915 for (i = 0; i < function->nupvals; i++) { 916 up = ucv_object_new(vm); 917 name = uc_chunk_debug_get_variable(&function->chunk, 0, i, true); 918 919 if (name) 920 ucv_object_add(up, "name", name); 921 922 if (upvals[i]->closed) { 923 ucv_object_add(up, "closed", ucv_boolean_new(true)); 924 ucv_object_add(up, "value", ucv_get(upvals[i]->value)); 925 } 926 else { 927 ucv_object_add(up, "closed", ucv_boolean_new(false)); 928 ucv_object_add(up, "slot", ucv_uint64_new(upvals[i]->slot)); 929 ucv_object_add(up, "value", 930 ucv_get(vm->stack.entries[upvals[i]->slot])); 931 } 932 933 if (!rv) 934 rv = ucv_array_new_length(vm, function->nupvals); 935 936 ucv_array_push(rv, up); 937 } 938 939 return rv; 940 } 941 942 /** 943 * Obtain information about the given value. 944 * 945 * The `getinfo()` function allows querying internal information about the 946 * given ucode value, such as the current reference count, the mark bit state 947 * etc. 948 * 949 * Returns a dictionary with value type specific details. 950 * 951 * Returns `null` if a `null` value was provided. 952 * 953 * @function module:debug#getinfo 954 * 955 * @param {*} value 956 * The value to query information for. 957 * 958 * @return {?module:debug.ValueInformation} 959 */ 960 961 /** 962 * @typedef {Object} module:debug.ValueInformation 963 * 964 * @property {string} type 965 * The name of the value type, one of `integer`, `boolean`, `string`, `double`, 966 * `array`, `object`, `regexp`, `cfunction`, `closure`, `upvalue` or `resource`. 967 * 968 * @property {*} value 969 * The value itself. 970 * 971 * @property {boolean} tagged 972 * Indicates whether the given value is internally stored as tagged pointer 973 * without an additional heap allocation. 974 * 975 * @property {boolean} [mark] 976 * Indicates whether the value has it's mark bit set, which is used for loop 977 * detection during recursive object traversal on garbage collection cycles or 978 * complex value stringification. Only applicable to non-tagged values. 979 * 980 * @property {number} [refcount] 981 * The current reference count of the value. Note that the `getinfo()` function 982 * places a reference to the value into the `value` field of the resulting 983 * information dictionary, so the ref count will always be at least 2 - one 984 * reference for the function call argument and one for the value property in 985 * the result dictionary. Only applicable to non-tagged values. 986 * 987 * @property {boolean} [unsigned] 988 * Whether the number value is internally stored as unsigned integer. Only 989 * applicable to non-tagged integer values. 990 * 991 * @property {number} [address] 992 * The address of the underlying C heap memory. Only applicable to non-tagged 993 * `string`, `array`, `object`, `cfunction` or `resource` values. 994 * 995 * @property {number} [length] 996 * The length of the underlying string memory. Only applicable to non-tagged 997 * `string` values. 998 * 999 * @property {number} [count] 1000 * The amount of elements in the underlying memory structure. Only applicable to 1001 * `array` and `object` values. 1002 * 1003 * @property {boolean} [constant] 1004 * Indicates whether the value is constant (immutable). Only applicable to 1005 * `array` and `object` values. 1006 * 1007 * @property {*} [prototype] 1008 * The associated prototype value, if any. Only applicable to `array`, `object` 1009 * and `prototype` values. 1010 * 1011 * @property {string} [source] 1012 * The original regex source pattern. Only applicable to `regexp` values. 1013 * 1014 * @property {boolean} [icase] 1015 * Whether the compiled regex has the `i` (ignore case) flag set. Only 1016 * applicable to `regexp` values. 1017 * 1018 * @property {boolean} [global] 1019 * Whether the compiled regex has the `g` (global) flag set. Only applicable to 1020 * `regexp` values. 1021 * 1022 * @property {boolean} [newline] 1023 * Whether the compiled regex has the `s` (single line) flag set. Only 1024 * applicable to `regexp` values. 1025 * 1026 * @property {number} [nsub] 1027 * The amount of capture groups within the regular expression. Only applicable 1028 * to `regexp` values. 1029 * 1030 * @property {string} [name] 1031 * The name of the non-anonymous function. Only applicable to `cfunction` and 1032 * `closure` values. Set to `null` for anonymous function values. 1033 * 1034 * @property {boolean} [arrow] 1035 * Indicates whether the ucode function value is an arrow function. Only 1036 * applicable to `closure` values. 1037 * 1038 * @property {boolean} [module] 1039 * Indicates whether the ucode function value is a module entry point. Only 1040 * applicable to `closure` values. 1041 * 1042 * @property {boolean} [strict] 1043 * Indicates whether the function body will be executed in strict mode. Only 1044 * applicable to `closure` values. 1045 * 1046 * @property {boolean} [vararg] 1047 * Indicates whether the ucode function takes a variable number of arguments. 1048 * Only applicable to `closure` values. 1049 * 1050 * @property {number} [nargs] 1051 * The number of arguments expected by the ucode function, excluding a potential 1052 * final variable argument ellipsis. Only applicable to `closure` values. 1053 * 1054 * @property {string[]} [argnames] 1055 * The names of the function arguments in their declaration order. Only 1056 * applicable to `closure` values. 1057 * 1058 * @property {number} [nupvals] 1059 * The number of upvalues associated with the ucode function. Only applicable to 1060 * `closure` values. 1061 * 1062 * @property {module:debug.UpvalRef[]} [upvals] 1063 * An array of upvalue information objects. Only applicable to `closure` values. 1064 * 1065 * @property {string} [filename] 1066 * The path of the source file the function was declared in. Only applicable to 1067 * `closure` values. 1068 * 1069 * @property {number} [line] 1070 * The source line number the function was declared at. Only applicable to 1071 * `closure` values. 1072 * 1073 * @property {number} [byte] 1074 * The source line offset the function was declared at. Only applicable to 1075 * `closure` values. 1076 * 1077 * @property {string} [type] 1078 * The resource type name. Only applicable to `resource` values. 1079 */ 1080 1081 static uc_value_t * 1082 uc_getinfo(uc_vm_t *vm, size_t nargs) 1083 { 1084 uc_value_t *uv = uc_fn_arg(0), *rv; 1085 uintptr_t pv = (uintptr_t)uv; 1086 uc_cfunction_t *uvcfn; 1087 uc_resource_t *uvres; 1088 uc_closure_t *uvfun; 1089 uc_source_t *source; 1090 uc_regexp_t *uvreg; 1091 uc_string_t *uvstr; 1092 uc_object_t *uvobj; 1093 uc_array_t *uvarr; 1094 size_t byte; 1095 1096 if (!uv) 1097 return NULL; 1098 1099 rv = ucv_object_new(vm); 1100 1101 ucv_object_add(rv, "type", ucv_string_new(ucv_typename(uv))); 1102 ucv_object_add(rv, "value", ucv_get(uv)); 1103 1104 if (pv & 3) { 1105 ucv_object_add(rv, "tagged", ucv_boolean_new(true)); 1106 } 1107 else { 1108 ucv_object_add(rv, "tagged", ucv_boolean_new(false)); 1109 ucv_object_add(rv, "mark", ucv_boolean_new(uv->mark)); 1110 ucv_object_add(rv, "refcount", ucv_uint64_new(uv->refcount)); 1111 } 1112 1113 switch (ucv_type(uv)) { 1114 case UC_INTEGER: 1115 ucv_object_add(rv, "unsigned", 1116 ucv_boolean_new(!(pv & 3) && uv->u64_or_constant)); 1117 1118 break; 1119 1120 case UC_STRING: 1121 if (pv & 3) { 1122 uvstr = (uc_string_t *)uv; 1123 1124 ucv_object_add(rv, "address", 1125 ucv_uint64_new((uintptr_t)uvstr->str)); 1126 1127 ucv_object_add(rv, "length", ucv_uint64_new(uvstr->length)); 1128 } 1129 1130 break; 1131 1132 case UC_ARRAY: 1133 uvarr = (uc_array_t *)uv; 1134 1135 ucv_object_add(rv, "address", 1136 ucv_uint64_new((uintptr_t)uvarr->entries)); 1137 1138 ucv_object_add(rv, "count", ucv_uint64_new(uvarr->count)); 1139 ucv_object_add(rv, "constant", ucv_boolean_new(uv->u64_or_constant)); 1140 ucv_object_add(rv, "prototype", ucv_get(uvarr->proto)); 1141 1142 break; 1143 1144 case UC_OBJECT: 1145 uvobj = (uc_object_t *)uv; 1146 1147 ucv_object_add(rv, "address", 1148 ucv_uint64_new((uintptr_t)uvobj->table)); 1149 1150 ucv_object_add(rv, "count", 1151 ucv_uint64_new(lh_table_length(uvobj->table))); 1152 1153 ucv_object_add(rv, "constant", ucv_boolean_new(uv->u64_or_constant)); 1154 ucv_object_add(rv, "prototype", ucv_get(uvobj->proto)); 1155 1156 break; 1157 1158 case UC_REGEXP: 1159 uvreg = (uc_regexp_t *)uv; 1160 1161 ucv_object_add(rv, "source", ucv_string_new(uvreg->source)); 1162 ucv_object_add(rv, "icase", ucv_boolean_new(uvreg->icase)); 1163 ucv_object_add(rv, "global", ucv_boolean_new(uvreg->global)); 1164 ucv_object_add(rv, "newline", ucv_boolean_new(uvreg->newline)); 1165 ucv_object_add(rv, "nsub", ucv_uint64_new(uvreg->regexp.re_nsub)); 1166 1167 break; 1168 1169 case UC_CFUNCTION: 1170 uvcfn = (uc_cfunction_t *)uv; 1171 1172 ucv_object_add(rv, "name", ucv_string_new(uvcfn->name)); 1173 ucv_object_add(rv, "address", ucv_uint64_new((uintptr_t)uvcfn->cfn)); 1174 1175 break; 1176 1177 case UC_CLOSURE: 1178 uvfun = (uc_closure_t *)uv; 1179 byte = uvfun->function->srcpos; 1180 source = uc_program_function_source(uvfun->function); 1181 1182 ucv_object_add(rv, "name", ucv_string_new(uvfun->function->name)); 1183 ucv_object_add(rv, "arrow", ucv_boolean_new(uvfun->function->arrow)); 1184 ucv_object_add(rv, "module", ucv_boolean_new(uvfun->function->module)); 1185 ucv_object_add(rv, "strict", ucv_boolean_new(uvfun->function->strict)); 1186 ucv_object_add(rv, "vararg", ucv_boolean_new(uvfun->function->vararg)); 1187 ucv_object_add(rv, "nargs", ucv_uint64_new(uvfun->function->nargs)); 1188 ucv_object_add(rv, "argnames", uc_getinfo_fnargs(vm, uvfun->function)); 1189 ucv_object_add(rv, "nupvals", ucv_uint64_new(uvfun->function->nupvals)); 1190 ucv_object_add(rv, "upvals", uc_getinfo_upvals(vm, uvfun)); 1191 ucv_object_add(rv, "filename", ucv_string_new(source->filename)); 1192 ucv_object_add(rv, "line", ucv_int64_new(uc_source_get_line(source, &byte))); 1193 ucv_object_add(rv, "byte", ucv_int64_new(byte)); 1194 1195 break; 1196 1197 case UC_RESOURCE: 1198 uvres = (uc_resource_t *)uv; 1199 1200 ucv_object_add(rv, "address", ucv_uint64_new((uintptr_t)uvres->data)); 1201 1202 if (uvres->type) { 1203 ucv_object_add(rv, "type", ucv_string_new(uvres->type->name)); 1204 ucv_object_add(rv, "prototype", ucv_get(uvres->type->proto)); 1205 } 1206 1207 break; 1208 1209 default: 1210 break; 1211 } 1212 1213 return rv; 1214 } 1215 1216 /** 1217 * @typedef {Object} module:debug.LocalInfo 1218 * 1219 * @property {number} index 1220 * The index of the local variable. 1221 * 1222 * @property {string} name 1223 * The name of the local variable. 1224 * 1225 * @property {*} value 1226 * The current value of the local variable. 1227 * 1228 * @property {number} linefrom 1229 * The source line number of the local variable declaration. 1230 * 1231 * @property {number} bytefrom 1232 * The source line offset of the local variable declaration. 1233 * 1234 * @property {number} lineto 1235 * The source line number where the local variable goes out of scope. 1236 * 1237 * @property {number} byteto 1238 * The source line offset where the local vatiable goes out of scope. 1239 */ 1240 static uc_value_t * 1241 uc_xlocal(uc_vm_t *vm, uc_value_t *level, uc_value_t *var, uc_value_t **set) 1242 { 1243 size_t lv, vn, vi, i, pos, slot = 0; 1244 uc_value_t *vname = NULL, *rv; 1245 uc_variables_t *variables; 1246 uc_callframe_t *frame; 1247 uc_source_t *source; 1248 uc_chunk_t *chunk; 1249 1250 lv = level ? ucv_uint64_get(level) : 1; 1251 1252 if ((level && errno) || lv >= vm->callframes.count) 1253 return NULL; 1254 1255 frame = &vm->callframes.entries[vm->callframes.count - lv - 1]; 1256 1257 if (!frame->closure) 1258 return NULL; 1259 1260 source = uc_program_function_source(frame->closure->function); 1261 chunk = &frame->closure->function->chunk; 1262 variables = &chunk->debuginfo.variables; 1263 1264 if (ucv_type(var) == UC_INTEGER) { 1265 vn = ucv_uint64_get(var); 1266 var = NULL; 1267 1268 if (errno || vn >= variables->count) 1269 return NULL; 1270 } 1271 else if (ucv_type(var) == UC_STRING) { 1272 vn = 0; 1273 } 1274 else { 1275 return NULL; 1276 } 1277 1278 pos = frame->ip - chunk->entries; 1279 1280 for (i = 0, vi = 0; i < variables->count; i++) { 1281 slot = variables->entries[i].slot; 1282 1283 if (slot >= (size_t)-1 / 2) 1284 continue; 1285 1286 if (variables->entries[i].from > pos || variables->entries[i].to < pos) 1287 continue; 1288 1289 vname = uc_chunk_debug_get_variable(chunk, pos, slot, false); 1290 1291 if (var ? ucv_is_equal(var, vname) : (vi == vn)) 1292 break; 1293 1294 ucv_put(vname); 1295 vname = NULL; 1296 vi++; 1297 } 1298 1299 if (i == variables->count) 1300 return NULL; 1301 1302 if (set) { 1303 ucv_put(vm->stack.entries[frame->stackframe + slot]); 1304 vm->stack.entries[frame->stackframe + slot] = ucv_get(*set); 1305 } 1306 1307 rv = ucv_object_new(vm); 1308 1309 ucv_object_add(rv, "index", ucv_uint64_new(vi)); 1310 ucv_object_add(rv, "name", vname); 1311 ucv_object_add(rv, "value", 1312 ucv_get(vm->stack.entries[frame->stackframe + slot])); 1313 1314 pos = uc_program_function_srcpos(frame->closure->function, 1315 variables->entries[i].from); 1316 1317 ucv_object_add(rv, "linefrom", 1318 ucv_uint64_new(uc_source_get_line(source, &pos))); 1319 1320 ucv_object_add(rv, "bytefrom", 1321 ucv_uint64_new(pos)); 1322 1323 pos = uc_program_function_srcpos(frame->closure->function, 1324 variables->entries[i].to); 1325 1326 ucv_object_add(rv, "lineto", 1327 ucv_uint64_new(uc_source_get_line(source, &pos))); 1328 1329 ucv_object_add(rv, "byteto", 1330 ucv_uint64_new(pos)); 1331 1332 return rv; 1333 } 1334 1335 /** 1336 * Obtain local variable. 1337 * 1338 * The `getlocal()` function retrieves information about the specified local 1339 * variable at the given call stack depth. 1340 * 1341 * The call stack depth specifies the amount of levels up local variables should 1342 * be queried. A value of `0` refers to this `getlocal()` function call itself, 1343 * `1` to the function calling `getlocal()` and so on. 1344 * 1345 * The variable to query might be either specified by name or by its index with 1346 * index numbers following the source code declaration order. 1347 * 1348 * Returns a dictionary holding information about the given variable. 1349 * 1350 * Returns `null` if the stack depth exceeds the size of the current call stack. 1351 * 1352 * Returns `null` if the invocation at the given stack depth is a C call. 1353 * 1354 * Returns `null` if the given variable name is not found or the given variable 1355 * index is invalid. 1356 * 1357 * @function module:debug#getlocal 1358 * 1359 * @param {number} [level=1] 1360 * The amount of call stack levels up local variables should be queried. 1361 * 1362 * @param {string|number} variable 1363 * The variable index or variable name to obtain information for. 1364 * 1365 * @returns {?module:debug.LocalInfo} 1366 */ 1367 static uc_value_t * 1368 uc_getlocal(uc_vm_t *vm, size_t nargs) 1369 { 1370 uc_value_t *level = uc_fn_arg(0); 1371 uc_value_t *var = uc_fn_arg(1); 1372 1373 return uc_xlocal(vm, level, var, NULL); 1374 } 1375 1376 /** 1377 * Set local variable. 1378 * 1379 * The `setlocal()` function manipulates the value of the specified local 1380 * variable at the given call stack depth. 1381 * 1382 * The call stack depth specifies the amount of levels up local variables should 1383 * be updated. A value of `0` refers to this `setlocal()` function call itself, 1384 * `1` to the function calling `setlocal()` and so on. 1385 * 1386 * The variable to update might be either specified by name or by its index with 1387 * index numbers following the source code declaration order. 1388 * 1389 * Returns a dictionary holding information about the updated variable. 1390 * 1391 * Returns `null` if the stack depth exceeds the size of the current call stack. 1392 * 1393 * Returns `null` if the invocation at the given stack depth is a C call. 1394 * 1395 * Returns `null` if the given variable name is not found or the given variable 1396 * index is invalid. 1397 * 1398 * @function module:debug#setlocal 1399 * 1400 * @param {number} [level=1] 1401 * The amount of call stack levels up local variables should be updated. 1402 * 1403 * @param {string|number} variable 1404 * The variable index or variable name to update. 1405 * 1406 * @param {*} [value=null] 1407 * The value to set the local variable to. 1408 * 1409 * @returns {?module:debug.LocalInfo} 1410 */ 1411 static uc_value_t * 1412 uc_setlocal(uc_vm_t *vm, size_t nargs) 1413 { 1414 uc_value_t *level = uc_fn_arg(0); 1415 uc_value_t *var = uc_fn_arg(1); 1416 uc_value_t *val = uc_fn_arg(2); 1417 1418 return uc_xlocal(vm, level, var, &val); 1419 } 1420 1421 1422 /** 1423 * @typedef {Object} module:debug.UpvalInfo 1424 * 1425 * @property {number} index 1426 * The index of the captured variable (upvalue). 1427 * 1428 * @property {string} name 1429 * The name of the captured variable. 1430 * 1431 * @property {boolean} closed 1432 * Indicates whether the captured variable is closed or not. A closed upvalue 1433 * means that the function outlived the declaration scope of the captured 1434 * variable. 1435 * 1436 * @property {*} value 1437 * The current value of the captured variable. 1438 */ 1439 static uc_value_t * 1440 uc_xupval(uc_vm_t *vm, uc_value_t *target, uc_value_t *var, uc_value_t **set) 1441 { 1442 uc_value_t *vname = NULL, *rv; 1443 uc_closure_t *closure = NULL; 1444 uc_upvalref_t *uref = NULL; 1445 uc_chunk_t *chunk; 1446 size_t vn, depth; 1447 1448 if (ucv_type(target) == UC_INTEGER) { 1449 depth = ucv_uint64_get(target); 1450 1451 if (errno || depth >= vm->callframes.count) 1452 return NULL; 1453 1454 depth = vm->callframes.count - depth - 1; 1455 closure = vm->callframes.entries[depth].closure; 1456 } 1457 else if (ucv_type(target) == UC_CLOSURE) { 1458 closure = (uc_closure_t *)target; 1459 } 1460 1461 if (!closure) 1462 return NULL; 1463 1464 chunk = &closure->function->chunk; 1465 1466 if (ucv_type(var) == UC_INTEGER) { 1467 vn = ucv_uint64_get(var); 1468 var = NULL; 1469 1470 if (errno || vn >= closure->function->nupvals) 1471 return NULL; 1472 1473 uref = closure->upvals[vn]; 1474 vname = uc_chunk_debug_get_variable(chunk, 0, vn, true); 1475 } 1476 else if (ucv_type(var) == UC_STRING) { 1477 for (vn = 0; vn < closure->function->nupvals; vn++) { 1478 vname = uc_chunk_debug_get_variable(chunk, 0, vn, true); 1479 1480 if (ucv_is_equal(vname, var)) { 1481 uref = closure->upvals[vn]; 1482 break; 1483 } 1484 1485 ucv_put(vname); 1486 vname = NULL; 1487 } 1488 } 1489 1490 if (!uref) 1491 return NULL; 1492 1493 rv = ucv_object_new(vm); 1494 1495 ucv_object_add(rv, "index", ucv_uint64_new(vn)); 1496 ucv_object_add(rv, "name", vname); 1497 1498 if (uref->closed) { 1499 if (set) { 1500 ucv_put(uref->value); 1501 uref->value = ucv_get(*set); 1502 } 1503 1504 ucv_object_add(rv, "closed", ucv_boolean_new(true)); 1505 ucv_object_add(rv, "value", ucv_get(uref->value)); 1506 } 1507 else { 1508 if (set) { 1509 ucv_put(vm->stack.entries[uref->slot]); 1510 vm->stack.entries[uref->slot] = ucv_get(*set); 1511 } 1512 1513 ucv_object_add(rv, "closed", ucv_boolean_new(false)); 1514 ucv_object_add(rv, "value", ucv_get(vm->stack.entries[uref->slot])); 1515 } 1516 1517 return rv; 1518 } 1519 1520 /** 1521 * Obtain captured variable (upvalue). 1522 * 1523 * The `getupval()` function retrieves information about the specified captured 1524 * variable associated with the given function value or the invoked function at 1525 * the given call stack depth. 1526 * 1527 * The call stack depth specifies the amount of levels up the function should be 1528 * selected to query associated captured variables for. A value of `0` refers to 1529 * this `getupval()` function call itself, `1` to the function calling 1530 * `getupval()` and so on. 1531 * 1532 * The variable to query might be either specified by name or by its index with 1533 * index numbers following the source code declaration order. 1534 * 1535 * Returns a dictionary holding information about the given variable. 1536 * 1537 * Returns `null` if the given function value is not a closure. 1538 * 1539 * Returns `null` if the stack depth exceeds the size of the current call stack. 1540 * 1541 * Returns `null` if the invocation at the given stack depth is not a closure. 1542 * 1543 * Returns `null` if the given variable name is not found or the given variable 1544 * index is invalid. 1545 * 1546 * @function module:debug#getupval 1547 * 1548 * @param {function|number} target 1549 * Either a function value referring to a closure to query upvalues for or a 1550 * stack depth number selecting a closure that many levels up. 1551 * 1552 * @param {string|number} variable 1553 * The variable index or variable name to obtain information for. 1554 * 1555 * @returns {?module:debug.UpvalInfo} 1556 */ 1557 static uc_value_t * 1558 uc_getupval(uc_vm_t *vm, size_t nargs) 1559 { 1560 uc_value_t *target = uc_fn_arg(0); 1561 uc_value_t *var = uc_fn_arg(1); 1562 1563 return uc_xupval(vm, target, var, NULL); 1564 } 1565 1566 /** 1567 * Set upvalue. 1568 * 1569 * The `setupval()` function manipulates the value of the specified captured 1570 * variable associated with the given function value or the invoked function at 1571 * the given call stack depth. 1572 * 1573 * The call stack depth specifies the amount of levels up the function should be 1574 * selected to update associated captured variables for. A value of `0` refers 1575 * to this `setupval()` function call itself, `1` to the function calling 1576 * `setupval()` and so on. 1577 * 1578 * The variable to update might be either specified by name or by its index with 1579 * index numbers following the source code declaration order. 1580 * 1581 * Returns a dictionary holding information about the updated variable. 1582 * 1583 * Returns `null` if the given function value is not a closure. 1584 * 1585 * Returns `null` if the stack depth exceeds the size of the current call stack. 1586 * 1587 * Returns `null` if the invocation at the given stack depth is not a closure. 1588 * 1589 * Returns `null` if the given variable name is not found or the given variable 1590 * index is invalid. 1591 * 1592 * @function module:debug#setupval 1593 * 1594 * @param {function|number} target 1595 * Either a function value referring to a closure to update upvalues for or a 1596 * stack depth number selecting a closure that many levels up. 1597 * 1598 * @param {string|number} variable 1599 * The variable index or variable name to update. 1600 * 1601 * @param {*} value 1602 * The value to set the variable to. 1603 * 1604 * @returns {?module:debug.UpvalInfo} 1605 */ 1606 static uc_value_t * 1607 uc_setupval(uc_vm_t *vm, size_t nargs) 1608 { 1609 uc_value_t *target = uc_fn_arg(0); 1610 uc_value_t *var = uc_fn_arg(1); 1611 uc_value_t *val = uc_fn_arg(2); 1612 1613 return uc_xupval(vm, target, var, &val); 1614 } 1615 1616 1617 static const uc_function_list_t debug_fns[] = { 1618 { "memdump", uc_memdump }, 1619 { "traceback", uc_traceback }, 1620 { "sourcepos", uc_sourcepos }, 1621 { "getinfo", uc_getinfo }, 1622 { "getlocal", uc_getlocal }, 1623 { "setlocal", uc_setlocal }, 1624 { "getupval", uc_getupval }, 1625 { "setupval", uc_setupval }, 1626 }; 1627 1628 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 1629 { 1630 uc_function_list_register(scope, debug_fns); 1631 1632 debug_setup(vm); 1633 } 1634
This page was automatically generated by LXR 0.3.1. • OpenWrt