summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-04-13 14:59:48 +0200
committerGitHub <noreply@github.com>2022-04-13 14:59:48 +0200
commit23ddf91d6380da392c8eea7b7fe12c2cd687b6de (patch)
treebbbb15caf100cd72f27636133e0bc7e643b3a266
parentc5fb8ca9794ac858cf45e7de5e3f99a9ac201df9 (diff)
parent111cf063880bf37f9ef5cea38a9f33f32f7e2a4d (diff)
Merge pull request #72 from jow-/vm-nested-function-call-return
vm: stop executing bytecode on return of nested calls
-rw-r--r--tests/custom/04_bugs/36_vm_nested_call_return52
-rw-r--r--vm.c21
2 files changed, 62 insertions, 11 deletions
diff --git a/tests/custom/04_bugs/36_vm_nested_call_return b/tests/custom/04_bugs/36_vm_nested_call_return
new file mode 100644
index 0000000..6a52b78
--- /dev/null
+++ b/tests/custom/04_bugs/36_vm_nested_call_return
@@ -0,0 +1,52 @@
+When indirectly invoking a managed function from manged code, e.g.
+on stringifying an object using it's tostring() prototype method
+during string concatenation, bytecode execution of the nested managed
+function call did not stop and return to the caller, but continued
+past the return of the invoked function, clobbering the VM context.
+
+
+-- Testcase --
+{%
+ let o = proto(
+ { color: "red" },
+ { tostring: function() { return "I am a " + this.color + " object" } }
+ );
+
+ print("Result: " + o + ".\n");
+%}
+-- End --
+
+-- Expect stdout --
+Result: I am a red object.
+-- End --
+
+
+-- Testcase --
+{%
+ let o = proto(
+ { color: "red" },
+ { tostring: function() { die("Exception while stringifying") } }
+ );
+
+ function t() {
+ try {
+ print("Result: " + o + ".\n");
+ }
+ catch (e) {
+ warn("Caught exception: " + e.stacktrace[0].context + "\n");
+ }
+ }
+
+ t();
+%}
+-- End --
+
+-- Expect stderr --
+Caught exception: In [anonymous function](), line 4, byte 62:
+ called from function t ([stdin]:9:23)
+ called from anonymous function ([stdin]:16:4)
+
+ ` { tostring: function() { die("Exception while stringifying") } }`
+ Near here ---------------------------------------------------------^
+
+-- End --
diff --git a/vm.c b/vm.c
index 7d4c10e..29ace38 100644
--- a/vm.c
+++ b/vm.c
@@ -2358,11 +2358,12 @@ uc_vm_execute_chunk(uc_vm_t *vm)
{
uc_callframe_t *frame = uc_vm_current_frame(vm);
uc_chunk_t *chunk = uc_vm_frame_chunk(frame);
+ size_t caller = vm->callframes.count - 1;
uc_value_t *retval;
uc_vm_insn_t insn;
uint8_t *ip;
- while (chunk) {
+ while (chunk && vm->callframes.count > caller) {
if (vm->trace) {
ip = frame->ip;
insn = uc_vm_decode_insn(vm, frame, chunk);
@@ -2596,23 +2597,21 @@ uc_vm_execute_chunk(uc_vm_t *vm)
return STATUS_EXIT;
}
- /* walk up callframes until something handles the exception or the root is reached */
+ /* walk up callframes until something handles the exception or the original caller is reached */
while (!uc_vm_handle_exception(vm)) {
- /* no further callframe to pop, report unhandled exception and terminate */
- if (vm->callframes.count <= 1) {
- uc_vm_reset_callframes(vm);
-
- return ERROR_RUNTIME;
- }
-
/* if VM returned into native function, don't bubble up */
if (!chunk)
return ERROR_RUNTIME;
/* no exception handler in current function, pop callframe */
- ucv_put(uc_vm_callframe_pop(vm));
+ if (vm->callframes.count > 0)
+ ucv_put(uc_vm_callframe_pop(vm));
+
+ /* no further callframe, report unhandled exception and terminate */
+ if (vm->callframes.count == 0 || vm->callframes.count <= caller)
+ return ERROR_RUNTIME;
- /* resume execution at topmost remaining callframe */
+ /* resume execution in next remaining callframe */
frame = uc_vector_last(&vm->callframes);
chunk = uc_vm_frame_chunk(frame);
}