commit 13e9493ea36df04e2c6b69e9b316d40c072ee88b (HEAD, refs/remotes/origin/master) Author: Philipp Stephani Date: Mon Jun 5 10:19:59 2017 +0200 Inline module_has_cleanup This constant is only used once, and we fail compilation anyway if it's false. * src/emacs-module.c (MODULE_SETJMP_1): Inline __has_attribute. diff --git a/src/emacs-module.c b/src/emacs-module.c index 8ddf157b39..71e04d869e 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -37,12 +37,6 @@ along with GNU Emacs. If not, see . */ /* Feature tests. */ -#if __has_attribute (cleanup) -enum { module_has_cleanup = true }; -#else -enum { module_has_cleanup = false }; -#endif - #ifdef WINDOWSNT #include #include "w32term.h" @@ -168,7 +162,7 @@ static emacs_value const module_nil = 0; module_out_of_memory (env); \ return retval; \ } \ - verify (module_has_cleanup); \ + verify (__has_attribute (cleanup)); \ struct handler *c __attribute__ ((cleanup (module_reset_handlerlist))) \ = c0; \ if (sys_setjmp (c->jmp)) \ commit 98e107ff0ca39027420a2ea100037402d23a0ea0 Author: Philipp Stephani Date: Mon Jun 5 10:04:20 2017 +0200 Add missing dependency to test module source file diff --git a/test/Makefile.in b/test/Makefile.in index 4f12a8ea48..7b8c967128 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -182,7 +182,7 @@ test_module_dir := $(srcdir)/data/emacs-module test_module_name := mod-test@MODULES_SUFFIX@ test_module := $(test_module_dir)/$(test_module_name) $(srcdir)/src/emacs-module-tests.log: $(test_module) -$(test_module): $(srcdir)/../src/emacs-module.[ch] +$(test_module): $(srcdir)/../src/emacs-module.[ch] $(test_module_dir)/mod-test.c $(MAKE) -C $(test_module_dir) $(test_module_name) SO=@MODULES_SUFFIX@ endif commit 646b74e50ef998c687794876ddc0f16766a5f57c Author: Paul Eggert Date: Mon Jun 5 00:17:05 2017 -0700 Omit space that broke ‘make check’ * src/print.c (print_vectorlike): Omit stray space. diff --git a/src/print.c b/src/print.c index e89f3d8072..76ae10fe13 100644 --- a/src/print.c +++ b/src/print.c @@ -1709,7 +1709,7 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag, if (symbol == NULL) { - print_c_string (" at ", printcharfun); + print_c_string ("at ", printcharfun); enum { pointer_bufsize = sizeof ptr * 16 / CHAR_BIT + 2 + 1 }; char buffer[pointer_bufsize]; int needed = snprintf (buffer, sizeof buffer, "%p", ptr); commit 6e4abc9d100732b0825f72b402dda8912d3d1755 Author: Paul Eggert Date: Sun Jun 4 23:52:10 2017 -0700 Remove easserts etc. from emacs-module.c Most of these seem to run afoul of the comment "Do NOT use 'eassert' for checking validity of user code in the module." * src/emacs-module.c (MODULE_FUNCTION_BEGIN_NO_CATCH) (module_non_local_exit_check, module_non_local_exit_clear) (module_non_local_exit_get, module_non_local_exit_signal) (module_non_local_exit_throw, module_make_string): Remove unnecessary easserts that pointers are nonnull. Hardware checks this for us nowadays, and the checks just clutter up the code. (module_extract_integer): Remove unnecessary verify that a C signed integer is in the range INTMAX_MIN..INTMAX_MAX. The C standard guarantees this. (module_copy_string_contents): Remove unnecessary eassert that Lisp strings are null-terminated. (module_function_arity): Remove unnecessary easserts that function arities are in range. diff --git a/src/emacs-module.c b/src/emacs-module.c index 56105123ff..8ddf157b39 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -219,7 +219,6 @@ static emacs_value const module_nil = 0; #define MODULE_FUNCTION_BEGIN_NO_CATCH(error_retval) \ do { \ - eassert (env != NULL); \ check_main_thread (); \ if (module_non_local_exit_check (env) != emacs_funcall_exit_return) \ return error_retval; \ @@ -308,7 +307,6 @@ module_free_global_ref (emacs_env *env, emacs_value ref) static enum emacs_funcall_exit module_non_local_exit_check (emacs_env *env) { - eassert (env != NULL); check_main_thread (); return env->private_members->pending_non_local_exit; } @@ -316,7 +314,6 @@ module_non_local_exit_check (emacs_env *env) static void module_non_local_exit_clear (emacs_env *env) { - eassert (env != NULL); check_main_thread (); env->private_members->pending_non_local_exit = emacs_funcall_exit_return; } @@ -324,9 +321,6 @@ module_non_local_exit_clear (emacs_env *env) static enum emacs_funcall_exit module_non_local_exit_get (emacs_env *env, emacs_value *sym, emacs_value *data) { - eassert (env != NULL); - eassert (sym != NULL); - eassert (data != NULL); check_main_thread (); struct emacs_env_private *p = env->private_members; if (p->pending_non_local_exit != emacs_funcall_exit_return) @@ -342,7 +336,6 @@ module_non_local_exit_get (emacs_env *env, emacs_value *sym, emacs_value *data) static void module_non_local_exit_signal (emacs_env *env, emacs_value sym, emacs_value data) { - eassert (env != NULL); check_main_thread (); if (module_non_local_exit_check (env) == emacs_funcall_exit_return) module_non_local_exit_signal_1 (env, value_to_lisp (sym), @@ -352,7 +345,6 @@ module_non_local_exit_signal (emacs_env *env, emacs_value sym, emacs_value data) static void module_non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value) { - eassert (env != NULL); check_main_thread (); if (module_non_local_exit_check (env) == emacs_funcall_exit_return) module_non_local_exit_throw_1 (env, value_to_lisp (tag), @@ -449,8 +441,6 @@ module_eq (emacs_env *env, emacs_value a, emacs_value b) static intmax_t module_extract_integer (emacs_env *env, emacs_value n) { - verify (MOST_NEGATIVE_FIXNUM >= INTMAX_MIN); - verify (MOST_POSITIVE_FIXNUM <= INTMAX_MAX); MODULE_FUNCTION_BEGIN (0); Lisp_Object l = value_to_lisp (n); CHECK_NUMBER (l); @@ -509,7 +499,6 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer, } *length = required_buf_size; - eassert (SREF (lisp_str_utf8, raw_size) == '\0'); memcpy (buffer, SDATA (lisp_str_utf8), raw_size + 1); return true; @@ -519,7 +508,6 @@ static emacs_value module_make_string (emacs_env *env, const char *str, ptrdiff_t length) { MODULE_FUNCTION_BEGIN (module_nil); - eassert (str != NULL); if (! (0 <= length && length <= STRING_BYTES_BOUND)) xsignal0 (Qoverflow_error); AUTO_STRING_WITH_LEN (lstr, str, length); @@ -726,11 +714,7 @@ Lisp_Object module_function_arity (const struct Lisp_Module_Function *const function) { ptrdiff_t minargs = function->min_arity; - eassert (minargs >= 0); - eassert (minargs <= MOST_POSITIVE_FIXNUM); ptrdiff_t maxargs = function->max_arity; - eassert (maxargs >= minargs || maxargs == MANY); - eassert (maxargs <= MOST_POSITIVE_FIXNUM); return Fcons (make_number (minargs), maxargs == MANY ? Qmany : make_number (maxargs)); } commit 620d65370afd319b706cea0eccffd0ee0ffd2e26 Author: Paul Eggert Date: Sun Jun 4 23:52:10 2017 -0700 Remove unnecessary checking in emacs-module.c * src/emacs-module.c (module_copy_string_contents): Remove checking, as string lengths are always nonnegative and less than STRING_BYTES_BOUND, and this is checked elsewhere. (module_make_string): Check length against STRING_BYTES_BOUND, a tighter bound than MOST_POSITIVE_FIXNUM. (funcall_module): Don't assume that an out-of-range integer is nonnegative. diff --git a/src/emacs-module.c b/src/emacs-module.c index f173bf9393..56105123ff 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -492,10 +492,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer, Lisp_Object lisp_str_utf8 = ENCODE_UTF_8 (lisp_str); ptrdiff_t raw_size = SBYTES (lisp_str_utf8); - ptrdiff_t required_buf_size; - if (INT_ADD_WRAPV (raw_size, 1, &required_buf_size)) - xsignal0 (Qoverflow_error); - eassert (required_buf_size > 0); + ptrdiff_t required_buf_size = raw_size + 1; eassert (length != NULL); @@ -523,7 +520,7 @@ module_make_string (emacs_env *env, const char *str, ptrdiff_t length) { MODULE_FUNCTION_BEGIN (module_nil); eassert (str != NULL); - if (length < 0 || length > MOST_POSITIVE_FIXNUM) + if (! (0 <= length && length <= STRING_BYTES_BOUND)) xsignal0 (Qoverflow_error); AUTO_STRING_WITH_LEN (lstr, str, length); return lisp_to_value (code_convert_string_norecord (lstr, Qutf_8, false)); @@ -675,7 +672,7 @@ funcall_module (Lisp_Object function, ptrdiff_t nargs, Lisp_Object *arglist) eassume (0 <= func->min_arity); if (! (func->min_arity <= nargs && (func->max_arity < 0 || nargs <= func->max_arity))) - xsignal2 (Qwrong_number_of_arguments, function, make_natnum (nargs)); + xsignal2 (Qwrong_number_of_arguments, function, make_number (nargs)); emacs_env pub; struct emacs_env_private priv; commit cef90102cb0366f26a9cf618497793d223d60a66 Author: Paul Eggert Date: Sun Jun 4 23:52:10 2017 -0700 SCHARS and STRING_BYTES are nonnegative Tell the compiler that SCHARS and STRING_BYTES are nonnegative, in the hopes that this will optimize a bit better. Also, check this at runtime if ENABLE_CHECKING. * src/lisp.h (SCHARS, STRING_BYTES): eassume that these functions return nonnegative values. (STRING_SET_CHARS) [ENABLE_CHECKING]: eassert that newsize is nonnegative. diff --git a/src/lisp.h b/src/lisp.h index ce939fcee6..c35bd1f6df 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -1346,7 +1346,9 @@ SSET (Lisp_Object string, ptrdiff_t index, unsigned char new) INLINE ptrdiff_t SCHARS (Lisp_Object string) { - return XSTRING (string)->size; + ptrdiff_t nchars = XSTRING (string)->size; + eassume (0 <= nchars); + return nchars; } #ifdef GC_CHECK_STRING_BYTES @@ -1356,10 +1358,12 @@ INLINE ptrdiff_t STRING_BYTES (struct Lisp_String *s) { #ifdef GC_CHECK_STRING_BYTES - return string_bytes (s); + ptrdiff_t nbytes = string_bytes (s); #else - return s->size_byte < 0 ? s->size : s->size_byte; + ptrdiff_t nbytes = s->size_byte < 0 ? s->size : s->size_byte; #endif + eassume (0 <= nbytes); + return nbytes; } INLINE ptrdiff_t @@ -1373,7 +1377,7 @@ STRING_SET_CHARS (Lisp_Object string, ptrdiff_t newsize) /* This function cannot change the size of data allocated for the string when it was created. */ eassert (STRING_MULTIBYTE (string) - ? newsize <= SBYTES (string) + ? 0 <= newsize && newsize <= SBYTES (string) : newsize == SCHARS (string)); XSTRING (string)->size = newsize; } commit 24f011d56aec273847181f9befbad491deb2f67e Author: Noam Postavsky Date: Sun Jun 4 23:54:51 2017 -0400 * lisp/desktop.el (desktop-clear): Skip the daemon's frame (Bug#26912). diff --git a/lisp/desktop.el b/lisp/desktop.el index 39dc92fabe..540d0e3b11 100644 --- a/lisp/desktop.el +++ b/lisp/desktop.el @@ -733,6 +733,10 @@ if different)." (condition-case err (unless (or (eq frame this) (eq frame mini) + ;; Don't delete daemon's initial frame, or + ;; we'll never be able to close the last + ;; client's frame (Bug#26912). + (if (daemonp) (not (frame-parameter frame 'client))) (frame-parameter frame 'desktop-dont-clear)) (delete-frame frame)) (error commit 2aa8b1564ea3dd8eb23c71315a19877cab508db3 Author: Philipp Stephani Date: Sun Jun 4 19:35:52 2017 +0200 Remove an unused error symbol * src/emacs-module.c (syms_of_module): Remove unused error symbol 'invalid-module-call'. diff --git a/src/emacs-module.c b/src/emacs-module.c index e6a109b196..f173bf9393 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -1009,11 +1009,6 @@ syms_of_module (void) Fput (Qmodule_load_failed, Qerror_message, build_pure_c_string ("Module load failed")); - DEFSYM (Qinvalid_module_call, "invalid-module-call"); - Fput (Qinvalid_module_call, Qerror_conditions, - listn (CONSTYPE_PURE, 2, Qinvalid_module_call, Qerror)); - Fput (Qinvalid_module_call, Qerror_message, - build_pure_c_string ("Invalid module call")); DEFSYM (Qmodule_open_failed, "module-open-failed"); Fput (Qmodule_open_failed, Qerror_conditions, listn (CONSTYPE_PURE, 3, commit 66da3f4afa53e5c5cfab17ca03a13a0d65083ffb Author: Philipp Stephani Date: Sun Jun 4 19:34:22 2017 +0200 Support quitting in modules The idea is that modules should call env->should_quit from time to time and return as quickly as possible if it returns true. * src/emacs-module.c (module_should_quit): New module function. (initialize_environment): Use it. (funcall_module): Process potential pending quit. * src/eval.c (maybe_quit): Add reference to module_should_quit. diff --git a/src/emacs-module.c b/src/emacs-module.c index f2efc83d25..e6a109b196 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -28,6 +28,7 @@ along with GNU Emacs. If not, see . */ #include "lisp.h" #include "dynlib.h" #include "coding.h" +#include "keyboard.h" #include "syssignal.h" #include @@ -612,6 +613,15 @@ module_vec_size (emacs_env *env, emacs_value vec) return ASIZE (lvec); } +/* This function should return true if and only if maybe_quit would do + anything. */ +static bool +module_should_quit (emacs_env *env) +{ + MODULE_FUNCTION_BEGIN_NO_CATCH (false); + return (! NILP (Vquit_flag) && NILP (Vinhibit_quit)) || pending_signals; +} + /* Subroutines. */ @@ -687,6 +697,10 @@ funcall_module (Lisp_Object function, ptrdiff_t nargs, Lisp_Object *arglist) eassert (&priv == pub.private_members); + /* Process the quit flag first, so that quitting doesn't get + overridden by other non-local exits. */ + maybe_quit (); + switch (priv.pending_non_local_exit) { case emacs_funcall_exit_return: @@ -916,6 +930,7 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) env->vec_set = module_vec_set; env->vec_get = module_vec_get; env->vec_size = module_vec_size; + env->should_quit = module_should_quit; Vmodule_environments = Fcons (make_save_ptr (env), Vmodule_environments); } diff --git a/src/emacs-module.h b/src/emacs-module.h index d9eeeabec3..b8bf2ed2d5 100644 --- a/src/emacs-module.h +++ b/src/emacs-module.h @@ -185,6 +185,9 @@ struct emacs_env_25 emacs_value val); ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vec); + + /* Returns whether a quit is pending. */ + bool (*should_quit) (emacs_env *env); }; /* Every module should define a function as follows. */ diff --git a/src/eval.c b/src/eval.c index 8aa33a1128..ef961046bc 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1474,7 +1474,10 @@ process_quit_flag (void) If quit-flag is set to `kill-emacs' the SIGINT handler has received a request to exit Emacs when it is safe to do. - When not quitting, process any pending signals. */ + When not quitting, process any pending signals. + + If you change this function, also adapt module_should_quit in + emacs-module.c. */ void maybe_quit (void) commit d37201722e2151df1f6b6fa1e2f33b5f91e27e03 Author: Philipp Stephani Date: Sun Jun 4 19:28:50 2017 +0200 Use more specific errors for module load failure * src/emacs-module.c (syms_of_module): Add more specific error symbols. (Fmodule_load): Use them. diff --git a/src/emacs-module.c b/src/emacs-module.c index 187a620cc0..f2efc83d25 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -626,15 +626,15 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0, CHECK_STRING (file); handle = dynlib_open (SSDATA (file)); if (!handle) - error ("Cannot load file %s: %s", SDATA (file), dynlib_error ()); + xsignal2 (Qmodule_open_failed, file, build_string (dynlib_error ())); gpl_sym = dynlib_sym (handle, "plugin_is_GPL_compatible"); if (!gpl_sym) - error ("Module %s is not GPL compatible", SDATA (file)); + xsignal1 (Qmodule_not_gpl_compatible, file); module_init = (emacs_init_function) dynlib_func (handle, "emacs_module_init"); if (!module_init) - error ("Module %s does not have an init function.", SDATA (file)); + xsignal1 (Qmissing_module_init_function, file); struct emacs_runtime_private rt; /* Includes the public emacs_env. */ struct emacs_env_private priv; @@ -652,7 +652,7 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0, { if (FIXNUM_OVERFLOW_P (r)) xsignal0 (Qoverflow_error); - xsignal2 (Qmodule_load_failed, file, make_number (r)); + xsignal2 (Qmodule_init_failed, file, make_number (r)); } return Qt; @@ -999,6 +999,34 @@ syms_of_module (void) listn (CONSTYPE_PURE, 2, Qinvalid_module_call, Qerror)); Fput (Qinvalid_module_call, Qerror_message, build_pure_c_string ("Invalid module call")); + DEFSYM (Qmodule_open_failed, "module-open-failed"); + Fput (Qmodule_open_failed, Qerror_conditions, + listn (CONSTYPE_PURE, 3, + Qmodule_open_failed, Qmodule_load_failed, Qerror)); + Fput (Qmodule_open_failed, Qerror_message, + build_pure_c_string ("Module could not be opened")); + + DEFSYM (Qmodule_not_gpl_compatible, "module-not-gpl-compatible"); + Fput (Qmodule_not_gpl_compatible, Qerror_conditions, + listn (CONSTYPE_PURE, 3, + Qmodule_not_gpl_compatible, Qmodule_load_failed, Qerror)); + Fput (Qmodule_not_gpl_compatible, Qerror_message, + build_pure_c_string ("Module is not GPL compatible")); + + DEFSYM (Qmissing_module_init_function, "missing-module-init-function"); + Fput (Qmissing_module_init_function, Qerror_conditions, + listn (CONSTYPE_PURE, 3, + Qmissing_module_init_function, Qmodule_load_failed, Qerror)); + Fput (Qmissing_module_init_function, Qerror_message, + build_pure_c_string ("Module does not export an " + "initialization function")); + + DEFSYM (Qmodule_init_failed, "module-init-failed"); + Fput (Qmodule_init_failed, Qerror_conditions, + listn (CONSTYPE_PURE, 3, + Qmodule_init_failed, Qmodule_load_failed, Qerror)); + Fput (Qmodule_init_failed, Qerror_message, + build_pure_c_string ("Module initialization failed")); DEFSYM (Qinvalid_arity, "invalid-arity"); Fput (Qinvalid_arity, Qerror_conditions, commit 27445a82f04ff848993821de1596923441859838 Author: Philipp Stephani Date: Sun Jun 4 19:26:24 2017 +0200 Remove an unneeded assertion * src/emacs-module.c (module_copy_string_contents): Remove unneeded assertion. If this assertion triggers, we raise an error anyway. diff --git a/src/emacs-module.c b/src/emacs-module.c index f7facb955b..187a620cc0 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -504,8 +504,6 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer, return true; } - eassert (*length >= 0); - if (*length < required_buf_size) { *length = required_buf_size; commit a8a93b11cfa673c14c9a0d93ba87a16459dcde00 Author: Philipp Stephani Date: Sun Jun 4 19:22:41 2017 +0200 Guard against signed integer overflows * src/emacs-module.c (module_extract_integer) (module_copy_string_contents, module_make_string): Guard against signed integer overflows. diff --git a/src/emacs-module.c b/src/emacs-module.c index d4047d67a3..f7facb955b 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -448,6 +448,8 @@ module_eq (emacs_env *env, emacs_value a, emacs_value b) static intmax_t module_extract_integer (emacs_env *env, emacs_value n) { + verify (MOST_NEGATIVE_FIXNUM >= INTMAX_MIN); + verify (MOST_POSITIVE_FIXNUM <= INTMAX_MAX); MODULE_FUNCTION_BEGIN (0); Lisp_Object l = value_to_lisp (n); CHECK_NUMBER (l); @@ -489,7 +491,9 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer, Lisp_Object lisp_str_utf8 = ENCODE_UTF_8 (lisp_str); ptrdiff_t raw_size = SBYTES (lisp_str_utf8); - ptrdiff_t required_buf_size = raw_size + 1; + ptrdiff_t required_buf_size; + if (INT_ADD_WRAPV (raw_size, 1, &required_buf_size)) + xsignal0 (Qoverflow_error); eassert (required_buf_size > 0); eassert (length != NULL); @@ -520,6 +524,8 @@ module_make_string (emacs_env *env, const char *str, ptrdiff_t length) { MODULE_FUNCTION_BEGIN (module_nil); eassert (str != NULL); + if (length < 0 || length > MOST_POSITIVE_FIXNUM) + xsignal0 (Qoverflow_error); AUTO_STRING_WITH_LEN (lstr, str, length); return lisp_to_value (code_convert_string_norecord (lstr, Qutf_8, false)); } commit 549706241e5ce6fe7f1131d7f132a19bdb1abdd9 Author: Philipp Stephani Date: Sun Jun 4 19:19:30 2017 +0200 Add a couple more assertions to the module code These can help module authors debug crashes. * emacs-module.c (module_non_local_exit_check) (module_non_local_exit_clear, module_non_local_exit_get) (module_non_local_exit_signal, module_non_local_exit_throw) (module_copy_string_contents, module_make_string) (funcall_module, initialize_environment): Add assertions diff --git a/src/emacs-module.c b/src/emacs-module.c index 1cd4eb2ddd..d4047d67a3 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -307,6 +307,7 @@ module_free_global_ref (emacs_env *env, emacs_value ref) static enum emacs_funcall_exit module_non_local_exit_check (emacs_env *env) { + eassert (env != NULL); check_main_thread (); return env->private_members->pending_non_local_exit; } @@ -314,6 +315,7 @@ module_non_local_exit_check (emacs_env *env) static void module_non_local_exit_clear (emacs_env *env) { + eassert (env != NULL); check_main_thread (); env->private_members->pending_non_local_exit = emacs_funcall_exit_return; } @@ -321,6 +323,9 @@ module_non_local_exit_clear (emacs_env *env) static enum emacs_funcall_exit module_non_local_exit_get (emacs_env *env, emacs_value *sym, emacs_value *data) { + eassert (env != NULL); + eassert (sym != NULL); + eassert (data != NULL); check_main_thread (); struct emacs_env_private *p = env->private_members; if (p->pending_non_local_exit != emacs_funcall_exit_return) @@ -336,6 +341,7 @@ module_non_local_exit_get (emacs_env *env, emacs_value *sym, emacs_value *data) static void module_non_local_exit_signal (emacs_env *env, emacs_value sym, emacs_value data) { + eassert (env != NULL); check_main_thread (); if (module_non_local_exit_check (env) == emacs_funcall_exit_return) module_non_local_exit_signal_1 (env, value_to_lisp (sym), @@ -345,6 +351,7 @@ module_non_local_exit_signal (emacs_env *env, emacs_value sym, emacs_value data) static void module_non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value) { + eassert (env != NULL); check_main_thread (); if (module_non_local_exit_check (env) == emacs_funcall_exit_return) module_non_local_exit_throw_1 (env, value_to_lisp (tag), @@ -483,6 +490,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer, Lisp_Object lisp_str_utf8 = ENCODE_UTF_8 (lisp_str); ptrdiff_t raw_size = SBYTES (lisp_str_utf8); ptrdiff_t required_buf_size = raw_size + 1; + eassert (required_buf_size > 0); eassert (length != NULL); @@ -501,6 +509,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buffer, } *length = required_buf_size; + eassert (SREF (lisp_str_utf8, raw_size) == '\0'); memcpy (buffer, SDATA (lisp_str_utf8), raw_size + 1); return true; @@ -510,6 +519,7 @@ static emacs_value module_make_string (emacs_env *env, const char *str, ptrdiff_t length) { MODULE_FUNCTION_BEGIN (module_nil); + eassert (str != NULL); AUTO_STRING_WITH_LEN (lstr, str, length); return lisp_to_value (code_convert_string_norecord (lstr, Qutf_8, false)); } @@ -701,7 +711,11 @@ Lisp_Object module_function_arity (const struct Lisp_Module_Function *const function) { ptrdiff_t minargs = function->min_arity; + eassert (minargs >= 0); + eassert (minargs <= MOST_POSITIVE_FIXNUM); ptrdiff_t maxargs = function->max_arity; + eassert (maxargs >= minargs || maxargs == MANY); + eassert (maxargs <= MOST_POSITIVE_FIXNUM); return Fcons (make_number (minargs), maxargs == MANY ? Qmany : make_number (maxargs)); } @@ -906,6 +920,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) static void finalize_environment (emacs_env *env, struct emacs_env_private *priv) { + eassert (env->private_members == priv); + eassert (XSAVE_POINTER (XCAR (Vmodule_environments), 0) == env); Vmodule_environments = XCDR (Vmodule_environments); } commit fb3a9fd3185e081b3442d37ef3c27543d75849ac Author: Philipp Stephani Date: Sun Jun 4 19:16:07 2017 +0200 ; Grammar fix diff --git a/src/emacs-module.c b/src/emacs-module.c index 7f5bd86c96..1cd4eb2ddd 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -351,7 +351,7 @@ module_non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value) value_to_lisp (value)); } -/* A module function is a pseudovector of subtype type +/* A module function is a pseudovector of subtype PVEC_MODULE_FUNCTION; see lisp.h for the definition. */ static emacs_value commit 034275ebe8c5f6fef6d7fe1c35670f732f8d14db Author: Philipp Stephani Date: Sun Jun 4 19:15:20 2017 +0200 ; Small comment fix * emacs-module.c (MODULE_FUNCTION_BEGIN): Don't say that the error value should be a sentinel value, because in almost all cases it isn't. diff --git a/src/emacs-module.c b/src/emacs-module.c index c276edab37..7f5bd86c96 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -226,7 +226,7 @@ static emacs_value const module_nil = 0; /* Use MODULE_FUNCTION_BEGIN to implement steps 2 through 4 for most environment functions. On error it will return its argument, which - should be a sentinel value. */ + can be a sentinel value. */ #define MODULE_FUNCTION_BEGIN(error_retval) \ MODULE_FUNCTION_BEGIN_NO_CATCH (error_retval); \ commit 9be8b2bf1d1679e7b60dd7d2dbfef2c68f046938 Author: Philipp Stephani Date: Sun Jun 4 19:12:23 2017 +0200 Use ATTRIBUTE_MAY_ALIAS where alias violations are likely In particular, alias violations are likely for the return values of dlsym(3), which get cast around arbitrarily. * src/emacs-module.c (Fmodule_load): Use ATTRIBUTE_MAY_ALIAS. diff --git a/src/dynlib.h b/src/dynlib.h index 6246c6a664..1d53b8e5b2 100644 --- a/src/dynlib.h +++ b/src/dynlib.h @@ -24,11 +24,14 @@ along with GNU Emacs. If not, see . */ typedef void *dynlib_handle_ptr; dynlib_handle_ptr dynlib_open (const char *path); -void *dynlib_sym (dynlib_handle_ptr h, const char *sym); -typedef struct dynlib_function_ptr_nonce *(*dynlib_function_ptr) (void); -dynlib_function_ptr dynlib_func (dynlib_handle_ptr h, const char *sym); -const char *dynlib_error (void); int dynlib_close (dynlib_handle_ptr h); +const char *dynlib_error (void); + +ATTRIBUTE_MAY_ALIAS void *dynlib_sym (dynlib_handle_ptr h, const char *sym); + +typedef struct dynlib_function_ptr_nonce *(ATTRIBUTE_MAY_ALIAS *dynlib_function_ptr) (void); +dynlib_function_ptr dynlib_func (dynlib_handle_ptr h, const char *sym); + /* Sets *FILE to the file name from which PTR was loaded, and *SYM to its symbol name. If the file or symbol name could not be determined, set the corresponding argument to NULL. */ diff --git a/src/emacs-module.c b/src/emacs-module.c index 0fb126e61f..c276edab37 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -658,7 +658,7 @@ funcall_module (Lisp_Object function, ptrdiff_t nargs, Lisp_Object *arglist) initialize_environment (&pub, &priv); USE_SAFE_ALLOCA; - emacs_value *args; + ATTRIBUTE_MAY_ALIAS emacs_value *args; if (plain_values) args = (emacs_value *) arglist; else commit 366e25a6d1caa30d8d336ce556f90f9ee46ca531 Author: Philipp Stephani Date: Sun Jun 4 19:05:46 2017 +0200 Simplify interface of dynlib_attr. Instead of returning bool, set the argument pointers to NULL if the information is not available. * src/dynlib.c (dynlib_addr): Don't return bool. diff --git a/src/dynlib.c b/src/dynlib.c index 95619236d4..79e98b0f28 100644 --- a/src/dynlib.c +++ b/src/dynlib.c @@ -28,6 +28,8 @@ along with GNU Emacs. If not, see . */ #include "dynlib.h" +#include + #ifdef WINDOWSNT /* MS-Windows systems. */ @@ -120,7 +122,7 @@ dynlib_sym (dynlib_handle_ptr h, const char *sym) return (void *)sym_addr; } -bool +void dynlib_addr (void *addr, const char **fname, const char **symname) { static char dll_filename[MAX_UTF8_PATH]; @@ -128,7 +130,6 @@ dynlib_addr (void *addr, const char **fname, const char **symname) static GetModuleHandleExA_Proc s_pfn_Get_Module_HandleExA = NULL; char *dll_fn = NULL; HMODULE hm_kernel32 = NULL; - bool result = false; HMODULE hm_dll = NULL; wchar_t mfn_w[MAX_PATH]; char mfn_a[MAX_PATH]; @@ -206,23 +207,18 @@ dynlib_addr (void *addr, const char **fname, const char **symname) dynlib_last_err = GetLastError (); } if (dll_fn) - { - dostounix_filename (dll_fn); - /* We cannot easily produce the function name, since - typically all of the module functions will be unexported, - and probably even static, which means the symbols can be - obtained only if we link against libbfd (and the DLL can - be stripped anyway). So we just show the address and the - file name; they can use that with addr2line or GDB to - recover the symbolic name. */ - sprintf (addr_str, "at 0x%x", (DWORD_PTR)addr); - *symname = addr_str; - result = true; - } + dostounix_filename (dll_fn); } *fname = dll_fn; - return result; + + /* We cannot easily produce the function name, since typically all + of the module functions will be unexported, and probably even + static, which means the symbols can be obtained only if we link + against libbfd (and the DLL can be stripped anyway). So we just + show the address and the file name; they can use that with + addr2line or GDB to recover the symbolic name. */ + *symname = NULL; } const char * @@ -283,19 +279,19 @@ dynlib_sym (dynlib_handle_ptr h, const char *sym) return dlsym (h, sym); } -bool +void dynlib_addr (void *ptr, const char **path, const char **sym) { + *path = NULL; + *sym = NULL; #ifdef HAVE_DLADDR Dl_info info; if (dladdr (ptr, &info) && info.dli_fname && info.dli_sname) { *path = info.dli_fname; *sym = info.dli_sname; - return true; } #endif - return false; } const char * diff --git a/src/dynlib.h b/src/dynlib.h index 5ccec11bc7..6246c6a664 100644 --- a/src/dynlib.h +++ b/src/dynlib.h @@ -27,8 +27,11 @@ dynlib_handle_ptr dynlib_open (const char *path); void *dynlib_sym (dynlib_handle_ptr h, const char *sym); typedef struct dynlib_function_ptr_nonce *(*dynlib_function_ptr) (void); dynlib_function_ptr dynlib_func (dynlib_handle_ptr h, const char *sym); -bool dynlib_addr (void *ptr, const char **path, const char **sym); const char *dynlib_error (void); int dynlib_close (dynlib_handle_ptr h); +/* Sets *FILE to the file name from which PTR was loaded, and *SYM to + its symbol name. If the file or symbol name could not be + determined, set the corresponding argument to NULL. */ +void dynlib_addr (void *ptr, const char **file, const char **sym); #endif /* DYNLIB_H */ commit 045d21c20a60e2c336568516d620d6f98ca3642d Author: Philipp Stephani Date: Sun Jun 4 19:02:50 2017 +0200 Rationalize environment lifetime management functions * src/emacs-module.c (Fmodule_load, funcall_module): Adapt callers. (finalize_environment): Add parameter for public part of the environment, like 'initialize_environment'. Add assertions. diff --git a/src/emacs-module.c b/src/emacs-module.c index f9e76b5f0f..0fb126e61f 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -100,8 +100,8 @@ static Lisp_Object value_to_lisp (emacs_value); static emacs_value lisp_to_value (Lisp_Object); static enum emacs_funcall_exit module_non_local_exit_check (emacs_env *); static void check_main_thread (void); -static void finalize_environment (struct emacs_env_private *); -static void initialize_environment (emacs_env *, struct emacs_env_private *priv); +static void initialize_environment (emacs_env *, struct emacs_env_private *); +static void finalize_environment (emacs_env *, struct emacs_env_private *); static void module_handle_signal (emacs_env *, Lisp_Object); static void module_handle_throw (emacs_env *, Lisp_Object); static void module_non_local_exit_signal_1 (emacs_env *, Lisp_Object, Lisp_Object); @@ -632,7 +632,7 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0, .get_environment = module_get_environment }; int r = module_init (&pub); - finalize_environment (&priv); + finalize_environment (&rt.pub, &priv); if (r != 0) { @@ -676,20 +676,20 @@ funcall_module (Lisp_Object function, ptrdiff_t nargs, Lisp_Object *arglist) switch (priv.pending_non_local_exit) { case emacs_funcall_exit_return: - finalize_environment (&priv); + finalize_environment (&pub, &priv); return value_to_lisp (ret); case emacs_funcall_exit_signal: { Lisp_Object symbol = priv.non_local_exit_symbol; Lisp_Object data = priv.non_local_exit_data; - finalize_environment (&priv); + finalize_environment (&pub, &priv); xsignal (symbol, data); } case emacs_funcall_exit_throw: { Lisp_Object tag = priv.non_local_exit_symbol; Lisp_Object value = priv.non_local_exit_data; - finalize_environment (&priv); + finalize_environment (&pub, &priv); Fthrow (tag, value); } default: @@ -904,7 +904,7 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) /* Must be called before the lifetime of the environment object ends. */ static void -finalize_environment (struct emacs_env_private *env) +finalize_environment (emacs_env *env, struct emacs_env_private *priv) { Vmodule_environments = XCDR (Vmodule_environments); } commit 3b0080de52db1756fc47f1642ee9980655421af9 Author: Philipp Stephani Date: Sun Jun 4 18:57:51 2017 +0200 Rework printing of module functions Fix a FIXME in emacs-module.c. Put the printing into print.c, like other types. * src/print.c (print_vectorlike): Add code to print module functions. * src/emacs-module.c (funcall_module): Stop calling 'module_format_fun_env'. Now that module functions are first-class objects, they can be added to signal data directly. (module_handle_signal): Remove now-unused function 'module_format_fun_env'. * test/src/emacs-module-tests.el (mod-test-sum-test): Adapt unit test. * src/eval.c (funcall_lambda): Adapt call to changed signature of 'funcall_module'. diff --git a/src/emacs-module.c b/src/emacs-module.c index f2eaa71de3..f9e76b5f0f 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -645,14 +645,13 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0, } Lisp_Object -funcall_module (const struct Lisp_Module_Function *const function, - ptrdiff_t nargs, Lisp_Object *arglist) +funcall_module (Lisp_Object function, ptrdiff_t nargs, Lisp_Object *arglist) { - eassume (0 <= function->min_arity); - if (! (function->min_arity <= nargs - && (function->max_arity < 0 || nargs <= function->max_arity))) - xsignal2 (Qwrong_number_of_arguments, module_format_fun_env (function), - make_number (nargs)); + const struct Lisp_Module_Function *func = XMODULE_FUNCTION (function); + eassume (0 <= func->min_arity); + if (! (func->min_arity <= nargs + && (func->max_arity < 0 || nargs <= func->max_arity))) + xsignal2 (Qwrong_number_of_arguments, function, make_natnum (nargs)); emacs_env pub; struct emacs_env_private priv; @@ -669,7 +668,7 @@ funcall_module (const struct Lisp_Module_Function *const function, args[i] = lisp_to_value (arglist[i]); } - emacs_value ret = function->subr (&pub, nargs, args, function->data); + emacs_value ret = func->subr (&pub, nargs, args, func->data); SAFE_FREE (); eassert (&priv == pub.private_members); @@ -942,35 +941,6 @@ module_handle_throw (emacs_env *env, Lisp_Object tag_val) } -/* Function environments. */ - -/* Return a string object that contains a user-friendly - representation of the function environment. */ -Lisp_Object -module_format_fun_env (const struct Lisp_Module_Function *env) -{ - /* Try to print a function name if possible. */ - /* FIXME: Move this function into print.c, then use prin1-to-string - above. */ - const char *path, *sym; - static char const noaddr_format[] = "#"; - char buffer[sizeof noaddr_format + INT_STRLEN_BOUND (intptr_t) + 256]; - char *buf = buffer; - ptrdiff_t bufsize = sizeof buffer; - ptrdiff_t size - = (dynlib_addr (env->subr, &path, &sym) - ? exprintf (&buf, &bufsize, buffer, -1, - "#", sym, path) - : sprintf (buffer, noaddr_format, env->subr)); - AUTO_STRING_WITH_LEN (unibyte_result, buffer, size); - Lisp_Object result = code_convert_string_norecord (unibyte_result, - Qutf_8, false); - if (buf != buffer) - xfree (buf); - return result; -} - - /* Segment initializer. */ void diff --git a/src/eval.c b/src/eval.c index f472efad52..8aa33a1128 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2952,7 +2952,7 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs, } #ifdef HAVE_MODULES else if (MODULE_FUNCTIONP (fun)) - return funcall_module (XMODULE_FUNCTION (fun), nargs, arg_vector); + return funcall_module (fun, nargs, arg_vector); #endif else emacs_abort (); diff --git a/src/lisp.h b/src/lisp.h index 7b8f1e754d..ce939fcee6 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3952,10 +3952,8 @@ XMODULE_FUNCTION (Lisp_Object o) extern Lisp_Object make_user_ptr (void (*finalizer) (void *), void *p); /* Defined in emacs-module.c. */ -extern Lisp_Object funcall_module (const struct Lisp_Module_Function *, - ptrdiff_t, Lisp_Object *); +extern Lisp_Object funcall_module (Lisp_Object, ptrdiff_t, Lisp_Object *); extern Lisp_Object module_function_arity (const struct Lisp_Module_Function *); -extern Lisp_Object module_format_fun_env (const struct Lisp_Module_Function *); extern void syms_of_module (void); #endif diff --git a/src/print.c b/src/print.c index 49408bbeb4..e89f3d8072 100644 --- a/src/print.c +++ b/src/print.c @@ -33,6 +33,7 @@ along with GNU Emacs. If not, see . */ #include "intervals.h" #include "blockinput.h" #include "xwidget.h" +#include "dynlib.h" #include #include @@ -1699,8 +1700,33 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag, #ifdef HAVE_MODULES case PVEC_MODULE_FUNCTION: - print_string (module_format_fun_env (XMODULE_FUNCTION (obj)), - printcharfun); + { + print_c_string ("#subr; + const char *file = NULL; + const char *symbol = NULL; + dynlib_addr (ptr, &file, &symbol); + + if (symbol == NULL) + { + print_c_string (" at ", printcharfun); + enum { pointer_bufsize = sizeof ptr * 16 / CHAR_BIT + 2 + 1 }; + char buffer[pointer_bufsize]; + int needed = snprintf (buffer, sizeof buffer, "%p", ptr); + eassert (needed <= sizeof buffer); + print_c_string (buffer, printcharfun); + } + else + print_c_string (symbol, printcharfun); + + if (file != NULL) + { + print_c_string (" from ", printcharfun); + print_c_string (file, printcharfun); + } + + printchar ('>', printcharfun); + } break; #endif diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index 5e78aebf7c..622bbadb3e 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -31,13 +31,13 @@ (should (= (mod-test-sum 1 2) 3)) (let ((descr (should-error (mod-test-sum 1 2 3)))) (should (eq (car descr) 'wrong-number-of-arguments)) - (should (stringp (nth 1 descr))) + (should (module-function-p (nth 1 descr))) (should (eq 0 (string-match (concat "#") - (nth 1 descr)))) + (prin1-to-string (nth 1 descr))))) (should (= (nth 2 descr) 3))) (should-error (mod-test-sum "1" 2) :type 'wrong-type-argument) (should-error (mod-test-sum 1 "2") :type 'wrong-type-argument) commit 18396997b30c053a905c9a509777625ccc01c3d5 Author: Philipp Stephani Date: Sun Jun 4 18:50:42 2017 +0200 Define helper macro to reduce code duplication * src/emacs-module.c (MODULE_FUNCTION_BEGIN_NO_CATCH): New helper macro. (MODULE_FUNCTION_BEGIN, module_type_of, module_is_not_nil, module_eq): Use it. diff --git a/src/emacs-module.c b/src/emacs-module.c index d3c4cac59f..f2eaa71de3 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -211,14 +211,25 @@ static emacs_value const module_nil = 0; instead of reporting the error back to Lisp, and also because 'eassert' is compiled to nothing in the release version. */ +/* Use MODULE_FUNCTION_BEGIN_NO_CATCH to implement steps 2 and 3 for + environment functions that are known to never exit non-locally. On + error it will return its argument, which can be a sentinel + value. */ + +#define MODULE_FUNCTION_BEGIN_NO_CATCH(error_retval) \ + do { \ + eassert (env != NULL); \ + check_main_thread (); \ + if (module_non_local_exit_check (env) != emacs_funcall_exit_return) \ + return error_retval; \ + } while (false) + /* Use MODULE_FUNCTION_BEGIN to implement steps 2 through 4 for most environment functions. On error it will return its argument, which should be a sentinel value. */ -#define MODULE_FUNCTION_BEGIN(error_retval) \ - check_main_thread (); \ - if (module_non_local_exit_check (env) != emacs_funcall_exit_return) \ - return error_retval; \ +#define MODULE_FUNCTION_BEGIN(error_retval) \ + MODULE_FUNCTION_BEGIN_NO_CATCH (error_retval); \ MODULE_HANDLE_NONLOCAL_EXIT (error_retval) static void @@ -416,18 +427,14 @@ module_type_of (emacs_env *env, emacs_value value) static bool module_is_not_nil (emacs_env *env, emacs_value value) { - check_main_thread (); - if (module_non_local_exit_check (env) != emacs_funcall_exit_return) - return false; + MODULE_FUNCTION_BEGIN_NO_CATCH (false); return ! NILP (value_to_lisp (value)); } static bool module_eq (emacs_env *env, emacs_value a, emacs_value b) { - check_main_thread (); - if (module_non_local_exit_check (env) != emacs_funcall_exit_return) - return false; + MODULE_FUNCTION_BEGIN_NO_CATCH (false); return EQ (value_to_lisp (a), value_to_lisp (b)); } commit db7438426ae4fd756213d56884dd52473d8f9336 Author: Philipp Stephani Date: Sun Jun 4 18:46:23 2017 +0200 Remove two FIXMEs that can't be fixed diff --git a/src/data.c b/src/data.c index 25859105ee..e4e55290e6 100644 --- a/src/data.c +++ b/src/data.c @@ -700,12 +700,10 @@ global value outside of any lexical scope. */) return (EQ (valcontents, Qunbound) ? Qnil : Qt); } -/* FIXME: It has been previously suggested to make this function an - alias for symbol-function, but upon discussion at Bug#23957, - there is a risk breaking backward compatibility, as some users of - fboundp may expect `t' in particular, rather than any true - value. An alias is still welcome so long as the compatibility - issues are addressed. */ +/* It has been previously suggested to make this function an alias for + symbol-function, but upon discussion at Bug#23957, there is a risk + breaking backward compatibility, as some users of fboundp may + expect `t' in particular, rather than any true value. */ DEFUN ("fboundp", Ffboundp, Sfboundp, 1, 1, 0, doc: /* Return t if SYMBOL's function definition is not void. */) (register Lisp_Object symbol) diff --git a/src/emacs-module.c b/src/emacs-module.c index 33c5fbd484..d3c4cac59f 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -88,8 +88,6 @@ struct emacs_env_private environment. */ struct emacs_runtime_private { - /* FIXME: Ideally, we would just define "struct emacs_runtime_private" - as a synonym of "emacs_env", but I don't know how to do that in C. */ emacs_env pub; }; commit bd3c6eeca1c69f95ab0c5eea5b542d0334abde68 Author: Eli Zaretskii Date: Sun Jun 4 19:27:13 2017 +0300 Avoid slow startup in daemon mode when global-linum-mode is on * lisp/linum.el (linum-on): Don't turn on linum-mode in a non-client frame of a daemon session. (Bug#27210) diff --git a/lisp/linum.el b/lisp/linum.el index 8baa263f0b..9cfb94dab6 100644 --- a/lisp/linum.el +++ b/lisp/linum.el @@ -112,7 +112,16 @@ Linum mode is a buffer-local minor mode." (define-globalized-minor-mode global-linum-mode linum-mode linum-on) (defun linum-on () - (unless (minibufferp) + (unless (or (minibufferp) + ;; Turning linum-mode in the daemon's initial frame + ;; could significantly slow down startup, if the buffer + ;; in which this is done is large, because Emacs thinks + ;; the "window" spans the entire buffer then. This + ;; could happen when restoring session via desktop.el, + ;; if some large buffer was under linum-mode when + ;; desktop was saved. So we disable linum-mode for + ;; non-client frames in a daemon session. + (and (daemonp) (null (frame-parameter nil 'client)))) (linum-mode 1))) (defun linum-delete-overlays () commit 7dd9e7e95c1e4502b7a9fd6a18211208bd2914a5 Author: Paul Eggert Date: Sun Jun 4 09:13:15 2017 -0700 Fix eldoc bug with curved quote * lisp/progmodes/elisp-mode.el (elisp-get-fnsym-args-string): Substitute quotes in documentation before returning it (Bug#27159). diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 6c6fb92504..b3f452ca5b 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -1372,7 +1372,7 @@ or elsewhere, return a 1-line docstring." (condition-case nil (documentation sym t) (invalid-function nil)) sym)) - (car doc)) + (substitute-command-keys (car doc))) (t (help-function-arglist sym))))) ;; Stringify, and store before highlighting, downcasing, etc. (elisp--last-data-store sym (elisp-function-argstring args) commit d5fcf9e458053af1c50f0d4dad4c59db056790e5 Author: Paul Eggert Date: Sun Jun 4 08:39:37 2017 -0700 Tune ‘format’ after recent fix * doc/lispref/strings.texi (Formatting Strings): * src/editfns.c (Fformat): Format field numbers no longer need to be unique, reverting the previous doc change since that has now been fixed. Also, document that %% should not have modifiers. * src/editfns.c (styled_format): Improve performance. Remove the need for the new prepass over the format string, by using a typically-more-generous bound for the info array size. Initialize the info array lazily. Move string inspection to the same area to help caching. Avoid the need for a converted_to_string bitfield by using EQ. Cache arg in a local and avoid some potential aliasing issues to help the compiler. Info array is now 0-origin, not 1-origin. diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index f365c80493..23961f99ef 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -926,7 +926,8 @@ digit. @item %% Replace the specification with a single @samp{%}. This format -specification is unusual in that it does not use a value. For example, +specification is unusual in that its only form is plain +@samp{%%} and that it does not use a value. For example, @code{(format "%% %d" 30)} returns @code{"% 30"}. @end table @@ -965,10 +966,9 @@ extra values to be formatted are ignored. decimal number immediately after the initial @samp{%}, followed by a literal dollar sign @samp{$}. It causes the format specification to convert the argument with the given number instead of the next -argument. Field numbers start at 1. A field number should differ -from the other field numbers in the same format. A format can contain -either numbered or unnumbered format specifications but not both, -except that @samp{%%} can be mixed with numbered specifications. +argument. Field numbers start at 1. A format can contain either +numbered or unnumbered format specifications but not both, except that +@samp{%%} can be mixed with numbered specifications. @example (format "%2$s, %3$s, %%, %1$s" "x" "y" "z") @@ -1026,8 +1026,7 @@ ignored. A specification can have a @dfn{width}, which is a decimal number that appears after any field number and flags. If the printed representation of the object contains fewer characters than this -width, @code{format} extends it with padding. The width is -ignored for the @samp{%%} specification. Any padding introduced by +width, @code{format} extends it with padding. Any padding introduced by the width normally consists of spaces inserted on the left: @example diff --git a/src/editfns.c b/src/editfns.c index 56aa8ce1a7..43b17f9f11 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -3891,8 +3891,8 @@ the next available argument, or the argument explicitly specified: The argument used for %d, %o, %x, %e, %f, %g or %c must be a number. Use %% to put a single % into the output. -A %-sequence may contain optional field number, flag, width, and -precision specifiers, as follows: +A %-sequence other than %% may contain optional field number, flag, +width, and precision specifiers, as follows: %character @@ -3901,10 +3901,9 @@ where field is [0-9]+ followed by a literal dollar "$", flags is followed by [0-9]+. If a %-sequence is numbered with a field with positive value N, the -Nth argument is substituted instead of the next one. A field number -should differ from the other field numbers in the same format. A -format can contain either numbered or unnumbered %-sequences but not -both, except that %% can be mixed with numbered %-sequences. +Nth argument is substituted instead of the next one. A format can +contain either numbered or unnumbered %-sequences but not both, except +that %% can be mixed with numbered %-sequences. The + flag character inserts a + before any positive number, while a space inserts a space before any positive number; these flags only @@ -3980,49 +3979,40 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) bool arg_intervals = false; USE_SAFE_ALLOCA; - /* Each element records, for one field, - the corresponding argument, - the start and end bytepos in the output string, - whether the argument has been converted to string (e.g., due to "%S"), - and whether the argument is a string with intervals. */ + /* Information recorded for each format spec. */ struct info { + /* The corresponding argument, converted to string if conversion + was needed. */ Lisp_Object argument; + + /* The start and end bytepos in the output string. */ ptrdiff_t start, end; - bool_bf converted_to_string : 1; + + /* Whether the argument is a string with intervals. */ bool_bf intervals : 1; } *info; CHECK_STRING (args[0]); char *format_start = SSDATA (args[0]); + bool multibyte_format = STRING_MULTIBYTE (args[0]); ptrdiff_t formatlen = SBYTES (args[0]); - /* The number of percent characters is a safe upper bound for the - number of format fields. */ - ptrdiff_t num_percent = 0; - for (ptrdiff_t i = 0; i < formatlen; ++i) - if (format_start[i] == '%') - ++num_percent; + /* Upper bound on number of format specs. Each uses at least 2 chars. */ + ptrdiff_t nspec_bound = SCHARS (args[0]) >> 1; /* Allocate the info and discarded tables. */ ptrdiff_t alloca_size; - if (INT_MULTIPLY_WRAPV (num_percent, sizeof *info, &alloca_size) - || INT_ADD_WRAPV (sizeof *info, alloca_size, &alloca_size) + if (INT_MULTIPLY_WRAPV (nspec_bound, sizeof *info, &alloca_size) || INT_ADD_WRAPV (formatlen, alloca_size, &alloca_size) || SIZE_MAX < alloca_size) memory_full (SIZE_MAX); - /* info[0] is unused. Unused elements have -1 for start. */ info = SAFE_ALLOCA (alloca_size); - memset (info, 0, alloca_size); - for (ptrdiff_t i = 0; i < num_percent + 1; i++) - { - info[i].argument = Qunbound; - info[i].start = -1; - } /* discarded[I] is 1 if byte I of the format string was not copied into the output. It is 2 if byte I was not the first byte of its character. */ - char *discarded = (char *) &info[num_percent + 1]; + char *discarded = (char *) &info[nspec_bound]; + memset (discarded, 0, formatlen); /* Try to determine whether the result should be multibyte. This is not always right; sometimes the result needs to be multibyte @@ -4030,8 +4020,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) or because a grave accent or apostrophe is requoted, and in that case, we won't know it here. */ - /* True if the format is multibyte. */ - bool multibyte_format = STRING_MULTIBYTE (args[0]); /* True if the output should be a multibyte string, which is true if any of the inputs is one. */ bool multibyte = multibyte_format; @@ -4042,6 +4030,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) int quoting_style = message ? text_quoting_style () : -1; ptrdiff_t ispec; + ptrdiff_t nspec = 0; /* If we start out planning a unibyte result, then discover it has to be multibyte, we jump back to retry. */ @@ -4155,11 +4144,14 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (! (n < nargs)) error ("Not enough arguments for format string"); - eassert (ispec < num_percent); - ++ispec; - - if (EQ (info[ispec].argument, Qunbound)) - info[ispec].argument = args[n]; + struct info *spec = &info[ispec++]; + if (nspec < ispec) + { + spec->argument = args[n]; + spec->intervals = false; + nspec = ispec; + } + Lisp_Object arg = spec->argument; /* For 'S', prin1 the argument, and then treat like 's'. For 's', princ any argument that is not a string or @@ -4167,16 +4159,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) happen after retrying. */ if ((conversion == 'S' || (conversion == 's' - && ! STRINGP (info[ispec].argument) - && ! SYMBOLP (info[ispec].argument)))) + && ! STRINGP (arg) && ! SYMBOLP (arg)))) { - if (! info[ispec].converted_to_string) + if (EQ (arg, args[n])) { Lisp_Object noescape = conversion == 'S' ? Qnil : Qt; - info[ispec].argument = - Fprin1_to_string (info[ispec].argument, noescape); - info[ispec].converted_to_string = true; - if (STRING_MULTIBYTE (info[ispec].argument) && ! multibyte) + spec->argument = arg = Fprin1_to_string (arg, noescape); + if (STRING_MULTIBYTE (arg) && ! multibyte) { multibyte = true; goto retry; @@ -4186,29 +4175,25 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } else if (conversion == 'c') { - if (INTEGERP (info[ispec].argument) - && ! ASCII_CHAR_P (XINT (info[ispec].argument))) + if (INTEGERP (arg) && ! ASCII_CHAR_P (XINT (arg))) { if (!multibyte) { multibyte = true; goto retry; } - info[ispec].argument = - Fchar_to_string (info[ispec].argument); - info[ispec].converted_to_string = true; + spec->argument = arg = Fchar_to_string (arg); } - if (info[ispec].converted_to_string) + if (!EQ (arg, args[n])) conversion = 's'; zero_flag = false; } - if (SYMBOLP (info[ispec].argument)) + if (SYMBOLP (arg)) { - info[ispec].argument = - SYMBOL_NAME (info[ispec].argument); - if (STRING_MULTIBYTE (info[ispec].argument) && ! multibyte) + spec->argument = arg = SYMBOL_NAME (arg); + if (STRING_MULTIBYTE (arg) && ! multibyte) { multibyte = true; goto retry; @@ -4239,12 +4224,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) else { ptrdiff_t nch, nby; - width = lisp_string_width (info[ispec].argument, - prec, &nch, &nby); + width = lisp_string_width (arg, prec, &nch, &nby); if (prec < 0) { - nchars_string = SCHARS (info[ispec].argument); - nbytes = SBYTES (info[ispec].argument); + nchars_string = SCHARS (arg); + nbytes = SBYTES (arg); } else { @@ -4254,11 +4238,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } convbytes = nbytes; - if (convbytes && multibyte && - ! STRING_MULTIBYTE (info[ispec].argument)) - convbytes = - count_size_as_multibyte (SDATA (info[ispec].argument), - nbytes); + if (convbytes && multibyte && ! STRING_MULTIBYTE (arg)) + convbytes = count_size_as_multibyte (SDATA (arg), nbytes); ptrdiff_t padding = width < field_width ? field_width - width : 0; @@ -4274,20 +4255,18 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) p += padding; nchars += padding; } - info[ispec].start = nchars; + spec->start = nchars; if (p > buf && multibyte && !ASCII_CHAR_P (*((unsigned char *) p - 1)) - && STRING_MULTIBYTE (info[ispec].argument) - && !CHAR_HEAD_P (SREF (info[ispec].argument, 0))) + && STRING_MULTIBYTE (arg) + && !CHAR_HEAD_P (SREF (arg, 0))) maybe_combine_byte = true; - p += copy_text (SDATA (info[ispec].argument), - (unsigned char *) p, + p += copy_text (SDATA (arg), (unsigned char *) p, nbytes, - STRING_MULTIBYTE (info[ispec].argument), - multibyte); + STRING_MULTIBYTE (arg), multibyte); nchars += nchars_string; @@ -4297,12 +4276,12 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) p += padding; nchars += padding; } - info[ispec].end = nchars; + spec->end = nchars; /* If this argument has text properties, record where in the result string it appears. */ - if (string_intervals (info[ispec].argument)) - info[ispec].intervals = arg_intervals = true; + if (string_intervals (arg)) + spec->intervals = arg_intervals = true; continue; } @@ -4313,8 +4292,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) || conversion == 'X')) error ("Invalid format operation %%%c", STRING_CHAR ((unsigned char *) format - 1)); - else if (! (INTEGERP (info[ispec].argument) - || (FLOATP (info[ispec].argument) && conversion != 'c'))) + else if (! (INTEGERP (arg) || (FLOATP (arg) && conversion != 'c'))) error ("Format specifier doesn't match argument type"); else { @@ -4376,7 +4354,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (INT_AS_LDBL) { *f = 'L'; - f += INTEGERP (info[ispec].argument); + f += INTEGERP (arg); } } else if (conversion != 'c') @@ -4408,22 +4386,22 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) ptrdiff_t sprintf_bytes; if (float_conversion) { - if (INT_AS_LDBL && INTEGERP (info[ispec].argument)) + if (INT_AS_LDBL && INTEGERP (arg)) { /* Although long double may have a rounding error if DIG_BITS_LBOUND * LDBL_MANT_DIG < FIXNUM_BITS - 1, it is more accurate than plain 'double'. */ - long double x = XINT (info[ispec].argument); + long double x = XINT (arg); sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x); } else sprintf_bytes = sprintf (sprintf_buf, convspec, prec, - XFLOATINT (info[ispec].argument)); + XFLOATINT (arg)); } else if (conversion == 'c') { /* Don't use sprintf here, as it might mishandle prec. */ - sprintf_buf[0] = XINT (info[ispec].argument); + sprintf_buf[0] = XINT (arg); sprintf_bytes = prec != 0; } else if (conversion == 'd' || conversion == 'i') @@ -4432,11 +4410,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) instead so it also works for values outside the integer range. */ printmax_t x; - if (INTEGERP (info[ispec].argument)) - x = XINT (info[ispec].argument); + if (INTEGERP (arg)) + x = XINT (arg); else { - double d = XFLOAT_DATA (info[ispec].argument); + double d = XFLOAT_DATA (arg); if (d < 0) { x = TYPE_MINIMUM (printmax_t); @@ -4456,11 +4434,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) { /* Don't sign-extend for octal or hex printing. */ uprintmax_t x; - if (INTEGERP (info[ispec].argument)) - x = XUINT (info[ispec].argument); + if (INTEGERP (arg)) + x = XUINT (arg); else { - double d = XFLOAT_DATA (info[ispec].argument); + double d = XFLOAT_DATA (arg); if (d < 0) x = 0; else @@ -4541,7 +4519,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) exponent_bytes = src + sprintf_bytes - e; } - info[ispec].start = nchars; + spec->start = nchars; if (! minus_flag) { memset (p, ' ', padding); @@ -4572,7 +4550,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) p += padding; nchars += padding; } - info[ispec].end = nchars; + spec->end = nchars; continue; } @@ -4681,7 +4659,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) if (CONSP (props)) { ptrdiff_t bytepos = 0, position = 0, translated = 0; - ptrdiff_t fieldn = 1; + ptrdiff_t fieldn = 0; /* Adjust the bounds of each text property to the proper start and end in the output string. */ @@ -4747,7 +4725,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) /* Add text properties from arguments. */ if (arg_intervals) - for (ptrdiff_t i = 1; i <= num_percent; i++) + for (ptrdiff_t i = 0; i < nspec; i++) if (info[i].intervals) { len = make_number (SCHARS (info[i].argument));