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

Sources/ucode/lib/debug.c

  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