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

Sources/ucode/vm.c

  1 /*
  2  * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <assert.h>
 18 #include <math.h>
 19 #include <errno.h>
 20 #include <limits.h>
 21 #include <fcntl.h>
 22 #include <unistd.h>
 23 
 24 #include "ucode/internal/vm.h" /* ISA: I_*, uc_vm_insn_t, __insns */
 25 #include "ucode/internal/program.h"
 26 #include "ucode/internal/chunk.h"
 27 #include "ucode/internal/lib.h" /* uc_error_context_format() */
 28 #include "ucode/internal/platform.h"
 29 
 30 #undef __insn
 31 #define __insn(_name) #_name,
 32 
 33 static const char *insn_names[__I_MAX] = {
 34         __insns
 35 };
 36 
 37 const int8_t uc_vm_insn_format[__I_MAX] = {
 38         [I_LOAD] = 4,
 39         [I_LOAD8] = 1,
 40         [I_LOAD16] = 2,
 41         [I_LOAD32] = 4,
 42 
 43         [I_LREXP] = 4,
 44 
 45         [I_LLOC] = 4,
 46         [I_LVAR] = 4,
 47         [I_LUPV] = 4,
 48 
 49         [I_CLFN] = 4,
 50         [I_ARFN] = 4,
 51 
 52         [I_SLOC] = 4,
 53         [I_SUPV] = 4,
 54         [I_SVAR] = 4,
 55 
 56         [I_ULOC] = 4,
 57         [I_UUPV] = 4,
 58         [I_UVAR] = 4,
 59         [I_UVAL] = 1,
 60 
 61         [I_NARR] = 4,
 62         [I_PARR] = 4,
 63 
 64         [I_NOBJ] = 4,
 65         [I_SOBJ] = 4,
 66 
 67         [I_JMP] = -4,
 68         [I_JMPZ] = -4,
 69         [I_JMPNT] = 4,
 70 
 71         [I_COPY] = 1,
 72 
 73         [I_CALL] = 4,
 74 
 75         [I_IMPORT] = 4,
 76         [I_EXPORT] = 4,
 77         [I_DYNLOAD] = 4
 78 };
 79 
 80 const char *exception_type_strings[] = {
 81         [EXCEPTION_SYNTAX] = "Syntax error",
 82         [EXCEPTION_RUNTIME] = "Runtime error",
 83         [EXCEPTION_TYPE] = "Type error",
 84         [EXCEPTION_REFERENCE] = "Reference error",
 85         [EXCEPTION_USER] = "Error",
 86         [EXCEPTION_EXIT] = "Exit"
 87 };
 88 
 89 
 90 static const char *
 91 uc_vm_insn_to_name(uc_vm_insn_t insn)
 92 {
 93         if (insn < 0 || insn >= __I_MAX)
 94                 return "(unknown)";
 95 
 96         return insn_names[insn];
 97 }
 98 
 99 int8_t
100 uc_vm_insn_to_argtype(uc_vm_insn_t insn)
101 {
102         if (insn < 0 || insn >= __I_MAX)
103                 return 0;
104 
105         return uc_vm_insn_format[insn];
106 }
107 
108 static void
109 uc_vm_reset_stack(uc_vm_t *vm)
110 {
111         while (vm->stack.count > 0) {
112                 vm->stack.count--;
113                 ucv_put(vm->stack.entries[vm->stack.count]);
114                 vm->stack.entries[vm->stack.count] = NULL;
115         }
116 }
117 
118 static uc_value_t *
119 uc_vm_callframe_pop(uc_vm_t *vm);
120 
121 static void
122 uc_vm_close_upvals(uc_vm_t *vm, size_t slot);
123 
124 static void
125 uc_vm_reset_callframes(uc_vm_t *vm)
126 {
127         while (vm->callframes.count > 0)
128                 ucv_put(uc_vm_callframe_pop(vm));
129 }
130 
131 static uc_value_t *
132 uc_vm_alloc_global_scope(uc_vm_t *vm)
133 {
134         uc_value_t *scope, *arr;
135         size_t i;
136 
137         scope = ucv_object_new(vm);
138 
139         /* build default require() search path */
140         arr = ucv_array_new(vm);
141 
142         for (i = 0; i < vm->config->module_search_path.count; i++)
143                 ucv_array_push(arr, ucv_string_new(vm->config->module_search_path.entries[i]));
144 
145         /* register module related constants */
146         ucv_object_add(scope, "REQUIRE_SEARCH_PATH", arr);
147         ucv_object_add(scope, "modules", ucv_object_new(vm));
148 
149         /* register scope math constants */
150         ucv_object_add(scope, "NaN", ucv_double_new(NAN));
151         ucv_object_add(scope, "Infinity", ucv_double_new(INFINITY));
152 
153         /* register global property */
154         ucv_object_add(scope, "global", ucv_get(scope));
155 
156         uc_vm_scope_set(vm, scope);
157 
158         return scope;
159 }
160 
161 static void
162 uc_vm_output_exception(uc_vm_t *vm, uc_exception_t *ex);
163 
164 static void
165 uc_vm_signal_handler(int sig)
166 {
167         uc_vm_t *vm = uc_thread_context_get()->signal_handler_vm;
168 
169         assert(vm);
170 
171         uc_vm_signal_raise(vm, sig);
172 }
173 
174 /* Actually wire up the self-pipe/handler array/sigaction template needed
175  * for ucode-level signal() callbacks to work, independent of whether the
176  * embedding host opted into this via config->setup_signal_handlers. Safe
177  * to call more than once (a no-op once already set up for this thread). */
178 void
179 uc_vm_signal_handlers_ensure(uc_vm_t *vm)
180 {
181         uc_thread_context_t *tctx = uc_thread_context_get();
182 
183         if (tctx->signal_handler_vm)
184                 return;
185 
186         if (pipe2(vm->signal.sigpipe, O_CLOEXEC | O_NONBLOCK) != 0)
187                 return;
188 
189         vm->signal.handler = ucv_array_new_length(vm, UC_SYSTEM_SIGNAL_COUNT);
190 
191         vm->signal.sa.sa_handler = uc_vm_signal_handler;
192         vm->signal.sa.sa_flags = SA_RESTART | SA_ONSTACK;
193         sigemptyset(&vm->signal.sa.sa_mask);
194 
195         tctx->signal_handler_vm = vm;
196 }
197 
198 static void
199 uc_vm_signal_handlers_setup(uc_vm_t *vm)
200 {
201         memset(&vm->signal, 0, sizeof(vm->signal));
202 
203         vm->signal.sigpipe[0] = -1;
204         vm->signal.sigpipe[1] = -1;
205 
206         if (!vm->config->setup_signal_handlers)
207                 return;
208 
209         uc_vm_signal_handlers_ensure(vm);
210 }
211 
212 static void
213 uc_vm_signal_handlers_reset(uc_vm_t *vm)
214 {
215         uc_thread_context_t *tctx = uc_thread_context_get();
216         struct sigaction sa = { 0 };
217         size_t i, signo;
218 
219         if (vm != tctx->signal_handler_vm)
220                 return;
221 
222         sa.sa_handler = SIG_DFL;
223         sigemptyset(&sa.sa_mask);
224 
225         for (signo = 0; signo < ucv_array_length(vm->signal.handler); signo++)
226                 if (ucv_is_callable(ucv_array_get(vm->signal.handler, signo)))
227                         sigaction(signo, &sa, NULL);
228 
229         for (i = 0; i < ARRAY_SIZE(vm->signal.sigpipe); i++) {
230                 if (vm->signal.sigpipe[i] > STDERR_FILENO)
231                         close(vm->signal.sigpipe[i]);
232 
233                 vm->signal.sigpipe[i] = -1;
234         }
235 
236                 tctx->signal_handler_vm = NULL;
237 }
238 
239 void uc_vm_break_init(uc_vm_t *vm)
240 {
241         vm->break_requested = false;
242         vm->break_notifyfd[0] = -1;
243         vm->break_notifyfd[1] = -1;
244 
245         if (pipe2(vm->break_notifyfd, O_CLOEXEC | O_NONBLOCK) == 0) {
246                 /* pipe created successfully */
247         }
248 }
249 
250 void uc_vm_break_cleanup(uc_vm_t *vm)
251 {
252         for (size_t i = 0; i < ARRAY_SIZE(vm->break_notifyfd); i++) {
253                 if (vm->break_notifyfd[i] > STDERR_FILENO)
254                         close(vm->break_notifyfd[i]);
255                 vm->break_notifyfd[i] = -1;
256         }
257 }
258 
259 void uc_vm_init(uc_vm_t *vm, uc_parse_config_t *config)
260 {
261         vm->exception.type = EXCEPTION_NONE;
262         vm->exception.message = NULL;
263 
264         vm->config = config ? config : &uc_default_parse_config;
265 
266         vm->open_upvals = NULL;
267 
268         vm->sources = lh_kchar_table_new(16, NULL);
269 
270         vm->values.prev = &vm->values;
271         vm->values.next = &vm->values;
272 
273         vm->strbuf = NULL;
274 
275         vm->output = stdout;
276 
277         uc_vm_reset_stack(vm);
278 
279         uc_vm_alloc_global_scope(vm);
280 
281         uc_vm_exception_handler_set(vm, uc_vm_output_exception);
282 
283         uc_vm_trace_set(vm, 0);
284 
285         uc_vm_signal_handlers_setup(vm);
286 
287         uc_vm_break_init(vm);
288 
289         uc_thread_context_get()->refcount++;
290 }
291 
292 void uc_vm_free(uc_vm_t *vm)
293 {
294         uc_thread_context_t *ctx;
295         uc_upvalref_t *ref;
296         size_t i;
297 
298         uc_vm_signal_handlers_reset(vm);
299 
300         uc_vm_break_cleanup(vm);
301 
302         ucv_put(vm->exception.stacktrace);
303         free(vm->exception.message);
304 
305         while (vm->open_upvals) {
306                 ref = vm->open_upvals->next;
307                 ucv_put(&vm->open_upvals->header);
308                 vm->open_upvals = ref;
309         }
310 
311         /* Resource type prototypes are part of the VM value pool, so release the
312          * restype refs before the final GC; any references still held by
313          * GC-managed values are dropped by ucv_freeall() itself. */
314         for (i = 0; i < vm->restypes.count; i++)
315                 ucv_put(vm->restypes.entries[i]->proto);
316 
317         uc_vm_reset_callframes(vm);
318         uc_vm_reset_stack(vm);
319         uc_vector_clear(&vm->stack);
320         uc_vector_clear(&vm->callframes);
321 
322         printbuf_free(vm->strbuf);
323 
324         ucv_freeall(vm);
325 
326         /* Release source cache entries after freeall so that all program refs
327          * to sources are already gone and the cache refs are the last ones. */
328         if (vm->sources) {
329                 struct lh_entry *entry, *next;
330 
331                 entry = vm->sources->head;
332 
333                 while (entry) {
334                         next = entry->next;
335                         ucv_put((uc_value_t *)lh_entry_v(entry));
336                         free((char *)lh_entry_k(entry));
337                         entry = next;
338                 }
339 
340                 lh_table_free(vm->sources);
341         }
342 
343         for (i = 0; i < vm->restypes.count; i++)
344                 free(vm->restypes.entries[i]);
345 
346         uc_vector_clear(&vm->restypes);
347 
348         for (i = 0; i < vm->breakpoints.count; i++)
349                 free(vm->breakpoints.entries[i]);
350 
351         uc_vector_clear(&vm->breakpoints);
352 
353         ctx = uc_thread_context_get();
354 
355         assert(ctx->refcount > 0);
356 
357         if (--ctx->refcount == 0)
358                 uc_thread_context_free();
359 }
360 
361 static uc_chunk_t *
362 uc_vm_frame_chunk(uc_callframe_t *frame)
363 {
364         return frame->closure ? &frame->closure->function->chunk : NULL;
365 }
366 
367 static uc_program_t *
368 uc_vm_frame_program(uc_callframe_t *frame)
369 {
370         return frame->closure ? frame->closure->function->program : NULL;
371 }
372 
373 static uc_source_t *
374 uc_vm_frame_source(uc_callframe_t *frame)
375 {
376         return frame->closure ? uc_program_function_source(frame->closure->function) : NULL;
377 }
378 
379 static uc_callframe_t *
380 uc_vm_current_frame(uc_vm_t *vm)
381 {
382         return uc_vector_last(&vm->callframes);
383 }
384 
385 static uc_program_t *
386 uc_vm_current_program(uc_vm_t *vm)
387 {
388         return uc_vm_frame_program(uc_vm_current_frame(vm));
389 }
390 
391 static uc_vm_insn_t
392 uc_vm_decode_insn(uc_vm_t *vm, uc_callframe_t *frame, uc_chunk_t *chunk)
393 {
394         uc_vm_insn_t insn;
395         int8_t argtype;
396 
397 #ifndef NDEBUG
398         uint8_t *end = chunk->entries + chunk->count;
399 #endif
400 
401         assert(frame->ip < end);
402 
403         for (size_t i = 0; i < vm->breakpoints.count; i++) {
404                 uc_breakpoint_t *bk = vm->breakpoints.entries[i];
405 
406                 if (bk != NULL && (bk->ip == NULL || bk->ip == frame->ip))
407                         bk->cb(vm, bk);
408         }
409 
410         insn = frame->ip[0];
411         frame->ip++;
412 
413         argtype = uc_vm_insn_to_argtype(insn);
414 
415         assert(frame->ip + abs(argtype) <= end);
416 
417         switch (argtype) {
418         case 0:
419                 break;
420 
421         case -4:
422                 vm->arg.s32 = (
423                         frame->ip[0] * 0x1000000UL +
424                         frame->ip[1] * 0x10000UL +
425                         frame->ip[2] * 0x100UL +
426                         frame->ip[3]
427                 ) - 0x7fffffff;
428                 frame->ip += 4;
429                 break;
430 
431         case 1:
432                 vm->arg.u8 = frame->ip[0];
433                 frame->ip++;
434                 break;
435 
436         case 2:
437                 vm->arg.u16 = (
438                         frame->ip[0] * 0x100 +
439                         frame->ip[1]
440                 );
441                 frame->ip += 2;
442                 break;
443 
444         case 4:
445                 vm->arg.u32 = (
446                         frame->ip[0] * 0x1000000UL +
447                         frame->ip[1] * 0x10000UL +
448                         frame->ip[2] * 0x100UL +
449                         frame->ip[3]
450                 );
451                 frame->ip += 4;
452                 break;
453 
454         default:
455                 fprintf(stderr, "Unhandled operand format: %" PRId8 "\n", argtype);
456                 abort();
457         }
458 
459         return insn;
460 }
461 
462 
463 static char *
464 uc_vm_format_val(uc_vm_t *vm, uc_value_t *val)
465 {
466         if (!vm->strbuf)
467                 vm->strbuf = xprintbuf_new();
468         else
469                 printbuf_reset(vm->strbuf);
470 
471         ucv_to_stringbuf(NULL, vm->strbuf, val, true);
472 
473         if (printbuf_length(vm->strbuf) >= 64) {
474                 printbuf_memset(vm->strbuf, 60, '.', 3);
475                 printbuf_memset(vm->strbuf, 63, 0, 1);
476         }
477 
478         return vm->strbuf->buf;
479 }
480 
481 static void
482 uc_vm_frame_dump(uc_vm_t *vm, uc_callframe_t *frame)
483 {
484         uc_chunk_t *chunk = uc_vm_frame_chunk(frame);
485         uc_function_t *function;
486         uc_closure_t *closure;
487         uc_upvalref_t *ref;
488         uc_value_t *v;
489         size_t i;
490 
491         fprintf(stderr, "  [*] CALLFRAME[%zx]\n",
492                 frame - vm->callframes.entries);
493 
494         fprintf(stderr, "   |- stackframe %zu/%zu\n",
495                 frame->stackframe, vm->stack.count);
496 
497         fprintf(stderr, "   |- ctx %s\n",
498                 uc_vm_format_val(vm, frame->ctx));
499 
500         if (frame->tco)
501                 fprintf(stderr, "   |- tailcall (%u callframes omitted)\n",
502                         frame->tco);
503 
504         if (chunk) {
505                 closure = frame->closure;
506                 function = closure->function;
507 
508                 fprintf(stderr, "   `- %zu upvalues\n",
509                         function->nupvals);
510 
511                 for (i = 0; i < function->nupvals; i++) {
512                         ref = closure->upvals[i];
513                         v = uc_chunk_debug_get_variable(chunk, 0, i, true);
514 
515                         fprintf(stderr, "     [%zu] <%p> %s ",
516                                 i, (void *)ref, uc_vm_format_val(vm, v));
517 
518                         if (!ref) {
519                                 fprintf(stderr, "{unresolved}\n");
520                         }
521                         else if (ref->closed) {
522                                 fprintf(stderr, "{closed} %s\n",
523                                         uc_vm_format_val(vm, ref->value));
524                         }
525                         else {
526                                 fprintf(stderr, "{open[%zu]} %s\n",
527                                         ref->slot,
528                                         uc_vm_format_val(vm, vm->stack.entries[ref->slot]));
529                         }
530 
531                         ucv_put(v);
532                 }
533         }
534 }
535 
536 static uc_value_t *
537 uc_vm_resolve_upval(uc_vm_t *vm, uc_value_t *value)
538 {
539         uc_upvalref_t *ref;
540         uc_value_t *rv;
541 
542 #ifdef __clang_analyzer__
543         /* Clang static analyzer does not understand that ucv_type(NULL) can't
544          * possibly yield UC_UPVALUE. Nudge it. */
545         if (value != NULL && ucv_type(value) == UC_UPVALUE)
546 #else
547         if (ucv_type(value) == UC_UPVALUE)
548 #endif
549         {
550                 ref = ucv_as_upvalue(value);
551 
552                 if (ref->closed)
553                         rv = ucv_get(ref->value);
554                 else
555                         rv = ucv_get(vm->stack.entries[ref->slot]);
556 
557                 ucv_put(value);
558 
559                 return rv;
560         }
561 
562         return value;
563 }
564 
565 void
566 uc_vm_stack_push(uc_vm_t *vm, uc_value_t *value)
567 {
568         uc_vector_push(&vm->stack, uc_vm_resolve_upval(vm, value));
569 
570         if (vm->trace) {
571                 fprintf(stderr, "  [+%zd] %s\n",
572                         vm->stack.count - 1,
573                         uc_vm_format_val(vm, vm->stack.entries[vm->stack.count - 1]));
574         }
575 }
576 
577 uc_value_t *
578 uc_vm_stack_pop(uc_vm_t *vm)
579 {
580         uc_value_t *rv;
581 
582         vm->stack.count--;
583         rv = vm->stack.entries[vm->stack.count];
584         vm->stack.entries[vm->stack.count] = NULL;
585 
586         if (vm->trace) {
587                 fprintf(stderr, "  [-%zd] %s\n",
588                         vm->stack.count,
589                         uc_vm_format_val(vm, rv));
590         }
591 
592         return rv;
593 }
594 
595 uc_value_t *
596 uc_vm_stack_peek(uc_vm_t *vm, size_t offset)
597 {
598         return vm->stack.entries[vm->stack.count + (-1 - offset)];
599 }
600 
601 static void
602 uc_vm_stack_set(uc_vm_t *vm, size_t offset, uc_value_t *value)
603 {
604         if (vm->trace) {
605                 fprintf(stderr, "  [!%zu] %s\n",
606                         offset,
607                         uc_vm_format_val(vm, value));
608         }
609 
610         ucv_put(vm->stack.entries[offset]);
611         vm->stack.entries[offset] = value;
612 }
613 
614 static void
615 uc_vm_call_native(uc_vm_t *vm, uc_value_t *ctx, uc_cfunction_t *fptr, bool mcall, size_t nargs)
616 {
617         uc_value_t *res = NULL;
618         uc_callframe_t *frame;
619 
620         /* add new callframe */
621         frame = uc_vector_push(&vm->callframes, {
622                 .stackframe = vm->stack.count - nargs - 1,
623                 .cfunction = fptr,
624                 .closure = NULL,
625                 .ctx = ctx,
626                 .mcall = mcall
627         });
628 
629         if (vm->trace)
630                 uc_vm_frame_dump(vm, frame);
631 
632         res = fptr->cfn(vm, nargs);
633 
634         /* Reset stack, check for callframe depth since an uncatched exception in managed
635          * code executed by fptr->cfn() could've reset the callframe stack already. */
636         if (vm->callframes.count > 0)
637                 ucv_put(uc_vm_callframe_pop(vm));
638 
639         /* push return value */
640         if (!vm->exception.type)
641                 uc_vm_stack_push(vm, res);
642         else
643                 ucv_put(res);
644 }
645 
646 /* Replaces the current call frame by an invocation frame for the given closure,
647  * reusing the stack slots formerly occupied by the current frame.
648  *
649  * Since neither the call frame vector nor the value stack grow when invoking the
650  * callee, this allows unbounded tail recursion.
651  *
652  * On entry, the stack holds the invoked function value and its arguments at the
653  * top, optionally preceded by the receiver value of a method invocation. */
654 static void
655 uc_vm_frame_reinit(uc_vm_t *vm, uc_closure_t *closure, uc_value_t *ctx,
656                    bool mcall, size_t nargs)
657 {
658         size_t i, blocksrc, blockdst, blocklen, blockofs;
659         uc_callframe_t *frame = uc_vm_current_frame(vm);
660         uc_value_t *oldclosure, *oldctx, *val;
661 
662         /* the slot the invoking code expects the eventual return value at, which is
663          * the slot of the current frame's function value, or - for method calls - of
664          * the receiver value immediately below it */
665         blockdst = frame->stackframe - (frame->mcall ? 1 : 0);
666 
667         /* number of stack slots occupied by the receiver, function and arguments */
668         blocklen = nargs + 1 + (mcall ? 1 : 0);
669 
670         /* slot of the invoked function value, or of the receiver value preceding it */
671         blocksrc = vm->stack.count - blocklen;
672 
673         /* the value block is relocated towards the start of the stack, so the slots
674          * it occupies are always read before being overwritten */
675         blockofs = blocksrc - blockdst;
676 
677         /* close upvalues referring to the current frame's stack slots before overwriting them */
678         uc_vm_close_upvals(vm, frame->stackframe);
679 
680         /* remember the value references owned by the current frame */
681         oldclosure = frame->closure ? &frame->closure->header : NULL;
682         oldctx = frame->ctx;
683 
684         /* move receiver, function value and arguments down into the slots of the frame
685          * being replaced, releasing the local variable, temporary and receiver values
686          * formerly held by them and clearing stale slots left above the new frame */
687         for (i = blockdst; i < vm->stack.count; i++) {
688                 val = (i - blockdst < blocklen) ? vm->stack.entries[i + blockofs] : NULL;
689 
690                 /* the slot holds a value owned by the current frame unless it is part of
691                  * the value block being relocated */
692                 if (i < blocksrc)
693                         ucv_put(vm->stack.entries[i]);
694 
695                 vm->stack.entries[i] = val;
696         }
697 
698         vm->stack.count = blockdst + blocklen;
699 
700         /* reinitialize the call frame in place; the receiver value of a method call
701          * occupies the slot below the invoked function value. the tco counter records
702          * that this frame replaced the tail calling one, so stack traces can indicate
703          * the collapsed frames */
704         frame->stackframe = blockdst + (mcall ? 1 : 0);
705         frame->cfunction = NULL;
706         frame->closure = closure;
707         frame->ctx = ctx;
708         frame->ip = closure->function->chunk.entries;
709         frame->mcall = mcall;
710         frame->strict = closure->function->strict;
711 
712         /* saturate rather than wrap, so the count stays a valid (if capped) number
713          * of omitted frames */
714         if (frame->tco < 0xffff)
715                 frame->tco++;
716 
717         if (vm->trace)
718                 uc_vm_frame_dump(vm, frame);
719 
720         /* release the references formerly owned by the current frame */
721         ucv_put(oldclosure);
722         ucv_put(oldctx);
723 }
724 
725 static bool
726 uc_vm_call_function(uc_vm_t *vm, uc_value_t *ctx, uc_value_t *fno, bool mcall,
727                    size_t argspec, bool tail)
728 {
729         size_t i, j, stackoff, nargs = argspec & 0xffff;
730         size_t nspreads = (argspec >> 16) & 0x7fff;
731         uc_callframe_t *frame = NULL;
732         uc_value_t *ellip, *arg;
733         uc_function_t *function;
734         uc_closure_t *closure;
735         uint16_t slot, tmp;
736         char *s;
737 
738         /* a tail invocation reuses the current frame, so it never grows the stack */
739         if (!tail && vm->callframes.count >= 1000) {
740                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Too much recursion");
741                 ucv_put(ctx);
742                 ucv_put(fno);
743 
744                 return false;
745         }
746 
747         stackoff = vm->stack.count - nargs - 1;
748 
749         /* argument list contains spread operations, we need to reshuffle the stack */
750         if (nspreads > 0) {
751                 frame = uc_vm_current_frame(vm);
752 
753                 /* create temporary array */
754                 ellip = ucv_array_new_length(vm, nargs);
755 
756                 /* pop original stack values and push to temp array in reverse order */
757                 for (i = 0; i < nargs; i++)
758                         ucv_array_push(ellip, uc_vm_stack_pop(vm));
759 
760                 /* for each spread value index ... */
761                 for (i = 0, slot = nargs; i < nspreads; i++) {
762                         /* decode stack depth value */
763                         tmp = frame->ip[0] * 0x100 + frame->ip[1];
764                         frame->ip += 2;
765 
766                         /* push each preceeding non-spread value to the stack */
767                         for (j = slot; j > tmp + 1UL; j--)
768                                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(ellip, j - 1)));
769 
770                         /* read spread value at index... */
771                         slot = tmp;
772                         arg = ucv_get(ucv_array_get(ellip, slot));
773 
774                         /* ... ensure that it is an array type ... */
775                         if (ucv_type(arg) != UC_ARRAY) {
776                                 s = ucv_to_string(vm, arg);
777                                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "(%s) is not iterable", s);
778                                 free(s);
779                                 ucv_put(ctx);
780                                 ucv_put(fno);
781                                 ucv_put(ellip);
782 
783                                 return false;
784                         }
785 
786                         /* ... and push each spread array value as argument to the stack */
787                         for (j = 0; j < ucv_array_length(arg); j++)
788                                 uc_vm_stack_push(vm, ucv_get(ucv_array_get(arg, j)));
789 
790                         ucv_put(arg);
791                 }
792 
793                 /* push remaining non-spread arguments to the stack */
794                 for (i = slot; i > 0; i--)
795                         uc_vm_stack_push(vm, ucv_get(ucv_array_get(ellip, i - 1)));
796 
797                 /* free temp array */
798                 ucv_put(ellip);
799 
800                 /* update arg count */
801                 nargs = vm->stack.count - stackoff - 1;
802         }
803 
804         /* __call__ dispatch: if fno is not itself callable (not a closure or
805          * cfunction) but carries a __call__ metamethod somewhere in its prototype
806          * chain, invoke that method with the instance as `this`. The instance
807          * becomes the new ctx; the original ctx (if any) is discarded. The tail
808          * flag is preserved so `return foo()` still TCOs through the dispatch. */
809         if (ucv_type(fno) != UC_CFUNCTION && ucv_type(fno) != UC_CLOSURE) {
810                 uc_value_t *meta = ucv_metamethod_lookup(fno, "__call__");
811 
812                 if (ucv_is_callable(meta)) {
813                         ucv_put(ctx);
814 
815                         /* the instance becomes `this` and is invoked in place of the
816                          * __call__ method, so hand the reference to ctx over */
817                         ctx = fno;
818                         fno = ucv_get(meta);
819                 }
820         }
821 
822         /* is a native function; such functions are always invoked in a fresh frame,
823          * even when requested as tail call, since the foreign code may rely on the
824          * stack layout of an ordinary invocation */
825         if (ucv_type(fno) == UC_CFUNCTION) {
826                 uc_vm_call_native(vm, ctx, ucv_as_cfunction(fno), mcall, nargs);
827 
828                 return true;
829         }
830 
831         if (ucv_type(fno) != UC_CLOSURE) {
832                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "left-hand side is not a function");
833                 ucv_put(ctx);
834                 ucv_put(fno);
835 
836                 return false;
837         }
838 
839         closure = ucv_as_closure(fno);
840         function = closure->function;
841 
842         /* fewer arguments on stack than function expects => pad */
843         if (nargs < function->nargs) {
844                 for (i = nargs; i < function->nargs; i++) {
845                         if (function->vararg && (i + 1) == function->nargs)
846                                 uc_vm_stack_push(vm, ucv_array_new_length(vm, 0));
847                         else
848                                 uc_vm_stack_push(vm, NULL);
849                 }
850         }
851 
852         /* more arguments on stack than function expects... */
853         else if (nargs > function->nargs - function->vararg) {
854                 /* is a vararg function => pass excess args as array */
855                 if (function->vararg) {
856                         ellip = ucv_array_new_length(vm, nargs - (function->nargs - 1));
857 
858                         for (i = function->nargs; i <= nargs; i++)
859                                 ucv_array_push(ellip, uc_vm_stack_peek(vm, nargs - i));
860 
861                         for (i = function->nargs; i <= nargs; i++)
862                                 uc_vm_stack_pop(vm);
863 
864                         uc_vm_stack_push(vm, ellip);
865                 }
866 
867                 /* static amount of args => drop excess values */
868                 else {
869                         for (i = function->nargs; i < nargs; i++)
870                                 ucv_put(uc_vm_stack_pop(vm));
871                 }
872         }
873 
874         /* tail call: reuse the current call frame instead of pushing a new one. The
875          * number of values above the invoked function may differ from the encoded
876          * argument count due to spread operations or excess argument handling */
877         if (tail) {
878                 uc_vm_frame_reinit(vm, closure, ctx, mcall, vm->stack.count - stackoff - 1);
879 
880                 return true;
881         }
882 
883         frame = uc_vector_push(&vm->callframes, {
884                 .stackframe = stackoff,
885                 .cfunction = NULL,
886                 .closure = closure,
887                 .ctx = ctx,
888                 .ip = function->chunk.entries,
889                 .mcall = mcall,
890                 .strict = function->strict
891         });
892 
893         if (vm->trace)
894                 uc_vm_frame_dump(vm, frame);
895 
896         return true;
897 }
898 
899 static uc_source_t *last_source = NULL;
900 static size_t last_srcpos = 0;
901 
902 static void
903 uc_dump_insn(uc_vm_t *vm, uint8_t *pos, uc_vm_insn_t insn)
904 {
905         uc_callframe_t *frame = uc_vm_current_frame(vm);
906         uc_chunk_t *chunk = uc_vm_frame_chunk(frame);
907         uc_stringbuf_t *buf = NULL;
908         uc_value_t *cnst = NULL;
909         uc_source_t *source;
910         int8_t argtype;
911         size_t srcpos;
912 
913         srcpos = uc_program_function_srcpos(frame->closure->function, pos - chunk->entries);
914         source = uc_vm_frame_source(frame);
915 
916         if (last_srcpos == 0 || last_source != source || srcpos != last_srcpos) {
917                 buf = xprintbuf_new();
918 
919                 uc_source_context_format(buf, source, srcpos, true);
920                 fwrite(buf->buf, 1, printbuf_length(buf), stderr);
921                 printbuf_free(buf);
922 
923                 last_source = source;
924                 last_srcpos = srcpos;
925         }
926 
927         fprintf(stderr, "%08zx  %s", pos - chunk->entries,
928                 uc_vm_insn_to_name(insn));
929 
930         argtype = uc_vm_insn_to_argtype(insn);
931 
932         switch (argtype) {
933         case 0:
934                 break;
935 
936         case -1:
937                 fprintf(stderr, " {%s%hhd}", vm->arg.s8 < 0 ? "" : "+", vm->arg.s8);
938                 break;
939 
940         case -2:
941                 fprintf(stderr, " {%c0x%hx}",
942                         vm->arg.s16 < 0 ? '-' : '+',
943                         (uint16_t)(vm->arg.s16 < 0 ? -vm->arg.s16 : vm->arg.s16));
944                 break;
945 
946         case -4:
947                 fprintf(stderr, " {%c0x%x}",
948                         vm->arg.s32 < 0 ? '-' : '+',
949                         (uint32_t)(vm->arg.s32 < 0 ? -vm->arg.s32 : vm->arg.s32));
950                 break;
951 
952         case 1:
953                 fprintf(stderr, " {%hhu}", vm->arg.u8);
954                 break;
955 
956         case 2:
957                 fprintf(stderr, " {0x%hx}", vm->arg.u16);
958                 break;
959 
960         case 4:
961                 fprintf(stderr, " {0x%x}", vm->arg.u32);
962                 break;
963 
964         default:
965                 fprintf(stderr, " (unknown operand format: %" PRId8 ")", argtype);
966                 break;
967         }
968 
969         switch (insn) {
970         case I_LOAD:
971         case I_LVAR:
972         case I_SVAR:
973                 cnst = uc_program_get_constant(uc_vm_frame_program(uc_vector_last(&vm->callframes)), vm->arg.u32);
974 
975                 fprintf(stderr, "\t; %s",
976                         cnst ? uc_vm_format_val(vm, cnst) : "(?)");
977 
978                 ucv_put(cnst);
979                 break;
980 
981         case I_LLOC:
982         case I_LUPV:
983         case I_SLOC:
984         case I_SUPV:
985                 cnst = uc_chunk_debug_get_variable(chunk, pos - chunk->entries, vm->arg.u32, (insn == I_LUPV || insn == I_SUPV));
986 
987                 fprintf(stderr, "\t; %s",
988                         cnst ? uc_vm_format_val(vm, cnst) : "(?)");
989 
990                 ucv_put(cnst);
991                 break;
992 
993         case I_ULOC:
994         case I_UUPV:
995                 cnst = uc_chunk_debug_get_variable(chunk, pos - chunk->entries, vm->arg.u32 & 0x00ffffff, (insn == I_UUPV));
996                 /* fall through */
997 
998         case I_UVAR:
999                 if (!cnst)
1000                         cnst = uc_program_get_constant(uc_vm_frame_program(uc_vector_last(&vm->callframes)), vm->arg.u32 & 0x00ffffff);
1001 
1002                 fprintf(stderr, "\t; %s (%s)",
1003                         cnst ? uc_vm_format_val(vm, cnst) : "(?)",
1004                         uc_vm_insn_to_name(vm->arg.u32 >> 24));
1005 
1006                 ucv_put(cnst);
1007                 break;
1008 
1009         case I_UVAL:
1010                 fprintf(stderr, "\t; (%s)", uc_vm_insn_to_name(vm->arg.u8));
1011                 break;
1012 
1013         default:
1014                 break;
1015         }
1016 
1017         fprintf(stderr, "\n");
1018 }
1019 
1020 static uc_value_t *
1021 uc_vm_exception_tostring(uc_vm_t *vm, size_t nargs)
1022 {
1023         uc_callframe_t *frame = uc_vm_current_frame(vm);
1024         uc_value_t *message = ucv_object_get(frame->ctx, "message", NULL);
1025 
1026         return message ? ucv_get(message) : ucv_string_new("Exception");
1027 }
1028 
1029 uc_value_t *
1030 uc_vm_exception_object(uc_vm_t *vm)
1031 {
1032         uc_exception_type_t type = vm->exception.type;
1033         const char *message = vm->exception.message;
1034         uc_value_t *stacktrace = vm->exception.stacktrace;
1035         uc_value_t *exception_prototype = uc_vm_registry_get(vm, "vm.exception.proto");
1036         uc_value_t *exo;
1037 
1038         if (exception_prototype == NULL) {
1039                 exception_prototype = ucv_object_new(vm);
1040 
1041                 ucv_object_add(exception_prototype, "tostring",
1042                         ucv_cfunction_new("tostring", uc_vm_exception_tostring));
1043 
1044                 uc_vm_registry_set(vm, "vm.exception.proto", exception_prototype);
1045         }
1046 
1047         exo = ucv_object_new(vm);
1048 
1049         ucv_object_add(exo, "type", ucv_string_new(exception_type_strings[type]));
1050         ucv_object_add(exo, "message", ucv_string_new(message));
1051         ucv_object_add(exo, "stacktrace", ucv_get(stacktrace));
1052 
1053         ucv_prototype_set(exo, ucv_get(exception_prototype));
1054 
1055         return exo;
1056 }
1057 
1058 static void
1059 uc_vm_clear_exception(uc_vm_t *vm)
1060 {
1061         vm->exception.type = EXCEPTION_NONE;
1062 
1063         ucv_put(vm->exception.stacktrace);
1064         vm->exception.stacktrace = NULL;
1065 
1066         free(vm->exception.message);
1067         vm->exception.message = NULL;
1068 }
1069 
1070 /* Well-known sentinel `uc_breakpoint_t.ip` value identifying the dedicated
1071  * "break on uncaught exception" system breakpoint (see debug.c's BK_UNCAUGHT).
1072  * It deliberately isn't a real bytecode address, so the ordinary
1073  * ip-matching breakpoint dispatch in uc_vm_decode_insn() - which walks
1074  * vm->breakpoints on every single instruction - never fires it by
1075  * accident; it is only ever invoked explicitly, from the exception label in
1076  * uc_vm_execute_chunk() below, at the one moment it actually applies. */
1077 static uint8_t uc_breakpoint_uncaught_exception_storage;
1078 uint8_t *const UC_BREAKPOINT_UNCAUGHT_EXCEPTION =
1079         &uc_breakpoint_uncaught_exception_storage;
1080 
1081 /* Non-destructively predict whether uc_vm_handle_exception()'s real unwind
1082  * loop (below) would find a handler for the currently raised exception
1083  * anywhere between the current callframe and `caller` (the frame depth this
1084  * uc_vm_execute_chunk() invocation was entered at - the same boundary its
1085  * own unwind loop stops at). Mirrors that loop's exact stopping conditions
1086  * (a native callframe, or reaching `caller`) but only inspects state; nops
1087  * of the stack/exception state, jumping ip. Used to decide whether to break
1088  * into the debugger *before* unwinding starts, while the original throwing
1089  * frame - locals, exact position - is still fully intact, since once
1090  * uc_vm_handle_exception() starts really popping frames that's gone. */
1091 static bool
1092 uc_vm_exception_would_be_caught(uc_vm_t *vm, size_t caller)
1093 {
1094         for (size_t i = vm->callframes.count; i > caller; i--) {
1095                 uc_callframe_t *frame = &vm->callframes.entries[i - 1];
1096 
1097                 if (!frame->closure)
1098                         return false;
1099 
1100                 uc_chunk_t *chunk = &frame->closure->function->chunk;
1101                 size_t pos = frame->ip - chunk->entries;
1102 
1103                 for (size_t j = 0; j < chunk->ehranges.count; j++) {
1104                         if (pos >= chunk->ehranges.entries[j].from &&
1105                             pos < chunk->ehranges.entries[j].to)
1106                                 return true;
1107                 }
1108         }
1109 
1110         return false;
1111 }
1112 
1113 static bool
1114 uc_vm_handle_exception(uc_vm_t *vm)
1115 {
1116         uc_callframe_t *frame = NULL;
1117         uc_chunk_t *chunk = NULL;
1118         uc_value_t *exo;
1119         size_t i, pos;
1120 
1121         if (vm->callframes.count)
1122                 frame = uc_vm_current_frame(vm);
1123 
1124         if (!frame || !frame->closure)
1125                 return false;
1126 
1127         chunk = uc_vm_frame_chunk(frame);
1128         pos = frame->ip - chunk->entries;
1129 
1130         /* iterate the known exception ranges, see if the current ip falls into any of them */
1131         for (i = 0; i < chunk->ehranges.count; i++) {
1132                 /* skip nonmatching ranges */
1133                 if (pos < chunk->ehranges.entries[i].from ||
1134                     pos >= chunk->ehranges.entries[i].to)
1135                         continue;
1136 
1137                 /* we found a matching range... first unwind stack */
1138                 while (vm->stack.count > frame->stackframe + chunk->ehranges.entries[i].slot)
1139                         ucv_put(uc_vm_stack_pop(vm));
1140 
1141                 /* prepare exception object and expose it to user handler code */
1142                 exo = uc_vm_exception_object(vm);
1143 
1144                 uc_vm_stack_push(vm, exo);
1145 
1146                 /* reset exception information */
1147                 uc_vm_clear_exception(vm);
1148 
1149                 /* jump to exception handler */
1150                 if (chunk->ehranges.entries[i].target >= chunk->count) {
1151                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "jump target out of range");
1152                         return false;
1153                 }
1154 
1155 #if 0
1156                 if (vm->trace && chunk->entries + chunk->ehranges.entries[i].target > frame->ip) {
1157                         while (frame->ip < chunk->entries + chunk->ehranges.entries[i].target) {
1158                                 fprintf(stderr, "(eh:skip) [%p:%zu] ", chunk, frame->ip - chunk->entries);
1159                                 uc_dump_insn(vm, frame->ip, uc_vm_decode_insn(vm, frame, chunk));
1160                         }
1161                 }
1162 #endif
1163 
1164                 frame->ip = chunk->entries + chunk->ehranges.entries[i].target;
1165 
1166                 return true;
1167         }
1168 
1169         return false;
1170 }
1171 
1172 static uc_value_t *
1173 uc_vm_capture_stacktrace(uc_vm_t *vm, size_t i)
1174 {
1175         uc_value_t *stacktrace, *entry, *last = NULL;
1176         uc_function_t *function;
1177         uc_callframe_t *frame;
1178         uc_source_t *source;
1179         size_t off, srcpos;
1180         char *name;
1181 
1182         stacktrace = ucv_array_new(vm);
1183 
1184         for (; i > 0; i--) {
1185                 frame = &vm->callframes.entries[i - 1];
1186                 entry = ucv_object_new(vm);
1187 
1188                 if (frame->closure) {
1189                         function = frame->closure->function;
1190                         source = uc_program_function_source(function);
1191 
1192                         off = (frame->ip - uc_vm_frame_chunk(frame)->entries) - 1;
1193                         srcpos = uc_program_function_srcpos(function, off);
1194 
1195                         ucv_object_add(entry, "filename", ucv_string_new(source->filename));
1196                         ucv_object_add(entry, "line", ucv_int64_new(uc_source_get_line(source, &srcpos)));
1197                         ucv_object_add(entry, "byte", ucv_int64_new(srcpos));
1198                 }
1199 
1200                 if (i > 1) {
1201                         if (frame->closure) {
1202                                 if (frame->closure->function->name[0])
1203                                         name = frame->closure->function->name;
1204                                 else if (frame->closure->is_arrow)
1205                                         name = "[arrow function]";
1206                                 else
1207                                         name = "[anonymous function]";
1208                         }
1209                         else {
1210                                 name = frame->cfunction->name;
1211                         }
1212 
1213                         ucv_object_add(entry, "function", ucv_string_new(name));
1214                 }
1215 
1216                 /* record the number of call frames collapsed into this one by tail call
1217                  * optimization, so consumers can indicate the missing frames */
1218                 if (frame->tco)
1219                         ucv_object_add(entry, "tco", ucv_int64_new(frame->tco));
1220 
1221                 if (!ucv_is_equal(last, entry)) {
1222                         ucv_array_push(stacktrace, entry);
1223                         last = entry;
1224                 }
1225                 else {
1226                         ucv_put(entry);
1227                 }
1228         }
1229 
1230         return stacktrace;
1231 }
1232 
1233 static uc_value_t *
1234 uc_vm_get_error_context(uc_vm_t *vm)
1235 {
1236         size_t offset, i, byte, line;
1237         uc_value_t *stacktrace;
1238         uc_callframe_t *frame;
1239         uc_stringbuf_t *buf;
1240         uc_chunk_t *chunk;
1241 
1242         if (vm->callframes.count == 0)
1243                 return NULL;
1244 
1245         /* skip to first non-native function call frame */
1246         for (i = vm->callframes.count; i > 1; i--)
1247                 if (vm->callframes.entries[i - 1].closure)
1248                         break;
1249 
1250         frame = &vm->callframes.entries[i - 1];
1251 
1252         if (!frame->closure)
1253                 return NULL;
1254 
1255         chunk = uc_vm_frame_chunk(frame);
1256         offset = uc_program_function_srcpos(frame->closure->function, (frame->ip - chunk->entries) - 1);
1257         stacktrace = uc_vm_capture_stacktrace(vm, i);
1258 
1259         buf = ucv_stringbuf_new();
1260 
1261         byte = offset;
1262         line = uc_source_get_line(uc_program_function_source(frame->closure->function), &byte);
1263 
1264         if (line)
1265                 uc_error_context_format(buf, uc_vm_frame_source(frame), stacktrace, offset);
1266         else if (frame->ip != chunk->entries)
1267                 ucv_stringbuf_printf(buf, "At instruction %zu", (frame->ip - chunk->entries) - 1);
1268         else
1269                 ucv_stringbuf_append(buf, "At start of program");
1270 
1271         ucv_object_add(ucv_array_get(stacktrace, 0), "context", ucv_stringbuf_finish(buf));
1272 
1273         return stacktrace;
1274 }
1275 
1276 void __attribute__((format(printf, 3, 0)))
1277 uc_vm_raise_exception(uc_vm_t *vm, uc_exception_type_t type, const char *fmt, ...)
1278 {
1279         va_list ap;
1280 
1281         vm->exception.type = type;
1282 
1283         free(vm->exception.message);
1284 
1285         va_start(ap, fmt);
1286         xvasprintf(&vm->exception.message, fmt, ap);
1287         va_end(ap);
1288 
1289         ucv_put(vm->exception.stacktrace);
1290         vm->exception.stacktrace = uc_vm_get_error_context(vm);
1291 }
1292 
1293 static bool
1294 uc_vm_test_strict_equality(uc_value_t *v1, uc_value_t *v2, bool nan_equal)
1295 {
1296         uc_type_t t1 = ucv_type(v1);
1297         uc_type_t t2 = ucv_type(v2);
1298         double d1, d2;
1299 
1300         if (t1 != t2)
1301                 return false;
1302 
1303         switch (t1) {
1304         case UC_DOUBLE:
1305                 d1 = ucv_as_double(v1)->dbl;
1306                 d2 = ucv_as_double(v2)->dbl;
1307 
1308                 if (isnan(d1) && isnan(d2))
1309                         return nan_equal;
1310 
1311                 return (d1 == d2);
1312 
1313         case UC_NULL:
1314         case UC_BOOLEAN:
1315         case UC_INTEGER:
1316         case UC_STRING:
1317                 return ucv_is_equal(v1, v2);
1318 
1319         default:
1320                 return (v1 == v2);
1321         }
1322 }
1323 
1324 
1325 static void
1326 uc_vm_insn_load(uc_vm_t *vm, uc_vm_insn_t insn)
1327 {
1328         switch (insn) {
1329         case I_LOAD:
1330                 uc_vm_stack_push(vm, uc_program_get_constant(uc_vm_current_program(vm), vm->arg.u32));
1331                 break;
1332 
1333         case I_LOAD8:
1334                 uc_vm_stack_push(vm, ucv_uint64_new(vm->arg.u8));
1335                 break;
1336 
1337         case I_LOAD16:
1338                 uc_vm_stack_push(vm, ucv_uint64_new(vm->arg.u16));
1339                 break;
1340 
1341         case I_LOAD32:
1342                 uc_vm_stack_push(vm, ucv_uint64_new(vm->arg.u32));
1343                 break;
1344 
1345         default:
1346                 break;
1347         }
1348 }
1349 
1350 static void
1351 uc_vm_insn_load_regexp(uc_vm_t *vm, uc_vm_insn_t insn)
1352 {
1353         uc_value_t *re, *jstr = uc_program_get_constant(uc_vm_current_program(vm), vm->arg.u32);
1354         bool icase = false, newline = false, global = false;
1355         char *str, *err = NULL;
1356 
1357         if (ucv_type(jstr) != UC_STRING || ucv_string_length(jstr) < 2) {
1358                 uc_vm_stack_push(vm, NULL);
1359                 ucv_put(jstr);
1360 
1361                 return;
1362         }
1363 
1364         str = ucv_string_get(jstr);
1365 
1366         global  = (*str & (1 << 0));
1367         icase   = (*str & (1 << 1));
1368         newline = (*str & (1 << 2));
1369 
1370         re = ucv_regexp_new(++str, icase, newline, global, &err);
1371 
1372         ucv_put(jstr);
1373 
1374         if (re)
1375                 uc_vm_stack_push(vm, re);
1376         else
1377                 uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "%s", err);
1378 
1379         free(err);
1380 }
1381 
1382 static void
1383 uc_vm_insn_load_null(uc_vm_t *vm, uc_vm_insn_t insn)
1384 {
1385         uc_vm_stack_push(vm, NULL);
1386 }
1387 
1388 static void
1389 uc_vm_insn_load_bool(uc_vm_t *vm, uc_vm_insn_t insn)
1390 {
1391         uc_vm_stack_push(vm, ucv_boolean_new(insn == I_LTRUE));
1392 }
1393 
1394 static void
1395 uc_vm_insn_load_var(uc_vm_t *vm, uc_vm_insn_t insn)
1396 {
1397         uc_value_t *name, *val = NULL;
1398         uc_value_t *scope, *next;
1399         bool found;
1400 
1401         scope = vm->globals;
1402         name = uc_program_get_constant(uc_vm_current_program(vm), vm->arg.u32);
1403 
1404         while (ucv_type(name) == UC_STRING) {
1405                 val = ucv_object_get(scope, ucv_string_get(name), &found);
1406 
1407                 if (found)
1408                         break;
1409 
1410                 next = ucv_prototype_get(scope);
1411 
1412                 if (!next) {
1413                         if (uc_vm_is_strict(vm)) {
1414                                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
1415                                                       "access to undeclared variable %s",
1416                                                       ucv_string_get(name));
1417                         }
1418 
1419                         break;
1420                 }
1421 
1422                 scope = next;
1423         }
1424 
1425         ucv_put(name);
1426 
1427         uc_vm_stack_push(vm, ucv_get(val));
1428 }
1429 
1430 static void
1431 uc_vm_insn_load_val(uc_vm_t *vm, uc_vm_insn_t insn)
1432 {
1433         uc_value_t *k = uc_vm_stack_pop(vm);
1434         uc_value_t *v = uc_vm_stack_pop(vm);
1435 
1436         switch (ucv_type(v)) {
1437         case UC_RESOURCE:
1438         case UC_OBJECT:
1439         case UC_ARRAY:
1440                 /* aware read: dispatches __get__ on a missing key */
1441                 uc_vm_stack_push(vm, ucv_key_get(vm, v, k));
1442                 break;
1443 
1444         default:
1445                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
1446                                       "left-hand side expression is %s",
1447                                       v ? "not an array or object" : "null");
1448 
1449                 break;
1450         }
1451 
1452         ucv_put(k);
1453         ucv_put(v);
1454 }
1455 
1456 static void
1457 uc_vm_insn_peek_val(uc_vm_t *vm, uc_vm_insn_t insn)
1458 {
1459         uc_value_t *k = uc_vm_stack_pop(vm);
1460         uc_value_t *v = uc_vm_stack_peek(vm, 0);
1461 
1462         switch (ucv_type(v)) {
1463         case UC_RESOURCE:
1464         case UC_OBJECT:
1465         case UC_ARRAY:
1466                 /* aware read: dispatches __get__ on a missing key */
1467                 uc_vm_stack_push(vm, ucv_key_get(vm, v, k));
1468                 break;
1469 
1470         default:
1471                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
1472                                                                 "left-hand side expression is %s",
1473                                                                 v ? "not an array or object" : "null");
1474 
1475                 break;
1476         }
1477 
1478         ucv_put(k);
1479 }
1480 
1481 static void
1482 uc_vm_insn_load_upval(uc_vm_t *vm, uc_vm_insn_t insn)
1483 {
1484         uc_callframe_t *frame = uc_vm_current_frame(vm);
1485         uc_upvalref_t *ref = frame->closure->upvals[vm->arg.u32];
1486 
1487         if (ref->closed)
1488                 uc_vm_stack_push(vm, ucv_get(ref->value));
1489         else
1490                 uc_vm_stack_push(vm, ucv_get(vm->stack.entries[ref->slot]));
1491 }
1492 
1493 static void
1494 uc_vm_insn_load_local(uc_vm_t *vm, uc_vm_insn_t insn)
1495 {
1496         uc_callframe_t *frame = uc_vm_current_frame(vm);
1497 
1498         uc_vm_stack_push(vm, ucv_get(vm->stack.entries[frame->stackframe + vm->arg.u32]));
1499 }
1500 
1501 static uc_upvalref_t *
1502 uc_vm_capture_upval(uc_vm_t *vm, size_t slot)
1503 {
1504         uc_upvalref_t *curr = vm->open_upvals;
1505         uc_upvalref_t *prev = NULL;
1506         uc_upvalref_t *created;
1507         char *s;
1508 
1509         while (curr && curr->slot > slot) {
1510                 prev = curr;
1511                 curr = curr->next;
1512         }
1513 
1514         if (curr && curr->slot == slot) {
1515                 if (vm->trace) {
1516                         s = ucv_to_string(NULL, vm->stack.entries[slot]);
1517                         fprintf(stderr, "  {+%zu} <%p> %s\n", slot, (void *)curr, s);
1518                         free(s);
1519                 }
1520 
1521                 return curr;
1522         }
1523 
1524         created = (uc_upvalref_t *)ucv_upvalref_new(slot);
1525         created->next = curr;
1526 
1527         if (vm->trace) {
1528                 s = ucv_to_string(NULL, vm->stack.entries[slot]);
1529                 fprintf(stderr, "  {*%zu} <%p> %s\n", slot, (void *)created, s);
1530                 free(s);
1531         }
1532 
1533         if (prev)
1534                 prev->next = created;
1535         else
1536                 vm->open_upvals = created;
1537 
1538         return created;
1539 }
1540 
1541 static void
1542 uc_vm_close_upvals(uc_vm_t *vm, size_t slot)
1543 {
1544         uc_upvalref_t *ref;
1545 
1546         while (vm->open_upvals && vm->open_upvals->slot >= slot) {
1547                 ref = vm->open_upvals;
1548                 ref->value = ucv_get(vm->stack.entries[ref->slot]);
1549                 ref->closed = true;
1550 
1551                 if (vm->trace) {
1552                         fprintf(stderr, "  {!%zu} <%p> %s\n", ref->slot,
1553                                 (void *)ref,
1554                                 uc_vm_format_val(vm, ref->value));
1555                 }
1556 
1557                 vm->open_upvals = ref->next;
1558                 ucv_put(&ref->header);
1559         }
1560 }
1561 
1562 static void
1563 uc_vm_insn_load_closure(uc_vm_t *vm, uc_vm_insn_t insn)
1564 {
1565         uc_callframe_t *frame = uc_vm_current_frame(vm);
1566         uc_function_t *function = uc_program_function_load(uc_vm_current_program(vm), vm->arg.u32);
1567         uc_closure_t *closure = (uc_closure_t *)ucv_closure_new(vm, function, insn == I_ARFN);
1568         volatile int32_t uv;
1569         size_t i;
1570 
1571         uc_vm_stack_push(vm, &closure->header);
1572 
1573         if (function->module)
1574                 return;
1575 
1576         for (i = 0; i < function->nupvals; i++) {
1577                 uv = (
1578                         frame->ip[0] * 0x1000000 +
1579                         frame->ip[1] * 0x10000 +
1580                         frame->ip[2] * 0x100 +
1581                         frame->ip[3]
1582                 ) - 0x7fffffff;
1583 
1584                 if (uv < 0)
1585                         closure->upvals[i] = uc_vm_capture_upval(vm, frame->stackframe - (uv + 1));
1586                 else
1587                         closure->upvals[i] = frame->closure->upvals[uv];
1588 
1589                 ucv_get(&closure->upvals[i]->header);
1590 
1591                 frame->ip += 4;
1592         }
1593 }
1594 
1595 static void
1596 uc_vm_insn_store_var(uc_vm_t *vm, uc_vm_insn_t insn)
1597 {
1598         uc_value_t *name, *v = uc_vm_stack_pop(vm);
1599         uc_value_t *scope, *next;
1600         bool found;
1601 
1602         scope = vm->globals;
1603         name = uc_program_get_constant(uc_vm_current_program(vm), vm->arg.u32);
1604 
1605         while (ucv_type(name) == UC_STRING) {
1606                 ucv_object_get(scope, ucv_string_get(name), &found);
1607 
1608                 if (found)
1609                         break;
1610 
1611                 next = ucv_prototype_get(scope);
1612 
1613                 if (!next) {
1614                         if (uc_vm_is_strict(vm)) {
1615                                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
1616                                                       "access to undeclared variable %s",
1617                                                       ucv_string_get(name));
1618                                 ucv_put(name);
1619                                 ucv_put(v);
1620 
1621                                 return;
1622                         }
1623 
1624                         break;
1625                 }
1626 
1627                 scope = next;
1628         }
1629 
1630         if (scope && ucv_type(name) == UC_STRING)
1631                 ucv_object_add(scope, ucv_string_get(name), ucv_get(v));
1632 
1633         ucv_put(name);
1634         uc_vm_stack_push(vm, v);
1635 }
1636 
1637 static bool
1638 assert_mutable_value(uc_vm_t *vm, uc_value_t *val)
1639 {
1640         if (ucv_is_constant(val)) {
1641                 uc_vm_stack_push(vm, NULL);
1642                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
1643                                       "%s value is immutable",
1644                                       ucv_typename(val));
1645 
1646                 return false;
1647         }
1648 
1649         return true;
1650 }
1651 
1652 static void
1653 uc_vm_insn_store_val(uc_vm_t *vm, uc_vm_insn_t insn)
1654 {
1655         uc_value_t *v = uc_vm_stack_pop(vm);
1656         uc_value_t *k = uc_vm_stack_pop(vm);
1657         uc_value_t *o = uc_vm_stack_pop(vm);
1658 
1659         switch (ucv_type(o)) {
1660         case UC_OBJECT:
1661         case UC_ARRAY:
1662         case UC_RESOURCE:
1663                 if (assert_mutable_value(vm, o)) {
1664                         /* ucv_key_set() retains the stored value on its own and returns a
1665                          * reference to it, which becomes the value of the assignment; for
1666                          * resources it dispatches a __set__ metamethod when present. In
1667                          * strict mode it raises a type error when a non-index property
1668                          * store on an array would otherwise be silently dropped. */
1669                         uc_vm_stack_push(vm, ucv_key_set(vm, o, k, v));
1670                 }
1671 
1672                 break;
1673 
1674         default:
1675                 uc_vm_raise_exception(vm, EXCEPTION_TYPE,
1676                                       "attempt to set property on %s value",
1677                                       ucv_typename(o));
1678         }
1679 
1680         ucv_put(v);
1681         ucv_put(o);
1682         ucv_put(k);
1683 }
1684 
1685 static void
1686 uc_vm_insn_store_upval(uc_vm_t *vm, uc_vm_insn_t insn)
1687 {
1688         uc_callframe_t *frame = uc_vm_current_frame(vm);
1689         uc_upvalref_t *ref = frame->closure->upvals[vm->arg.u32];
1690         uc_value_t *val = ucv_get(uc_vm_stack_peek(vm, 0));
1691 
1692         if (ref->closed) {
1693                 ucv_put(ref->value);
1694                 ref->value = val;
1695         }
1696         else {
1697                 uc_vm_stack_set(vm, ref->slot, val);
1698         }
1699 }
1700 
1701 static void
1702 uc_vm_insn_store_local(uc_vm_t *vm, uc_vm_insn_t insn)
1703 {
1704         uc_callframe_t *frame = uc_vm_current_frame(vm);
1705         uc_value_t *val = ucv_get(uc_vm_stack_peek(vm, 0));
1706 
1707         uc_vm_stack_set(vm, frame->stackframe + vm->arg.u32, val);
1708 }
1709 
1710 static int64_t
1711 int64(uc_value_t *nv, uint64_t *u64)
1712 {
1713         int64_t n;
1714 
1715         n = ucv_int64_get(nv);
1716         *u64 = 0;
1717 
1718         if (errno == ERANGE) {
1719                 n = INT64_MAX;
1720                 *u64 = ucv_uint64_get(nv);
1721         }
1722 
1723         return n;
1724 }
1725 
1726 static uint64_t
1727 abs64(int64_t n)
1728 {
1729         if (n == INT64_MIN)
1730                 return 0x8000000000000000ULL;
1731 
1732         if (n < 0)
1733                 return -n;
1734 
1735         return n;
1736 }
1737 
1738 
1739 static uc_value_t *
1740 uc_vm_value_bitop(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_value_t *operand)
1741 {
1742         uc_value_t *nv1, *nv2, *rv = NULL;
1743         uint64_t u1, u2;
1744         int64_t n1, n2;
1745 
1746         nv1 = ucv_to_number(value);
1747         nv2 = ucv_to_number(operand);
1748 
1749         n1 = int64(nv1, &u1);
1750         n2 = int64(nv2, &u2);
1751 
1752         if (n1 < 0 || n2 < 0) {
1753                 switch (operation) {
1754                 case I_LSHIFT:
1755                         rv = ucv_int64_new(n1 << n2);
1756                         break;
1757 
1758                 case I_RSHIFT:
1759                         rv = ucv_int64_new(n1 >> n2);
1760                         break;
1761 
1762                 case I_BAND:
1763                         rv = ucv_int64_new(n1 & n2);
1764                         break;
1765 
1766                 case I_BXOR:
1767                         rv = ucv_int64_new(n1 ^ n2);
1768                         break;
1769 
1770                 case I_BOR:
1771                         rv = ucv_int64_new(n1 | n2);
1772                         break;
1773 
1774                 default:
1775                         break;
1776                 }
1777         }
1778         else {
1779                 if (!u1) u1 = (uint64_t)n1;
1780                 if (!u2) u2 = (uint64_t)n2;
1781 
1782                 switch (operation) {
1783                 case I_LSHIFT:
1784                         rv = ucv_uint64_new(u1 << (u2 % (sizeof(uint64_t) * CHAR_BIT)));
1785                         break;
1786 
1787                 case I_RSHIFT:
1788                         rv = ucv_uint64_new(u1 >> (u2 % (sizeof(uint64_t) * CHAR_BIT)));
1789                         break;
1790 
1791                 case I_BAND:
1792                         rv = ucv_uint64_new(u1 & u2);
1793                         break;
1794 
1795                 case I_BXOR:
1796                         rv = ucv_uint64_new(u1 ^ u2);
1797                         break;
1798 
1799                 case I_BOR:
1800                         rv = ucv_uint64_new(u1 | u2);
1801                         break;
1802 
1803                 default:
1804                         break;
1805                 }
1806         }
1807 
1808         ucv_put(nv1);
1809         ucv_put(nv2);
1810 
1811         return rv;
1812 }
1813 
1814 static uc_value_t *
1815 uc_vm_string_concat(uc_vm_t *vm, uc_value_t *v1, uc_value_t *v2)
1816 {
1817         char buf[sizeof(void *)], *s1, *s2, *str;
1818         uc_value_t *ustr;
1819         uc_stringbuf_t *sbuf;
1820         size_t l1, l2;
1821 
1822         /* optimize cases for string+string concat... */
1823         if (ucv_type(v1) == UC_STRING && ucv_type(v2) == UC_STRING) {
1824                 s1 = ucv_string_get(v1);
1825                 s2 = ucv_string_get(v2);
1826                 l1 = ucv_string_length(v1);
1827                 l2 = ucv_string_length(v2);
1828 
1829                 /* ... result fits into a tagged pointer */
1830                 if (l1 + l2 + 1 < sizeof(buf)) {
1831                         memcpy(&buf[0], s1, l1);
1832                         memcpy(&buf[l1], s2, l2);
1833 
1834                         return ucv_string_new_length(buf, l1 + l2);
1835                 }
1836                 else {
1837                         ustr = ucv_string_alloc(&str, l1 + l2);
1838                         memcpy(&str[0], s1, l1);
1839                         memcpy(&str[l1], s2, l2);
1840 
1841                         return ustr;
1842                 }
1843         }
1844 
1845         sbuf = ucv_stringbuf_new();
1846 
1847         ucv_to_stringbuf(vm, sbuf, v1, false);
1848         ucv_to_stringbuf(vm, sbuf, v2, false);
1849 
1850         return ucv_stringbuf_finish(sbuf);
1851 }
1852 
1853 static uint64_t
1854 upow64(uint64_t base, uint64_t exponent)
1855 {
1856         uint64_t result = 1;
1857 
1858         while (exponent) {
1859                 if (exponent & 1)
1860                         result *= base;
1861 
1862                 exponent >>= 1;
1863                 base *= base;
1864         }
1865 
1866         return result;
1867 }
1868 
1869 static uc_value_t *
1870 uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_value_t *operand)
1871 {
1872         uc_value_t *nv1, *nv2, *rv = NULL;
1873         uint64_t u1, u2;
1874         int64_t n1, n2;
1875         double d1, d2;
1876 
1877         if (operation == I_LSHIFT || operation == I_RSHIFT ||
1878             operation == I_BAND || operation == I_BXOR || operation == I_BOR)
1879                 return uc_vm_value_bitop(vm, operation, value, operand);
1880 
1881         if (operation == I_ADD && (ucv_type(value) == UC_STRING || ucv_type(operand) == UC_STRING))
1882                 return uc_vm_string_concat(vm, value, operand);
1883 
1884         nv1 = ucv_to_number(value);
1885         nv2 = ucv_to_number(operand);
1886 
1887         /* any operation involving NaN results in NaN */
1888         if (!nv1 || !nv2) {
1889                 ucv_put(nv1);
1890                 ucv_put(nv2);
1891 
1892                 return ucv_double_new(NAN);
1893         }
1894         if (ucv_type(nv1) == UC_DOUBLE || ucv_type(nv2) == UC_DOUBLE) {
1895                 d1 = ucv_double_get(nv1);
1896                 d2 = ucv_double_get(nv2);
1897 
1898                 switch (operation) {
1899                 case I_ADD:
1900                 case I_PLUS:
1901                         rv = ucv_double_new(d1 + d2);
1902                         break;
1903 
1904                 case I_SUB:
1905                 case I_MINUS:
1906                         rv = ucv_double_new(d1 - d2);
1907                         break;
1908 
1909                 case I_MUL:
1910                         rv = ucv_double_new(d1 * d2);
1911                         break;
1912 
1913                 case I_DIV:
1914                         if (d2 == 0.0)
1915                                 rv = ucv_double_new(INFINITY);
1916                         else if (isnan(d2))
1917                                 rv = ucv_double_new(NAN);
1918                         else if (!isfinite(d2))
1919                                 rv = ucv_double_new(isfinite(d1) ? 0.0 : NAN);
1920                         else
1921                                 rv = ucv_double_new(d1 / d2);
1922 
1923                         break;
1924 
1925                 case I_MOD:
1926                         rv = ucv_double_new(fmod(d1, d2));
1927                         break;
1928 
1929                 case I_EXP:
1930                         rv = ucv_double_new(pow(d1, d2));
1931                         break;
1932 
1933                 default:
1934                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
1935                                               "undefined arithmetic operation %d",
1936                                               operation);
1937                         break;
1938                 }
1939         }
1940         else {
1941                 n1 = int64(nv1, &u1);
1942                 n2 = int64(nv2, &u2);
1943 
1944                 switch (operation) {
1945                 case I_ADD:
1946                 case I_PLUS:
1947                         if (n1 < 0 || n2 < 0) {
1948                                 if (u1)
1949                                         rv = ucv_uint64_new(u1 - abs64(n2));
1950                                 else if (u2)
1951                                         rv = ucv_uint64_new(u2 - abs64(n1));
1952                                 else
1953                                         rv = ucv_int64_new(n1 + n2);
1954                         }
1955                         else {
1956                                 if (!u1) u1 = (uint64_t)n1;
1957                                 if (!u2) u2 = (uint64_t)n2;
1958 
1959                                 rv = ucv_uint64_new(u1 + u2);
1960                         }
1961 
1962                         break;
1963 
1964                 case I_SUB:
1965                 case I_MINUS:
1966                         if (n1 < 0 && n2 < 0) {
1967                                 if (n1 > n2)
1968                                         rv = ucv_uint64_new(abs64(n2) - abs64(n1));
1969                                 else
1970                                         rv = ucv_int64_new(n1 - n2);
1971                         }
1972                         else if (n1 >= 0 && n2 >= 0) {
1973                                 if (!u1) u1 = (uint64_t)n1;
1974                                 if (!u2) u2 = (uint64_t)n2;
1975 
1976                                 if (u2 > u1)
1977                                         rv = ucv_int64_new(-(u2 - u1));
1978                                 else
1979                                         rv = ucv_uint64_new(u1 - u2);
1980                         }
1981                         else if (n1 >= 0) {
1982                                 if (!u1) u1 = (uint64_t)n1;
1983 
1984                                 rv = ucv_uint64_new(u1 + abs64(n2));
1985                         }
1986                         else {
1987                                 rv = ucv_int64_new(n1 - n2);
1988                         }
1989 
1990                         break;
1991 
1992                 case I_MUL:
1993                         if (n1 < 0 && n2 < 0) {
1994                                 rv = ucv_uint64_new(abs64(n1) * abs64(n2));
1995                         }
1996                         else if (n1 >= 0 && n2 >= 0) {
1997                                 if (!u1) u1 = (uint64_t)n1;
1998                                 if (!u2) u2 = (uint64_t)n2;
1999 
2000                                 rv = ucv_uint64_new(u1 * u2);
2001                         }
2002                         else {
2003                                 rv = ucv_int64_new(n1 * n2);
2004                         }
2005 
2006                         break;
2007 
2008                 case I_DIV:
2009                         if (n2 == 0) {
2010                                 rv = ucv_double_new(INFINITY);
2011                         }
2012                         else if (n1 == INT64_MIN && n2 == -1) {
2013                                 /* the mathematical result 2^63 only fits into uint64_t;
2014                                  * the signed division would trap with SIGFPE */
2015                                 rv = ucv_uint64_new((uint64_t)INT64_MAX + 1);
2016                         }
2017                         else if (n1 < 0 || n2 < 0) {
2018                                 rv = ucv_int64_new(n1 / n2);
2019                         }
2020                         else {
2021                                 if (!u1) u1 = (uint64_t)n1;
2022                                 if (!u2) u2 = (uint64_t)n2;
2023 
2024                                 rv = ucv_uint64_new(u1 / u2);
2025                         }
2026 
2027                         break;
2028 
2029                 case I_MOD:
2030                         if (n2 == 0) {
2031                                 rv = ucv_double_new(NAN);
2032                         }
2033                         else if (n1 == INT64_MIN && n2 == -1) {
2034                                 /* the signed modulo would trap with SIGFPE */
2035                                 rv = ucv_int64_new(0);
2036                         }
2037                         else if (n1 < 0 || n2 < 0) {
2038                                 rv = ucv_int64_new(n1 % n2);
2039                         }
2040                         else {
2041                                 if (!u1) u1 = (uint64_t)n1;
2042                                 if (!u2) u2 = (uint64_t)n2;
2043 
2044                                 rv = ucv_uint64_new(u1 % u2);
2045                         }
2046 
2047                         break;
2048 
2049                 case I_EXP:
2050                         if (n1 < 0 || n2 < 0) {
2051                                 /* the result is negative iff the base is negative and the
2052                                  * exponent is odd */
2053                                 if (n2 < 0)
2054                                         rv = ucv_double_new(((n1 < 0 && (n2 & 1)) ? -1.0 : 1.0) /
2055                                                 (double)upow64(abs64(n1), abs64(n2)));
2056                                 else if (n2 & 1)
2057                                         rv = ucv_int64_new((int64_t)-upow64(abs64(n1), abs64(n2)));
2058                                 else
2059                                         rv = ucv_uint64_new(upow64(abs64(n1), abs64(n2)));
2060                         }
2061                         else {
2062                                 if (!u1) u1 = (uint64_t)n1;
2063                                 if (!u2) u2 = (uint64_t)n2;
2064 
2065                                 rv = ucv_uint64_new(upow64(u1, u2));
2066                         }
2067 
2068                         break;
2069 
2070                 default:
2071                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
2072                                               "undefined arithmetic operation %d",
2073                                               operation);
2074                         break;
2075                 }
2076         }
2077 
2078         ucv_put(nv1);
2079         ucv_put(nv2);
2080 
2081         return rv;
2082 }
2083 
2084 static void
2085 uc_vm_insn_update_var(uc_vm_t *vm, uc_vm_insn_t insn)
2086 {
2087         uc_value_t *name, *val = NULL, *inc = uc_vm_stack_pop(vm);
2088         uc_value_t *scope, *next;
2089         bool found;
2090 
2091         scope = vm->globals;
2092         name = uc_program_get_constant(uc_vm_current_program(vm), vm->arg.u32 & 0x00FFFFFF);
2093 
2094         assert(ucv_type(name) == UC_STRING);
2095 
2096         while (true) {
2097                 val = ucv_object_get(scope, ucv_string_get(name), &found);
2098 
2099                 if (found)
2100                         break;
2101 
2102                 next = ucv_prototype_get(scope);
2103 
2104                 if (!next) {
2105                         if (uc_vm_is_strict(vm)) {
2106                                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
2107                                                       "access to undeclared variable %s",
2108                                                       ucv_string_get(name));
2109                                 ucv_put(name);
2110                                 ucv_put(inc);
2111 
2112                                 return;
2113                         }
2114 
2115                         break;
2116                 }
2117 
2118                 scope = next;
2119         }
2120 
2121         val = uc_vm_value_arith(vm, vm->arg.u32 >> 24, val, inc);
2122 
2123         ucv_object_add(scope, ucv_string_get(name), ucv_get(val));
2124         uc_vm_stack_push(vm, val);
2125 
2126         ucv_put(name);
2127         ucv_put(inc);
2128 }
2129 
2130 static void
2131 uc_vm_insn_update_val(uc_vm_t *vm, uc_vm_insn_t insn)
2132 {
2133         uc_value_t *inc = uc_vm_stack_pop(vm);
2134         uc_value_t *k = uc_vm_stack_pop(vm);
2135         uc_value_t *v = uc_vm_stack_pop(vm);
2136         uc_value_t *val = NULL;
2137         uc_vm_insn_t op;
2138 
2139         switch (ucv_type(v)) {
2140         case UC_OBJECT:
2141         case UC_ARRAY:
2142         case UC_RESOURCE:
2143                 if (assert_mutable_value(vm, v)) {
2144                         uc_value_t *nv, *rv;
2145 
2146                         /* reading the property may dispatch a __get__ metamethod which runs
2147                          * on the current stack, clobbers vm->arg and may collect garbage, so
2148                          * remember the operation and keep the operands reachable */
2149                         op = vm->arg.u8;
2150                         uc_vm_stack_push(vm, ucv_get(inc));
2151 
2152                         val = ucv_key_get(vm, v, k);
2153                         uc_vm_stack_push(vm, ucv_get(val));
2154 
2155                         nv = uc_vm_value_arith(vm, op, val, inc);
2156 
2157                         ucv_put(uc_vm_stack_pop(vm));
2158                         ucv_put(uc_vm_stack_pop(vm));
2159 
2160                         /* an aborted read leaves nothing to store; the exception it raised is
2161                          * left to propagate instead of overwriting the property */
2162                         rv = vm->exception.type != EXCEPTION_NONE ? NULL : ucv_key_set(vm, v, k, nv);
2163 
2164                         /* the store keeps a reference of its own; rv is the value of the
2165                          * update expression, null if storing failed */
2166                         ucv_put(nv);
2167 
2168                         uc_vm_stack_push(vm, rv);
2169                 }
2170 
2171                 break;
2172 
2173         default:
2174                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
2175                                       "left-hand side expression is %s",
2176                                       v ? "not an array or object" : "null");
2177 
2178                 break;
2179         }
2180 
2181         ucv_put(val);
2182         ucv_put(inc);
2183         ucv_put(v);
2184         ucv_put(k);
2185 }
2186 
2187 static void
2188 uc_vm_insn_update_upval(uc_vm_t *vm, uc_vm_insn_t insn)
2189 {
2190         uc_callframe_t *frame = uc_vm_current_frame(vm);
2191         size_t slot = vm->arg.u32 & 0x00FFFFFF;
2192         uc_upvalref_t *ref = frame->closure->upvals[slot];
2193         uc_value_t *inc = uc_vm_stack_pop(vm);
2194         uc_value_t *val;
2195 
2196         if (ref->closed)
2197                 val = ref->value;
2198         else
2199                 val = vm->stack.entries[ref->slot];
2200 
2201         val = uc_vm_value_arith(vm, vm->arg.u32 >> 24, val, inc);
2202 
2203         uc_vm_stack_push(vm, val);
2204 
2205         ucv_put(inc);
2206 
2207         if (ref->closed) {
2208                 ucv_put(ref->value);
2209                 ref->value = ucv_get(uc_vm_stack_peek(vm, 0));
2210         }
2211         else {
2212                 uc_vm_stack_set(vm, ref->slot, ucv_get(uc_vm_stack_peek(vm, 0)));
2213         }
2214 }
2215 
2216 static void
2217 uc_vm_insn_update_local(uc_vm_t *vm, uc_vm_insn_t insn)
2218 {
2219         uc_callframe_t *frame = uc_vm_current_frame(vm);
2220         size_t slot = vm->arg.u32 & 0x00FFFFFF;
2221         uc_value_t *inc = uc_vm_stack_pop(vm);
2222         uc_value_t *val;
2223 
2224         val = uc_vm_value_arith(vm, vm->arg.u32 >> 24,
2225                                 vm->stack.entries[frame->stackframe + slot], inc);
2226 
2227         uc_vm_stack_push(vm, val);
2228 
2229         ucv_put(inc);
2230         uc_vm_stack_set(vm, frame->stackframe + slot, ucv_get(uc_vm_stack_peek(vm, 0)));
2231 }
2232 
2233 static void
2234 uc_vm_insn_narr(uc_vm_t *vm, uc_vm_insn_t insn)
2235 {
2236         uc_value_t *arr = ucv_array_new_length(vm, vm->arg.u32);
2237 
2238         uc_vm_stack_push(vm, arr);
2239 }
2240 
2241 static void
2242 uc_vm_insn_parr(uc_vm_t *vm, uc_vm_insn_t insn)
2243 {
2244         uc_value_t *arr = uc_vm_stack_peek(vm, vm->arg.u32);
2245         size_t idx;
2246 
2247         for (idx = 0; idx < vm->arg.u32; idx++)
2248                 ucv_array_push(arr, uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1));
2249 
2250         for (idx = 0; idx < vm->arg.u32; idx++)
2251                 uc_vm_stack_pop(vm);
2252 
2253         //uc_vm_shrink(state, vm->arg.u32);
2254 }
2255 
2256 static void
2257 uc_vm_insn_marr(uc_vm_t *vm, uc_vm_insn_t insn)
2258 {
2259         uc_value_t *src = uc_vm_stack_pop(vm);
2260         uc_value_t *dst = uc_vm_stack_peek(vm, 0);
2261         size_t i;
2262         char *s;
2263 
2264         if (ucv_type(src) != UC_ARRAY) {
2265                 s = ucv_to_string(vm, src);
2266                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "(%s) is not iterable", s);
2267                 ucv_put(src);
2268                 free(s);
2269 
2270                 return;
2271         }
2272 
2273         for (i = 0; i < ucv_array_length(src); i++)
2274                 ucv_array_push(dst, ucv_get(ucv_array_get(src, i)));
2275 
2276         ucv_put(src);
2277 }
2278 
2279 static void
2280 uc_vm_insn_nobj(uc_vm_t *vm, uc_vm_insn_t insn)
2281 {
2282         uc_value_t *obj = ucv_object_new(vm);
2283 
2284         uc_vm_stack_push(vm, obj);
2285 }
2286 
2287 static void
2288 uc_vm_insn_sobj(uc_vm_t *vm, uc_vm_insn_t insn)
2289 {
2290         uc_value_t *obj = uc_vm_stack_peek(vm, vm->arg.u32);
2291         size_t idx;
2292 
2293         /* object literals create own keys, a __set__ metamethod of the object under
2294          * construction must not intercept the keys it is built from */
2295         for (idx = 0; idx < vm->arg.u32; idx += 2)
2296                 ucv_put(ucv_key_rawset(vm, obj,
2297                         uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1),
2298                         uc_vm_stack_peek(vm, vm->arg.u32 - idx - 2)));
2299 
2300         for (idx = 0; idx < vm->arg.u32; idx++)
2301                 ucv_put(uc_vm_stack_pop(vm));
2302 }
2303 
2304 static void
2305 uc_vm_insn_mobj(uc_vm_t *vm, uc_vm_insn_t insn)
2306 {
2307         uc_value_t *src = uc_vm_stack_pop(vm);
2308         uc_value_t *dst = uc_vm_stack_peek(vm, 0);
2309         size_t i;
2310         char *s;
2311 
2312         switch (ucv_type(src)) {
2313         case UC_OBJECT:
2314                 ; /* a label can only be part of a statement and a declaration is not a statement */
2315                 ucv_object_foreach(src, k, v)
2316                         ucv_object_add(dst, k, ucv_get(v));
2317 
2318                 ucv_put(src);
2319                 break;
2320 
2321         case UC_ARRAY:
2322                 for (i = 0; i < ucv_array_length(src); i++) {
2323                         xasprintf(&s, "%zu", i);
2324                         ucv_object_add(dst, s, ucv_get(ucv_array_get(src, i)));
2325                         free(s);
2326                 }
2327 
2328                 ucv_put(src);
2329                 break;
2330 
2331         default:
2332                 s = ucv_to_string(vm, src);
2333                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Value (%s) is not iterable", s);
2334                 free(s);
2335 
2336                 break;
2337         }
2338 }
2339 
2340 static void
2341 uc_vm_insn_arith(uc_vm_t *vm, uc_vm_insn_t insn)
2342 {
2343         uc_value_t *r2 = uc_vm_stack_pop(vm);
2344         uc_value_t *r1 = uc_vm_stack_pop(vm);
2345         uc_value_t *rv;
2346 
2347         rv = uc_vm_value_arith(vm, insn, r1, r2);
2348 
2349         ucv_put(r1);
2350         ucv_put(r2);
2351 
2352         uc_vm_stack_push(vm, rv);
2353 }
2354 
2355 static void
2356 uc_vm_insn_plus_minus(uc_vm_t *vm, uc_vm_insn_t insn)
2357 {
2358         uc_value_t *v = uc_vm_stack_pop(vm), *nv;
2359         bool is_sub = (insn == I_MINUS);
2360         int64_t n;
2361         double d;
2362 
2363         if (ucv_type(v) == UC_STRING) {
2364                 nv = uc_number_parse(ucv_string_get(v), NULL);
2365 
2366                 if (nv) {
2367                         ucv_put(v);
2368                         v = nv;
2369                 }
2370         }
2371 
2372         switch (ucv_type(v)) {
2373         case UC_INTEGER:
2374                 n = ucv_int64_get(v);
2375 
2376                 /* numeric value is in range 9223372036854775808..18446744073709551615 */
2377                 if (errno == ERANGE) {
2378                         if (is_sub)
2379                                 /* make negation of large numeric value result in smallest negative value */
2380                                 uc_vm_stack_push(vm, ucv_int64_new(INT64_MIN));
2381                         else
2382                                 /* for positive number coercion return value as-is */
2383                                 uc_vm_stack_push(vm, ucv_get(v));
2384                 }
2385 
2386                 /* numeric value is in range -9223372036854775808..9223372036854775807 */
2387                 else {
2388                         if (is_sub) {
2389                                 if (n == INT64_MIN)
2390                                         /* make negation of minimum value result in maximum signed positive value */
2391                                         uc_vm_stack_push(vm, ucv_int64_new(INT64_MAX));
2392                                 else
2393                                         /* for all other values flip the sign */
2394                                         uc_vm_stack_push(vm, ucv_int64_new(-n));
2395                         }
2396                         else {
2397                                 /* for positive number coercion return value as-is */
2398                                 uc_vm_stack_push(vm, ucv_get(v));
2399                         }
2400                 }
2401 
2402                 break;
2403 
2404         case UC_DOUBLE:
2405                 d = ucv_double_get(v);
2406                 uc_vm_stack_push(vm, ucv_double_new(is_sub ? -d : d));
2407                 break;
2408 
2409         case UC_BOOLEAN:
2410                 n = (int64_t)ucv_boolean_get(v);
2411                 uc_vm_stack_push(vm, ucv_int64_new(is_sub ? -n : n));
2412                 break;
2413 
2414         case UC_NULL:
2415                 uc_vm_stack_push(vm, ucv_int64_new(0));
2416                 break;
2417 
2418         default:
2419                 uc_vm_stack_push(vm, ucv_double_new(NAN));
2420         }
2421 
2422         ucv_put(v);
2423 }
2424 
2425 static void
2426 uc_vm_insn_bitop(uc_vm_t *vm, uc_vm_insn_t insn)
2427 {
2428         uc_value_t *r2 = uc_vm_stack_pop(vm);
2429         uc_value_t *r1 = uc_vm_stack_pop(vm);
2430         uc_value_t *rv;
2431 
2432         rv = uc_vm_value_bitop(vm, insn, r1, r2);
2433 
2434         ucv_put(r1);
2435         ucv_put(r2);
2436 
2437         uc_vm_stack_push(vm, rv);
2438 }
2439 
2440 static void
2441 uc_vm_insn_complement(uc_vm_t *vm, uc_vm_insn_t insn)
2442 {
2443         uc_value_t *v = uc_vm_stack_pop(vm);
2444         uc_value_t *nv;
2445         uint64_t u;
2446         int64_t n;
2447 
2448         nv = ucv_to_number(v);
2449         n = int64(nv, &u);
2450 
2451         if (n < 0) {
2452                 uc_vm_stack_push(vm, ucv_int64_new(~n));
2453         }
2454         else {
2455                 if (!u) u = (uint64_t)n;
2456 
2457                 uc_vm_stack_push(vm, ucv_uint64_new(~u));
2458         }
2459 
2460         ucv_put(nv);
2461         ucv_put(v);
2462 }
2463 
2464 static void
2465 uc_vm_insn_rel(uc_vm_t *vm, uc_vm_insn_t insn)
2466 {
2467         uc_value_t *r2 = uc_vm_stack_pop(vm);
2468         uc_value_t *r1 = uc_vm_stack_pop(vm);
2469 
2470         bool res = ucv_compare(insn, r1, r2, NULL);
2471 
2472         ucv_put(r1);
2473         ucv_put(r2);
2474 
2475         uc_vm_stack_push(vm, ucv_boolean_new(res));
2476 }
2477 
2478 /* Test whether `key` names a property of `scope` or of anything in its
2479  * prototype chain, the way a property read resolves it - but without
2480  * dispatching __get__, so a purely virtual property stays hidden. */
2481 static bool
2482 uc_vm_key_exists(uc_value_t *scope, uc_value_t *key)
2483 {
2484         uc_value_t *o;
2485         bool found = false;
2486 
2487         if (ucv_type(key) != UC_STRING)
2488                 return false;
2489 
2490         for (o = scope; o != NULL && !found; o = ucv_prototype_get(o))
2491                 ucv_object_get(o, ucv_string_get(key), &found);
2492 
2493         return found;
2494 }
2495 
2496 static void
2497 uc_vm_insn_in(uc_vm_t *vm, uc_vm_insn_t insn)
2498 {
2499         uc_value_t *r2 = uc_vm_stack_pop(vm);
2500         uc_value_t *r1 = uc_vm_stack_pop(vm);
2501         uc_value_t *item;
2502         size_t arrlen, arridx;
2503         bool found = false;
2504 
2505         switch (ucv_type(r2)) {
2506         case UC_ARRAY:
2507                 /* value membership: is r1 one of the array elements? */
2508                 for (arridx = 0, arrlen = ucv_array_length(r2);
2509                      arridx < arrlen; arridx++) {
2510                         item = ucv_array_get(r2, arridx);
2511 
2512                         if (uc_vm_test_strict_equality(r1, item, true)) {
2513                                 found = true;
2514                                 break;
2515                         }
2516                 }
2517 
2518                 /* arrays can carry a prototype providing the tested key as well */
2519                 if (!found)
2520                         found = uc_vm_key_exists(r2, r1);
2521 
2522                 break;
2523 
2524         case UC_OBJECT:
2525                 found = uc_vm_key_exists(r2, r1);
2526 
2527                 break;
2528 
2529         default:
2530                 found = false;
2531         }
2532 
2533         ucv_put(r1);
2534         ucv_put(r2);
2535 
2536         uc_vm_stack_push(vm, ucv_boolean_new(found));
2537 }
2538 
2539 static void
2540 uc_vm_insn_equality(uc_vm_t *vm, uc_vm_insn_t insn)
2541 {
2542         uc_value_t *r2 = uc_vm_stack_pop(vm);
2543         uc_value_t *r1 = uc_vm_stack_pop(vm);
2544         bool equal = uc_vm_test_strict_equality(r1, r2, false);
2545 
2546         ucv_put(r1);
2547         ucv_put(r2);
2548 
2549         uc_vm_stack_push(vm, ucv_boolean_new((insn == I_EQS) ? equal : !equal));
2550 }
2551 
2552 static void
2553 uc_vm_insn_not(uc_vm_t *vm, uc_vm_insn_t insn)
2554 {
2555         uc_value_t *r1 = uc_vm_stack_pop(vm);
2556 
2557         uc_vm_stack_push(vm, ucv_boolean_new(!ucv_is_truish(r1)));
2558         ucv_put(r1);
2559 }
2560 
2561 static void
2562 uc_vm_insn_jmp(uc_vm_t *vm, uc_vm_insn_t insn)
2563 {
2564         uc_callframe_t *frame = uc_vm_current_frame(vm);
2565         uc_chunk_t *chunk = uc_vm_frame_chunk(frame);
2566         int32_t addr = vm->arg.s32;
2567 
2568         /* ip already has been incremented */
2569         addr -= 5;
2570 
2571         if (frame->ip + addr < chunk->entries ||
2572             frame->ip + addr >= chunk->entries + chunk->count) {
2573                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "jump target out of range");
2574                 return;
2575         }
2576 
2577         frame->ip += addr;
2578 }
2579 
2580 static void
2581 uc_vm_insn_jmpz(uc_vm_t *vm, uc_vm_insn_t insn)
2582 {
2583         uc_callframe_t *frame = uc_vm_current_frame(vm);
2584         uc_chunk_t *chunk = uc_vm_frame_chunk(frame);
2585         uc_value_t *v = uc_vm_stack_pop(vm);
2586         int32_t addr = vm->arg.s32;
2587 
2588         /* ip already has been incremented */
2589         addr -= 5;
2590 
2591         if (frame->ip + addr < chunk->entries ||
2592             frame->ip + addr >= chunk->entries + chunk->count) {
2593                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "jump target out of range");
2594                 return;
2595         }
2596 
2597         if (!ucv_is_truish(v))
2598                 frame->ip += addr;
2599 
2600         ucv_put(v);
2601 }
2602 
2603 static void
2604 uc_vm_insn_jmpnt(uc_vm_t *vm, uc_vm_insn_t insn)
2605 {
2606         uc_callframe_t *frame = uc_vm_current_frame(vm);
2607         uc_chunk_t *chunk = uc_vm_frame_chunk(frame);
2608         int16_t addr = (vm->arg.u32 & 0xffff) - 0x7fff;
2609         uint16_t types = (vm->arg.u32 >> 16) & 0x1fff;
2610         uint8_t depth = (vm->arg.u32 >> 29) & 0x7;
2611         uc_value_t *v = uc_vm_stack_peek(vm, 0);
2612         size_t i;
2613 
2614         /* ip already has been incremented */
2615         addr -= 5;
2616 
2617         if (frame->ip + addr < chunk->entries ||
2618             frame->ip + addr >= chunk->entries + chunk->count) {
2619                 uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "jump target out of range");
2620                 return;
2621         }
2622 
2623         if (!(types & (1u << ucv_type(v)))) {
2624                 for (i = 0; i <= depth; i++)
2625                         ucv_put(uc_vm_stack_pop(vm));
2626 
2627                 uc_vm_stack_push(vm, NULL);
2628                 frame->ip += addr;
2629         }
2630 }
2631 
2632 
2633 static void
2634 uc_vm_object_iterator_free(void *ud)
2635 {
2636         uc_object_iterator_t *iter = ud;
2637 
2638         uc_list_remove(&iter->list);
2639 }
2640 
2641 static uc_resource_type_t uc_vm_object_iterator_type = {
2642         .name = "object iterator",
2643         .free = uc_vm_object_iterator_free
2644 };
2645 
2646 static bool
2647 uc_vm_object_iterator_next(uc_vm_t *vm, uc_vm_insn_t insn,
2648                            uc_value_t *k, uc_value_t *v)
2649 {
2650         uc_resource_t *res = ucv_as_resource(k);
2651         uc_object_t *obj = ucv_as_object(v);
2652         uc_object_iterator_t *iter;
2653 
2654         if (!res) {
2655                 /* object is empty */
2656                 if (!obj->table->head)
2657                         return false;
2658 
2659                 res = xalloc(sizeof(*res) + sizeof(uc_object_iterator_t));
2660                 res->header.type = UC_RESOURCE;
2661                 res->header.refcount = 1;
2662                 res->type = &uc_vm_object_iterator_type;
2663 
2664                 iter = res->data = (char *)res + sizeof(*res);
2665                 iter->table = obj->table;
2666                 iter->u.pos = obj->table->head;
2667 
2668                 uc_list_insert(&uc_thread_context_get()->object_iterators, &iter->list);
2669         }
2670         else if (ucv_type(k) == UC_RESOURCE &&
2671                  res->type == &uc_vm_object_iterator_type && res->data != NULL) {
2672 
2673                 iter = res->data;
2674         }
2675         else {
2676                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid object iterator");
2677 
2678                 return false;
2679         }
2680 
2681         /* no next key */
2682         if (!iter->u.pos) {
2683                 uc_list_remove(&iter->list);
2684 
2685                 return false;
2686         }
2687 
2688         uc_vm_stack_push(vm, ucv_string_new(iter->u.pos->k));
2689 
2690         if (insn == I_NEXTKV)
2691                 uc_vm_stack_push(vm, ucv_get((uc_value_t *)iter->u.pos->v));
2692 
2693         uc_vm_stack_push(vm, &res->header);
2694         ucv_put(v);
2695 
2696         iter->u.pos = iter->u.pos->next;
2697 
2698         return true;
2699 }
2700 
2701 static bool
2702 uc_vm_array_iterator_next(uc_vm_t *vm, uc_vm_insn_t insn,
2703                           uc_value_t *k, uc_value_t *v)
2704 {
2705         uint64_t n;
2706 
2707         if (!k) {
2708                 /* array is empty */
2709                 if (!ucv_array_length(v))
2710                         return false;
2711 
2712                 k = ucv_resource_new(NULL, NULL);
2713                 n = 0;
2714         }
2715         else if (ucv_type(k) == UC_RESOURCE) {
2716                 n = (uintptr_t)ucv_resource_data(k, NULL);
2717         }
2718         else {
2719                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid array iterator");
2720 
2721                 return false;
2722         }
2723 
2724         /* no next index */
2725         if (n >= ucv_array_length(v))
2726                 return false;
2727 
2728         if (insn == I_NEXTKV)
2729                 uc_vm_stack_push(vm, ucv_uint64_new(n));
2730 
2731         uc_vm_stack_push(vm, ucv_get(ucv_array_get(v, n)));
2732 
2733         uc_vm_stack_push(vm, k);
2734         ucv_put(v);
2735 
2736         ucv_as_resource(k)->data = (void *)(uintptr_t)(n + 1);
2737 
2738         return true;
2739 }
2740 
2741 static void
2742 uc_vm_insn_next(uc_vm_t *vm, uc_vm_insn_t insn)
2743 {
2744         uc_value_t *k = uc_vm_stack_pop(vm);
2745         uc_value_t *v = uc_vm_stack_pop(vm);
2746 
2747         switch (ucv_type(v)) {
2748         case UC_OBJECT:
2749                 if (uc_vm_object_iterator_next(vm, insn, k, v))
2750                         return;
2751 
2752                 break;
2753 
2754         case UC_ARRAY:
2755                 if (uc_vm_array_iterator_next(vm, insn, k, v))
2756                         return;
2757 
2758                 break;
2759 
2760         default:
2761                 break;
2762         }
2763 
2764         uc_vm_stack_push(vm, NULL);
2765         uc_vm_stack_push(vm, NULL);
2766 
2767         if (insn == I_NEXTKV)
2768                 uc_vm_stack_push(vm, NULL);
2769 
2770         ucv_put(k);
2771         ucv_put(v);
2772 }
2773 
2774 static void
2775 uc_vm_insn_close_upval(uc_vm_t *vm, uc_vm_insn_t insn)
2776 {
2777         uc_vm_close_upvals(vm, vm->stack.count - 1);
2778         ucv_put(uc_vm_stack_pop(vm));
2779 }
2780 
2781 static void
2782 uc_vm_insn_call(uc_vm_t *vm)
2783 {
2784         bool mcall = (vm->arg.u32 & 0x80000000);
2785         size_t nargs = (vm->arg.u32 & 0xffff);
2786         size_t nspreads = (vm->arg.u32 >> 16) & 0x7fff;
2787         uint8_t *ip = uc_vm_current_frame(vm)->ip + 2 * nspreads;
2788         uc_value_t *fno = uc_vm_stack_peek(vm, nargs);
2789         uc_value_t *ctx = NULL;
2790         bool tail;
2791 
2792         if (!ucv_is_arrowfn(fno))
2793                 ctx = mcall ? uc_vm_stack_peek(vm, nargs + 1) : NULL;
2794         else if (vm->callframes.count > 0)
2795                 ctx = uc_vm_current_frame(vm)->ctx;
2796 
2797         /* a tail call is indicated by a 0x00 marker that the compiler emits
2798          * immediately after the enclosing I_RETURN. ip points just past the call's
2799          * operand span, so ip[0] is the I_RETURN and ip[1] the marker. Neither VM
2800          * executes the marker: older VMs return before reaching it, and this VM
2801          * jumps to the callee at the call site. */
2802         tail = (ip[0] == I_RETURN) && (ip[1] == 0x00);
2803 
2804         uc_vm_call_function(vm, ucv_get(ctx), ucv_get(fno), mcall, vm->arg.u32, tail);
2805 }
2806 
2807 static void
2808 uc_vm_insn_print(uc_vm_t *vm, uc_vm_insn_t insn)
2809 {
2810         uc_value_t *v = uc_vm_stack_pop(vm);
2811         char *p;
2812 
2813         switch (ucv_type(v)) {
2814         case UC_OBJECT:
2815         case UC_ARRAY:
2816                 p = ucv_to_jsonstring(vm, v);
2817                 fwrite(p, 1, strlen(p), vm->output);
2818                 free(p);
2819                 break;
2820 
2821         case UC_STRING:
2822                 fwrite(ucv_string_get(v), 1, ucv_string_length(v), vm->output);
2823                 break;
2824 
2825         case UC_NULL:
2826                 break;
2827 
2828         default:
2829                 p = ucv_to_string(vm, v);
2830                 fwrite(p, 1, strlen(p), vm->output);
2831                 free(p);
2832         }
2833 
2834         ucv_put(v);
2835 }
2836 
2837 static void
2838 uc_vm_insn_delete(uc_vm_t *vm, uc_vm_insn_t insn)
2839 {
2840         uc_value_t *k = uc_vm_stack_pop(vm);
2841         uc_value_t *v = uc_vm_stack_pop(vm);
2842         bool rv;
2843 
2844         switch (ucv_type(v)) {
2845         case UC_OBJECT:
2846                 if (assert_mutable_value(vm, v)) {
2847                         rv = ucv_key_delete(vm, v, k);
2848                         uc_vm_stack_push(vm, ucv_boolean_new(rv));
2849                 }
2850 
2851                 break;
2852 
2853         default:
2854                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
2855                                       "left-hand side expression is %s",
2856                                       v ? "not an object" : "null");
2857 
2858                 break;
2859         }
2860 
2861         ucv_put(k);
2862         ucv_put(v);
2863 }
2864 
2865 static void
2866 uc_vm_insn_import(uc_vm_t *vm, uc_vm_insn_t insn)
2867 {
2868         uc_callframe_t *frame = uc_vm_current_frame(vm);
2869         uc_program_t *prog = uc_vm_current_program(vm);
2870         uint16_t from = vm->arg.u32 & 0xffff;
2871         uint16_t to = vm->arg.u32 >> 16;
2872         uc_value_t *name, *modobj;
2873         uint32_t cidx;
2874 
2875         /* is a wildcard import * from ... */
2876         if (to == 0xffff) {
2877                 to = from;
2878                 modobj = ucv_object_new(vm);
2879 
2880                 /* instruction is followed by u16 containing the offset of the
2881                  * first module export and `from` times u32 values containing
2882                  * the constant indexes of the names */
2883                 for (from = frame->ip[0] * 0x100 + frame->ip[1], frame->ip += 2;
2884                      from < prog->exports.count && to > 0;
2885                      from++, to--) {
2886 
2887                         cidx = (
2888                                 frame->ip[0] * 0x1000000UL +
2889                                 frame->ip[1] * 0x10000UL +
2890                                 frame->ip[2] * 0x100UL +
2891                                 frame->ip[3]
2892                         );
2893 
2894                         frame->ip += 4;
2895 
2896                         name = uc_program_get_constant(uc_vm_current_program(vm), cidx);
2897 
2898                         if (ucv_type(name) == UC_STRING && prog->exports.entries[from])
2899                                 ucv_object_add(modobj, ucv_string_get(name),
2900                                         ucv_get(&prog->exports.entries[from]->header));
2901 
2902                         ucv_put(name);
2903                 }
2904 
2905                 ucv_set_constant(modobj, true);
2906 
2907                 uc_vm_stack_push(vm, modobj);
2908         }
2909 
2910         /* module export available, patch into upvalue */
2911         else if (from < prog->exports.count && prog->exports.entries[from]) {
2912                 frame->closure->upvals[to] = prog->exports.entries[from];
2913                 ucv_get(&prog->exports.entries[from]->header);
2914         }
2915 
2916         /* module export missing, e.g. due to premature return in module,
2917          * patch up dummy upvalue ref with `null` value */
2918         else {
2919                 frame->closure->upvals[to] = (uc_upvalref_t *)ucv_upvalref_new(0);
2920                 frame->closure->upvals[to]->closed = true;
2921         }
2922 }
2923 
2924 static void
2925 uc_vm_insn_export(uc_vm_t *vm, uc_vm_insn_t insn)
2926 {
2927         uc_callframe_t *frame = uc_vm_current_frame(vm);
2928         uc_program_t *prog = uc_vm_current_program(vm);
2929         uc_upvalref_t *ref = uc_vm_capture_upval(vm, frame->stackframe + vm->arg.u32);
2930 
2931         uc_vector_push(&prog->exports, ref);
2932         ucv_get(&ref->header);
2933 }
2934 
2935 static void
2936 uc_vm_insn_dynload(uc_vm_t *vm, uc_vm_insn_t insn)
2937 {
2938         uc_callframe_t *frame = uc_vm_current_frame(vm);
2939         uc_value_t *name, *export, *modscope, *modobj;
2940         uint16_t count = vm->arg.u32 & 0xffff;
2941         uint16_t to = vm->arg.u32 >> 16;
2942         uint32_t cidx;
2943         bool found;
2944 
2945         /* Attempt to load module. Will raise exception on error */
2946         name = uc_vm_stack_pop(vm);
2947         modscope = uc_require_library(vm, name, true);
2948         ucv_put(name);
2949 
2950         if (!modscope)
2951                 return;
2952 
2953         /* If count is zero, we're doing a wildcard import. Shallow copy module
2954          * object, mark it constant and patch into the target upvalue. */
2955         if (count == 0) {
2956                 modobj = ucv_object_new(vm);
2957 
2958                 ucv_object_foreach(modscope, k, v)
2959                         ucv_object_add(modobj, k, ucv_get(v));
2960 
2961                 ucv_set_constant(modobj, true);
2962 
2963                 uc_vm_stack_push(vm, modobj);
2964         }
2965 
2966         /* ... otherwise we're importing a specific list of names */
2967         else {
2968                 /* Instruction is followed by `count` times u32 values containing
2969                  * the import name constant indexes */
2970                 while (count > 0) {
2971                         cidx = (
2972                                 frame->ip[0] * 0x1000000UL +
2973                                 frame->ip[1] * 0x10000UL +
2974                                 frame->ip[2] * 0x100UL +
2975                                 frame->ip[3]
2976                         );
2977 
2978                         frame->ip += 4;
2979 
2980                         name = uc_program_get_constant(uc_vm_current_program(vm), cidx);
2981                         export = ucv_object_get(modscope, ucv_string_get(name), &found);
2982 
2983                         if (!found) {
2984                                 uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
2985                                                       "Module does not export %s",
2986                                                       ucv_string_get(name));
2987 
2988                                 ucv_put(name);
2989                                 ucv_put(modscope);
2990 
2991                                 return;
2992                         }
2993 
2994                         ucv_put(name);
2995 
2996                         frame->closure->upvals[to] = (uc_upvalref_t *)ucv_upvalref_new(0);
2997                         frame->closure->upvals[to]->closed = true;
2998                         frame->closure->upvals[to]->value = ucv_get(export);
2999 
3000                         count--;
3001                         to++;
3002                 }
3003         }
3004 
3005         ucv_put(modscope);
3006 }
3007 
3008 static void
3009 uc_vm_gc_step(uc_vm_t *vm)
3010 {
3011         size_t curr_count = 0, prev_count = 0;
3012         uc_weakref_t *ref;
3013 
3014         if (!(vm->gc_flags & UC_GC_ENABLED))
3015                 return;
3016 
3017         if (vm->alloc_refs >= vm->gc_interval) {
3018                 if (vm->trace) {
3019                         for (ref = vm->values.next; ref != &vm->values; ref = ref->next)
3020                                 prev_count++;
3021 
3022                         ucv_gc(vm);
3023 
3024                         for (ref = vm->values.next; ref != &vm->values; ref = ref->next)
3025                                 curr_count++;
3026 
3027                         fprintf(stderr, "! GC reclaimed %zu object(s)\n", prev_count - curr_count);
3028                 }
3029                 else {
3030                         ucv_gc(vm);
3031                 }
3032         }
3033 }
3034 
3035 static uc_value_t *
3036 uc_vm_callframe_pop(uc_vm_t *vm)
3037 {
3038         uc_callframe_t *frame = uc_vm_current_frame(vm);
3039         uc_value_t *retval;
3040 
3041         /* close upvalues */
3042         uc_vm_close_upvals(vm, frame->stackframe);
3043 
3044         if (vm->stack.count > frame->stackframe)
3045                 retval = uc_vm_stack_pop(vm);
3046         else
3047                 retval = NULL;
3048 
3049         /* reset function stack frame */
3050         while (vm->stack.count > frame->stackframe)
3051                 ucv_put(uc_vm_stack_pop(vm));
3052 
3053         /* for method calls, release context as well */
3054         if (frame->mcall)
3055                 ucv_put(uc_vm_stack_pop(vm));
3056 
3057         /* release function */
3058         if (frame->closure)
3059                 ucv_put(&frame->closure->header);
3060 
3061         if (frame->cfunction)
3062                 ucv_put(&frame->cfunction->header);
3063 
3064         /* release context */
3065         ucv_put(frame->ctx);
3066 
3067         vm->callframes.count--;
3068 
3069         return retval;
3070 }
3071 
3072 static void
3073 uc_vm_output_exception(uc_vm_t *vm, uc_exception_t *ex)
3074 {
3075         uc_value_t *ctx;
3076 
3077         if (ex->type == EXCEPTION_USER)
3078                 fprintf(stderr, "%s\n", ex->message);
3079         else
3080                 fprintf(stderr, "%s: %s\n",
3081                             exception_type_strings[ex->type] ? exception_type_strings[ex->type] : "Error",
3082                             ex->message);
3083 
3084         ctx = ucv_object_get(ucv_array_get(ex->stacktrace, 0), "context", NULL);
3085 
3086         if (ctx)
3087                 fprintf(stderr, "%s\n", ucv_string_get(ctx));
3088 
3089         fprintf(stderr, "\n");
3090 }
3091 
3092 uc_exception_type_t
3093 uc_vm_signal_dispatch(uc_vm_t *vm)
3094 {
3095         uc_exception_type_t ex;
3096         uc_value_t *handler;
3097         uint64_t mask;
3098         size_t i, j;
3099         int sig, rv;
3100 
3101         /* Check whether the signal self-pipe was actually set up, rather than
3102          * re-checking config->setup_signal_handlers directly: the pipe may
3103          * have been lazily initialized on demand via
3104          * uc_vm_signal_handlers_ensure() after the fact (see lib/debug.c),
3105          * independent of what the original config requested. */
3106         if (vm->signal.sigpipe[0] < 0)
3107                 return EXCEPTION_NONE;
3108 
3109         for (i = 0; i < ARRAY_SIZE(vm->signal.raised); i++) {
3110                 if (!vm->signal.raised[i])
3111                         continue;
3112 
3113                 do {
3114                         rv = read(vm->signal.sigpipe[0], &sig, sizeof(sig));
3115                 } while (rv > 0 || (rv == -1 && errno == EINTR));
3116 
3117                 for (j = 0; j < 64; j++) {
3118                         mask = 1ull << j;
3119 
3120                         if (vm->signal.raised[i] & mask) {
3121                                 vm->signal.raised[i] &= ~mask;
3122 
3123                                 sig = i * 64 + j;
3124                                 handler = ucv_array_get(vm->signal.handler, sig);
3125 
3126                                 if (ucv_is_callable(handler)) {
3127                                         uc_vm_stack_push(vm, ucv_get(handler));
3128                                         uc_vm_stack_push(vm, ucv_int64_new(sig));
3129 
3130                                         ex = uc_vm_call(vm, false, 1);
3131 
3132                                         if (ex != EXCEPTION_NONE)
3133                                                 return ex;
3134 
3135                                         ucv_put(uc_vm_stack_pop(vm));
3136                                 }
3137                         }
3138                 }
3139         }
3140 
3141         return EXCEPTION_NONE;
3142 }
3143 
3144 static uc_vm_status_t
3145 uc_vm_exception_type_to_status(uc_vm_t *vm)
3146 {
3147         switch (vm->exception.type) {
3148         case EXCEPTION_NONE:   return STATUS_OK;
3149         case EXCEPTION_EXIT:   return STATUS_EXIT;
3150         case EXCEPTION_SYNTAX: return ERROR_COMPILE;
3151         default:               return ERROR_RUNTIME;
3152         }
3153 }
3154 
3155 static uc_vm_status_t
3156 uc_vm_execute_chunk(uc_vm_t *vm)
3157 {
3158         uc_callframe_t *frame = NULL;
3159         uc_chunk_t *chunk = NULL;
3160         size_t caller = vm->callframes.count - 1;
3161         uc_value_t *retval;
3162         uc_vm_insn_t insn;
3163         uint8_t *ip;
3164 
3165         while (vm->callframes.count > caller) {
3166                 frame = &vm->callframes.entries[vm->callframes.count - 1];
3167                 chunk = uc_vm_frame_chunk(frame);
3168 
3169                 if (!chunk)
3170                         break;
3171 
3172                 if (vm->trace) {
3173                         ip = frame->ip;
3174                         insn = uc_vm_decode_insn(vm, frame, chunk);
3175                         uc_dump_insn(vm, ip, insn);
3176                 }
3177                 else {
3178                         insn = uc_vm_decode_insn(vm, frame, chunk);
3179                 }
3180 
3181                 switch (insn) {
3182                 case I_LOAD:
3183                 case I_LOAD8:
3184                 case I_LOAD16:
3185                 case I_LOAD32:
3186                         uc_vm_insn_load(vm, insn);
3187                         break;
3188 
3189                 case I_LREXP:
3190                         uc_vm_insn_load_regexp(vm, insn);
3191                         break;
3192 
3193                 case I_LNULL:
3194                         uc_vm_insn_load_null(vm, insn);
3195                         break;
3196 
3197                 case I_LTRUE:
3198                 case I_LFALSE:
3199                         uc_vm_insn_load_bool(vm, insn);
3200                         break;
3201 
3202                 case I_LTHIS:
3203                         uc_vm_stack_push(vm, ucv_get(frame->ctx));
3204                         break;
3205 
3206                 case I_LVAR:
3207                         uc_vm_insn_load_var(vm, insn);
3208                         break;
3209 
3210                 case I_LVAL:
3211                         uc_vm_insn_load_val(vm, insn);
3212                         break;
3213 
3214                 case I_PVAL:
3215                         uc_vm_insn_peek_val(vm, insn);
3216                         break;
3217 
3218                 case I_LUPV:
3219                         uc_vm_insn_load_upval(vm, insn);
3220                         break;
3221 
3222                 case I_LLOC:
3223                         uc_vm_insn_load_local(vm, insn);
3224                         break;
3225 
3226                 case I_CLFN:
3227                 case I_ARFN:
3228                         uc_vm_insn_load_closure(vm, insn);
3229                         break;
3230 
3231                 case I_NARR:
3232                         uc_vm_insn_narr(vm, insn);
3233                         break;
3234 
3235                 case I_PARR:
3236                         uc_vm_insn_parr(vm, insn);
3237                         break;
3238 
3239                 case I_MARR:
3240                         uc_vm_insn_marr(vm, insn);
3241                         break;
3242 
3243                 case I_NOBJ:
3244                         uc_vm_insn_nobj(vm, insn);
3245                         break;
3246 
3247                 case I_SOBJ:
3248                         uc_vm_insn_sobj(vm, insn);
3249                         break;
3250 
3251                 case I_MOBJ:
3252                         uc_vm_insn_mobj(vm, insn);
3253                         break;
3254 
3255                 case I_SVAR:
3256                         uc_vm_insn_store_var(vm, insn);
3257                         break;
3258 
3259                 case I_SVAL:
3260                         uc_vm_insn_store_val(vm, insn);
3261                         break;
3262 
3263                 case I_SUPV:
3264                         uc_vm_insn_store_upval(vm, insn);
3265                         break;
3266 
3267                 case I_SLOC:
3268                         uc_vm_insn_store_local(vm, insn);
3269                         break;
3270 
3271                 case I_UVAR:
3272                         uc_vm_insn_update_var(vm, insn);
3273                         break;
3274 
3275                 case I_UVAL:
3276                         uc_vm_insn_update_val(vm, insn);
3277                         break;
3278 
3279                 case I_UUPV:
3280                         uc_vm_insn_update_upval(vm, insn);
3281                         break;
3282 
3283                 case I_ULOC:
3284                         uc_vm_insn_update_local(vm, insn);
3285                         break;
3286 
3287                 case I_ADD:
3288                 case I_SUB:
3289                 case I_MUL:
3290                 case I_DIV:
3291                 case I_MOD:
3292                 case I_EXP:
3293                         uc_vm_insn_arith(vm, insn);
3294                         break;
3295 
3296                 case I_PLUS:
3297                 case I_MINUS:
3298                         uc_vm_insn_plus_minus(vm, insn);
3299                         break;
3300 
3301                 case I_LSHIFT:
3302                 case I_RSHIFT:
3303                 case I_BAND:
3304                 case I_BXOR:
3305                 case I_BOR:
3306                         uc_vm_insn_bitop(vm, insn);
3307                         break;
3308 
3309                 case I_COMPL:
3310                         uc_vm_insn_complement(vm, insn);
3311                         break;
3312 
3313                 case I_EQS:
3314                 case I_NES:
3315                         uc_vm_insn_equality(vm, insn);
3316                         break;
3317 
3318                 case I_EQ:
3319                 case I_NE:
3320                 case I_LT:
3321                 case I_LE:
3322                 case I_GT:
3323                 case I_GE:
3324                         uc_vm_insn_rel(vm, insn);
3325                         break;
3326 
3327                 case I_IN:
3328                         uc_vm_insn_in(vm, insn);
3329                         break;
3330 
3331                 case I_NOT:
3332                         uc_vm_insn_not(vm, insn);
3333                         break;
3334 
3335                 case I_JMP:
3336                         uc_vm_insn_jmp(vm, insn);
3337                         break;
3338 
3339                 case I_JMPZ:
3340                         uc_vm_insn_jmpz(vm, insn);
3341                         break;
3342 
3343                 case I_JMPNT:
3344                         uc_vm_insn_jmpnt(vm, insn);
3345                         break;
3346 
3347                 case I_NEXTK:
3348                 case I_NEXTKV:
3349                         uc_vm_insn_next(vm, insn);
3350                         break;
3351 
3352                 case I_COPY:
3353                         uc_vm_stack_push(vm, ucv_get(uc_vm_stack_peek(vm, vm->arg.u8)));
3354                         break;
3355 
3356                 case I_POP:
3357                         ucv_put(uc_vm_stack_pop(vm));
3358                         uc_vm_gc_step(vm);
3359                         break;
3360 
3361                 case I_CUPV:
3362                         uc_vm_insn_close_upval(vm, insn);
3363                         break;
3364 
3365                 case I_CALL:
3366                         uc_vm_insn_call(vm);
3367 
3368                         if (vm->callframes.count == 0)
3369                                 return uc_vm_exception_type_to_status(vm);
3370 
3371                         /* frame and chunk are re-derived from the (now callee) top callframe
3372                          * at the top of the loop; nothing between here and there reads them. */
3373                         break;
3374 
3375                 case I_RETURN:
3376                         retval = uc_vm_callframe_pop(vm);
3377 
3378                         uc_vm_stack_push(vm, retval);
3379 
3380                         if (vm->callframes.count == 0)
3381                                 return STATUS_OK;
3382                         break;
3383 
3384                 case I_PRINT:
3385                         uc_vm_insn_print(vm, insn);
3386                         break;
3387 
3388                 case I_DELETE:
3389                         uc_vm_insn_delete(vm, insn);
3390                         break;
3391 
3392                 case I_IMPORT:
3393                         uc_vm_insn_import(vm, insn);
3394                         break;
3395 
3396                 case I_EXPORT:
3397                         uc_vm_insn_export(vm, insn);
3398                         break;
3399 
3400                 case I_DYNLOAD:
3401                         uc_vm_insn_dynload(vm, insn);
3402                         break;
3403 
3404                 default:
3405                         uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "unknown opcode %d", insn);
3406                         break;
3407                 }
3408 
3409 exception:
3410                 /* previous instruction raised exception */
3411                 if (vm->exception.type != EXCEPTION_NONE) {
3412                         /* VM termination was requested */
3413                         if (vm->exception.type == EXCEPTION_EXIT) {
3414                                 uc_vm_reset_callframes(vm);
3415 
3416                                 return STATUS_EXIT;
3417                         }
3418 
3419                         /* If a debugger has armed the dedicated "break on uncaught
3420                          * exception" system breakpoint and nothing between here and
3421                          * this invocation's original call depth would actually handle
3422                          * this exception, give it a chance to inspect the fully intact
3423                          * stack *before* uc_vm_handle_exception()'s loop below starts
3424                          * popping frames - once that happens, the original throwing
3425                          * frame's locals and exact position are gone for good. */
3426                         if (!uc_vm_exception_would_be_caught(vm, caller)) {
3427                                 for (size_t i = 0; i < vm->breakpoints.count; i++) {
3428                                         uc_breakpoint_t *bk = vm->breakpoints.entries[i];
3429 
3430                                         if (bk != NULL && bk->ip == UC_BREAKPOINT_UNCAUGHT_EXCEPTION) {
3431                                                 bk->cb(vm, bk);
3432 
3433                                                 /* "quit" was issued from within the breakpoint's
3434                                                  * CLI session */
3435                                                 if (vm->exception.type == EXCEPTION_EXIT) {
3436                                                         uc_vm_reset_callframes(vm);
3437 
3438                                                         return STATUS_EXIT;
3439                                                 }
3440 
3441                                                 break;
3442                                         }
3443                                 }
3444                         }
3445 
3446                         /* walk up callframes until something handles the exception or the original caller is reached */
3447                         while (!uc_vm_handle_exception(vm)) {
3448                                 /* no further callframe, report unhandled exception and terminate */
3449                                 if (vm->callframes.count == 0)
3450                                         return ERROR_RUNTIME;
3451 
3452                                 /* if VM returned into native function, don't bubble up */
3453                                 if (!vm->callframes.entries[vm->callframes.count - 1].closure)
3454                                         return ERROR_RUNTIME;
3455 
3456                                 /* no exception handler in current function, pop callframe */
3457                                 ucv_put(uc_vm_callframe_pop(vm));
3458 
3459                                 /* do not bubble past original call depth */
3460                                 if (vm->callframes.count <= caller)
3461                                         return ERROR_RUNTIME;
3462                         }
3463                 }
3464 
3465                 /* run handler for signal(s) delivered during previous instruction */
3466                 if (uc_vm_signal_dispatch(vm) != EXCEPTION_NONE)
3467                         goto exception;
3468 
3469                 /* check for break request */
3470                 if (vm->break_requested) {
3471                         vm->break_requested = false;
3472                         return STATUS_BREAK;
3473                 }
3474         }
3475 
3476         return STATUS_OK;
3477 }
3478 
3479 uc_vm_status_t
3480 uc_vm_execute(uc_vm_t *vm, uc_program_t *program, uc_value_t **retval)
3481 {
3482         uc_closure_t *closure = ucv_as_closure(uc_program_main(vm, program));
3483         uc_function_t *fn = closure->function;
3484         uc_vm_status_t status;
3485         uc_callframe_t *frame;
3486         uc_stringbuf_t *buf;
3487         uc_value_t *val;
3488 
3489         frame = uc_vector_push(&vm->callframes, {
3490                 .closure = closure,
3491                 .stackframe = 0,
3492                 .ip = fn->chunk.entries,
3493                 .strict = fn->strict
3494         });
3495 
3496         if (vm->trace) {
3497                 buf = xprintbuf_new();
3498 
3499                 uc_source_context_format(buf, uc_vm_frame_source(frame), 0, true);
3500 
3501                 fwrite(buf->buf, 1, printbuf_length(buf), stderr);
3502                 printbuf_free(buf);
3503 
3504                 uc_vm_frame_dump(vm, frame);
3505         }
3506 
3507         //uc_vm_stack_push(vm, closure->header.jso);
3508         uc_vm_stack_push(vm, NULL);
3509 
3510         status = uc_vm_execute_chunk(vm);
3511 
3512         switch (status) {
3513         case STATUS_OK:
3514                 val = uc_vm_stack_pop(vm);
3515 
3516                 if (retval)
3517                         *retval = val;
3518                 else
3519                         ucv_put(val);
3520 
3521                 break;
3522 
3523         case STATUS_EXIT:
3524                 if (retval)
3525                         *retval = ucv_int64_new(vm->arg.s32);
3526 
3527                 break;
3528 
3529         case STATUS_BREAK:
3530                 /* Break requested - exit gracefully without error */
3531                 if (retval)
3532                         *retval = NULL;
3533 
3534                 break;
3535 
3536         default:
3537                 if (vm->exhandler)
3538                         vm->exhandler(vm, &vm->exception);
3539 
3540                 if (retval)
3541                         *retval = NULL;
3542 
3543                 break;
3544         }
3545 
3546         return status;
3547 }
3548 
3549 uc_exception_type_t
3550 uc_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
3551 {
3552         uc_value_t *ctx = mcall ? ucv_get(uc_vm_stack_peek(vm, nargs + 1)) : NULL;
3553         uc_value_t *fno = ucv_get(uc_vm_stack_peek(vm, nargs));
3554 
3555         uc_vm_clear_exception(vm);
3556 
3557         if (uc_vm_call_function(vm, ctx, fno, mcall, nargs & 0xffff, false)) {
3558                 if (ucv_type(fno) != UC_CFUNCTION)
3559                         uc_vm_execute_chunk(vm);
3560         }
3561 
3562         return vm->exception.type;
3563 }
3564 
3565 uc_value_t *
3566 uc_vm_scope_get(uc_vm_t *vm)
3567 {
3568         return vm->globals;
3569 }
3570 
3571 void
3572 uc_vm_scope_set(uc_vm_t *vm, uc_value_t *ctx)
3573 {
3574         ucv_put(vm->globals);
3575         vm->globals = ctx;
3576 }
3577 
3578 uc_value_t *
3579 uc_vm_invoke(uc_vm_t *vm, const char *fname, size_t nargs, ...)
3580 {
3581         uc_exception_type_t ex;
3582         uc_value_t *fno, *arg;
3583         va_list ap;
3584         size_t i;
3585 
3586         fno = ucv_property_get(vm->globals, fname);
3587 
3588         if (!ucv_is_callable(fno))
3589                 return NULL;
3590 
3591         uc_vm_stack_push(vm, ucv_get(fno));
3592 
3593         va_start(ap, nargs);
3594 
3595         for (i = 0; i < nargs; i++) {
3596                 arg = va_arg(ap, uc_value_t *);
3597                 uc_vm_stack_push(vm, ucv_get(arg));
3598         }
3599 
3600         va_end(ap);
3601 
3602         ex = uc_vm_call(vm, false, nargs);
3603 
3604         if (ex) {
3605                 if (vm->exhandler)
3606                         vm->exhandler(vm, &vm->exception);
3607 
3608                 return NULL;
3609         }
3610 
3611         return uc_vm_stack_pop(vm);
3612 }
3613 
3614 uc_exception_handler_t *
3615 uc_vm_exception_handler_get(uc_vm_t *vm)
3616 {
3617         return vm->exhandler;
3618 }
3619 
3620 void
3621 uc_vm_exception_handler_set(uc_vm_t *vm, uc_exception_handler_t *exhandler)
3622 {
3623         vm->exhandler = exhandler;
3624 }
3625 
3626 uint32_t
3627 uc_vm_trace_get(uc_vm_t *vm)
3628 {
3629         return vm->trace;
3630 }
3631 
3632 void
3633 uc_vm_trace_set(uc_vm_t *vm, uint32_t level)
3634 {
3635         vm->trace = level;
3636 }
3637 
3638 bool
3639 uc_vm_registry_exists(uc_vm_t *vm, const char *key)
3640 {
3641         bool exists;
3642 
3643         ucv_object_get(vm->registry, key, &exists);
3644 
3645         return exists;
3646 }
3647 
3648 uc_value_t *
3649 uc_vm_registry_get(uc_vm_t *vm, const char *key)
3650 {
3651         return ucv_object_get(vm->registry, key, NULL);
3652 }
3653 
3654 void
3655 uc_vm_registry_set(uc_vm_t *vm, const char *key, uc_value_t *value)
3656 {
3657         if (!vm->registry)
3658                 vm->registry = ucv_object_new(vm);
3659 
3660         ucv_object_add(vm->registry, key, value);
3661 }
3662 
3663 bool
3664 uc_vm_registry_delete(uc_vm_t *vm, const char *key)
3665 {
3666         return ucv_object_delete(vm->registry, key);
3667 }
3668 
3669 bool
3670 uc_vm_gc_start(uc_vm_t *vm, uint16_t interval)
3671 {
3672         bool changed = false;
3673 
3674         if (vm->gc_interval != interval) {
3675                 vm->gc_interval = interval;
3676                 changed = true;
3677         }
3678 
3679         if (!(vm->gc_flags & UC_GC_ENABLED)) {
3680                 vm->gc_flags |= UC_GC_ENABLED;
3681                 changed = true;
3682         }
3683 
3684         return changed;
3685 }
3686 
3687 bool
3688 uc_vm_gc_stop(uc_vm_t *vm)
3689 {
3690         if (!(vm->gc_flags & UC_GC_ENABLED))
3691                 return false;
3692 
3693         vm->gc_flags &= ~UC_GC_ENABLED;
3694 
3695         return true;
3696 }
3697 
3698 void
3699 uc_vm_signal_raise(uc_vm_t *vm, int signo)
3700 {
3701         uint8_t signum = signo;
3702 
3703         if (signo <= 0 || signo >= UC_SYSTEM_SIGNAL_COUNT)
3704                 return;
3705 
3706         vm->signal.raised[signo / 64] |= (1ull << (signo % 64));
3707 
3708         if (write(vm->signal.sigpipe[1], &signum, sizeof(signum)) == -1) {}
3709 }
3710 
3711 int
3712 uc_vm_signal_notifyfd(uc_vm_t *vm)
3713 {
3714         return vm->signal.sigpipe[0];
3715 }
3716 
3717 bool
3718 uc_vm_break_requested(uc_vm_t *vm)
3719 {
3720         return vm->break_requested;
3721 }
3722 
3723 void
3724 uc_vm_break_request(uc_vm_t *vm)
3725 {
3726         vm->break_requested = true;
3727 
3728         if (vm->break_notifyfd[1] >= 0) {
3729                 char c = 'B';
3730                 if (write(vm->break_notifyfd[1], &c, 1) == -1) {}
3731         }
3732 }
3733 
3734 int
3735 uc_vm_break_notifyfd(uc_vm_t *vm)
3736 {
3737         return vm->break_notifyfd[0];
3738 }
3739 
3740 uc_vm_status_t
3741 uc_vm_resume(uc_vm_t *vm)
3742 {
3743         uc_vm_status_t status = uc_vm_execute_chunk(vm);
3744 
3745         switch (status) {
3746         case STATUS_OK:
3747         case STATUS_EXIT:
3748         case STATUS_BREAK:
3749                 break;
3750 
3751         default:
3752                 if (vm->exhandler)
3753                         vm->exhandler(vm, &vm->exception);
3754 
3755                 break;
3756         }
3757 
3758         return status;
3759 }
3760 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt