Improve handling of signals and 'throw' in modules
* src/emacs-module.c: Add commentary explaining how to write functions in this file. (module_make_global_ref, module_free_global_ref) (module_non_local_exit_signal, module_non_local_exit_throw) (module_make_function, module_funcall, module_intern) (module_type_of, module_is_not_nil, module_eq) (module_extract_integer, module_make_integer) (module_extract_float, module_make_float) (module_copy_string_contents, module_make_string) (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr) (module_get_user_finalizer, module_set_user_finalizer) (module_vec_set, module_vec_get, module_vec_size) (module_non_local_exit_signal_1, module_non_local_exit_throw_1): Do nothing and return with failure indication immediately, if some previous module call signaled an error or wants to throw. See http://lists.gnu.org/archive/html/emacs-devel/2015-11/msg02133.html for the relevant discussions.
This commit is contained in:
parent
165d6e3fdd
commit
1ed316d275
1 changed files with 96 additions and 37 deletions
|
@ -198,7 +198,8 @@ static void module_wrong_type (emacs_env *, Lisp_Object, Lisp_Object);
|
|||
its local variable C must stay live in later code. */
|
||||
|
||||
#define MODULE_SETJMP_1(handlertype, handlerfunc, retval, c, dummy) \
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return); \
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return) \
|
||||
return retval; \
|
||||
struct handler *c = push_handler_nosignal (Qt, handlertype); \
|
||||
if (!c) \
|
||||
{ \
|
||||
|
@ -236,7 +237,38 @@ struct module_fun_env
|
|||
static Lisp_Object module_call_func;
|
||||
|
||||
|
||||
/* Implementation of runtime and environment functions. */
|
||||
/* Implementation of runtime and environment functions.
|
||||
|
||||
These should abide by the following rules:
|
||||
|
||||
1. The first argument should always be a pointer to emacs_env.
|
||||
|
||||
2. Each function should first call check_main_thread. Note that
|
||||
this function is a no-op unless Emacs was built with
|
||||
--enable-checking.
|
||||
|
||||
3. The very next thing each function should do is check that the
|
||||
emacs_env object does not have a non-local exit indication set,
|
||||
by calling module_non_local_exit_check. If that returns
|
||||
anything but emacs_funcall_exit_return, the function should do
|
||||
nothing and return immediately with an error indication, without
|
||||
clobbering the existing error indication in emacs_env. This is
|
||||
needed for correct reporting of Lisp errors to the Emacs Lisp
|
||||
interpreter.
|
||||
|
||||
4. Any function that needs to call Emacs facilities, such as
|
||||
encoding or decoding functions, or 'intern', or 'make_string',
|
||||
should protect itself from signals and 'throw' in the called
|
||||
Emacs functions, by placing the macros MODULE_HANDLE_SIGNALS
|
||||
and/or MODULE_HANDLE_THROW right after the above 2 tests.
|
||||
|
||||
5. Do NOT use 'eassert' for checking validity of user code in the
|
||||
module. Instead, make those checks part of the code, and if the
|
||||
check fails, call 'module_non_local_exit_signal_1' or
|
||||
'module_non_local_exit_throw_1' to report the error. This is
|
||||
because using 'eassert' in these situations will abort Emacs
|
||||
instead of reporting the error back to Lisp, and also because
|
||||
'eassert' is compiled to nothing in the release version. */
|
||||
|
||||
/* Catch signals and throws only if the code can actually signal or
|
||||
throw. If checking is enabled, abort if the current thread is not
|
||||
|
@ -256,7 +288,8 @@ static emacs_value
|
|||
module_make_global_ref (emacs_env *env, emacs_value ref)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
struct Lisp_Hash_Table *h = XHASH_TABLE (Vmodule_refs_hash);
|
||||
Lisp_Object new_obj = value_to_lisp (ref);
|
||||
|
@ -287,7 +320,8 @@ static void
|
|||
module_free_global_ref (emacs_env *env, emacs_value ref)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return;
|
||||
/* TODO: This probably never signals. */
|
||||
/* FIXME: Wait a minute. Shouldn't this function report an error if
|
||||
the hash lookup fails? */
|
||||
|
@ -343,18 +377,18 @@ static void
|
|||
module_non_local_exit_signal (emacs_env *env, emacs_value sym, emacs_value data)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
module_non_local_exit_signal_1 (env, value_to_lisp (sym),
|
||||
value_to_lisp (data));
|
||||
if (module_non_local_exit_check (env) == emacs_funcall_exit_return)
|
||||
module_non_local_exit_signal_1 (env, value_to_lisp (sym),
|
||||
value_to_lisp (data));
|
||||
}
|
||||
|
||||
static void
|
||||
module_non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
module_non_local_exit_throw_1 (env, value_to_lisp (tag),
|
||||
value_to_lisp (value));
|
||||
if (module_non_local_exit_check (env) == emacs_funcall_exit_return)
|
||||
module_non_local_exit_throw_1 (env, value_to_lisp (tag),
|
||||
value_to_lisp (value));
|
||||
}
|
||||
|
||||
/* A module function is lambda function that calls `module-call',
|
||||
|
@ -370,7 +404,8 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
|
|||
void *data)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
|
||||
if (! (0 <= min_arity
|
||||
|
@ -407,7 +442,8 @@ module_funcall (emacs_env *env, emacs_value fun, ptrdiff_t nargs,
|
|||
emacs_value args[])
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
MODULE_HANDLE_THROW;
|
||||
|
||||
|
@ -428,7 +464,8 @@ static emacs_value
|
|||
module_intern (emacs_env *env, const char *name)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
return lisp_to_value (env, intern (name));
|
||||
}
|
||||
|
@ -437,7 +474,8 @@ static emacs_value
|
|||
module_type_of (emacs_env *env, emacs_value value)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
return lisp_to_value (env, Ftype_of (value_to_lisp (value)));
|
||||
}
|
||||
|
||||
|
@ -445,7 +483,8 @@ static bool
|
|||
module_is_not_nil (emacs_env *env, emacs_value value)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return false;
|
||||
return ! NILP (value_to_lisp (value));
|
||||
}
|
||||
|
||||
|
@ -453,7 +492,8 @@ static bool
|
|||
module_eq (emacs_env *env, emacs_value a, emacs_value b)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return false;
|
||||
return EQ (value_to_lisp (a), value_to_lisp (b));
|
||||
}
|
||||
|
||||
|
@ -461,7 +501,8 @@ static intmax_t
|
|||
module_extract_integer (emacs_env *env, emacs_value n)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return 0;
|
||||
Lisp_Object l = value_to_lisp (n);
|
||||
if (! INTEGERP (l))
|
||||
{
|
||||
|
@ -475,7 +516,8 @@ static emacs_value
|
|||
module_make_integer (emacs_env *env, intmax_t n)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
if (! (MOST_NEGATIVE_FIXNUM <= n && n <= MOST_POSITIVE_FIXNUM))
|
||||
{
|
||||
module_non_local_exit_signal_1 (env, Qoverflow_error, Qnil);
|
||||
|
@ -488,7 +530,8 @@ static double
|
|||
module_extract_float (emacs_env *env, emacs_value f)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return 0;
|
||||
Lisp_Object lisp = value_to_lisp (f);
|
||||
if (! FLOATP (lisp))
|
||||
{
|
||||
|
@ -502,7 +545,8 @@ static emacs_value
|
|||
module_make_float (emacs_env *env, double d)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
return lisp_to_value (env, make_float (d));
|
||||
}
|
||||
|
@ -512,7 +556,8 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer,
|
|||
ptrdiff_t *length)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return false;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
Lisp_Object lisp_str = value_to_lisp (value);
|
||||
if (! STRINGP (lisp_str))
|
||||
|
@ -557,7 +602,8 @@ static emacs_value
|
|||
module_make_string (emacs_env *env, const char *str, ptrdiff_t length)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
MODULE_HANDLE_SIGNALS;
|
||||
if (length > STRING_BYTES_BOUND)
|
||||
{
|
||||
|
@ -573,6 +619,8 @@ static emacs_value
|
|||
module_make_user_ptr (emacs_env *env, emacs_finalizer_function fin, void *ptr)
|
||||
{
|
||||
check_main_thread ();
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
return lisp_to_value (env, make_user_ptr (fin, ptr));
|
||||
}
|
||||
|
||||
|
@ -580,7 +628,8 @@ static void *
|
|||
module_get_user_ptr (emacs_env *env, emacs_value uptr)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
Lisp_Object lisp = value_to_lisp (uptr);
|
||||
if (! USER_PTRP (lisp))
|
||||
{
|
||||
|
@ -594,7 +643,8 @@ static void
|
|||
module_set_user_ptr (emacs_env *env, emacs_value uptr, void *ptr)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return;
|
||||
Lisp_Object lisp = value_to_lisp (uptr);
|
||||
if (! USER_PTRP (lisp))
|
||||
module_wrong_type (env, Quser_ptr, lisp);
|
||||
|
@ -605,7 +655,8 @@ static emacs_finalizer_function
|
|||
module_get_user_finalizer (emacs_env *env, emacs_value uptr)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
Lisp_Object lisp = value_to_lisp (uptr);
|
||||
if (! USER_PTRP (lisp))
|
||||
{
|
||||
|
@ -620,7 +671,8 @@ module_set_user_finalizer (emacs_env *env, emacs_value uptr,
|
|||
emacs_finalizer_function fin)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return;
|
||||
Lisp_Object lisp = value_to_lisp (uptr);
|
||||
if (! USER_PTRP (lisp))
|
||||
module_wrong_type (env, Quser_ptr, lisp);
|
||||
|
@ -631,7 +683,8 @@ static void
|
|||
module_vec_set (emacs_env *env, emacs_value vec, ptrdiff_t i, emacs_value val)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return;
|
||||
Lisp_Object lvec = value_to_lisp (vec);
|
||||
if (! VECTORP (lvec))
|
||||
{
|
||||
|
@ -653,7 +706,8 @@ static emacs_value
|
|||
module_vec_get (emacs_env *env, emacs_value vec, ptrdiff_t i)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return NULL;
|
||||
Lisp_Object lvec = value_to_lisp (vec);
|
||||
if (! VECTORP (lvec))
|
||||
{
|
||||
|
@ -675,7 +729,8 @@ static ptrdiff_t
|
|||
module_vec_size (emacs_env *env, emacs_value vec)
|
||||
{
|
||||
check_main_thread ();
|
||||
eassert (module_non_local_exit_check (env) == emacs_funcall_exit_return);
|
||||
if (module_non_local_exit_check (env) != emacs_funcall_exit_return)
|
||||
return 0;
|
||||
Lisp_Object lvec = value_to_lisp (vec);
|
||||
if (! VECTORP (lvec))
|
||||
{
|
||||
|
@ -806,10 +861,12 @@ module_non_local_exit_signal_1 (emacs_env *env, Lisp_Object sym,
|
|||
Lisp_Object data)
|
||||
{
|
||||
struct emacs_env_private *p = env->private_members;
|
||||
eassert (p->pending_non_local_exit == emacs_funcall_exit_return);
|
||||
p->pending_non_local_exit = emacs_funcall_exit_signal;
|
||||
p->non_local_exit_symbol.v = sym;
|
||||
p->non_local_exit_data.v = data;
|
||||
if (p->pending_non_local_exit == emacs_funcall_exit_return)
|
||||
{
|
||||
p->pending_non_local_exit = emacs_funcall_exit_signal;
|
||||
p->non_local_exit_symbol.v = sym;
|
||||
p->non_local_exit_data.v = data;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -817,10 +874,12 @@ module_non_local_exit_throw_1 (emacs_env *env, Lisp_Object tag,
|
|||
Lisp_Object value)
|
||||
{
|
||||
struct emacs_env_private *p = env->private_members;
|
||||
eassert (p->pending_non_local_exit == emacs_funcall_exit_return);
|
||||
p->pending_non_local_exit = emacs_funcall_exit_throw;
|
||||
p->non_local_exit_symbol.v = tag;
|
||||
p->non_local_exit_data.v = value;
|
||||
if (p->pending_non_local_exit == emacs_funcall_exit_return)
|
||||
{
|
||||
p->pending_non_local_exit = emacs_funcall_exit_throw;
|
||||
p->non_local_exit_symbol.v = tag;
|
||||
p->non_local_exit_data.v = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module version of `wrong_type_argument'. */
|
||||
|
|
Loading…
Add table
Reference in a new issue