include $(TOPDIR)/rules.mk
PKG_NAME:=ucode
-PKG_RELEASE:=1
+PKG_RELEASE:=2
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=https://github.com/jow-/ucode.git
void __attribute__((format(printf, 3, 0)))
uc_vm_raise_exception(uc_vm_t *vm, uc_exception_type_t type, const char *fmt, ...);
-+uc_value_t *uc_vm_exception_value(uc_vm_t *vm);
++uc_value_t *uc_vm_exception_object(uc_vm_t *vm);
uc_vm_status_t uc_vm_execute(uc_vm_t *vm, uc_program_t *fn, uc_value_t **retval);
uc_value_t *uc_vm_invoke(uc_vm_t *vm, const char *fname, size_t nargs, ...);
--- a/vm.c
+++ b/vm.c
-@@ -814,9 +814,12 @@ uc_vm_exception_tostring(uc_vm_t *vm, si
+@@ -813,9 +813,12 @@ uc_vm_exception_tostring(uc_vm_t *vm, si
return message ? ucv_get(message) : ucv_string_new("Exception");
}
-static uc_value_t *
-uc_vm_exception_new(uc_vm_t *vm, uc_exception_type_t type, const char *message, uc_value_t *stacktrace)
+uc_value_t *
-+uc_vm_exception_value(uc_vm_t *vm)
++uc_vm_exception_object(uc_vm_t *vm)
{
+ uc_exception_type_t type = vm->exception.type;
+ const char *message = vm->exception.message;
uc_value_t *exception_prototype = uc_vm_registry_get(vm, "vm.exception.proto");
uc_value_t *exo;
-@@ -881,7 +884,7 @@ uc_vm_handle_exception(uc_vm_t *vm)
+@@ -880,7 +883,7 @@ uc_vm_handle_exception(uc_vm_t *vm)
ucv_put(uc_vm_stack_pop(vm));
/* prepare exception object and expose it to user handler code */
- exo = uc_vm_exception_new(vm, vm->exception.type, vm->exception.message, vm->exception.stacktrace);
-+ exo = uc_vm_exception_value(vm);
++ exo = uc_vm_exception_object(vm);
uc_vm_stack_push(vm, exo);
+++ /dev/null
-Date: Wed, 11 Jun 2025 18:31:39 +0200
-Subject: [PATCH] uloop: add exception_handler_set function
-
-This allows calling the provided handler on exceptions, avoiding the
-existing behavior of calling uloop_end().
-
----
-
---- a/lib/uloop.c
-+++ b/lib/uloop.c
-@@ -114,27 +114,48 @@ uc_uloop_cb_free(uc_uloop_cb_t *cb)
- }
-
- static bool
-+uc_uloop_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
-+{
-+ uc_value_t *exh, *val;
-+
-+ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE)
-+ return true;
-+
-+ exh = uc_vm_registry_get(vm, "uloop.ex_handler");
-+ if (!ucv_is_callable(exh))
-+ goto error;
-+
-+ val = uc_vm_exception_value(vm);
-+ uc_vm_stack_push(vm, ucv_get(exh));
-+ uc_vm_stack_push(vm, ucv_get(val));
-+
-+ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE)
-+ goto error;
-+
-+ ucv_put(uc_vm_stack_pop(vm));
-+
-+ return false;
-+
-+error:
-+ uloop_end();
-+ return false;
-+}
-+
-+static void
- uc_uloop_cb_invoke(uc_uloop_cb_t *cb, uc_value_t *arg)
- {
- uc_vm_t *vm = cb->vm;
- uc_value_t *func = ucv_resource_value_get(cb->obj, 0);
-
- if (!ucv_is_callable(func))
-- return false;
-+ return;
-
- uc_vm_stack_push(vm, ucv_get(cb->obj));
- uc_vm_stack_push(vm, ucv_get(func));
- uc_vm_stack_push(vm, ucv_get(arg));
-
-- if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
-- uloop_end();
--
-- return false;
-- }
--
-- ucv_put(uc_vm_stack_pop(vm));
--
-- return true;
-+ if (uc_uloop_vm_call(vm, true, 1))
-+ ucv_put(uc_vm_stack_pop(vm));
- }
-
- /**
-@@ -1550,11 +1571,8 @@ uc_uloop_task_output_cb(struct uloop_fd
- uc_vm_stack_push(vm, ucv_get(obj));
- uc_vm_stack_push(vm, ucv_get(task->input_cb));
-
-- if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) {
-- uloop_end();
--
-+ if (!uc_uloop_vm_call(vm, true, 0))
- return;
-- }
-
- msg = uc_vm_stack_pop(vm);
- uc_uloop_pipe_send_common(vm, msg, task->input_fd);
-@@ -1572,14 +1590,10 @@ uc_uloop_task_output_cb(struct uloop_fd
- uc_vm_stack_push(vm, ucv_get(task->output_cb));
- uc_vm_stack_push(vm, msg);
-
-- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) {
-- ucv_put(uc_vm_stack_pop(vm));
-- }
-- else {
-- uloop_end();
--
-+ if (!uc_uloop_vm_call(vm, true, 1))
- return;
-- }
-+
-+ ucv_put(uc_vm_stack_pop(vm));
- }
- else {
- ucv_put(msg);
-@@ -1703,7 +1717,7 @@ uc_uloop_task(uc_vm_t *vm, size_t nargs)
- uc_vm_stack_push(vm, func);
- uc_vm_stack_push(vm, ucv_get(p));
-
-- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) {
-+ if (uc_uloop_vm_call(vm, false, 1)) {
- res = uc_vm_stack_pop(vm);
- uc_uloop_pipe_send_common(vm, res, tpipe->output);
- ucv_put(res);
-@@ -2167,6 +2181,19 @@ uc_uloop_signal(uc_vm_t *vm, size_t narg
- }
- #endif
-
-+static uc_value_t *
-+uc_uloop_exception_handler_set(uc_vm_t *vm, size_t nargs)
-+{
-+ uc_value_t *arg = uc_fn_arg(0);
-+
-+ if (arg && !ucv_is_callable(arg))
-+ return NULL;
-+
-+ uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg));
-+
-+ return ucv_boolean_new(true);
-+}
-+
-
- static const uc_function_list_t timer_fns[] = {
- { "set", uc_uloop_timer_set },
-@@ -2233,6 +2260,7 @@ static const uc_function_list_t global_f
- #ifdef HAVE_ULOOP_SIGNAL
- { "signal", uc_uloop_signal },
- #endif
-+ { "exception_handler_set", uc_uloop_exception_handler_set },
- };
-
-
--- /dev/null
+Date: Wed, 11 Jun 2025 18:31:39 +0200
+Subject: [PATCH] uloop: add guard() function
+
+This allows calling the provided handler on exceptions, avoiding the
+existing behavior of calling uloop_end().
+
+---
+
+--- a/lib/uloop.c
++++ b/lib/uloop.c
+@@ -114,27 +114,48 @@ uc_uloop_cb_free(uc_uloop_cb_t *cb)
+ }
+
+ static bool
++uc_uloop_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
++{
++ uc_value_t *exh, *val;
++
++ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE)
++ return true;
++
++ exh = uc_vm_registry_get(vm, "uloop.ex_handler");
++ if (!ucv_is_callable(exh))
++ goto error;
++
++ val = uc_vm_exception_object(vm);
++ uc_vm_stack_push(vm, ucv_get(exh));
++ uc_vm_stack_push(vm, val);
++
++ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE)
++ goto error;
++
++ ucv_put(uc_vm_stack_pop(vm));
++
++ return false;
++
++error:
++ uloop_end();
++ return false;
++}
++
++static void
+ uc_uloop_cb_invoke(uc_uloop_cb_t *cb, uc_value_t *arg)
+ {
+ uc_vm_t *vm = cb->vm;
+ uc_value_t *func = ucv_resource_value_get(cb->obj, 0);
+
+ if (!ucv_is_callable(func))
+- return false;
++ return;
+
+ uc_vm_stack_push(vm, ucv_get(cb->obj));
+ uc_vm_stack_push(vm, ucv_get(func));
+ uc_vm_stack_push(vm, ucv_get(arg));
+
+- if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
+- uloop_end();
+-
+- return false;
+- }
+-
+- ucv_put(uc_vm_stack_pop(vm));
+-
+- return true;
++ if (uc_uloop_vm_call(vm, true, 1))
++ ucv_put(uc_vm_stack_pop(vm));
+ }
+
+ /**
+@@ -1550,11 +1571,8 @@ uc_uloop_task_output_cb(struct uloop_fd
+ uc_vm_stack_push(vm, ucv_get(obj));
+ uc_vm_stack_push(vm, ucv_get(task->input_cb));
+
+- if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) {
+- uloop_end();
+-
++ if (!uc_uloop_vm_call(vm, true, 0))
+ return;
+- }
+
+ msg = uc_vm_stack_pop(vm);
+ uc_uloop_pipe_send_common(vm, msg, task->input_fd);
+@@ -1572,14 +1590,10 @@ uc_uloop_task_output_cb(struct uloop_fd
+ uc_vm_stack_push(vm, ucv_get(task->output_cb));
+ uc_vm_stack_push(vm, msg);
+
+- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE) {
+- ucv_put(uc_vm_stack_pop(vm));
+- }
+- else {
+- uloop_end();
+-
++ if (!uc_uloop_vm_call(vm, true, 1))
+ return;
+- }
++
++ ucv_put(uc_vm_stack_pop(vm));
+ }
+ else {
+ ucv_put(msg);
+@@ -1703,7 +1717,7 @@ uc_uloop_task(uc_vm_t *vm, size_t nargs)
+ uc_vm_stack_push(vm, func);
+ uc_vm_stack_push(vm, ucv_get(p));
+
+- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE) {
++ if (uc_uloop_vm_call(vm, false, 1)) {
+ res = uc_vm_stack_pop(vm);
+ uc_uloop_pipe_send_common(vm, res, tpipe->output);
+ ucv_put(res);
+@@ -2167,6 +2181,22 @@ uc_uloop_signal(uc_vm_t *vm, size_t narg
+ }
+ #endif
+
++static uc_value_t *
++uc_uloop_guard(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *arg = uc_fn_arg(0);
++
++ if (!nargs)
++ return ucv_get(uc_vm_registry_get(vm, "uloop.ex_handler"));
++
++ if (arg && !ucv_is_callable(arg))
++ return NULL;
++
++ uc_vm_registry_set(vm, "uloop.ex_handler", ucv_get(arg));
++
++ return ucv_boolean_new(true);
++}
++
+
+ static const uc_function_list_t timer_fns[] = {
+ { "set", uc_uloop_timer_set },
+@@ -2233,6 +2263,7 @@ static const uc_function_list_t global_f
+ #ifdef HAVE_ULOOP_SIGNAL
+ { "signal", uc_uloop_signal },
+ #endif
++ { "guard", uc_uloop_guard },
+ };
+
+
+++ /dev/null
-Date: Wed, 11 Jun 2025 18:40:10 +0200
-Subject: [PATCH] ubus: add exception_handler_set function
-
-This allows calling the provided handler on exceptions, avoiding the
-existing behavior of calling uloop_end().
-
----
-
---- a/lib/ubus.c
-+++ b/lib/ubus.c
-@@ -573,6 +573,40 @@ uc_ubus_call_cb(struct ubus_request *req
- }
-
- static void
-+uc_ubus_vm_handle_exception(uc_vm_t *vm)
-+{
-+ uc_value_t *exh, *val;
-+
-+ exh = uc_vm_registry_get(vm, "ubus.ex_handler");
-+ if (!ucv_is_callable(exh))
-+ goto error;
-+
-+ val = uc_vm_exception_value(vm);
-+ uc_vm_stack_push(vm, ucv_get(exh));
-+ uc_vm_stack_push(vm, ucv_get(val));
-+
-+ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE)
-+ goto error;
-+
-+ ucv_put(uc_vm_stack_pop(vm));
-+ return;
-+
-+error:
-+ uloop_end();
-+}
-+
-+static bool
-+uc_ubus_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
-+{
-+ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE)
-+ return true;
-+
-+ uc_ubus_vm_handle_exception(vm);
-+
-+ return false;
-+}
-+
-+static void
- uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply)
- {
- uc_value_t *this = ucv_get(defer->res);
-@@ -623,10 +657,8 @@ uc_ubus_call_data_user_cb(struct ubus_re
- uc_vm_stack_push(vm, ucv_get(func));
- uc_vm_stack_push(vm, ucv_get(reply));
-
-- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 1))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
- }
-
-@@ -645,10 +677,8 @@ uc_ubus_call_fd_cb(struct ubus_request *
- uc_vm_stack_push(vm, ucv_get(func));
- uc_vm_stack_push(vm, ucv_int64_new(fd));
-
-- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 1))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
- }
-
-@@ -927,10 +957,8 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu
- uc_vm_stack_push(vm, ucv_get(replycb));
- uc_vm_stack_push(vm, ucv_int64_new(rv));
-
-- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, false, 1))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
-
- ucv_put(res->res);
- }
-@@ -1199,10 +1227,8 @@ uc_ubus_object_notify_data_cb(struct ubu
- uc_vm_stack_push(vm, ucv_int64_new(type));
- uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true));
-
-- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 2))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
- }
-
-@@ -1222,10 +1248,8 @@ uc_ubus_object_notify_status_cb(struct u
- uc_vm_stack_push(vm, ucv_int64_new(idx));
- uc_vm_stack_push(vm, ucv_int64_new(ret));
-
-- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 2))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
- }
-
-@@ -1245,10 +1269,8 @@ uc_ubus_object_notify_complete_cb(struct
- uc_vm_stack_push(vm, ucv_int64_new(idx));
- uc_vm_stack_push(vm, ucv_int64_new(ret));
-
-- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 2))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
-
- notifyctx->complete = true;
-@@ -1579,7 +1601,7 @@ uc_ubus_handle_reply_common(struct ubus_
- /* treat other exceptions as fatal and halt uloop */
- default:
- uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR);
-- uloop_end();
-+ uc_ubus_vm_handle_exception(vm);
- break;
- }
-
-@@ -1638,10 +1660,8 @@ uc_ubus_object_subscribe_cb(struct ubus_
- uc_vm_stack_push(uuobj->vm, ucv_get(uuobj->res));
- uc_vm_stack_push(uuobj->vm, ucv_get(func));
-
-- if (uc_vm_call(uuobj->vm, true, 0) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(uuobj->vm, true, 0))
- ucv_put(uc_vm_stack_pop(uuobj->vm));
-- else
-- uloop_end();
- }
-
- static bool
-@@ -1917,10 +1937,8 @@ uc_ubus_listener_cb(struct ubus_context
- uc_vm_stack_push(vm, ucv_string_new(type));
- uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true));
-
-- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 2))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
-
- static uc_value_t *
-@@ -2040,10 +2058,8 @@ uc_ubus_subscriber_remove_cb(struct ubus
- uc_vm_stack_push(vm, ucv_get(func));
- uc_vm_stack_push(vm, ucv_uint64_new(id));
-
-- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(vm, true, 1))
- ucv_put(uc_vm_stack_pop(vm));
-- else
-- uloop_end();
- }
-
- static uc_value_t *
-@@ -2334,10 +2350,8 @@ uc_ubus_channel_disconnect_cb(struct ubu
- uc_vm_stack_push(c->vm, ucv_get(c->res));
- uc_vm_stack_push(c->vm, ucv_get(func));
-
-- if (uc_vm_call(c->vm, true, 0) == EXCEPTION_NONE)
-+ if (uc_ubus_vm_call(c->vm, true, 0))
- ucv_put(uc_vm_stack_pop(c->vm));
-- else
-- uloop_end();
- }
-
- blob_buf_free(&c->buf);
-@@ -2438,10 +2452,25 @@ uc_ubus_channel_connect(uc_vm_t *vm, siz
- }
-
-
-+static uc_value_t *
-+uc_ubus_exception_handler_set(uc_vm_t *vm, size_t nargs)
-+{
-+ uc_value_t *arg = uc_fn_arg(0);
-+
-+ if (arg && !ucv_is_callable(arg))
-+ return NULL;
-+
-+ uc_vm_registry_set(vm, "ubus.ex_handler", ucv_get(arg));
-+
-+ return ucv_boolean_new(true);
-+}
-+
-+
- static const uc_function_list_t global_fns[] = {
- { "error", uc_ubus_error },
- { "connect", uc_ubus_connect },
- { "open_channel", uc_ubus_channel_connect },
-+ { "exception_handler_set", uc_ubus_exception_handler_set },
- };
-
- static const uc_function_list_t conn_fns[] = {
--- /dev/null
+Date: Wed, 11 Jun 2025 18:40:10 +0200
+Subject: [PATCH] ubus: add guard() function
+
+This allows calling the provided handler on exceptions, avoiding the
+existing behavior of calling uloop_end().
+
+---
+
+--- a/lib/ubus.c
++++ b/lib/ubus.c
+@@ -575,6 +575,40 @@ uc_ubus_call_cb(struct ubus_request *req
+ }
+
+ static void
++uc_ubus_vm_handle_exception(uc_vm_t *vm)
++{
++ uc_value_t *exh, *val;
++
++ exh = uc_vm_registry_get(vm, "ubus.ex_handler");
++ if (!ucv_is_callable(exh))
++ goto error;
++
++ val = uc_vm_exception_object(vm);
++ uc_vm_stack_push(vm, ucv_get(exh));
++ uc_vm_stack_push(vm, val);
++
++ if (uc_vm_call(vm, false, 1) != EXCEPTION_NONE)
++ goto error;
++
++ ucv_put(uc_vm_stack_pop(vm));
++ return;
++
++error:
++ uloop_end();
++}
++
++static bool
++uc_ubus_vm_call(uc_vm_t *vm, bool mcall, size_t nargs)
++{
++ if (uc_vm_call(vm, mcall, nargs) == EXCEPTION_NONE)
++ return true;
++
++ uc_ubus_vm_handle_exception(vm);
++
++ return false;
++}
++
++static void
+ uc_ubus_call_user_cb(uc_ubus_deferred_t *defer, int ret, uc_value_t *reply)
+ {
+ uc_value_t *this = ucv_get(defer->res);
+@@ -625,10 +659,8 @@ uc_ubus_call_data_user_cb(struct ubus_re
+ uc_vm_stack_push(vm, ucv_get(func));
+ uc_vm_stack_push(vm, ucv_get(reply));
+
+- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 1))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+ }
+
+@@ -647,10 +679,8 @@ uc_ubus_call_fd_cb(struct ubus_request *
+ uc_vm_stack_push(vm, ucv_get(func));
+ uc_vm_stack_push(vm, ucv_int64_new(fd));
+
+- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 1))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+ }
+
+@@ -929,10 +959,8 @@ uc_ubus_defer_common(uc_vm_t *vm, uc_ubu
+ uc_vm_stack_push(vm, ucv_get(replycb));
+ uc_vm_stack_push(vm, ucv_int64_new(rv));
+
+- if (uc_vm_call(vm, false, 1) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, false, 1))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+
+ ucv_put(res->res);
+ }
+@@ -1201,10 +1229,8 @@ uc_ubus_object_notify_data_cb(struct ubu
+ uc_vm_stack_push(vm, ucv_int64_new(type));
+ uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true));
+
+- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 2))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+ }
+
+@@ -1224,10 +1250,8 @@ uc_ubus_object_notify_status_cb(struct u
+ uc_vm_stack_push(vm, ucv_int64_new(idx));
+ uc_vm_stack_push(vm, ucv_int64_new(ret));
+
+- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 2))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+ }
+
+@@ -1247,10 +1271,8 @@ uc_ubus_object_notify_complete_cb(struct
+ uc_vm_stack_push(vm, ucv_int64_new(idx));
+ uc_vm_stack_push(vm, ucv_int64_new(ret));
+
+- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 2))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+
+ notifyctx->complete = true;
+@@ -1581,7 +1603,7 @@ uc_ubus_handle_reply_common(struct ubus_
+ /* treat other exceptions as fatal and halt uloop */
+ default:
+ uc_ubus_request_finish_common(callctx, UBUS_STATUS_UNKNOWN_ERROR);
+- uloop_end();
++ uc_ubus_vm_handle_exception(vm);
+ break;
+ }
+
+@@ -1640,10 +1662,8 @@ uc_ubus_object_subscribe_cb(struct ubus_
+ uc_vm_stack_push(uuobj->vm, ucv_get(uuobj->res));
+ uc_vm_stack_push(uuobj->vm, ucv_get(func));
+
+- if (uc_vm_call(uuobj->vm, true, 0) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(uuobj->vm, true, 0))
+ ucv_put(uc_vm_stack_pop(uuobj->vm));
+- else
+- uloop_end();
+ }
+
+ static bool
+@@ -1919,10 +1939,8 @@ uc_ubus_listener_cb(struct ubus_context
+ uc_vm_stack_push(vm, ucv_string_new(type));
+ uc_vm_stack_push(vm, blob_array_to_ucv(vm, blob_data(msg), blob_len(msg), true));
+
+- if (uc_vm_call(vm, true, 2) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 2))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+
+ static uc_value_t *
+@@ -2042,10 +2060,8 @@ uc_ubus_subscriber_remove_cb(struct ubus
+ uc_vm_stack_push(vm, ucv_get(func));
+ uc_vm_stack_push(vm, ucv_uint64_new(id));
+
+- if (uc_vm_call(vm, true, 1) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(vm, true, 1))
+ ucv_put(uc_vm_stack_pop(vm));
+- else
+- uloop_end();
+ }
+
+ static uc_value_t *
+@@ -2370,10 +2386,8 @@ uc_ubus_channel_disconnect_cb(struct ubu
+ uc_vm_stack_push(c->vm, ucv_get(c->res));
+ uc_vm_stack_push(c->vm, ucv_get(func));
+
+- if (uc_vm_call(c->vm, true, 0) == EXCEPTION_NONE)
++ if (uc_ubus_vm_call(c->vm, true, 0))
+ ucv_put(uc_vm_stack_pop(c->vm));
+- else
+- uloop_end();
+ }
+
+ blob_buf_free(&c->buf);
+@@ -2474,10 +2488,28 @@ uc_ubus_channel_connect(uc_vm_t *vm, siz
+ }
+
+
++static uc_value_t *
++uc_ubus_guard(uc_vm_t *vm, size_t nargs)
++{
++ uc_value_t *arg = uc_fn_arg(0);
++
++ if (!nargs)
++ return ucv_get(uc_vm_registry_get(vm, "ubus.ex_handler"));
++
++ if (arg && !ucv_is_callable(arg))
++ return NULL;
++
++ uc_vm_registry_set(vm, "ubus.ex_handler", ucv_get(arg));
++
++ return ucv_boolean_new(true);
++}
++
++
+ static const uc_function_list_t global_fns[] = {
+ { "error", uc_ubus_error },
+ { "connect", uc_ubus_connect },
+ { "open_channel", uc_ubus_channel_connect },
++ { "guard", uc_ubus_guard },
+ };
+
+ static const uc_function_list_t conn_fns[] = {