diff options
| author | Philipp Stephani | 2017-06-05 13:29:14 +0200 |
|---|---|---|
| committer | Philipp Stephani | 2017-06-12 15:22:27 +0200 |
| commit | cf97132764572928adc77fd555d04a9f41cd3cfc (patch) | |
| tree | 65f707de87b811caec7b08f96938059859cf001c /test | |
| parent | b49dd3b047bf07994d9577e08daba55f143a8bb1 (diff) | |
| download | emacs-cf97132764572928adc77fd555d04a9f41cd3cfc.tar.gz emacs-cf97132764572928adc77fd555d04a9f41cd3cfc.zip | |
Implement module assertions for users
Add a new command-line option '-module-assertions' that users can
enable developing or debugging a module. If this option is present,
Emacs performs additional checks to verify that modules fulfill their
requirements. These checks are expensive and crash Emacs if modules
are invalid, so disable them by default.
This is a command-line option instead of an ordinary variable because
changing it while Emacs is running would cause data structure
imbalances.
* src/emacs.c (main): New command line option '-module-assertions'.
* src/emacs-module.c (module_assert_main_thread)
(module_assert_runtime, module_assert_env, module_assert_value):
New functions to assert module requirements.
(syms_of_module): New uninterned variable 'module-runtimes'.
(init_module_assertions, in_main_thread, module_abort): New helper
functions.
(initialize_environment): Initialize value list. If assertions are
enabled, use a heap-allocated environment object.
(finalize_environment): Add assertion that environment list is never
empty.
(finalize_runtime_unwind): Pop module runtime object stack.
(value_to_lisp): Assert that the value is valid.
(lisp_to_value): Record new value if assertions are enabled.
(mark_modules): Mark allocated object list.
(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): Assert thread and environment.
(module_get_environment): Assert thread and runtime.
(module_make_function, module_funcall, module_intern)
(module_funcall, module_make_integer, module_make_float)
(module_make_string, module_make_user_ptr, module_vec_get)
(funcall_module, Fmodule_load): Adapt callers.
(module_make_global_ref): If assertions are enabled, use the global
environment to store global values.
(module_free_global_ref): Remove value from global value list.
* test/Makefile.in (EMACSOPT): Enable module assertions when testing
modules.
* test/data/emacs-module/mod-test.c (Fmod_test_invalid_store)
(Fmod_test_invalid_load): New functions to test module assertions.
(emacs_module_init): Bind the new functions.
* test/src/emacs-module-tests.el (mod-test-emacs): New constant for
the Emacs binary file.
(mod-test-file): New constant for the test module file name.
(module--test-assertions): New unit test.
Diffstat (limited to 'test')
| -rw-r--r-- | test/Makefile.in | 2 | ||||
| -rw-r--r-- | test/data/emacs-module/mod-test.c | 24 | ||||
| -rw-r--r-- | test/src/emacs-module-tests.el | 40 |
3 files changed, 62 insertions, 4 deletions
diff --git a/test/Makefile.in b/test/Makefile.in index 7b8c967128f..0c24c48e60e 100644 --- a/test/Makefile.in +++ b/test/Makefile.in | |||
| @@ -68,7 +68,7 @@ EMACS_EXTRAOPT= | |||
| 68 | # Command line flags for Emacs. | 68 | # Command line flags for Emacs. |
| 69 | # Apparently MSYS bash would convert "-L :" to "-L ;" anyway, | 69 | # Apparently MSYS bash would convert "-L :" to "-L ;" anyway, |
| 70 | # but we might as well be explicit. | 70 | # but we might as well be explicit. |
| 71 | EMACSOPT = -batch --no-site-file --no-site-lisp -L "$(SEPCHAR)$(srcdir)" $(EMACS_EXTRAOPT) | 71 | EMACSOPT = -batch --no-site-file --no-site-lisp -module-assertions -L "$(SEPCHAR)$(srcdir)" $(EMACS_EXTRAOPT) |
| 72 | 72 | ||
| 73 | # Prevent any settings in the user environment causing problems. | 73 | # Prevent any settings in the user environment causing problems. |
| 74 | unexport EMACSDATA EMACSDOC EMACSPATH GREP_OPTIONS | 74 | unexport EMACSDATA EMACSDOC EMACSPATH GREP_OPTIONS |
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index 309179d1501..fc29a0d6b9a 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c | |||
| @@ -213,6 +213,28 @@ Fmod_test_vector_eq (emacs_env *env, ptrdiff_t nargs, emacs_value args[], | |||
| 213 | return env->intern (env, "t"); | 213 | return env->intern (env, "t"); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | static emacs_value invalid_stored_value; | ||
| 217 | |||
| 218 | /* The next two functions perform a possibly-invalid operation: they | ||
| 219 | store a value in a static variable and load it. This causes | ||
| 220 | undefined behavior if the environment that the value was created | ||
| 221 | from is no longer live. The module assertions check for this | ||
| 222 | error. */ | ||
| 223 | |||
| 224 | static emacs_value | ||
| 225 | Fmod_test_invalid_store (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | ||
| 226 | void *data) | ||
| 227 | { | ||
| 228 | return invalid_stored_value = env->make_integer (env, 123); | ||
| 229 | } | ||
| 230 | |||
| 231 | static emacs_value | ||
| 232 | Fmod_test_invalid_load (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | ||
| 233 | void *data) | ||
| 234 | { | ||
| 235 | return invalid_stored_value; | ||
| 236 | } | ||
| 237 | |||
| 216 | 238 | ||
| 217 | /* Lisp utilities for easier readability (simple wrappers). */ | 239 | /* Lisp utilities for easier readability (simple wrappers). */ |
| 218 | 240 | ||
| @@ -260,6 +282,8 @@ emacs_module_init (struct emacs_runtime *ert) | |||
| 260 | DEFUN ("mod-test-userptr-get", Fmod_test_userptr_get, 1, 1, NULL, NULL); | 282 | DEFUN ("mod-test-userptr-get", Fmod_test_userptr_get, 1, 1, NULL, NULL); |
| 261 | DEFUN ("mod-test-vector-fill", Fmod_test_vector_fill, 2, 2, NULL, NULL); | 283 | DEFUN ("mod-test-vector-fill", Fmod_test_vector_fill, 2, 2, NULL, NULL); |
| 262 | DEFUN ("mod-test-vector-eq", Fmod_test_vector_eq, 2, 2, NULL, NULL); | 284 | DEFUN ("mod-test-vector-eq", Fmod_test_vector_eq, 2, 2, NULL, NULL); |
| 285 | DEFUN ("mod-test-invalid-store", Fmod_test_invalid_store, 0, 0, NULL, NULL); | ||
| 286 | DEFUN ("mod-test-invalid-load", Fmod_test_invalid_load, 0, 0, NULL, NULL); | ||
| 263 | 287 | ||
| 264 | #undef DEFUN | 288 | #undef DEFUN |
| 265 | 289 | ||
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index 622bbadb3ef..99a853b17e0 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el | |||
| @@ -19,9 +19,17 @@ | |||
| 19 | 19 | ||
| 20 | (require 'ert) | 20 | (require 'ert) |
| 21 | 21 | ||
| 22 | (require 'mod-test | 22 | (defconst mod-test-emacs |
| 23 | (expand-file-name "data/emacs-module/mod-test" | 23 | (expand-file-name invocation-name invocation-directory) |
| 24 | (getenv "EMACS_TEST_DIRECTORY"))) | 24 | "File name of the Emacs binary currently running.") |
| 25 | |||
| 26 | (eval-and-compile | ||
| 27 | (defconst mod-test-file | ||
| 28 | (substitute-in-file-name | ||
| 29 | "$EMACS_TEST_DIRECTORY/data/emacs-module/mod-test") | ||
| 30 | "File name of the module test file.")) | ||
| 31 | |||
| 32 | (require 'mod-test mod-test-file) | ||
| 25 | 33 | ||
| 26 | ;; | 34 | ;; |
| 27 | ;; Basic tests. | 35 | ;; Basic tests. |
| @@ -174,4 +182,30 @@ changes." | |||
| 174 | (should (equal (help-function-arglist #'mod-test-sum) | 182 | (should (equal (help-function-arglist #'mod-test-sum) |
| 175 | '(arg1 arg2)))) | 183 | '(arg1 arg2)))) |
| 176 | 184 | ||
| 185 | (ert-deftest module--test-assertions () | ||
| 186 | "Check that -module-assertions work." | ||
| 187 | (skip-unless (file-executable-p mod-test-emacs)) | ||
| 188 | ;; This doesn’t yet cause undefined behavior. | ||
| 189 | (should (eq (mod-test-invalid-store) 123)) | ||
| 190 | (with-temp-buffer | ||
| 191 | (should (equal (call-process mod-test-emacs nil t nil | ||
| 192 | "-batch" "-Q" "-module-assertions" "-eval" | ||
| 193 | (prin1-to-string | ||
| 194 | `(progn | ||
| 195 | (require 'mod-test ,mod-test-file) | ||
| 196 | ;; Storing and reloading a local | ||
| 197 | ;; value causes undefined | ||
| 198 | ;; behavior, which should be | ||
| 199 | ;; detected by the module | ||
| 200 | ;; assertions. | ||
| 201 | (mod-test-invalid-store) | ||
| 202 | (mod-test-invalid-load)))) | ||
| 203 | ;; FIXME: This string is probably different on | ||
| 204 | ;; Windows and Linux. | ||
| 205 | "Abort trap: 6")) | ||
| 206 | (re-search-backward (rx bos "Emacs module assertion: " | ||
| 207 | "Emacs value not found in " | ||
| 208 | (+ digit) " values of " | ||
| 209 | (+ digit) " environments" ?\n eos)))) | ||
| 210 | |||
| 177 | ;;; emacs-module-tests.el ends here | 211 | ;;; emacs-module-tests.el ends here |